feat(tutorials): delegate control to tutors
This commit is contained in:
parent
31524132ca
commit
261f3ed92f
@ -412,6 +412,7 @@ UnknownAuthPredicate tag@String: Authorisierungsprädikat "#{tag}" ist dem Syste
|
|||||||
UnauthorizedRedirect: Die angeforderte Seite existiert nicht oder Sie haben keine Berechtigung, die angeforderte Seite zu sehen.
|
UnauthorizedRedirect: Die angeforderte Seite existiert nicht oder Sie haben keine Berechtigung, die angeforderte Seite zu sehen.
|
||||||
UnauthorizedSelf: Aktueller Nutzer ist nicht angegebener Benutzer.
|
UnauthorizedSelf: Aktueller Nutzer ist nicht angegebener Benutzer.
|
||||||
UnauthorizedTutorialTutor: Sie sind nicht Tutor für dieses Tutorium.
|
UnauthorizedTutorialTutor: Sie sind nicht Tutor für dieses Tutorium.
|
||||||
|
UnauthorizedTutorialTutorControl: Tutoren dürfen dieses Tutorium nicht editieren.
|
||||||
UnauthorizedCourseTutor: Sie sind nicht Tutor für diesen Kurs.
|
UnauthorizedCourseTutor: Sie sind nicht Tutor für diesen Kurs.
|
||||||
UnauthorizedTutor: Sie sind nicht Tutor.
|
UnauthorizedTutor: Sie sind nicht Tutor.
|
||||||
UnauthorizedTutorialRegisterGroup: Sie sind bereits in einem Tutorium mit derselben Registrierungs-Gruppe.
|
UnauthorizedTutorialRegisterGroup: Sie sind bereits in einem Tutorium mit derselben Registrierungs-Gruppe.
|
||||||
@ -1126,6 +1127,7 @@ AuthTagDevelopment: Seite ist nicht in Entwicklung
|
|||||||
AuthTagLecturer: Nutzer ist Dozent
|
AuthTagLecturer: Nutzer ist Dozent
|
||||||
AuthTagCorrector: Nutzer ist Korrektor
|
AuthTagCorrector: Nutzer ist Korrektor
|
||||||
AuthTagTutor: Nutzer ist Tutor
|
AuthTagTutor: Nutzer ist Tutor
|
||||||
|
AuthTagTutorControl: Tutoren haben Kontrolle über ihre Tutorium
|
||||||
AuthTagTime: Zeitliche Einschränkungen sind erfüllt
|
AuthTagTime: Zeitliche Einschränkungen sind erfüllt
|
||||||
AuthTagStaffTime: Zeitliche Einschränkungen für Lehrbeteiligte sind erfüllt
|
AuthTagStaffTime: Zeitliche Einschränkungen für Lehrbeteiligte sind erfüllt
|
||||||
AuthTagAllocationTime: Zeitliche Einschränkungen durch Zentralanmeldung sind erfüllt
|
AuthTagAllocationTime: Zeitliche Einschränkungen durch Zentralanmeldung sind erfüllt
|
||||||
@ -1276,6 +1278,8 @@ TutorialDeregisterUntil: Abmeldungen bis
|
|||||||
TutorialsHeading: Tutorien
|
TutorialsHeading: Tutorien
|
||||||
TutorialEdit: Bearbeiten
|
TutorialEdit: Bearbeiten
|
||||||
TutorialDelete: Löschen
|
TutorialDelete: Löschen
|
||||||
|
TutorialTutorControlled: Tutoren dürfen Tutorium editieren
|
||||||
|
TutorialTutorControlledTip: Sollen Tutoren beliebige Aspekte dieses Tutoriums (Name, Registrierungs-Gruppe, Raum, Zeit, andere Tutoren, ...) beliebig editieren dürfen?
|
||||||
|
|
||||||
CourseExams: Prüfungen
|
CourseExams: Prüfungen
|
||||||
CourseTutorials: Übungen
|
CourseTutorials: Übungen
|
||||||
|
|||||||
@ -10,6 +10,7 @@ Tutorial json
|
|||||||
registerTo UTCTime Maybe
|
registerTo UTCTime Maybe
|
||||||
deregisterUntil UTCTime Maybe
|
deregisterUntil UTCTime Maybe
|
||||||
lastChanged UTCTime default=now()
|
lastChanged UTCTime default=now()
|
||||||
|
tutorControlled Bool default=false
|
||||||
UniqueTutorial course name
|
UniqueTutorial course name
|
||||||
deriving Generic
|
deriving Generic
|
||||||
Tutor
|
Tutor
|
||||||
|
|||||||
4
routes
4
routes
@ -158,12 +158,12 @@
|
|||||||
/tuts CTutorialListR GET !tutor -- THIS route is used to check for overall course tutor access!
|
/tuts CTutorialListR GET !tutor -- THIS route is used to check for overall course tutor access!
|
||||||
/tuts/new CTutorialNewR GET POST
|
/tuts/new CTutorialNewR GET POST
|
||||||
/tuts/#TutorialName TutorialR:
|
/tuts/#TutorialName TutorialR:
|
||||||
/edit TEditR GET POST
|
/edit TEditR GET POST !tutorANDtutor-control
|
||||||
/delete TDeleteR GET POST
|
/delete TDeleteR GET POST
|
||||||
/participants TUsersR GET POST !tutor
|
/participants TUsersR GET POST !tutor
|
||||||
/register TRegisterR POST !timeANDcapacityANDcourse-registeredANDregister-group !timeANDtutorial-registered
|
/register TRegisterR POST !timeANDcapacityANDcourse-registeredANDregister-group !timeANDtutorial-registered
|
||||||
/communication TCommR GET POST !tutor
|
/communication TCommR GET POST !tutor
|
||||||
/tutor-invite TInviteR GET POST
|
/tutor-invite TInviteR GET POST !tutorANDtutor-control
|
||||||
/exams CExamListR GET !free
|
/exams CExamListR GET !free
|
||||||
/exams/new CExamNewR GET POST
|
/exams/new CExamNewR GET POST
|
||||||
/exams/#ExamName ExamR:
|
/exams/#ExamName ExamR:
|
||||||
|
|||||||
@ -132,6 +132,12 @@ data Transaction
|
|||||||
{ transactionOffice :: UserId
|
{ transactionOffice :: UserId
|
||||||
, transactionField :: StudyTermsId
|
, transactionField :: StudyTermsId
|
||||||
}
|
}
|
||||||
|
| TransactionTutorialEdit
|
||||||
|
{ transactionTutorial :: TutorialId
|
||||||
|
}
|
||||||
|
| TransactionTutorialDelete
|
||||||
|
{ transactionTutorial :: TutorialId
|
||||||
|
}
|
||||||
|
|
||||||
deriving (Eq, Ord, Read, Show, Generic, Typeable)
|
deriving (Eq, Ord, Read, Show, Generic, Typeable)
|
||||||
|
|
||||||
|
|||||||
@ -799,6 +799,13 @@ tagAccessPredicate AuthTutor = APDB $ \mAuthId route _ -> exceptT return return
|
|||||||
_ -> do
|
_ -> do
|
||||||
guardMExceptT (not $ Map.null resMap) (unauthorizedI MsgUnauthorizedTutor)
|
guardMExceptT (not $ Map.null resMap) (unauthorizedI MsgUnauthorizedTutor)
|
||||||
return Authorized
|
return Authorized
|
||||||
|
tagAccessPredicate AuthTutorControl = APDB $ \_ route _ -> case route of
|
||||||
|
CTutorialR tid ssh csh tutn _ -> maybeT (unauthorizedI MsgUnauthorizedTutorialTutorControl) $ do
|
||||||
|
Entity cid _ <- $cachedHereBinary (tid, ssh, csh) . MaybeT . getBy $ TermSchoolCourseShort tid ssh csh
|
||||||
|
Entity _ Tutorial{..} <- $cachedHereBinary (cid, tutn) . MaybeT . getBy $ UniqueTutorial cid tutn
|
||||||
|
guard tutorialTutorControlled
|
||||||
|
return Authorized
|
||||||
|
r -> $unsupportedAuthPredicate AuthTutorControl r
|
||||||
tagAccessPredicate AuthTime = APDB $ \mAuthId route _ -> case route of
|
tagAccessPredicate AuthTime = APDB $ \mAuthId route _ -> case route of
|
||||||
CApplicationR tid ssh csh _ _ -> maybeT (unauthorizedI MsgUnauthorizedApplicationTime) $ do
|
CApplicationR tid ssh csh _ _ -> maybeT (unauthorizedI MsgUnauthorizedApplicationTime) $ do
|
||||||
course <- $cachedHereBinary (tid, ssh, csh) . MaybeT . getKeyBy $ TermSchoolCourseShort tid ssh csh
|
course <- $cachedHereBinary (tid, ssh, csh) . MaybeT . getKeyBy $ TermSchoolCourseShort tid ssh csh
|
||||||
|
|||||||
@ -54,7 +54,7 @@ getInfoLecturerR =
|
|||||||
tooltipPlanned = [whamlet| _{MsgLecturerInfoTooltipPlanned} |]
|
tooltipPlanned = [whamlet| _{MsgLecturerInfoTooltipPlanned} |]
|
||||||
tooltipNewU2W = [whamlet| _{MsgLecturerInfoTooltipNewU2W} |]
|
tooltipNewU2W = [whamlet| _{MsgLecturerInfoTooltipNewU2W} |]
|
||||||
newU2WFeat, probFeatInline, plannedFeat, plannedFeatInline :: WidgetFor UniWorX ()
|
newU2WFeat, probFeatInline, plannedFeat, plannedFeatInline :: WidgetFor UniWorX ()
|
||||||
newU2WFeat = [whamlet| ^{iconTooltip tooltipNew (Just IconAnnounce) True} |] -- to be used inside text blocks
|
newU2WFeat = [whamlet| ^{iconTooltip tooltipNewU2W (Just IconAnnounce) True} |] -- to be used inside text blocks
|
||||||
probFeatInline = [whamlet| ^{iconTooltip tooltipProblem (Just IconProblem) True} |] -- to be used inside text blocks
|
probFeatInline = [whamlet| ^{iconTooltip tooltipProblem (Just IconProblem) True} |] -- to be used inside text blocks
|
||||||
plannedFeat = [whamlet| ^{iconTooltip tooltipPlanned (Just IconPlanned) False} |]
|
plannedFeat = [whamlet| ^{iconTooltip tooltipPlanned (Just IconPlanned) False} |]
|
||||||
plannedFeatInline = [whamlet| ^{iconTooltip tooltipPlanned (Just IconPlanned) True} |] -- to be used inside text blocks
|
plannedFeatInline = [whamlet| ^{iconTooltip tooltipPlanned (Just IconPlanned) True} |] -- to be used inside text blocks
|
||||||
@ -66,4 +66,4 @@ getInfoLecturerR =
|
|||||||
let expiryTime = UTCTime (addGregorianMonthsRollOver 1 $ fromGregorian year month day) 0
|
let expiryTime = UTCTime (addGregorianMonthsRollOver 1 $ fromGregorian year month day) 0
|
||||||
if currentTime > expiryTime
|
if currentTime > expiryTime
|
||||||
then mempty
|
then mempty
|
||||||
else toWidget [whamlet| ^{iconTooltip tooltipNewU2W (Just IconNew) False} |]
|
else toWidget [whamlet| ^{iconTooltip tooltipNew (Just IconNew) False} |]
|
||||||
|
|||||||
@ -35,5 +35,5 @@ postTDeleteR tid ssh csh tutn = do
|
|||||||
, drSuccessMessage = SomeMessage MsgTutorialDeleted
|
, drSuccessMessage = SomeMessage MsgTutorialDeleted
|
||||||
, drAbort = SomeRoute $ CTutorialR tid ssh csh tutn TUsersR
|
, drAbort = SomeRoute $ CTutorialR tid ssh csh tutn TUsersR
|
||||||
, drSuccess = SomeRoute $ CourseR tid ssh csh CTutorialListR
|
, drSuccess = SomeRoute $ CourseR tid ssh csh CTutorialListR
|
||||||
, drDelete = const id -- TODO: audit
|
, drDelete = \tutid' act -> act <* audit (TransactionTutorialDelete tutid')
|
||||||
}
|
}
|
||||||
|
|||||||
@ -42,6 +42,7 @@ postTEditR tid ssh csh tutn = do
|
|||||||
, tfDeregisterUntil = tutorialDeregisterUntil
|
, tfDeregisterUntil = tutorialDeregisterUntil
|
||||||
, tfTutors = Set.fromList (map Right tutorIds)
|
, tfTutors = Set.fromList (map Right tutorIds)
|
||||||
<> Set.mapMonotonic Left (Map.keysSet tutorInvites)
|
<> Set.mapMonotonic Left (Map.keysSet tutorInvites)
|
||||||
|
, tfTutorControlled = tutorialTutorControlled
|
||||||
}
|
}
|
||||||
|
|
||||||
return (cid, tutid, template)
|
return (cid, tutid, template)
|
||||||
@ -63,8 +64,11 @@ postTEditR tid ssh csh tutn = do
|
|||||||
, tutorialRegisterTo = tfRegisterTo
|
, tutorialRegisterTo = tfRegisterTo
|
||||||
, tutorialDeregisterUntil = tfDeregisterUntil
|
, tutorialDeregisterUntil = tfDeregisterUntil
|
||||||
, tutorialLastChanged = now
|
, tutorialLastChanged = now
|
||||||
|
, tutorialTutorControlled = tfTutorControlled
|
||||||
}
|
}
|
||||||
when (is _Nothing insertRes) $ do
|
when (is _Nothing insertRes) $ do
|
||||||
|
audit $ TransactionTutorialEdit tutid
|
||||||
|
|
||||||
let (invites, adds) = partitionEithers $ Set.toList tfTutors
|
let (invites, adds) = partitionEithers $ Set.toList tfTutors
|
||||||
|
|
||||||
deleteWhere [ TutorTutorial ==. tutid ]
|
deleteWhere [ TutorTutorial ==. tutid ]
|
||||||
|
|||||||
@ -18,10 +18,11 @@ import qualified Data.CaseInsensitive as CI
|
|||||||
data TutorialForm = TutorialForm
|
data TutorialForm = TutorialForm
|
||||||
{ tfName :: TutorialName
|
{ tfName :: TutorialName
|
||||||
, tfType :: CI Text
|
, tfType :: CI Text
|
||||||
|
, tfRegGroup :: Maybe (CI Text)
|
||||||
|
, tfTutorControlled :: Bool
|
||||||
, tfCapacity :: Maybe Int
|
, tfCapacity :: Maybe Int
|
||||||
, tfRoom :: Text
|
, tfRoom :: Text
|
||||||
, tfTime :: Occurrences
|
, tfTime :: Occurrences
|
||||||
, tfRegGroup :: Maybe (CI Text)
|
|
||||||
, tfRegisterFrom :: Maybe UTCTime
|
, tfRegisterFrom :: Maybe UTCTime
|
||||||
, tfRegisterTo :: Maybe UTCTime
|
, tfRegisterTo :: Maybe UTCTime
|
||||||
, tfDeregisterUntil :: Maybe UTCTime
|
, tfDeregisterUntil :: Maybe UTCTime
|
||||||
@ -66,10 +67,11 @@ tutorialForm cid template html = do
|
|||||||
flip (renderAForm FormStandard) html $ TutorialForm
|
flip (renderAForm FormStandard) html $ TutorialForm
|
||||||
<$> areq (textField & cfStrip & cfCI) (fslpI MsgTutorialName (mr MsgTutorialName) & setTooltip MsgTutorialNameTip) (tfName <$> template)
|
<$> areq (textField & cfStrip & cfCI) (fslpI MsgTutorialName (mr MsgTutorialName) & setTooltip MsgTutorialNameTip) (tfName <$> template)
|
||||||
<*> areq (textField & cfStrip & cfCI & addDatalist tutTypeDatalist) (fslpI MsgTutorialType $ mr MsgTutorialType) (tfType <$> template)
|
<*> areq (textField & cfStrip & cfCI & addDatalist tutTypeDatalist) (fslpI MsgTutorialType $ mr MsgTutorialType) (tfType <$> template)
|
||||||
|
<*> aopt (textField & cfStrip & cfCI) (fslI MsgTutorialRegGroup & setTooltip MsgTutorialRegGroupTip) ((tfRegGroup <$> template) <|> Just (Just "tutorial"))
|
||||||
|
<*> apopt checkBoxField (fslI MsgTutorialTutorControlled & setTooltip MsgTutorialTutorControlledTip) (tfTutorControlled <$> template)
|
||||||
<*> aopt (natFieldI MsgTutorialCapacityNonPositive) (fslpI MsgTutorialCapacity (mr MsgTutorialCapacity) & setTooltip MsgTutorialCapacityTip) (tfCapacity <$> template)
|
<*> aopt (natFieldI MsgTutorialCapacityNonPositive) (fslpI MsgTutorialCapacity (mr MsgTutorialCapacity) & setTooltip MsgTutorialCapacityTip) (tfCapacity <$> template)
|
||||||
<*> areq textField (fslpI MsgTutorialRoom $ mr MsgTutorialRoomPlaceholder) (tfRoom <$> template)
|
<*> areq textField (fslpI MsgTutorialRoom $ mr MsgTutorialRoomPlaceholder) (tfRoom <$> template)
|
||||||
<*> occurrencesAForm ("occurrences" :: Text) (tfTime <$> template)
|
<*> occurrencesAForm ("occurrences" :: Text) (tfTime <$> template)
|
||||||
<*> aopt (textField & cfStrip & cfCI) (fslI MsgTutorialRegGroup & setTooltip MsgTutorialRegGroupTip) ((tfRegGroup <$> template) <|> Just (Just "tutorial"))
|
|
||||||
<*> aopt utcTimeField (fslpI MsgRegisterFrom (mr MsgDate)
|
<*> aopt utcTimeField (fslpI MsgRegisterFrom (mr MsgDate)
|
||||||
& setTooltip MsgCourseRegisterFromTip
|
& setTooltip MsgCourseRegisterFromTip
|
||||||
) (tfRegisterFrom <$> template)
|
) (tfRegisterFrom <$> template)
|
||||||
|
|||||||
@ -35,8 +35,11 @@ postCTutorialNewR tid ssh csh = do
|
|||||||
, tutorialRegisterTo = tfRegisterTo
|
, tutorialRegisterTo = tfRegisterTo
|
||||||
, tutorialDeregisterUntil = tfDeregisterUntil
|
, tutorialDeregisterUntil = tfDeregisterUntil
|
||||||
, tutorialLastChanged = now
|
, tutorialLastChanged = now
|
||||||
|
, tutorialTutorControlled = tfTutorControlled
|
||||||
}
|
}
|
||||||
whenIsJust insertRes $ \tutid -> do
|
whenIsJust insertRes $ \tutid -> do
|
||||||
|
audit $ TransactionTutorialEdit tutid
|
||||||
|
|
||||||
let (invites, adds) = partitionEithers $ Set.toList tfTutors
|
let (invites, adds) = partitionEithers $ Set.toList tfTutors
|
||||||
insertMany_ $ map (Tutor tutid) adds
|
insertMany_ $ map (Tutor tutid) adds
|
||||||
sinkInvitationsF tutorInvitationConfig $ map (, tutid, (InvDBDataTutor, InvTokenDataTutor)) invites
|
sinkInvitationsF tutorInvitationConfig $ map (, tutid, (InvDBDataTutor, InvTokenDataTutor)) invites
|
||||||
|
|||||||
@ -43,6 +43,7 @@ data AuthTag -- sortiert nach gewünschter Reihenfolge auf /authpreds, d.h. Prä
|
|||||||
| AuthLecturer
|
| AuthLecturer
|
||||||
| AuthCorrector
|
| AuthCorrector
|
||||||
| AuthTutor
|
| AuthTutor
|
||||||
|
| AuthTutorControl
|
||||||
| AuthExamOffice
|
| AuthExamOffice
|
||||||
| AuthAllocationRegistered
|
| AuthAllocationRegistered
|
||||||
| AuthCourseRegistered
|
| AuthCourseRegistered
|
||||||
|
|||||||
@ -1,5 +1,11 @@
|
|||||||
$newline never
|
$newline never
|
||||||
<dl .deflist>
|
<dl .deflist>
|
||||||
|
<dt .deflist__dt>
|
||||||
|
^{formatGregorianW 2019 10 14}
|
||||||
|
<dd .deflist__dd>
|
||||||
|
<ul>
|
||||||
|
<li>Kontrolle über Einstellungen eines Tutoriums kann an Tutoren deligiert werden
|
||||||
|
|
||||||
<dt .deflist__dt>
|
<dt .deflist__dt>
|
||||||
^{formatGregorianW 2019 10 10}
|
^{formatGregorianW 2019 10 10}
|
||||||
<dd .deflist__dd>
|
<dd .deflist__dd>
|
||||||
|
|||||||
@ -254,7 +254,13 @@ $newline text
|
|||||||
Eine Tutoriumsgruppe kann beliebig viele Tutoren haben und ein Tutor kann beliebig viele Tutoriengruppen betreuen.
|
Eine Tutoriumsgruppe kann beliebig viele Tutoren haben und ein Tutor kann beliebig viele Tutoriengruppen betreuen.
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Tutoren haben Zugriff auf die Namen und Studiendaten ihrer Tutoriums-Teilnehmer und können auch Mitteilungen an sie verschicken (analog zu Kursmitteilungen).
|
Tutoren haben Zugriff auf die Namen und Studiendaten ihrer Tutoriums-Teilnehmer, können Mitteilungen an sie verschicken (analog zu Kursmitteilungen) und Teilnehmer aus ihrem Tutorium entfernen.
|
||||||
|
|
||||||
|
<p>
|
||||||
|
^{newFeat 2019 10 14} Optional kann den Tutoren volle Kontrolle über
|
||||||
|
ihre Tutorien überlassen werden (bis auf Löschen des Tutoriums), Tutoren
|
||||||
|
können dann insbesondere eigenständig den regulären Raum und die Zeit
|
||||||
|
ihres Tutoriums anpassen.
|
||||||
|
|
||||||
<dt .deflist__dt> Anmeldung
|
<dt .deflist__dt> Anmeldung
|
||||||
<dd .deflist__dd>
|
<dd .deflist__dd>
|
||||||
@ -277,10 +283,9 @@ $newline text
|
|||||||
<p>
|
<p>
|
||||||
Um die Anmeldung in beliebig viele Tutoriumsgruppen zuzulassen können alle Registrierungs-Gruppen leer gelassen werden.
|
Um die Anmeldung in beliebig viele Tutoriumsgruppen zuzulassen können alle Registrierungs-Gruppen leer gelassen werden.
|
||||||
|
|
||||||
<dt .deflist__dt> ^{plannedFeat} Nachmeldung
|
<dt .deflist__dt> ^{newFeat 2019 10 10} Nachmeldung
|
||||||
<dt .deflist__dd>
|
<dt .deflist__dd>
|
||||||
<p>
|
Kursverwalter können über die Teilnehmerliste der Veranstaltung Kursteilnehmer in Tutorien einteilen.
|
||||||
Es gibt zur Zeit keine Möglichkeit für die Kursverwalter oder Tutoren, Teilnehmer zu einem Tutorium hinzuzufügen.
|
|
||||||
|
|
||||||
|
|
||||||
<section id="exams">
|
<section id="exams">
|
||||||
|
|||||||
@ -716,6 +716,7 @@ fillDb = do
|
|||||||
, tutorialRegisterTo = Nothing
|
, tutorialRegisterTo = Nothing
|
||||||
, tutorialDeregisterUntil = Nothing
|
, tutorialDeregisterUntil = Nothing
|
||||||
, tutorialLastChanged = now
|
, tutorialLastChanged = now
|
||||||
|
, tutorialTutorControlled = True
|
||||||
}
|
}
|
||||||
void . insert $ Tutor tut1 gkleen
|
void . insert $ Tutor tut1 gkleen
|
||||||
void . insert $ TutorialParticipant tut1 fhamann
|
void . insert $ TutorialParticipant tut1 fhamann
|
||||||
@ -734,6 +735,7 @@ fillDb = do
|
|||||||
, tutorialRegisterTo = Nothing
|
, tutorialRegisterTo = Nothing
|
||||||
, tutorialDeregisterUntil = Nothing
|
, tutorialDeregisterUntil = Nothing
|
||||||
, tutorialLastChanged = now
|
, tutorialLastChanged = now
|
||||||
|
, tutorialTutorControlled = False
|
||||||
}
|
}
|
||||||
void . insert $ Tutor tut2 gkleen
|
void . insert $ Tutor tut2 gkleen
|
||||||
-- datenbanksysteme
|
-- datenbanksysteme
|
||||||
|
|||||||
@ -70,6 +70,7 @@ instance Arbitrary Tutorial where
|
|||||||
<*> arbitrary
|
<*> arbitrary
|
||||||
<*> arbitrary
|
<*> arbitrary
|
||||||
<*> arbitrary
|
<*> arbitrary
|
||||||
|
<*> arbitrary
|
||||||
shrink = genericShrink
|
shrink = genericShrink
|
||||||
|
|
||||||
instance Arbitrary User where
|
instance Arbitrary User where
|
||||||
|
|||||||
Reference in New Issue
Block a user