From e10cfe9c581e7b3b9ca93aced2293592a57ac78b Mon Sep 17 00:00:00 2001 From: Gregor Kleen Date: Fri, 12 Jun 2020 10:24:34 +0200 Subject: [PATCH] fix(corrections-overview): behavioural fixes --- frontend/src/app.sass | 2 +- frontend/src/utils/alerts/alerts.sass | 6 +- messages/uniworx/de-de-formal.msg | 2 + messages/uniworx/en-eu.msg | 2 + src/Handler/Submission/Assign.hs | 128 +++++++----- templates/corrections-overview.hamlet | 290 ++++++++++++++------------ 6 files changed, 241 insertions(+), 189 deletions(-) diff --git a/frontend/src/app.sass b/frontend/src/app.sass index 5d25396bc..29763b0fd 100644 --- a/frontend/src/app.sass +++ b/frontend/src/app.sass @@ -1223,7 +1223,7 @@ a.breadcrumbs__home font-size: 14px font-family: monospace -.func-field__wrapper, .allocation-missing-prios, .allocation-users__accept +.func-field__wrapper, .allocation-missing-prios, .allocation-users__accept, .corrections-overview__section max-height: 75vh overflow: auto diff --git a/frontend/src/utils/alerts/alerts.sass b/frontend/src/utils/alerts/alerts.sass index 340b85f01..937ddc42f 100644 --- a/frontend/src/utils/alerts/alerts.sass +++ b/frontend/src/utils/alerts/alerts.sass @@ -107,9 +107,10 @@ text-align: right position: absolute left: 0px - top: 0 + bottom: 0 width: 50px height: 100% + max-height: 100vh z-index: 40 &::before @@ -130,9 +131,10 @@ text-align: right position: absolute right: 0px - top: 0 + bottom: 0 width: 60px height: 100% + max-height: 100vh transition: all .3s ease z-index: 40 diff --git a/messages/uniworx/de-de-formal.msg b/messages/uniworx/de-de-formal.msg index 173abe440..486bf31ae 100644 --- a/messages/uniworx/de-de-formal.msg +++ b/messages/uniworx/de-de-formal.msg @@ -32,6 +32,7 @@ BtnLecInvDecline: Ablehnen BtnCorrInvAccept: Annehmen BtnCorrInvDecline: Ablehnen BtnSubmissionsAssign: Abgaben automatisch zuteilen +BtnSubmissionsAssignAll: Abgaben automatisch zuteilen BtnAllocationCompute: Vergabe berechnen BtnAllocationAccept: Vergabe akzeptieren BtnSystemMessageHide: Verstecken @@ -633,6 +634,7 @@ NrSubmissionsNotCorrected: Unkorrigiert NrSubmissionsNotCorrectedShort: Unkg. CorrectionTime: Korrekturdauer AssignSubmissionsRandomWarning: Die Zuteilungsvorschau kann von der tatsächlichen Zuteilung abweichen, wenn mehrere Blätter auf einmal zugeteilt werden, da beim Ausgleich der Kontigente nur bereits zugeteilte Abgaben berücksichtigt werden. Da es ein randomisierte Prozess ist, kann es auch bei einzelnen Blättern gerinfgügige Abweichungen geben. +AssignSubmissionsAssignableSheets: Korrekturen verteilen für: CorrectionsUploaded num@Int64: #{num} Korrekturen wurden gespeichert: NoCorrectionsUploaded: In der hochgeladenen Datei wurden keine Korrekturen gefunden. diff --git a/messages/uniworx/en-eu.msg b/messages/uniworx/en-eu.msg index 22b9216ce..386421505 100644 --- a/messages/uniworx/en-eu.msg +++ b/messages/uniworx/en-eu.msg @@ -32,6 +32,7 @@ BtnLecInvDecline: Decline BtnCorrInvAccept: Accept BtnCorrInvDecline: Decline BtnSubmissionsAssign: Assign submissions automatically +BtnSubmissionsAssignAll: Automatically distribute corrections BtnAllocationCompute: Compute allocation BtnAllocationAccept: Accept allocation BtnSystemMessageHide: Hide @@ -631,6 +632,7 @@ NrSubmissionsNotCorrected: Not corrected NrSubmissionsNotCorrectedShort: N.corr. CorrectionTime: Correction time AssignSubmissionsRandomWarning: The assignment preview might be different from the actual assignment if multiple sheets are being distributed. This is due to the fact that only assigned submissions are considered when handling corrector-deficits. Due to this being a randomised process small differences are also possible for a single sheet. +AssignSubmissionsAssignableSheets: Distribute corrections for: CorrectionsUploaded num: Successfully saved #{num} #{pluralEN num "correction" "corrections"}: NoCorrectionsUploaded: No corrections could be found within the uploaded file. diff --git a/src/Handler/Submission/Assign.hs b/src/Handler/Submission/Assign.hs index 3094607a6..1a9f93c00 100644 --- a/src/Handler/Submission/Assign.hs +++ b/src/Handler/Submission/Assign.hs @@ -4,7 +4,7 @@ module Handler.Submission.Assign , getSAssignR, postSAssignR ) where -import Import hiding (link) +import Import hiding (link, unzip) import Handler.Utils hiding (colSchool) import Handler.Utils.Corrections @@ -12,7 +12,7 @@ import Handler.Utils.Submission import Data.List as List (foldl, foldr) import qualified Data.Set as Set -import Data.Map.Strict ((!)) +import Data.Map.Strict ((!), (!?)) import qualified Data.Map.Strict as Map import qualified Data.Text as Text @@ -20,6 +20,8 @@ import qualified Data.CaseInsensitive as CI import qualified Database.Esqueleto as E +import Data.List.NonEmpty (unzip) + getSubAssignR, postSubAssignR :: TermId -> SchoolId -> CourseShorthand -> SheetName -> CryptoFileNameSubmission -> Handler Html getSubAssignR = postSubAssignR @@ -56,15 +58,6 @@ postSubAssignR tid ssh csh shn cID = do $(widgetFile "submission-assign") -data ButtonSubmissionsAssign = BtnSubmissionsAssign - deriving (Enum, Eq, Ord, Bounded, Read, Show, Generic, Typeable) -instance Universe ButtonSubmissionsAssign -instance Finite ButtonSubmissionsAssign -nullaryPathPiece ''ButtonSubmissionsAssign camelToPathPiece -embedRenderMessage ''UniWorX ''ButtonSubmissionsAssign id -instance Button UniWorX ButtonSubmissionsAssign where - btnClasses BtnSubmissionsAssign = [BCIsButton, BCPrimary] - getCAssignR, postCAssignR :: TermId -> SchoolId -> CourseShorthand -> Handler Html getCAssignR = postCAssignR postCAssignR tid ssh csh = do @@ -77,35 +70,22 @@ postSAssignR tid ssh csh shn = do (shid,cid) <- runDB $ fetchSheetIdCourseId tid ssh csh shn assignHandler tid ssh csh cid [shid] -{- TODO: Feature: - make distivt buttons for each sheet, so that users see which sheet will be assigned. - Currently this information is available within the page heading! - - Stub: -data ButtonCorrectionsAssign = BtnCorrectionsAssignAll | BtnCorrectionsAssignSheet SheetName - deriving (Enum, Eq, Ord, Bounded, Read, Show, Generic, Typeable) -instance Button UniWorX ButtonCorrectionsAssign --- Are those needed any more? -instance Universe ButtonCorrectionsAssign -instance Finite ButtonCorrectionsAssign -nullaryPathPiece ''ButtonCorrectionsAssign camelToPathPiece -embedRenderMessage ''UniWorX ''ButtonCorrectionsAssign id -instance Button UniWorX ButtonCorrectionsAssign where - btnClasses BtnCorrectionsAssign = [BCIsButton, BCPrimary] --- use runButtonForm' instead later on --} assignHandler :: TermId -> SchoolId -> CourseShorthand -> CourseId -> [SheetId] -> Handler Html assignHandler tid ssh csh cid assignSids = do - -- evaluate form first, since it affects DB action - (btnWdgt, btnResult) <- runButtonForm FIDAssignSubmissions - + currentRoute <- fromMaybe (error "assignHandler called from 404-handler") <$> liftHandler getCurrentRoute + -- gather data - (orderedSheetNames, assignSheetNames, nrParticipants, groupsPossible, infoMap, correctorMap, assignment) <- runDB $ do + (orderedSheetNames, assignSheetNames, nrParticipants, groupsPossible, infoMap, correctorMap, assignment, ((btnViews, btnCsrf), btnEncoding)) <- runDB $ do -- cid <- getKeyBy404 $ TermSchoolCourseShort tid ssh csh nrParticipants <- count [CourseParticipantCourse ==. cid, CourseParticipantState ==. CourseParticipantActive ] sheetList <- selectList [SheetCourse ==. cid] [Desc SheetActiveTo, Desc SheetActiveFrom] + assignSids' <- if + | null assignSids -> -- assignAll; we distinguish assignSids' here avoid useless Alerts + selectKeysList [SheetCourse ==. cid] [Asc SheetActiveTo] + | otherwise -> return $ filter (`elem` map entityKey sheetList) assignSids + let orderedSheetNames = fmap (\(Entity _ Sheet{sheetName}) -> sheetName) sheetList sheets = entities2map sheetList sheetIds = Map.keys sheets @@ -114,6 +94,12 @@ assignHandler tid ssh csh cid assignSids = do let foldFun (Entity _ Sheet{sheetGrouping=sgr}) acc = acc || sgr /= NoGroups in List.foldr foldFun False sheetList assignSheetNames = fmap sheetName $ mapMaybe (\sid -> Map.lookup sid sheets) assignSids + assignSheetNames' = fmap sheetName $ mapMaybe (\sid -> Map.lookup sid sheets) assignSids' + + assignButtons = Map.fromSet (maybe BtnSubmissionsAssignAll BtnSubmissionsAssign) $ Set.fromList . bool (Nothing :) id (null sheetList) $ map Just assignSheetNames' + + ((btnResult, btnViews'), btnEncoding) <- runFormPost . identifyForm FIDAssignSubmissions $ \csrf -> + fmap (over _1 (asum . fmap (hoistMaybe =<<)) . over _2 (, csrf) . unzip) . for assignButtons $ \btn -> mopt (buttonField btn) "" Nothing -- plan or assign unassigned submissions for given sheets let buildA :: (Map SheetName ((Set SubmissionId, Set SubmissionId), Map (Maybe UserId) Int, Map UserId Rational)) -> SheetId -> DB (Map SheetName ((Set SubmissionId, Set SubmissionId), Map (Maybe UserId) Int, Map UserId Rational)) @@ -128,24 +114,29 @@ assignHandler tid ssh csh cid assignSids = do ignoreExceptions (SubmissionsNotFound _sids_not_found) = return mempty -- cannot happen, since last argument to planSubmissions is Nothing (plan,deficit) <- lift $ handle ignoreExceptions $ planSubmissions sid Nothing guard $ not $ null plan -- only proceed if there is a plan for this sheet - -- implement assignment plan - status <- lift $ case btnResult of - Nothing -> return (Set.empty, Set.empty) - (Just BtnSubmissionsAssign) -> do - status@(sub_ok,sub_fail) <- writeSubmissionPlan plan - let nr_ok = olength sub_ok - nr_fail = olength sub_fail - alert_ok = toMaybe (nr_ok > 0) $ SomeMessage $ MsgUpdatedSheetCorrectorsAutoAssigned nr_ok - alert_fail = toMaybe (nr_fail > 0) $ SomeMessage $ MsgUpdatedSheetCorrectorsAutoFailed nr_fail - msg_status = bool Success Error $ nr_fail > 0 - msg_header = SomeMessage $ shn <> ":" - when (nr_ok > 0 || nr_fail > 0) $ - addMessageI msg_status $ UniWorXMessages $ msg_header : catMaybes [alert_ok, alert_fail] - return status - return $ Map.insert shn (status, countMapElems plan, deficit) acc - assignSids' <- if null assignSids -- assignAll; we distinguish assignSids' here avoid useless Alerts - then selectKeysList [SheetCourse ==. cid] [Asc SheetActiveTo] - else return assignSids + status@(sub_ok, sub_fail) <- fmap fold . formResultMaybe btnResult $ \btn -> lift . maybeT (return Nothing) $ do + guard $ case btn of + BtnSubmissionsAssignAll -> True + BtnSubmissionsAssign shn' -> shn' == shn + + status@(sub_ok,sub_fail) <- lift $ writeSubmissionPlan plan + let nr_ok = olength sub_ok + nr_fail = olength sub_fail + alert_ok = toMaybe (nr_ok > 0) $ SomeMessage $ MsgUpdatedSheetCorrectorsAutoAssigned nr_ok + alert_fail = toMaybe (nr_fail > 0) $ SomeMessage $ MsgUpdatedSheetCorrectorsAutoFailed nr_fail + msg_status = bool Success Error $ nr_fail > 0 + msg_header = SomeMessage $ shn <> ":" + if | nr_ok > 0 || nr_fail > 0 -> do + addMessageI msg_status $ UniWorXMessages $ msg_header : catMaybes [alert_ok, alert_fail] + return $ Just status + | otherwise -> do + addMessageI Error $ MsgSheetsUnassignable $ CI.original shn + return Nothing + if | null sub_ok && null sub_fail -> + return $ Map.insert shn (status, countMapElems plan, deficit) acc + | otherwise -> do + (plan', deficit') <- lift $ handle ignoreExceptions $ planSubmissions sid Nothing + return $ Map.insert shn (status, countMapElems plan', deficit') acc assignment <- foldM buildA Map.empty assignSids' correctors <- E.select . E.from $ \(corrector `E.InnerJoin` user) -> do @@ -202,7 +193,7 @@ assignHandler tid ssh csh cid assignSids = do } in Map.insertWith (Map.unionWith (<>)) shnm cinf m - return (orderedSheetNames, assignSheetNames, nrParticipants, groupsPossible, infoMap, correctorMap, assignment) + return (orderedSheetNames, assignSheetNames, nrParticipants, groupsPossible, infoMap, correctorMap, assignment, (btnViews', btnEncoding)) let -- infoMap :: Map SheetName (Map (Maybe UserId) CorrectionInfo) -- repeated here for easier reference -- create aggregate maps @@ -268,14 +259,39 @@ assignHandler tid ssh csh cid assignSids = do showAvgsDays :: Maybe NominalDiffTime -> Integer -> Text showAvgsDays Nothing _ = mempty showAvgsDays (Just dt) n = formatDiffDays $ dt / fromIntegral n - let headingShort - | 0 < Map.size assignment = MsgMenuCorrectionsAssignSheet $ Text.intercalate ", " $ fmap CI.original $ Map.keys assignment - | otherwise = MsgMenuCorrectionsAssign - headingLong = prependCourseTitle tid ssh csh MsgMenuCorrectionsAssign + let headingShort = MsgMenuCorrectionsAssign + headingLong = prependCourseTitle tid ssh csh MsgMenuCorrectionsAssign unassignableSheets = filter (\shn -> Map.notMember shn assignment) assignSheetNames unless (null unassignableSheets) $ addMessageI Warning $ MsgSheetsUnassignable $ Text.intercalate ", " $ fmap CI.original unassignableSheets siteLayoutMsg headingShort $ do setTitleI headingLong - $(widgetFile "corrections-overview") + + let doWrap wdgt + | null btnViews = wdgt + | otherwise = wrapForm (toWidget btnCsrf <> wdgt) def + { formAction = Just . SomeRoute $ currentRoute + , formEncoding = btnEncoding + , formSubmit = FormNoSubmit + } + sheetBtnViews = Map.fromList [ (shn, btn) | (Just shn, btn) <- Map.toList btnViews, shn `elem` assignSheetNames' ] + assignSheetNames' = Map.keys $ Map.filter (\(_, new, _) -> any (> 0) $ Map.delete Nothing new) assignment + + doWrap $(widgetFile "corrections-overview") + + +data ButtonSubmissionsAssign + = BtnSubmissionsAssign SheetName + | BtnSubmissionsAssignAll + deriving (Eq, Ord, Read, Show, Generic, Typeable) +derivePathPiece ''ButtonSubmissionsAssign (camelToPathPiece' 2) "--" + +instance RenderMessage UniWorX ButtonSubmissionsAssign where + renderMessage f ls = \case + BtnSubmissionsAssign _ -> mr MsgBtnSubmissionsAssign + BtnSubmissionsAssignAll -> mr MsgBtnSubmissionsAssignAll + where mr = renderMessage f ls + +instance Button UniWorX ButtonSubmissionsAssign where + btnClasses _ = [BCIsButton, BCPrimary] diff --git a/templates/corrections-overview.hamlet b/templates/corrections-overview.hamlet index cb85fe3e0..d3a45dc65 100644 --- a/templates/corrections-overview.hamlet +++ b/templates/corrections-overview.hamlet @@ -2,146 +2,176 @@ $newline never

