Single submission assign corrector

This commit is contained in:
SJost 2018-11-12 17:12:24 +01:00
parent 225bd9b01d
commit 673d98ff91
9 changed files with 109 additions and 55 deletions

3
.vscode/tasks.json vendored
View File

@ -28,7 +28,8 @@
"focus": false, "focus": false,
"panel": "dedicated", "panel": "dedicated",
"showReuseMessage": false "showReuseMessage": false
} },
"problemMatcher": []
}, },
{ {
"label": "test", "label": "test",

View File

@ -142,6 +142,8 @@ SubmissionGroupName: Gruppenname
CorrectionsTitle: Zugewiesene Korrekturen CorrectionsTitle: Zugewiesene Korrekturen
CourseCorrectionsTitle: Korrekturen für diesen Kurs CourseCorrectionsTitle: Korrekturen für diesen Kurs
CorrectorsHead sheetName@SheetName: Korrektoren für #{sheetName} CorrectorsHead sheetName@SheetName: Korrektoren für #{sheetName}
CorrectorAssignTitle: Korrektor zuweisen
Unauthorized: Sie haben hierfür keine explizite Berechtigung. Unauthorized: Sie haben hierfür keine explizite Berechtigung.
UnauthorizedAnd l@Text r@Text: (#{l} UND #{r}) UnauthorizedAnd l@Text r@Text: (#{l} UND #{r})
@ -189,6 +191,7 @@ CorByProportionExcludingTutorial proportion@Rational: #{display proportion} Ante
DeleteRow: Zeile entfernen DeleteRow: Zeile entfernen
ProportionNegative: Anteile dürfen nicht negativ sein ProportionNegative: Anteile dürfen nicht negativ sein
CorrectorUpdated: Korrektor erfolgreich aktualisiert
CorrectorsUpdated: Korrektoren erfolgreich aktualisiert CorrectorsUpdated: Korrektoren erfolgreich aktualisiert
CorrectorsPlaceholder: Korrektoren... CorrectorsPlaceholder: Korrektoren...
CorrectorsDefaulted: Korrektoren-Liste wurde aus bisherigen Übungsblättern diesen Kurses generiert. Es sind keine Daten gespeichert. CorrectorsDefaulted: Korrektoren-Liste wurde aus bisherigen Übungsblättern diesen Kurses generiert. Es sind keine Daten gespeichert.
@ -258,7 +261,7 @@ RatingDone: Bewertung fertiggestellt
RatingPercent: Erreicht RatingPercent: Erreicht
RatingFiles: Korrigierte Dateien RatingFiles: Korrigierte Dateien
PointsNotPositive: Punktzahl darf nicht negativ sein PointsNotPositive: Punktzahl darf nicht negativ sein
PointsTooHigh maxPoints@Points: Punktzahl darf nicht höher als #{tshow maxPoints} sein PointsTooHigh maxPoints@Points: Punktzahl darf nicht höher als #{tshow maxPoints} sein
RatingPointsDone: Abgabe zählt als korrigiert, gdw. Punktezahl gesetzt ist RatingPointsDone: Abgabe zählt als korrigiert, gdw. Punktezahl gesetzt ist
ColumnRatingPointsDone: Punktzahl/Abgeschlossen ColumnRatingPointsDone: Punktzahl/Abgeschlossen
Pseudonyms: Pseudonyme Pseudonyms: Pseudonyme
@ -366,7 +369,7 @@ SheetGrading: Bewertung
SheetGradingPoints maxPoints@Points: #{tshow maxPoints} Punkte SheetGradingPoints maxPoints@Points: #{tshow maxPoints} Punkte
SheetGradingPassPoints maxPoints@Points passingPoints@Points: Bestanden ab #{tshow passingPoints} von #{tshow maxPoints} Punkten SheetGradingPassPoints maxPoints@Points passingPoints@Points: Bestanden ab #{tshow passingPoints} von #{tshow maxPoints} Punkten
SheetGradingPassBinary: Bestanden/Nicht Bestanden SheetGradingPassBinary: Bestanden/Nicht Bestanden
SheetGradingInfo: "Bestanden nach Punkten" zählt sowohl zur maximal erreichbaren Gesamtpunktzahl also auch zur Anzahl der zu bestehenden Blätter. SheetGradingInfo: "Bestanden nach Punkten" zählt sowohl zur maximal erreichbaren Gesamtpunktzahl also auch zur Anzahl der zu bestehenden Blätter.
SheetGradingPoints': Punkte SheetGradingPoints': Punkte
SheetGradingPassPoints': Bestehen nach Punkten SheetGradingPassPoints': Bestehen nach Punkten
@ -424,7 +427,7 @@ HelpUser: Meinen Benutzeraccount
HelpAnonymous: Keine Antwort (Anonym) HelpAnonymous: Keine Antwort (Anonym)
HelpEMail: E-Mail HelpEMail: E-Mail
HelpRequest: Supportanfrage / Verbesserungsvorschlag HelpRequest: Supportanfrage / Verbesserungsvorschlag
HelpProblemPage: Problematische Seite HelpProblemPage: Problematische Seite
HelpIntroduction: Wenn Ihnen die Benutzung dieser Webseite Schwierigkeiten bereitet oder Sie einen verbesserbaren Umstand entdecken bitten wir Sie uns das zu melden, auch wenn Sie Ihr Problem bereits selbst lösen konnten. Wir passen die Seite ständig an und versuchen sie auch für zukünftige Benutzer so einsichtig wie möglich zu halten. HelpIntroduction: Wenn Ihnen die Benutzung dieser Webseite Schwierigkeiten bereitet oder Sie einen verbesserbaren Umstand entdecken bitten wir Sie uns das zu melden, auch wenn Sie Ihr Problem bereits selbst lösen konnten. Wir passen die Seite ständig an und versuchen sie auch für zukünftige Benutzer so einsichtig wie möglich zu halten.
HelpSent: Ihre Supportanfrage wurde weitergeleitet. HelpSent: Ihre Supportanfrage wurde weitergeleitet.
@ -484,6 +487,7 @@ ErrMsgCouldNotDecodeNonce: Konnte secretbox-nonce nicht dekodieren
ErrMsgCouldNotOpenSecretbox: Konnte libsodium-secretbox nicht öffnen (Verschlüsselte Daten sind nicht authentisch) ErrMsgCouldNotOpenSecretbox: Konnte libsodium-secretbox nicht öffnen (Verschlüsselte Daten sind nicht authentisch)
ErrMsgCouldNotDecodePlaintext utf8Err@Text: Konnte Klartext nicht UTF8-dekodieren: #{utf8Err} ErrMsgCouldNotDecodePlaintext utf8Err@Text: Konnte Klartext nicht UTF8-dekodieren: #{utf8Err}
ErrMsgHeading: Fehlermeldung entschlüsseln ErrMsgHeading: Fehlermeldung entschlüsseln
ErrorCryptoIdMismatch: Verschlüsselte Id der Abgabe passte nicht zu anderen Daten
InvalidRoute: Konnte URL nicht interpretieren InvalidRoute: Konnte URL nicht interpretieren

14
models
View File

@ -15,7 +15,7 @@ User json
notificationSettings NotificationSettings notificationSettings NotificationSettings
UniqueAuthentication ident UniqueAuthentication ident
UniqueEmail email UniqueEmail email
deriving Show deriving Show Eq
UserAdmin UserAdmin
user UserId user UserId
school SchoolId school SchoolId
@ -46,9 +46,9 @@ Term json
start Day -- TermKey :: TermIdentifier -> TermId start Day -- TermKey :: TermIdentifier -> TermId
end Day end Day
holidays [Day] holidays [Day]
lectureStart Day lectureStart Day
lectureEnd Day lectureEnd Day
active Bool active Bool
Primary name -- newtype Key Term = TermKey { unTermKey :: TermIdentifier } Primary name -- newtype Key Term = TermKey { unTermKey :: TermIdentifier }
deriving Show -- type TermId = Key Term deriving Show -- type TermId = Key Term
School json School json
@ -57,7 +57,7 @@ School json
UniqueSchool name UniqueSchool name
UniqueSchoolShorthand shorthand -- required for Normalisation of CI Text UniqueSchoolShorthand shorthand -- required for Normalisation of CI Text
Primary shorthand -- newtype Key School = SchoolKey { unSchoolKey :: SchoolShorthand } Primary shorthand -- newtype Key School = SchoolKey { unSchoolKey :: SchoolShorthand }
deriving Eq deriving Eq
DegreeCourse json DegreeCourse json
course CourseId course CourseId
degree StudyDegreeId degree StudyDegreeId
@ -89,7 +89,7 @@ CourseFavourite
course CourseId course CourseId
UniqueCourseFavourite user course UniqueCourseFavourite user course
deriving Show deriving Show
Lecturer Lecturer
user UserId user UserId
course CourseId course CourseId
UniqueLecturer user course UniqueLecturer user course
@ -135,7 +135,7 @@ SheetFile
file FileId file FileId
type SheetFileType type SheetFileType
UniqueSheetFile file sheet type UniqueSheetFile file sheet type
File File
title FilePath title FilePath
content ByteString Maybe -- Nothing iff this is a directory content ByteString Maybe -- Nothing iff this is a directory
modified UTCTime modified UTCTime

3
routes
View File

@ -23,7 +23,7 @@
-- !isWrite -- only if it is write access (i.e. POST only) why needed??? -- !isWrite -- only if it is write access (i.e. POST only) why needed???
-- --
-- !deprecated -- like free, but logs and gives a warning; entirely disabled in production -- !deprecated -- like free, but logs and gives a warning; entirely disabled in production
-- !development -- like free, but only for development builds -- !development -- like free, but only for development builds
/static StaticR Static appStatic !free /static StaticR Static appStatic !free
/auth AuthR Auth getAuth !free /auth AuthR Auth getAuth !free
@ -79,6 +79,7 @@
/sub/#CryptoFileNameSubmission SubmissionR !correctorANDisRead: /sub/#CryptoFileNameSubmission SubmissionR !correctorANDisRead:
/ SubShowR GET POST !ownerANDtime !ownerANDisRead / SubShowR GET POST !ownerANDtime !ownerANDisRead
/archive/#{ZIPArchiveName SubmissionFileType} SubArchiveR GET !owner /archive/#{ZIPArchiveName SubmissionFileType} SubArchiveR GET !owner
/assign SAssignR GET POST !lecturerANDtime
/correction CorrectionR GET POST !corrector !ownerANDisReadANDrated /correction CorrectionR GET POST !corrector !ownerANDisReadANDrated
!/#SubmissionFileType/*FilePath SubDownloadR GET !owner !/#SubmissionFileType/*FilePath SubDownloadR GET !owner
/correctors SCorrR GET POST /correctors SCorrR GET POST

View File

@ -208,7 +208,7 @@ instance RenderMessage UniWorX (SheetType) where
renderMessage foundation ls sheetType = case sheetType of renderMessage foundation ls sheetType = case sheetType of
NotGraded -> mr $ SheetTypeHeader NotGraded NotGraded -> mr $ SheetTypeHeader NotGraded
other -> mr (grading other) <> ", " <> mr (SheetTypeHeader other) other -> mr (grading other) <> ", " <> mr (SheetTypeHeader other)
where where
mr :: RenderMessage UniWorX msg => msg -> Text mr :: RenderMessage UniWorX msg => msg -> Text
mr = renderMessage foundation ls mr = renderMessage foundation ls
@ -432,12 +432,13 @@ knownTags = Map.fromList -- should not throw exceptions, i.e. no getBy404 or req
guard visible guard visible
case subRoute of case subRoute of
SFileR SheetExercise _ -> guard $ sheetActiveFrom <= cTime SFileR SheetExercise _ -> guard $ sheetActiveFrom <= cTime
SFileR SheetHint _ -> guard $ maybe False (<= cTime) sheetHintFrom SFileR SheetHint _ -> guard $ maybe False (<= cTime) sheetHintFrom
SFileR SheetSolution _ -> guard $ maybe False (<= cTime) sheetSolutionFrom SFileR SheetSolution _ -> guard $ maybe False (<= cTime) sheetSolutionFrom
SubmissionNewR -> guard active SubmissionNewR -> guard active
SubmissionR _ _ -> guard active SubmissionR _ SAssignR -> guard $ not active -- Correctors can only be assigned when the Sheet is inactive, since submissions are subject to change
_ -> return () SubmissionR _ _ -> guard active
_ -> return ()
return Authorized return Authorized
@ -525,7 +526,7 @@ knownTags = Map.fromList -- should not throw exceptions, i.e. no getBy404 or req
SystemMessage{..} <- MaybeT $ get smId SystemMessage{..} <- MaybeT $ get smId
isAuthenticated <- isJust <$> liftHandlerT maybeAuthId isAuthenticated <- isJust <$> liftHandlerT maybeAuthId
guard $ not systemMessageAuthenticatedOnly || isAuthenticated guard $ not systemMessageAuthenticatedOnly || isAuthenticated
return Authorized return Authorized
r -> $unsupportedAuthPredicate "authentication" r r -> $unsupportedAuthPredicate "authentication" r
) )
,("isRead", APHandler . const $ bool (return Authorized) (unauthorizedI MsgUnauthorizedWrite)) ,("isRead", APHandler . const $ bool (return Authorized) (unauthorizedI MsgUnauthorizedWrite))
@ -601,7 +602,7 @@ instance Yesod UniWorX where
cid <- MaybeT . getKeyBy $ TermSchoolCourseShort tid ssh csh cid <- MaybeT . getKeyBy $ TermSchoolCourseShort tid ssh csh
user <- MaybeT $ get uid user <- MaybeT $ get uid
let courseFavourite = CourseFavourite uid now cid let courseFavourite = CourseFavourite uid now cid
$logDebugS "updateFavourites" [st|Updating/Inserting: #{tshow courseFavourite}|] $logDebugS "updateFavourites" [st|Updating/Inserting: #{tshow courseFavourite}|]
-- update Favourites -- update Favourites
void . lift $ upsertBy void . lift $ upsertBy
@ -650,7 +651,7 @@ instance Yesod UniWorX where
#{formatted} #{formatted}
|] |]
| otherwise -> plaintext | otherwise -> plaintext
errPage = case err of errPage = case err of
NotFound -> [whamlet|<p>_{MsgErrorResponseNotFound}|] NotFound -> [whamlet|<p>_{MsgErrorResponseNotFound}|]
InternalError err' -> encrypted err' [whamlet|<p .errMsg>#{err'}|] InternalError err' -> encrypted err' [whamlet|<p .errMsg>#{err'}|]
@ -938,7 +939,7 @@ defaultLinks = fmap catMaybes . mapM runMaybeT $ -- Define the menu items of the
} }
, do , do
mCurrentRoute <- getCurrentRoute mCurrentRoute <- getCurrentRoute
return MenuItem return MenuItem
{ menuItemType = NavbarRight { menuItemType = NavbarRight
, menuItemLabel = MsgMenuHelp , menuItemLabel = MsgMenuHelp
@ -1226,6 +1227,14 @@ pageActions (CSubmissionR tid ssh csh shn cid SubShowR) =
, menuItemModal = False , menuItemModal = False
, menuItemAccessCallback' = return True , menuItemAccessCallback' = return True
} }
, MenuItem
{ menuItemType = PageActionPrime
, menuItemLabel = MsgCorrectorAssignTitle
, menuItemIcon = Nothing
, menuItemRoute = SomeRoute $ CSubmissionR tid ssh csh shn cid SAssignR
, menuItemModal = True
, menuItemAccessCallback' = return True
}
] ]
pageActions (CSheetR tid ssh csh shn SCorrR) = pageActions (CSheetR tid ssh csh shn SCorrR) =
[ MenuItem [ MenuItem
@ -1455,7 +1464,7 @@ routeNormalizers =
hasChanged shn sheetName hasChanged shn sheetName
return $ CSheetR tid ssh csh sheetName subRoute return $ CSheetR tid ssh csh sheetName subRoute
-- How to run database actions. -- How to run database actions.
instance YesodPersist UniWorX where instance YesodPersist UniWorX where
type YesodPersistBackend UniWorX = SqlBackend type YesodPersistBackend UniWorX = SqlBackend
@ -1525,7 +1534,7 @@ instance YesodAuth UniWorX where
userEmail' = lookup (Attr "mail") ldapData userEmail' = lookup (Attr "mail") ldapData
userDisplayName' = lookup (Attr "displayName") ldapData userDisplayName' = lookup (Attr "displayName") ldapData
userSurname' = lookup (Attr "sn") ldapData userSurname' = lookup (Attr "sn") ldapData
userAuthentication userAuthentication
| isPWHash = error "PWHash should only work for users that are already known" | isPWHash = error "PWHash should only work for users that are already known"
| otherwise = AuthLDAP | otherwise = AuthLDAP
@ -1631,13 +1640,13 @@ instance YesodMail UniWorX where
mailSmtp act = do mailSmtp act = do
pool <- maybe (throwM MailNotAvailable) return =<< getsYesod appSmtpPool pool <- maybe (throwM MailNotAvailable) return =<< getsYesod appSmtpPool
withResource pool act withResource pool act
mailT ctx mail = defMailT ctx $ do mailT ctx mail = defMailT ctx $ do
void setMailObjectId void setMailObjectId
setDateCurrent setDateCurrent
replaceMailHeader "Auto-Submitted" $ Just "auto-generated" replaceMailHeader "Auto-Submitted" $ Just "auto-generated"
ret <- mail ret <- mail
setMailSmtpData setMailSmtpData
return ret return ret

View File

@ -83,11 +83,7 @@ colTerm = sortable (Just "term") (i18nCell MsgTerm)
colCourse :: IsDBTable m a => Colonnade _ CorrectionTableData (DBCell m a) colCourse :: IsDBTable m a => Colonnade _ CorrectionTableData (DBCell m a)
colCourse = sortable (Just "course") (i18nCell MsgCourse) colCourse = sortable (Just "course") (i18nCell MsgCourse)
$ \DBRow{ dbrOutput=(_, _, course, _, _) } -> $ \DBRow{ dbrOutput=(_, _, (_,csh,tid,sid), _, _) } -> courseCellCL (tid,sid,csh)
let tid = course ^. _3
ssh = course ^. _4
csh = course ^. _2
in anchorCell (CourseR tid ssh csh CShowR) [whamlet|#{display csh}|]
colSheet :: IsDBTable m a => Colonnade _ CorrectionTableData (DBCell m a) colSheet :: IsDBTable m a => Colonnade _ CorrectionTableData (DBCell m a)
colSheet = sortable (Just "sheet") (i18nCell MsgSheet) colSheet = sortable (Just "sheet") (i18nCell MsgSheet)
@ -135,7 +131,7 @@ colSMatrikel :: IsDBTable m a => Colonnade _ CorrectionTableData (DBCell m a)
colSMatrikel = sortable Nothing (i18nCell MsgMatrikelNr) $ \DBRow{ dbrOutput=(_, _, _, _, users) } -> let colSMatrikel = sortable Nothing (i18nCell MsgMatrikelNr) $ \DBRow{ dbrOutput=(_, _, _, _, users) } -> let
protoCell = listCell (Map.toList users) $ \(userId, (User{..}, _)) -> anchorCellM (AdminUserR <$> encrypt userId) (maybe mempty toWidget userMatrikelnummer) protoCell = listCell (Map.toList users) $ \(userId, (User{..}, _)) -> anchorCellM (AdminUserR <$> encrypt userId) (maybe mempty toWidget userMatrikelnummer)
in protoCell & cellAttrs <>~ [("class", "list--inline list--comma-separated")] in protoCell & cellAttrs <>~ [("class", "list--inline list--comma-separated")]
colRating :: IsDBTable m a => Colonnade _ CorrectionTableData (DBCell m a) colRating :: IsDBTable m a => Colonnade _ CorrectionTableData (DBCell m a)
colRating = sortable (Just "rating") (i18nCell MsgRating) $ \DBRow{ dbrOutput=(Entity subId Submission{..}, Entity _ Sheet{..}, course, _, _) } -> colRating = sortable (Just "rating") (i18nCell MsgRating) $ \DBRow{ dbrOutput=(Entity subId Submission{..}, Entity _ Sheet{..}, course, _, _) } ->
let csh = course ^. _2 let csh = course ^. _2
@ -181,7 +177,7 @@ colCommentField = sortable Nothing (i18nCell MsgRatingComment) $ formCell
type CorrectionTableExpr = (E.SqlExpr (Entity Course) `E.InnerJoin` E.SqlExpr (Entity Sheet) `E.InnerJoin` E.SqlExpr (Entity Submission)) `E.LeftOuterJoin` E.SqlExpr (Maybe (Entity User)) type CorrectionTableExpr = (E.SqlExpr (Entity Course) `E.InnerJoin` E.SqlExpr (Entity Sheet) `E.InnerJoin` E.SqlExpr (Entity Submission)) `E.LeftOuterJoin` E.SqlExpr (Maybe (Entity User))
makeCorrectionsTable :: ( IsDBTable m x, ToSortable h, Functor h ) makeCorrectionsTable :: ( IsDBTable m x, ToSortable h, Functor h )
=> _ -> Colonnade h CorrectionTableData (DBCell m x) -> PSValidator m x -> _ -> Handler (DBResult m x) => _ -> Colonnade h CorrectionTableData (DBCell m x) -> PSValidator m x -> _ -> Handler (DBResult m x)
makeCorrectionsTable whereClause dbtColonnade psValidator dbtProj' = do makeCorrectionsTable whereClause dbtColonnade psValidator dbtProj' = do
@ -303,9 +299,10 @@ correctionsR whereClause (formColonnade -> displayColumns) psValidator actions =
alreadyAssigned <- selectList [SubmissionId <-. subs, SubmissionRatingBy !=. Nothing] [] alreadyAssigned <- selectList [SubmissionId <-. subs, SubmissionRatingBy !=. Nothing] []
unless (null alreadyAssigned) $ do unless (null alreadyAssigned) $ do
mr <- (toHtml . ) <$> getMessageRender mr <- (toHtml . ) <$> getMessageRender
alreadyAssigned' <- forM alreadyAssigned $ \Entity{..} -> (, entityVal) <$> (encrypt entityKey :: DB CryptoFileNameSubmission) alreadyAssigned' <- forM alreadyAssigned $ \Entity{..} -> (, entityVal) <$> (encrypt entityKey :: DB CryptoFileNameSubmission)
addMessage Warning =<< withUrlRenderer ($(ihamletFile "templates/messages/submissionsAlreadyAssigned.hamlet") mr) addMessage Warning =<< withUrlRenderer ($(ihamletFile "templates/messages/submissionsAlreadyAssigned.hamlet") mr)
let unassigned = Set.fromList subs `Set.difference` Set.fromList (entityKey <$> alreadyAssigned) let unassigned = Set.fromList subs `Set.difference` Set.fromList (entityKey <$> alreadyAssigned)
unless (null unassigned) $ do unless (null unassigned) $ do
num <- updateWhereCount [SubmissionId <-. Set.toList unassigned] num <- updateWhereCount [SubmissionId <-. Set.toList unassigned]
[ SubmissionRatingBy =. Just uid [ SubmissionRatingBy =. Just uid
@ -337,7 +334,7 @@ correctionsR whereClause (formColonnade -> displayColumns) psValidator actions =
alreadyAssigned <- selectList [SubmissionId <-. subs, SubmissionRatingBy !=. Nothing] [] alreadyAssigned <- selectList [SubmissionId <-. subs, SubmissionRatingBy !=. Nothing] []
unless (null alreadyAssigned) $ do unless (null alreadyAssigned) $ do
mr <- (toHtml . ) <$> getMessageRender mr <- (toHtml . ) <$> getMessageRender
alreadyAssigned' <- forM alreadyAssigned $ \Entity{..} -> (, entityVal) <$> (encrypt entityKey :: DB CryptoFileNameSubmission) alreadyAssigned' <- forM alreadyAssigned $ \Entity{..} -> (, entityVal) <$> (encrypt entityKey :: DB CryptoFileNameSubmission)
addMessage Warning =<< withUrlRenderer ($(ihamletFile "templates/messages/submissionsAlreadyAssigned.hamlet") mr) addMessage Warning =<< withUrlRenderer ($(ihamletFile "templates/messages/submissionsAlreadyAssigned.hamlet") mr)
let unassigned = Set.fromList subs `Set.difference` Set.fromList (entityKey <$> alreadyAssigned) let unassigned = Set.fromList subs `Set.difference` Set.fromList (entityKey <$> alreadyAssigned)
unless (null unassigned) $ do unless (null unassigned) $ do
@ -356,7 +353,7 @@ correctionsR whereClause (formColonnade -> displayColumns) psValidator actions =
type ActionCorrections' = (ActionCorrections, AForm (HandlerT UniWorX IO) ActionCorrectionsData) type ActionCorrections' = (ActionCorrections, AForm (HandlerT UniWorX IO) ActionCorrectionsData)
downloadAction :: ActionCorrections' downloadAction :: ActionCorrections'
downloadAction = ( CorrDownload downloadAction = ( CorrDownload
, pure CorrDownloadData , pure CorrDownloadData
@ -483,8 +480,8 @@ postCorrectionR tid ssh csh shn cid = do
let ratingComment = fmap Text.strip submissionRatingComment >>= (\c -> c <$ guard (not $ null c)) let ratingComment = fmap Text.strip submissionRatingComment >>= (\c -> c <$ guard (not $ null c))
pointsForm = case sheetType of pointsForm = case sheetType of
NotGraded -> pure Nothing NotGraded -> pure Nothing
_otherwise -> aopt (pointsFieldMax $ preview (_grading . _maxPoints) sheetType) _otherwise -> aopt (pointsFieldMax $ preview (_grading . _maxPoints) sheetType)
(fslpI MsgRatingPoints "Punktezahl") (fslpI MsgRatingPoints "Punktezahl")
(Just submissionRatingPoints) (Just submissionRatingPoints)
((corrResult, corrForm), corrEncoding) <- runFormPost . identForm FIDcorrection . renderAForm FormStandard $ (,,) ((corrResult, corrForm), corrEncoding) <- runFormPost . identForm FIDcorrection . renderAForm FormStandard $ (,,)
@ -500,7 +497,7 @@ postCorrectionR tid ssh csh shn cid = do
case corrResult of case corrResult of
FormMissing -> return () FormMissing -> return ()
FormFailure errs -> mapM_ (addMessage Error . toHtml) errs FormFailure errs -> mapM_ (addMessage Error . toHtml) errs
FormSuccess (rated, ratingPoints', ratingComment') FormSuccess (rated, ratingPoints', ratingComment')
| errs <- validateRating sheetType Rating' | errs <- validateRating sheetType Rating'
{ ratingPoints=ratingPoints' { ratingPoints=ratingPoints'
, ratingComment=ratingComment' , ratingComment=ratingComment'
@ -511,7 +508,7 @@ postCorrectionR tid ssh csh shn cid = do
runDBJobs $ do runDBJobs $ do
uid <- liftHandlerT requireAuthId uid <- liftHandlerT requireAuthId
now <- liftIO getCurrentTime now <- liftIO getCurrentTime
update sub [ SubmissionRatingBy =. Just uid update sub [ SubmissionRatingBy =. Just uid
, SubmissionRatingTime =. (now <$ guard rated) , SubmissionRatingTime =. (now <$ guard rated)
, SubmissionRatingPoints =. ratingPoints' , SubmissionRatingPoints =. ratingPoints'
@ -522,7 +519,7 @@ postCorrectionR tid ssh csh shn cid = do
when (rated && isNothing submissionRatingTime) $ do when (rated && isNothing submissionRatingTime) $ do
$logDebugS "CorrectionR" [st|Rated #{tshow sub}|] $logDebugS "CorrectionR" [st|Rated #{tshow sub}|]
queueDBJob . JobQueueNotification $ NotificationSubmissionRated sub queueDBJob . JobQueueNotification $ NotificationSubmissionRated sub
redirect $ CSubmissionR tid ssh csh shn cid CorrectionR redirect $ CSubmissionR tid ssh csh shn cid CorrectionR
@ -531,7 +528,7 @@ postCorrectionR tid ssh csh shn cid = do
FormFailure errs -> mapM_ (addMessage Error . toHtml) errs FormFailure errs -> mapM_ (addMessage Error . toHtml) errs
FormSuccess fileUploads -> do FormSuccess fileUploads -> do
uid <- requireAuthId uid <- requireAuthId
void . runDBJobs . runConduit $ transPipe (lift . lift) fileUploads .| extractRatingsMsg .| sinkSubmission uid (Right sub) True void . runDBJobs . runConduit $ transPipe (lift . lift) fileUploads .| extractRatingsMsg .| sinkSubmission uid (Right sub) True
addMessageI Success MsgRatingFilesUpdated addMessageI Success MsgRatingFilesUpdated
@ -545,7 +542,7 @@ postCorrectionR tid ssh csh shn cid = do
_ -> notFound _ -> notFound
getCorrectionUserR tid ssh csh shn cid = do getCorrectionUserR tid ssh csh shn cid = do
sub <- decrypt cid sub <- decrypt cid
results <- runDB $ correctionData tid ssh csh shn sub results <- runDB $ correctionData tid ssh csh shn sub
case results of case results of
@ -557,7 +554,7 @@ getCorrectionUserR tid ssh csh shn cid = do
$(widgetFile "correction-user") $(widgetFile "correction-user")
_ -> notFound _ -> notFound
getCorrectionsUploadR, postCorrectionsUploadR :: Handler Html getCorrectionsUploadR, postCorrectionsUploadR :: Handler Html
getCorrectionsUploadR = postCorrectionsUploadR getCorrectionsUploadR = postCorrectionsUploadR
postCorrectionsUploadR = do postCorrectionsUploadR = do
@ -577,7 +574,7 @@ postCorrectionsUploadR = do
subs' <- traverse encrypt $ Set.toList subs :: Handler [CryptoFileNameSubmission] subs' <- traverse encrypt $ Set.toList subs :: Handler [CryptoFileNameSubmission]
mr <- (toHtml .) <$> getMessageRender mr <- (toHtml .) <$> getMessageRender
addMessage Success =<< withUrlRenderer ($(ihamletFile "templates/messages/correctionsUploaded.hamlet") mr) addMessage Success =<< withUrlRenderer ($(ihamletFile "templates/messages/correctionsUploaded.hamlet") mr)
defaultLayout $ defaultLayout $
$(widgetFile "corrections-upload") $(widgetFile "corrections-upload")
@ -692,17 +689,17 @@ postCorrectionsCreateR = do
insertMany_ . flip map spGroup $ \SheetPseudonym{sheetPseudonymUser} -> SubmissionUser insertMany_ . flip map spGroup $ \SheetPseudonym{sheetPseudonymUser} -> SubmissionUser
{ submissionUserUser = sheetPseudonymUser { submissionUserUser = sheetPseudonymUser
, submissionUserSubmission = subId , submissionUserSubmission = subId
} }
addMessageI Warning $ MsgSheetNoGroupSubmission sheetGroupDesc addMessageI Warning $ MsgSheetNoGroupSubmission sheetGroupDesc
redirect CorrectionsGradeR redirect CorrectionsGradeR
defaultLayout $ defaultLayout $
$(widgetFile "corrections-create") $(widgetFile "corrections-create")
where where
partitionEithers' :: [[Either a b]] -> ([[b]], [a]) partitionEithers' :: [[Either a b]] -> ([[b]], [a])
partitionEithers' = runWriter . mapM (WriterT . Identity . swap . partitionEithers) partitionEithers' = runWriter . mapM (WriterT . Identity . swap . partitionEithers)
textToList :: Textarea -> Handler (Either UniWorXMessage [[Pseudonym]]) textToList :: Textarea -> Handler (Either UniWorXMessage [[Pseudonym]])
textToList (map (map Text.strip . Text.splitOn ",") . filter (not . Text.null) . Text.lines . unTextarea -> ws) textToList (map (map Text.strip . Text.splitOn ",") . filter (not . Text.null) . Text.lines . unTextarea -> ws)
= let = let
@ -735,7 +732,7 @@ postCorrectionsGradeR = do
psValidator = def psValidator = def
& defaultSorting [("ratingtime", SortDesc)] :: PSValidator (RWST (Maybe (Env, FileEnv), UniWorX, [Lang]) Enctype Ints (HandlerT UniWorX IO)) (FormResult (DBFormResult CorrectionTableData SubmissionId (Bool, Maybe Points, Maybe Text))) & defaultSorting [("ratingtime", SortDesc)] :: PSValidator (RWST (Maybe (Env, FileEnv), UniWorX, [Lang]) Enctype Ints (HandlerT UniWorX IO)) (FormResult (DBFormResult CorrectionTableData SubmissionId (Bool, Maybe Points, Maybe Text)))
unFormResult = getDBFormResult $ \DBRow{ dbrOutput = (Entity _ sub@Submission{..}, _, _, _, _) } -> (submissionRatingDone sub, submissionRatingPoints, submissionRatingComment) unFormResult = getDBFormResult $ \DBRow{ dbrOutput = (Entity _ sub@Submission{..}, _, _, _, _) } -> (submissionRatingDone sub, submissionRatingPoints, submissionRatingComment)
tableForm <- makeCorrectionsTable whereClause displayColumns psValidator $ \i@(Entity subId _, Entity _ Sheet{ sheetName = shn }, (_, csh, tid, ssh), _, _) -> do tableForm <- makeCorrectionsTable whereClause displayColumns psValidator $ \i@(Entity subId _, Entity _ Sheet{ sheetName = shn }, (_, csh, tid, ssh), _, _) -> do
cID <- encrypt subId cID <- encrypt subId
void . assertM (== Authorized) . lift $ evalAccessDB (CSubmissionR tid ssh csh shn cID CorrectionR) True void . assertM (== Authorized) . lift $ evalAccessDB (CSubmissionR tid ssh csh shn cID CorrectionR) True
@ -762,3 +759,43 @@ postCorrectionsGradeR = do
defaultLayout $ defaultLayout $
$(widgetFile "corrections-grade") $(widgetFile "corrections-grade")
getSAssignR, postSAssignR :: TermId -> SchoolId -> CourseShorthand -> SheetName -> CryptoFileNameSubmission -> Handler Html
getSAssignR = postSAssignR
postSAssignR tid ssh csh shn cID = do
let actionUrl = CSubmissionR tid ssh csh shn cID SAssignR
sId <- decrypt cID
(currentCorrector, sheetCorrectors) <- runDB $ do
Submission{submissionRatingBy, submissionSheet} <- get404 sId
-- Beginn Verify that CryptoId matches ordinary prameters
-- Necessarry, since authorisation checks those parameters only, but can be changed by user!
Sheet{sheetCourse, sheetName} <- get404 submissionSheet
Course{courseTerm, courseSchool, courseShorthand} <- get404 sheetCourse
let cidMatches = and [tid==courseTerm, ssh==courseSchool, csh==courseShorthand, shn==sheetName]
unless cidMatches $ invalidArgsI [MsgErrorCryptoIdMismatch]
-- End Verification
sheetCorrectors <- map (sheetCorrectorUser . entityVal) <$> selectList [SheetCorrectorSheet ==. submissionSheet] []
userCorrector <- traverse getJustEntity submissionRatingBy
return (userCorrector, maybe id (:) submissionRatingBy sheetCorrectors)
$logDebugS "SAssignR" $ tshow currentCorrector
let correctorField = selectField $ optionsPersistCryptoId [UserId <-. sheetCorrectors] [Asc UserSurname, Asc UserDisplayName] userDisplayName
((corrResult, corrForm), corrEncoding) <- runFormPost . renderAForm FormStandard $
aopt correctorField (fslI MsgCorrector) (Just currentCorrector)
<* submitButton
formResult corrResult $ \(fmap entityKey -> mbUserId) -> do
when (mbUserId /= fmap entityKey currentCorrector) . runDB $ do
now <- liftIO getCurrentTime
update sId [ SubmissionRatingBy =. mbUserId
, SubmissionRatingAssigned =. (now <$ mbUserId)
]
addMessageI Success MsgCorrectorUpdated
redirect actionUrl
defaultLayout $ do
setTitleI MsgCorrectorAssignTitle
$(widgetFile "submission-assign")

View File

@ -1,2 +1,2 @@
<form .form-horizontal method=post action=@{actionUrl}#forms enctype=#{formEnctype}> <form method=post action=@{actionUrl}#forms enctype=#{formEnctype}>
^{formWidget} ^{formWidget}

View File

@ -1,5 +1,5 @@
$maybe text <- formText $maybe text <- formText
<h3> <h3>
_{text} _{text}
<form .form-horizontal method=post action=@{actionUrl}#forms enctype=#{formEnctype}> <form method=post action=@{actionUrl}#forms enctype=#{formEnctype}>
^{formWidget} ^{formWidget}

View File

@ -0,0 +1,2 @@
<form method=post action=@{actionUrl} enctype=#{corrEncoding}>
^{corrForm}