refactor(sheets): prepare for sheet-scoped statements

This commit is contained in:
Sarah Vaupel 2021-06-09 14:09:40 +02:00 committed by Gregor Kleen
parent 1d8a2cef60
commit 6e7e8a2b20
6 changed files with 90 additions and 44 deletions

View File

@ -15,7 +15,7 @@ Sheet -- exercise sheet for a given course
anonymousCorrection Bool default=true anonymousCorrection Bool default=true
requireExamRegistration ExamId Maybe -- Students may only submit if they are registered for the given exam requireExamRegistration ExamId Maybe -- Students may only submit if they are registered for the given exam
allowNonPersonalisedSubmission Bool default=true allowNonPersonalisedSubmission Bool default=true
authorshipStatement AuthorshipStatementDefinitionId Maybe -- authorshipStatement AuthorshipStatementDefinitionId Maybe -- TODO: sheet-specific authorship statement; for exam-unrelated sheets and for exam setting overrides
CourseSheet course name CourseSheet course name
deriving Generic deriving Generic
SheetEdit -- who edited when a row in table "Course", kept indefinitely SheetEdit -- who edited when a row in table "Course", kept indefinitely
@ -65,4 +65,4 @@ FallbackPersonalisedSheetFilesKey
secret ByteString secret ByteString
generated UTCTime generated UTCTime
UniqueFallbackPersonalisedSheetFilesKey course index UniqueFallbackPersonalisedSheetFilesKey course index
deriving Generic deriving Generic

View File