_{MsgCorrectionSheets} - _{MsgCourseParticipants nrParticipants} - - - - -
_{MsgSheet} - $if groupsPossible - _{MsgNrSubmittorsTotal} - _{MsgNrSubmissionsTotal} - _{MsgNrSubmissionsNotAssigned} - _{MsgNrSubmissionsNotCorrected} - _{MsgCorrectionTime} -
- _{MsgGenericNumChange} - _{MsgGenericMin} - _{MsgGenericAvg} - _{MsgGenericMax} - $# Always iterate over orderedSheetNames for consistent sorting! Newest first, except in this table - $forall sheetName <- reverse orderedSheetNames - $maybe CorrectionInfo{ciSubmittors, ciSubmissions, ciAssigned, ciCorrected, ciMin, ciTot, ciMax} <- Map.lookup sheetName sheetMap -
^{simpleLink (toWidget sheetName) (CSheetR tid ssh csh sheetName SSubsR)} - $if groupsPossible - #{ciSubmittors} - #{ciSubmissions} - $maybe ((splus,sfailed),_,_) <- Map.lookup sheetName assignment - $if 0 < Set.size sfailed - #{ciSubmissions - ciAssigned} - (-#{show (Set.size splus)}, failed: #{show (Set.size sfailed)}) - $elseif 0 < Set.size splus - #{ciSubmissions - ciAssigned} - (-#{show (Set.size splus)}) - $else +

+ _{MsgCourseParticipants nrParticipants} + +

+ + + + +
_{MsgSheet} + $if groupsPossible + _{MsgNrSubmittorsTotal} + _{MsgNrSubmissionsTotal} + _{MsgNrSubmissionsNotAssigned} + _{MsgNrSubmissionsNotCorrected} + _{MsgCorrectionTime} +
+ _{MsgGenericNumChange} + _{MsgGenericMin} + _{MsgGenericAvg} + _{MsgGenericMax} + $# Always iterate over orderedSheetNames for consistent sorting! Newest first, except in this table + $forall sheetName <- reverse orderedSheetNames + $maybe CorrectionInfo{ciSubmittors, ciSubmissions, ciAssigned, ciCorrected, ciMin, ciTot, ciMax} <- Map.lookup sheetName sheetMap +
^{simpleLink (toWidget sheetName) (CSheetR tid ssh csh sheetName SSubsR)} + $if groupsPossible + #{ciSubmittors} + #{ciSubmissions} + $maybe ((splus,sfailed),_,_) <- Map.lookup sheetName assignment + $if 0 < Set.size sfailed + #{ciSubmissions - ciAssigned} + (-#{show (Set.size splus)}, failed: #{show (Set.size sfailed)}) + $elseif 0 < Set.size splus + #{ciSubmissions - ciAssigned} + (-#{show (Set.size splus)}) + $else + #{ciSubmissions - ciAssigned} + + $nothing #{ciSubmissions - ciAssigned} - $nothing - #{ciSubmissions - ciAssigned} - - #{ciSubmissions - ciCorrected} - #{showDiffDays ciMin} - #{showAvgsDays ciTot ciCorrected} - #{showDiffDays ciMax} + #{ciSubmissions - ciCorrected} + #{showDiffDays ciMin} + #{showAvgsDays ciTot ciCorrected} + #{showDiffDays ciMax}