@ -22,15 +22,20 @@ import Handler.Sheet.PersonalisedFiles
getSEditR, postSEditR :: TermId -> SchoolId -> CourseShorthand -> SheetName -> Handler Html getSEditR, postSEditR :: TermId -> SchoolId -> CourseShorthand -> SheetName -> Handler Html
getSEditR = postSEditR getSEditR = postSEditR
postSEditR tid ssh csh shn = do postSEditR tid ssh csh shn = do
(Entity sid Sheet{..}, sheetFileIds, currentLoads, hasPersonalisedFiles, mAuthorshipStatement) <- runDB $ do (Entity sid Sheet{..}, sheetFileIds, currentLoads, hasPersonalisedFiles) <- runDB $ do
ent@(Entity sid Sheet{..}) <- fetchSheet tid ssh csh shn ent@(Entity sid _) <- fetchSheet tid ssh csh shn
fti <- getFtIdMap $ entityKey ent fti <- getFtIdMap $ entityKey ent
cLoads <- Map.union cLoads <- Map.union
<$> fmap (foldMap $ \(Entity _ SheetCorrector{..}) -> Map.singleton (Right sheetCorrectorUser) (InvDBDataSheetCorrector sheetCorrectorLoad sheetCorrectorState, InvTokenDataSheetCorrector)) (selectList [ SheetCorrectorSheet ==. sid ] []) <$> fmap (foldMap $ \(Entity _ SheetCorrector{..}) -> Map.singleton (Right sheetCorrectorUser) (InvDBDataSheetCorrector sheetCorrectorLoad sheetCorrectorState, InvTokenDataSheetCorrector)) (selectList [ SheetCorrectorSheet ==. sid ] [])
<*> fmap (fmap (, InvTokenDataSheetCorrector) . Map.mapKeysMonotonic Left) (sourceInvitationsF sid) <*> fmap (fmap (, InvTokenDataSheetCorrector) . Map.mapKeysMonotonic Left) (sourceInvitationsF sid)
hasPersonalisedFiles <- exists [ PersonalisedSheetFileSheet ==. sid ] hasPersonalisedFiles <- exists [ PersonalisedSheetFileSheet ==. sid ]
mAuthorshipStatement <- maybe (pure Nothing) get sheetAuthorshipStatement -- TODO: update statement if school authorship statement was updated?
return (ent, fti, cLoads, hasPersonalisedFiles, mAuthorshipStatement) -- mSchoolAuthorshipStatement <- runMaybeT $ do
-- Entity _ School{..} <- MaybeT . getEntity $ ssh
-- definitionId <- MaybeT . return $ schoolSheetAuthorshipStatementDefinition
-- MaybeT . getEntity $ definitionId
-- mAuthorshipStatement <- maybe (pure Nothing) getEntity sheetAuthorshipStatement
return (ent, fti, cLoads, hasPersonalisedFiles)
let template = Just $ SheetForm let template = Just $ SheetForm
{ sfName = sheetName { sfName = sheetName
, sfDescription = sheetDescription , sfDescription = sheetDescription
@ -56,7 +61,6 @@ postSEditR tid ssh csh shn = do
, spffAllowNonPersonalisedSubmission = sheetAllowNonPersonalisedSubmission , spffAllowNonPersonalisedSubmission = sheetAllowNonPersonalisedSubmission
, spffFiles = Nothing , spffFiles = Nothing
} }
, sfAuthorshipStatement = authorshipStatementDefinitionContent <$> mAuthorshipStatement
} }
let action = uniqueReplace sid -- More specific error message for edit old sheet could go here by using myReplaceUnique instead let action = uniqueReplace sid -- More specific error message for edit old sheet could go here by using myReplaceUnique instead
@ -73,18 +77,29 @@ handleSheetEdit tid ssh csh msId template dbAction = do
saveOkay <- runDBJobs $ do saveOkay <- runDBJobs $ do
actTime <- liftIO getCurrentTime actTime <- liftIO getCurrentTime
let insertNewOrKeepStatement mNewStatement = do -- let insertNewOrKeepStatement = \case
mOldAuthorshipStatement <- runMaybeT $ do -- -- statement disabled:
sId <- MaybeT . return $ msId -- Nothing -> pure Nothing
Entity _ Sheet{..} <- MaybeT $ getEntity sId -- -- use school preset (i.e. return the id of a *copy*):
statementId <- MaybeT . return $ sheetAuthorshipStatement -- Just Nothing -> runMaybeT $ do
MaybeT $ getEntity statementId -- Entity _ School{..} <- MaybeT . getEntity $ ssh
if -- schoolStatementId <- MaybeT . return $ schoolSheetAuthorshipStatementDefinition
| Just newDef@(AuthorshipStatementDefinition newContent) <- mNewStatement -- Entity _ AuthorshipStatementDefinition{..} <- MaybeT . getEntity $ schoolStatementId
, maybe True ((/=) newContent . authorshipStatementDefinitionContent . entityVal) mOldAuthorshipStatement -- lift . insert $ AuthorshipStatementDefinition authorshipStatementDefinitionContent
-> Just <$> (insert newDef) -- -- use custom statement:
| otherwise -> return $ entityKey <$> mOldAuthorshipStatement -- Just (Just newContent) -> do
mNewAuthorshipStatementId <- insertNewOrKeepStatement $ AuthorshipStatementDefinition <$> sfAuthorshipStatement -- mOldAuthorshipStatement <- runMaybeT $ do
-- sId <- MaybeT . return $ msId
-- Entity _ Sheet{..} <- MaybeT . getEntity $ sId
-- statementId <- MaybeT . return $ sheetAuthorshipStatement
-- MaybeT . getEntity $ statementId
-- if
-- -- statement modified: insert new statement
-- | maybe True ((/=) newContent . authorshipStatementDefinitionContent . entityVal) mOldAuthorshipStatement
-- -> Just <$> (insert $ AuthorshipStatementDefinition newContent)
-- -- statement not modified: return id of old statement
-- | otherwise -> return $ entityKey <$> mOldAuthorshipStatement
-- mNewAuthorshipStatementId <- insertNewOrKeepStatement sfAuthorshipStatement
let newSheet = Sheet let newSheet = Sheet
{ sheetCourse = cid { sheetCourse = cid
@ -103,7 +118,7 @@ handleSheetEdit tid ssh csh msId template dbAction = do
, sheetAnonymousCorrection = sfAnonymousCorrection , sheetAnonymousCorrection = sfAnonymousCorrection
, sheetRequireExamRegistration = sfRequireExamRegistration , sheetRequireExamRegistration = sfRequireExamRegistration
, sheetAllowNonPersonalisedSubmission = maybe True spffAllowNonPersonalisedSubmission sfPersonalF , sheetAllowNonPersonalisedSubmission = maybe True spffAllowNonPersonalisedSubmission sfPersonalF
, sheetAuthorshipStatement = mNewAuthorshipStatementId -- , sheetAuthorshipStatement = Nothing -- TODO: implement sheet-specific statements
} }
mbsid <- dbAction newSheet mbsid <- dbAction newSheet
case mbsid of case mbsid of

View File

@ -42,7 +42,7 @@ data SheetForm = SheetForm
, sfMarkingText :: Maybe StoredMarkup , sfMarkingText :: Maybe StoredMarkup
, sfAnonymousCorrection :: Bool , sfAnonymousCorrection :: Bool
, sfCorrectors :: Loads , sfCorrectors :: Loads
, sfAuthorshipStatement :: Maybe StoredMarkup -- , sfAuthorshipStatement :: Maybe (Either AuthorshipStatementDefinitionId StoredMarkup) -- TODO: exam-unrelated statement, override for exam setting
} }
data SheetPersonalisedFilesForm = SheetPersonalisedFilesForm data SheetPersonalisedFilesForm = SheetPersonalisedFilesForm
@ -66,12 +66,14 @@ makeSheetForm cId msId template = identifyForm FIDsheet . validateForm validateS
(Just sId) -> liftHandler $ runDB $ getFtIdMap sId (Just sId) -> liftHandler $ runDB $ getFtIdMap sId
MsgRenderer mr <- getMsgRenderer MsgRenderer mr <- getMsgRenderer
ctime <- ceilingQuarterHour <$> liftIO getCurrentTime ctime <- ceilingQuarterHour <$> liftIO getCurrentTime
mSchoolAuthorshipStatement <- liftHandler . runDB . runMaybeT $ do -- TODO: use
Entity _ Course{courseSchool} <- MaybeT . getEntity $ cId ((_school, _mSchoolAuthorshipStatement), _course) <- liftHandler . runDB $ do
Entity _ School{..} <- MaybeT . getEntity $ courseSchool course@Course{courseSchool} <- get404 cId
statementId <- MaybeT . return $ bool Nothing schoolSheetAuthorshipStatementDefinition (schoolSheetAuthorshipStatementMode /= SchoolAuthorshipStatementModeNone) school@School{..} <- get404 courseSchool
MaybeT . getEntity $ statementId mSchoolAuthorshipStatement <- runMaybeT $ do
-- TODO: compare versions: school > msId if school statement is newer than msId statement, msId > school otherwise (TODO: add lastEdited to model) statementId <- MaybeT . return $ schoolSheetAuthorshipStatementDefinition
MaybeT . getEntity $ statementId
return ((school, mSchoolAuthorshipStatement), course)
sheetPersonalisedFilesForm <- makeSheetPersonalisedFilesForm $ template >>= sfPersonalF sheetPersonalisedFilesForm <- makeSheetPersonalisedFilesForm $ template >>= sfPersonalF
flip (renderAForm FormStandard) html $ SheetForm flip (renderAForm FormStandard) html $ SheetForm
<$> areq (textField & cfStrip & cfCI) (fslI MsgSheetName) (sfName <$> template) <$> areq (textField & cfStrip & cfCI) (fslI MsgSheetName) (sfName <$> template)
@ -104,8 +106,37 @@ makeSheetForm cId msId template = identifyForm FIDsheet . validateForm validateS
<*> aopt htmlField (fslI MsgSheetMarking) (sfMarkingText <$> template) <*> aopt htmlField (fslI MsgSheetMarking) (sfMarkingText <$> template)
<*> apopt checkBoxField (fslI MsgSheetAnonymousCorrection & setTooltip MsgSheetAnonymousCorrectionTip) (sfAnonymousCorrection <$> template) <*> apopt checkBoxField (fslI MsgSheetAnonymousCorrection & setTooltip MsgSheetAnonymousCorrectionTip) (sfAnonymousCorrection <$> template)
<*> correctorForm (maybe mempty sfCorrectors template) <*> correctorForm (maybe mempty sfCorrectors template)
<* aformSection MsgSheetAuthorshipStatement -- <* aformSection MsgSheetAuthorshipStatementSection
<*> optionalActionA (apreq htmlField (fslI MsgSheetAuthorshipStatement) (join (sfAuthorshipStatement <$> template) <|> authorshipStatementDefinitionContent . entityVal <$> mSchoolAuthorshipStatement)) (fslI MsgSheetAuthorshipStatementRequired & setTooltip MsgSheetAuthorshipStatementRequiredTip) ((is _Just . sfAuthorshipStatement <$> template) <|> (pure $ is _Just mSchoolAuthorshipStatement)) -- TODO: disable option and set accordingly if school mode prevents edits -- TODO: add info: applies to exam-unrelated sheets only, will be overriden if sheet is related to an exam and this exam has an authorship statement
-- TODO: compare versions: school > msId if school statement is newer than msId statement, msId > school otherwise (TODO: add lastEdited to model)
-- <*> optionalActionA
-- ( optionalActionA
-- ( apreq htmlField
-- (fslI MsgSheetAuthorshipStatementCustom)
-- (join (join (sfAuthorshipStatement <$> template) <|> (Just . authorshipStatementDefinitionContent . entityVal <$> mSchoolAuthorshipStatement)))
-- )
-- ( fslI MsgSheetAuthorshipStatementUseSchoolDefault
-- & setTooltip MsgSheetAuthorshipStatementUseSchoolDefaultTip
-- -- TODO: disable if school mode prevents custom statements
-- -- & addAttr "disabled" "disabled"
-- )
-- (
-- -- TODO: set accordingly if school mode prevents custom statements
-- pure $ is _Just mSchoolAuthorshipStatement
-- )
-- -- TODO: display current school statement
-- -- <* maybe (pure ()) (authorshipStatementDefinitionContent . entityVal) mSchoolAuthorshipStatement
-- )
-- ( fslI MsgSheetAuthorshipStatementRequired
-- & setTooltip MsgSheetAuthorshipStatementRequiredTip
-- -- TODO: disable if school mode enforces/disables statements for this sheet
-- -- & addAttr "disabled" "disabled"
-- )
-- (
-- -- TODO: set accordingly if school mode enforces/disables statements for this sheet
-- -- TODO: school statement > template iff the school statement is newer
-- (is _Just . sfAuthorshipStatement <$> template) <|> (pure $ is _Just mSchoolAuthorshipStatement)
-- )
where where
makeSheetPersonalisedFilesForm :: Maybe SheetPersonalisedFilesForm -> MForm Handler (AForm Handler SheetPersonalisedFilesForm) makeSheetPersonalisedFilesForm :: Maybe SheetPersonalisedFilesForm -> MForm Handler (AForm Handler SheetPersonalisedFilesForm)
makeSheetPersonalisedFilesForm template' = do makeSheetPersonalisedFilesForm template' = do
@ -165,8 +196,8 @@ makeSheetForm cId msId template = identifyForm FIDsheet . validateForm validateS
|| sfType == NotGraded || sfType == NotGraded
-- TODO: do authorship statement validation -- TODO: do authorship statement validation
-- TODO: if school mode is none or required for this sheet (exam-related/exam-umrelated?), statementRequired must be set accordingly -- TODO: if school mode is none or required for this sheet (exam-related/exam-umrelated?), statement must be set accordingly (Just for required, Nothing for none)
-- TODO: authorship statement definition must not be empty when statement is required -- TODO: if school prevents custom statements, statement must match current school statement
correctorForm :: Loads -> AForm Handler Loads correctorForm :: Loads -> AForm Handler Loads
correctorForm loads' = wFormToAForm $ do correctorForm loads' = wFormToAForm $ do