_{MsgCorrectionCorrectors} - - - - - -
_{MsgCorrector} - _{MsgGenericAll} - _{MsgCorDeficitProportion} - _{MsgCorrectionTime} - - $# Always iterate over orderedSheetNames for consistent sorting! Newest first, except in this table - $forall shn <- orderedSheetNames - - ^{simpleLink (toWidget shn) (CSheetR tid ssh csh shn SShowR)} -
_{MsgNrSubmissionsTotal} - _{MsgNrSubmissionsNotCorrected} - _{MsgGenericMin} - _{MsgGenericAvg} - _{MsgGenericMax} - $# Always iterate over orderedSheetNames for consistent sorting! Newest first, except in this table - $forall _shn <- orderedSheetNames - _{MsgCorProportion} - _{MsgNrSubmissionsTotalShort} - _{MsgGenericNumChange} - _{MsgNrSubmissionsNotCorrectedShort} - _{MsgGenericAvg} - $forall (CorrectionInfo{ciCorrector, ciSubmissions=ciSubmissionsNr, ciCorrected, ciMin, ciTot, ciMax}) <- corrInfos - $with (nameW,loadM, name) <- getCorrector ciCorrector - $# TODO: User proper Tooltips instead of title attribute here, once Tooltips work with tables -
^{nameW} - #{ciSubmissionsNr} - $with total <- ciSubmissions corrMapSum - $if total > 0 - \ (#{textPercent' True 0 ciSubmissionsNr total}) - #{ciSubmissionsNr - ciCorrected} - - $maybe deficit <- getCorrDeficit ciCorrector - #{rationalToFixed3 deficit} - #{showDiffDays ciMin} - #{showAvgsDays ciTot ciCorrected} - #{showDiffDays ciMax} - $# Always iterate over orderedSheetNames for consistent sorting! Newest first, except in this table - $forall shn <- orderedSheetNames - $maybe CorrectionInfo{ciSubmissions=sheetSubmissionsNr} <- Map.lookup shn sheetMap - - $maybe SheetCorrector{sheetCorrectorLoad, sheetCorrectorState} <- Map.lookup shn loadM - #{showCompactCorrectorLoad sheetCorrectorLoad sheetCorrectorState} - $if sheetCorrectorState == CorrectorNormal - $maybe Load{byProportion=total} <- Map.lookup shn sheetLoad - $if total > 0 - \ (#{textPercent' True 0 (byProportion sheetCorrectorLoad) total}) - $maybe CorrectionInfo{ciSubmissions,ciCorrected,ciTot} <- getCorrSheetStatus ciCorrector shn - #{ciSubmissions} - $if sheetSubmissionsNr > 0 - \ (#{textPercent' True 0 ciSubmissions sheetSubmissionsNr}) - $maybe nrNew <- getCorrNewAssignment ciCorrector shn - $# #{ciAssigned} `ciSubmissions` is here always identical to `ciAssigned` and also works for `ciCorrector == Nothing`. ciAssigned only useful in aggregate maps like `sheetMap` - (+#{nrNew}) - $nothing - - #{ciSubmissions - ciCorrected} - #{showAvgsDays ciTot ciCorrected} - $nothing - - - - - $if not (null orderedSheetNames) -
Σ - $with ciSubmissionsNr <- ciSubmissions corrMapSum - $with ciCorrectedNr <- ciCorrected corrMapSum - #{ciSubmissionsNr} - #{ciSubmissionsNr - ciCorrectedNr} - #{ciCorrected corrMapSum} - #{showDiffDays (ciMin corrMapSum)} - #{showAvgsDays (ciTot corrMapSum) (ciCorrected corrMapSum)} - #{showDiffDays (ciMax corrMapSum)} - $# Always iterate over orderedSheetNames for consistent sorting! Newest first, except in this table - $forall shn <- orderedSheetNames - $maybe CorrectionInfo{ciSubmissions} <- Map.lookup shn sheetMap - #{getLoadSum shn} - #{ciSubmissions} - ^{simpleLinkI (SomeMessage MsgMenuCorrectorsChange) (CSheetR tid ssh csh shn SEditR)} - +
+ - + + + + +
- - - + _{MsgCorrector} + _{MsgGenericAll} + _{MsgCorDeficitProportion} + _{MsgCorrectionTime} + $# Always iterate over orderedSheetNames for consistent sorting! Newest first, except in this table $forall shn <- orderedSheetNames ^{simpleLink (toWidget shn) (CSheetR tid ssh csh shn SShowR)} +
_{MsgNrSubmissionsTotal} + _{MsgNrSubmissionsNotCorrected} + _{MsgGenericMin} + _{MsgGenericAvg} + _{MsgGenericMax} + $# Always iterate over orderedSheetNames for consistent sorting! Newest first, except in this table + $forall _shn <- orderedSheetNames + _{MsgCorProportion} + _{MsgNrSubmissionsTotalShort} + _{MsgGenericNumChange} + _{MsgNrSubmissionsNotCorrectedShort} + _{MsgGenericAvg} + $forall (CorrectionInfo{ciCorrector, ciSubmissions=ciSubmissionsNr, ciCorrected, ciMin, ciTot, ciMax}) <- corrInfos + $with (nameW,loadM, name) <- getCorrector ciCorrector + $# TODO: User proper Tooltips instead of title attribute here, once Tooltips work with tables +
^{nameW} + #{ciSubmissionsNr} + $with total <- ciSubmissions corrMapSum + $if total > 0 + \ (#{textPercent' True 0 ciSubmissionsNr total}) + #{ciSubmissionsNr - ciCorrected} + + $maybe deficit <- getCorrDeficit ciCorrector + #{rationalToFixed3 deficit} + #{showDiffDays ciMin} + #{showAvgsDays ciTot ciCorrected} + #{showDiffDays ciMax} + $# Always iterate over orderedSheetNames for consistent sorting! Newest first, except in this table + $forall shn <- orderedSheetNames + $maybe CorrectionInfo{ciSubmissions=sheetSubmissionsNr} <- Map.lookup shn sheetMap + + $maybe SheetCorrector{sheetCorrectorLoad, sheetCorrectorState} <- Map.lookup shn loadM + #{showCompactCorrectorLoad sheetCorrectorLoad sheetCorrectorState} + $if sheetCorrectorState == CorrectorNormal + $maybe Load{byProportion=total} <- Map.lookup shn sheetLoad + $if total > 0 + \ (#{textPercent' True 0 (byProportion sheetCorrectorLoad) total}) + $maybe CorrectionInfo{ciSubmissions,ciCorrected,ciTot} <- getCorrSheetStatus ciCorrector shn + #{ciSubmissions} + $if sheetSubmissionsNr > 0 + \ (#{textPercent' True 0 ciSubmissions sheetSubmissionsNr}) + $maybe nrNew <- getCorrNewAssignment ciCorrector shn + $# #{ciAssigned} `ciSubmissions` is here always identical to `ciAssigned` and also works for `ciCorrector == Nothing`. ciAssigned only useful in aggregate maps like `sheetMap` + (+#{nrNew}) + $nothing + + #{ciSubmissions - ciCorrected} + #{showAvgsDays ciTot ciCorrected} + $nothing + + + + + $if not (null orderedSheetNames) +
Σ + $with ciSubmissionsNr <- ciSubmissions corrMapSum + $with ciCorrectedNr <- ciCorrected corrMapSum + #{ciSubmissionsNr} + #{ciSubmissionsNr - ciCorrectedNr} + #{ciCorrected corrMapSum} + #{showDiffDays (ciMin corrMapSum)} + #{showAvgsDays (ciTot corrMapSum) (ciCorrected corrMapSum)} + #{showDiffDays (ciMax corrMapSum)} + $# Always iterate over orderedSheetNames for consistent sorting! Newest first, except in this table + $forall shn <- orderedSheetNames + $maybe CorrectionInfo{ciSubmissions} <- Map.lookup shn sheetMap + #{getLoadSum shn} + #{ciSubmissions} + ^{simpleLinkI (SomeMessage MsgMenuCorrectorsChange) (CSheetR tid ssh csh shn SEditR)} - ^{btnWdgt} + $if not (null assignment) +
+ + + + $# Always iterate over orderedSheetNames for consistent sorting! Newest first, except in this table + $forall shn <- orderedSheetNames + + $maybe btnView <- sheetBtnViews !? shn + $if has (ix shn) assignment + ^{fvWidget btnView} + +
+ + + + $# Always iterate over orderedSheetNames for consistent sorting! Newest first, except in this table + $forall shn <- orderedSheetNames + + ^{simpleLink (toWidget shn) (CSheetR tid ssh csh shn SShowR)} + +

+ _{MsgAssignSubmissionsRandomWarning} + +$if not (null assignment) + $maybe btnView <- btnViews !? Nothing +

+

+ _{MsgAssignSubmissionsAssignableSheets} +