View File

@ -38,16 +38,16 @@ postSheetNewR tid ssh csh = do
firstEdit = E.subSelectMaybe . E.from $ \sheetEdit -> do firstEdit = E.subSelectMaybe . E.from $ \sheetEdit -> do
E.where_ $ sheetEdit E.^. SheetEditSheet E.==. sheet E.^. SheetId E.where_ $ sheetEdit E.^. SheetEditSheet E.==. sheet E.^. SheetId
return . E.min_ $ sheetEdit E.^. SheetEditTime return . E.min_ $ sheetEdit E.^. SheetEditTime
mAuthorshipStatement = E.subSelect . E.from $ \authorshipStatementDefinition -> do -- mAuthorshipStatement = E.subSelect . E.from $ \authorshipStatementDefinition -> do
E.where_ $ E.just (authorshipStatementDefinition E.^. AuthorshipStatementDefinitionId) E.==. sheet E.^. SheetAuthorshipStatement -- E.where_ $ E.just (authorshipStatementDefinition E.^. AuthorshipStatementDefinitionId) E.==. sheet E.^. SheetAuthorshipStatement
return $ authorshipStatementDefinition E.^. AuthorshipStatementDefinitionContent -- return $ authorshipStatementDefinition E.^. AuthorshipStatementDefinitionContent
return (sheet, firstEdit, mAuthorshipStatement) return (sheet, firstEdit)
cid <- getKeyBy404 $ TermSchoolCourseShort tid ssh csh cid <- getKeyBy404 $ TermSchoolCourseShort tid ssh csh
loads <- defaultLoads cid loads <- defaultLoads cid
return (lSheets, loads) return (lSheets, loads)
now <- liftIO getCurrentTime now <- liftIO getCurrentTime
let template = case lastSheets of let template = case lastSheets of
((Entity {entityVal=Sheet{..}}, E.Value fEdit, E.Value mAuthorshipStatement):_) -> ((Entity {entityVal=Sheet{..}}, E.Value fEdit):_) ->
let addTime = addWeeks $ max 1 $ weeksToAdd (fromMaybe (UTCTime systemEpochDay 0) $ sheetActiveTo <|> fEdit) now let addTime = addWeeks $ max 1 $ weeksToAdd (fromMaybe (UTCTime systemEpochDay 0) $ sheetActiveTo <|> fEdit) now
in Just $ SheetForm in Just $ SheetForm
{ sfName = stepTextCounterCI sheetName { sfName = stepTextCounterCI sheetName
@ -70,7 +70,7 @@ postSheetNewR tid ssh csh = do
, sfAnonymousCorrection = sheetAnonymousCorrection , sfAnonymousCorrection = sheetAnonymousCorrection
, sfRequireExamRegistration = Nothing , sfRequireExamRegistration = Nothing
, sfPersonalF = Nothing , sfPersonalF = Nothing
, sfAuthorshipStatement = mAuthorshipStatement -- , sfAuthorshipStatement = mAuthorshipStatement
} }
_other -> Nothing _other -> Nothing
let action = -- More specific error message for new sheet could go here, if insertUnique returns Nothing let action = -- More specific error message for new sheet could go here, if insertUnique returns Nothing

View File

@ -22,8 +22,8 @@ getSShowR tid ssh csh shn = do
Entity sid sheet <- runDB $ fetchSheet tid ssh csh shn Entity sid sheet <- runDB $ fetchSheet tid ssh csh shn
seeAllModificationTimestamps <- hasReadAccessTo $ CSheetR tid ssh csh shn SIsCorrR -- ordinary users should not see modification dates older than visibility seeAllModificationTimestamps <- hasReadAccessTo $ CSheetR tid ssh csh shn SIsCorrR -- ordinary users should not see modification dates older than visibility
mayEdit <- hasWriteAccessTo $ CSheetR tid ssh csh shn SEditR -- mayEdit <- hasWriteAccessTo $ CSheetR tid ssh csh shn SEditR
maySubmit <- hasWriteAccessTo $ CSheetR tid ssh csh shn SubmissionNewR -- maySubmit <- hasWriteAccessTo $ CSheetR tid ssh csh shn SubmissionNewR
let sftVisible :: IsDBTable m a => SheetFileType -> DBCell m a let sftVisible :: IsDBTable m a => SheetFileType -> DBCell m a
sftVisible sft | Just dts <- sheetFileTypeDates sheet sft sftVisible sft | Just dts <- sheetFileTypeDates sheet sft

View File

@ -70,11 +70,11 @@ $maybe descr <- sheetDescription sheet
_{MsgTableSheetType} _{MsgTableSheetType}
<dd .deflist__dd> <dd .deflist__dd>
^{sTypeDesc tr} ^{sTypeDesc tr}
$if mayEdit || maySubmit $# $if mayEdit || maySubmit
<dt .deflist__dt> $# <dt .deflist__dt>
_{MsgSheetAuthorshipStatementSection} $# _{MsgSheetAuthorshipStatementSection}
<dd .deflist__dd> $# <dd .deflist__dd>
_{maybe MsgSheetAuthorshipStatementIsRequiredFalse (const MsgSheetAuthorshipStatementIsRequiredTrue) (sheetAuthorshipStatement sheet)} $# _{maybe MsgSheetAuthorshipStatementIsRequiredFalse (const MsgSheetAuthorshipStatementIsRequiredTrue) (sheetAuthorshipStatement sheet)}
$maybe marktxt <- markingText $maybe marktxt <- markingText
<section> <section>