feat(submissions): optionally disable consideration for deficit
This commit is contained in:
parent
8c4228dcba
commit
c6a6ec721c
@ -61,6 +61,8 @@ SheetSubmissionModeNoneWithoutNotGraded: Es wurde "Keine Abgabe" eingestellt, je
|
|||||||
SheetWarnNoActiveTo: "Aktiv bis/Ende Abgabezeitraum" sollte stets angegeben werden
|
SheetWarnNoActiveTo: "Aktiv bis/Ende Abgabezeitraum" sollte stets angegeben werden
|
||||||
CountTutProp: Tutorien zählen gegen Proportion
|
CountTutProp: Tutorien zählen gegen Proportion
|
||||||
CountTutPropTip: Wenn Abgaben nach Tutorium zugeteilt werden, zählen diese Zuteilungen in Bezug auf den jeweiligen Anteil?
|
CountTutPropTip: Wenn Abgaben nach Tutorium zugeteilt werden, zählen diese Zuteilungen in Bezug auf den jeweiligen Anteil?
|
||||||
|
ConsiderDeficits: Defizite ausgleichen
|
||||||
|
ConsiderDeficitsTip: Wenn einem Korrektor/einer Korrektorin (nach aktuellem Datenstand) über alle Blätter des Kurses hinweg weniger Korrekturen zugeteilt wurden als nach den Anteilen vorgesehen, soll versucht werden diese Defizite mit diesem Übungsblatt auszugleichen?
|
||||||
SheetCorrector: Korrektor
|
SheetCorrector: Korrektor
|
||||||
CorrectorExists: Nutzer:in ist bereits als Korrektor:in eingetragen
|
CorrectorExists: Nutzer:in ist bereits als Korrektor:in eingetragen
|
||||||
SheetCorrectorState: Status
|
SheetCorrectorState: Status
|
||||||
|
|||||||
@ -61,6 +61,8 @@ SheetSubmissionModeNoneWithoutNotGraded: The sheet was configured to be "No subm
|
|||||||
SheetWarnNoActiveTo: “Active to/Submission period end” should always be specified
|
SheetWarnNoActiveTo: “Active to/Submission period end” should always be specified
|
||||||
CountTutProp: Tutorials count against proportion
|
CountTutProp: Tutorials count against proportion
|
||||||
CountTutPropTip: If submissions are assigned by tutorial, do those assignments count with regard to the set proportion?
|
CountTutPropTip: If submissions are assigned by tutorial, do those assignments count with regard to the set proportion?
|
||||||
|
ConsiderDeficits: Compensate deficits
|
||||||
|
ConsiderDeficitsTip: When a corrector (as per the current state) was assigned fewer or more corrections than would be expected according to their proportions, this is considered a deficit. Should Uni2work try to compensate for these deficits when assigning corrections for this sheet?
|
||||||
SheetCorrector: Corrector
|
SheetCorrector: Corrector
|
||||||
CorrectorExists: User already is a corrector
|
CorrectorExists: User already is a corrector
|
||||||
SheetCorrectorState: State
|
SheetCorrectorState: State
|
||||||
|
|||||||
@ -1,13 +0,0 @@
|
|||||||
ChangelogItemFeature: Feature
|
|
||||||
ChangelogItemBugfix: Bugfix
|
|
||||||
SexNotKnown: Unbekannt
|
|
||||||
SexMale: Männlich
|
|
||||||
SexFemale: Weiblich
|
|
||||||
SexNotApplicable: Keine Angabe
|
|
||||||
NoSubmissions: Keine Abgabe
|
|
||||||
CorrectorSubmissions: Abgabe extern mit Pseudonym
|
|
||||||
UserSubmissions: Direkte Abgabe in Uni2work
|
|
||||||
SystemExamOffice: Prüfungsverwaltung
|
|
||||||
SystemFaculty: Fakultätsmitglied
|
|
||||||
SystemStudent: Student:in
|
|
||||||
BothSubmissions: Abgabe direkt in Uni2work & extern mit Pseudonym
|
|
||||||
13
messages/uniworx/categories/model_types/de-de-formal.msg
Normal file
13
messages/uniworx/categories/model_types/de-de-formal.msg
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
ChangelogItemFeature: Feature
|
||||||
|
ChangelogItemBugfix: Bugfix
|
||||||
|
SexNotKnown: Unknown
|
||||||
|
SexMale: Male
|
||||||
|
SexFemale: Female
|
||||||
|
SexNotApplicable: Not applicable
|
||||||
|
NoSubmissions: No submission
|
||||||
|
CorrectorSubmissions: External submission via pseudonym
|
||||||
|
UserSubmissions: Direct submission in Uni2work
|
||||||
|
SystemExamOffice: Exam office
|
||||||
|
SystemFaculty: Faculty member
|
||||||
|
SystemStudent: Student
|
||||||
|
BothSubmissions: Submission either directly in Uni2work or externally via pseudonym
|
||||||
@ -128,7 +128,7 @@ MenuGlobalWorkflowInstanceList: Systemweite Workflows
|
|||||||
MenuTopWorkflowInstanceList: Workflows
|
MenuTopWorkflowInstanceList: Workflows
|
||||||
MenuTopWorkflowWorkflowList: Laufende Workflows
|
MenuTopWorkflowWorkflowList: Laufende Workflows
|
||||||
MenuTopWorkflowWorkflowListHeader: Workflows
|
MenuTopWorkflowWorkflowListHeader: Workflows
|
||||||
MenuGlossary:
|
MenuGlossary: Begriffsverzeichnis
|
||||||
MenuVersion: Versionsgeschichte
|
MenuVersion: Versionsgeschichte
|
||||||
MenuCourseNewsNew: Neue Kursnachricht
|
MenuCourseNewsNew: Neue Kursnachricht
|
||||||
MenuCourseNewsEdit: Kursnachricht bearbeiten
|
MenuCourseNewsEdit: Kursnachricht bearbeiten
|
||||||
|
|||||||
@ -21,6 +21,7 @@ function translations() {
|
|||||||
msgFile=$1
|
msgFile=$1
|
||||||
|
|
||||||
sed -r 's/^([^ :]+).*$/\1/' ${msgFile} \
|
sed -r 's/^([^ :]+).*$/\1/' ${msgFile} \
|
||||||
|
| sed -r '/^\s*#/d' \
|
||||||
| sort
|
| sort
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -166,8 +166,7 @@ correctorForm loads' = wFormToAForm $ do
|
|||||||
loads :: Map (Either UserEmail UserId) (CorrectorState, Load)
|
loads :: Map (Either UserEmail UserId) (CorrectorState, Load)
|
||||||
loads = loads' <&> \(InvDBDataSheetCorrector load cState, InvTokenDataSheetCorrector) -> (cState, load)
|
loads = loads' <&> \(InvDBDataSheetCorrector load cState, InvTokenDataSheetCorrector) -> (cState, load)
|
||||||
|
|
||||||
countTutRes <- wpopt checkBoxField (fslI MsgCountTutProp & setTooltip MsgCountTutPropTip) . Just . any (\(_, Load{..}) -> Just True == byTutorial) $ Map.elems loads
|
countTutRes <- wpopt checkBoxField (fslI MsgCountTutProp & setTooltip MsgCountTutPropTip) . Just . any (\(_, Load{..}) -> fromMaybe False byTutorial) $ Map.elems loads
|
||||||
|
|
||||||
|
|
||||||
let
|
let
|
||||||
previousCorrectors :: E.SqlQuery (E.SqlExpr (Entity User))
|
previousCorrectors :: E.SqlQuery (E.SqlExpr (Entity User))
|
||||||
@ -203,13 +202,15 @@ correctorForm loads' = wFormToAForm $ do
|
|||||||
miCell _ userIdent initRes nudge csrf = do
|
miCell _ userIdent initRes nudge csrf = do
|
||||||
(stateRes, stateView) <- mreq (selectField optionsFinite) (fslI MsgSheetCorrectorState & addName (nudge "state")) $ (fst <$> initRes) <|> Just CorrectorNormal
|
(stateRes, stateView) <- mreq (selectField optionsFinite) (fslI MsgSheetCorrectorState & addName (nudge "state")) $ (fst <$> initRes) <|> Just CorrectorNormal
|
||||||
(byTutRes, byTutView) <- mreq checkBoxField ("" & addName (nudge "bytut")) $ (isJust . byTutorial . snd <$> initRes) <|> Just False
|
(byTutRes, byTutView) <- mreq checkBoxField ("" & addName (nudge "bytut")) $ (isJust . byTutorial . snd <$> initRes) <|> Just False
|
||||||
|
(deficitRes, deficitView) <- mreq checkBoxField ("" & addName (nudge "deficit")) $ ((/= 0) . byDeficit . snd <$> initRes) <|> Just True
|
||||||
(propRes, propView) <- mreq (checkBool (>= 0) MsgProportionNegative rationalField) (fslI MsgSheetCorrectorProportion & addName (nudge "prop")) $ (byProportion . snd <$> initRes) <|> Just 0
|
(propRes, propView) <- mreq (checkBool (>= 0) MsgProportionNegative rationalField) (fslI MsgSheetCorrectorProportion & addName (nudge "prop")) $ (byProportion . snd <$> initRes) <|> Just 0
|
||||||
let
|
let
|
||||||
res :: FormResult (CorrectorState, Load)
|
res :: FormResult (CorrectorState, Load)
|
||||||
res = (,) <$> stateRes <*> (Load <$> tutRes' <*> propRes)
|
res = (,) <$> stateRes <*> (Load <$> tutRes' <*> propRes <*> deficitRes')
|
||||||
tutRes'
|
tutRes'
|
||||||
| FormSuccess True <- byTutRes = Just <$> countTutRes
|
| FormSuccess True <- byTutRes = Just <$> countTutRes
|
||||||
| otherwise = Nothing <$ byTutRes
|
| otherwise = Nothing <$ byTutRes
|
||||||
|
deficitRes' = bool 0 1 <$> deficitRes
|
||||||
identWidget <- case userIdent of
|
identWidget <- case userIdent of
|
||||||
Left email -> return . toWidget $ mailtoHtml email
|
Left email -> return . toWidget $ mailtoHtml email
|
||||||
Right uid -> do
|
Right uid -> do
|
||||||
|
|||||||
@ -186,8 +186,10 @@ planSubmissions sid restriction = do
|
|||||||
|
|
||||||
-- | How many additional submission should the given corrector be assigned, if possible?
|
-- | How many additional submission should the given corrector be assigned, if possible?
|
||||||
calculateDeficit :: UserId -> Map SubmissionId (Maybe UserId, Map UserId _, SheetId) -> Rational
|
calculateDeficit :: UserId -> Map SubmissionId (Maybe UserId, Map UserId _, SheetId) -> Rational
|
||||||
calculateDeficit corrector submissionState = getSum $ foldMap Sum deficitBySheet
|
calculateDeficit corrector submissionState = (* byDeficit corrLoad) . getSum $ foldMap Sum deficitBySheet
|
||||||
where
|
where
|
||||||
|
corrLoad = Map.findWithDefault mempty corrector sheetCorrectors
|
||||||
|
|
||||||
sheetSizes :: Map SheetId Integer
|
sheetSizes :: Map SheetId Integer
|
||||||
-- ^ Number of assigned submissions (to anyone) per sheet
|
-- ^ Number of assigned submissions (to anyone) per sheet
|
||||||
sheetSizes = Map.map getSum . Map.fromListWith mappend $ do
|
sheetSizes = Map.map getSum . Map.fromListWith mappend $ do
|
||||||
|
|||||||
@ -320,26 +320,40 @@ classifySubmissionMode (SubmissionMode True (Just _)) = SubmissionModeBoth
|
|||||||
|
|
||||||
-- | Specify a corrector's workload
|
-- | Specify a corrector's workload
|
||||||
data Load -- = ByTutorial { countsToLoad :: Bool } | ByProportion { load :: Rational }
|
data Load -- = ByTutorial { countsToLoad :: Bool } | ByProportion { load :: Rational }
|
||||||
= Load { byTutorial :: Maybe Bool -- ^ @Just@ all from Tutorial, @True@ if counting towards overall workload
|
= Load { byTutorial :: Maybe Bool -- ^ @Just@ all from Tutorial, @True@ if counting towards overall workload
|
||||||
, byProportion :: Rational -- ^ workload proportion of all submission not assigned to tutorial leaders
|
, byProportion :: Rational -- ^ workload proportion of all submission not assigned to tutorial leaders
|
||||||
|
, byDeficit :: Rational -- ^ multiply accumulated deficit by this before considering for distribution
|
||||||
}
|
}
|
||||||
deriving (Show, Read, Eq, Ord, Generic)
|
deriving (Show, Read, Eq, Ord, Generic)
|
||||||
deriving anyclass (Hashable, NFData)
|
deriving anyclass (Hashable, NFData)
|
||||||
|
|
||||||
deriveJSON defaultOptions ''Load
|
instance ToJSON Load where
|
||||||
|
toJSON Load{..} = Aeson.object $ catMaybes
|
||||||
|
[ ("byTutorial" Aeson..=) . Just <$> byTutorial
|
||||||
|
, ("byProportion" Aeson..=) <$> assertM' (/= 0) byProportion
|
||||||
|
, ("byDeficit" Aeson..=) <$> assertM' (/= 1) byDeficit
|
||||||
|
]
|
||||||
|
instance FromJSON Load where
|
||||||
|
parseJSON = Aeson.withObject "Load" $ \o -> do
|
||||||
|
byTutorial <- o Aeson..:? "byTutorial"
|
||||||
|
byProportion <- o Aeson..:? "byProportion" Aeson..!= 0
|
||||||
|
byDeficit <- o Aeson..:? "byDeficit" Aeson..!= 1
|
||||||
|
return Load{..}
|
||||||
|
|
||||||
derivePersistFieldJSON ''Load
|
derivePersistFieldJSON ''Load
|
||||||
|
|
||||||
instance Semigroup Load where
|
instance Semigroup Load where
|
||||||
(Load byTut prop) <> (Load byTut' prop') = Load byTut'' (prop + prop')
|
(Load byTut prop byDeficit) <> (Load byTut' prop' byDeficit') = Load byTut'' (prop + prop') byDeficit''
|
||||||
where
|
where
|
||||||
byTut''
|
byTut''
|
||||||
| Nothing <- byTut = byTut'
|
| Nothing <- byTut = byTut'
|
||||||
| Nothing <- byTut' = byTut
|
| Nothing <- byTut' = byTut
|
||||||
| Just a <- byTut
|
| Just a <- byTut
|
||||||
, Just b <- byTut' = Just $ a || b
|
, Just b <- byTut' = Just $ a || b
|
||||||
|
byDeficit'' = byDeficit * byDeficit'
|
||||||
|
|
||||||
instance Monoid Load where
|
instance Monoid Load where
|
||||||
mempty = Load Nothing 0
|
mempty = Load Nothing 0 1
|
||||||
mappend = (<>)
|
mappend = (<>)
|
||||||
|
|
||||||
{- Use (is _ByTutorial) instead of this unneeded definition:
|
{- Use (is _ByTutorial) instead of this unneeded definition:
|
||||||
@ -363,8 +377,15 @@ derivePersistField "CorrectorState"
|
|||||||
showCompactCorrectorLoad :: Load -> CorrectorState -> Text
|
showCompactCorrectorLoad :: Load -> CorrectorState -> Text
|
||||||
showCompactCorrectorLoad load CorrectorMissing = "[" <> showCompactCorrectorLoad load CorrectorNormal <> "]"
|
showCompactCorrectorLoad load CorrectorMissing = "[" <> showCompactCorrectorLoad load CorrectorNormal <> "]"
|
||||||
showCompactCorrectorLoad load CorrectorExcused = "{" <> showCompactCorrectorLoad load CorrectorNormal <> "}"
|
showCompactCorrectorLoad load CorrectorExcused = "{" <> showCompactCorrectorLoad load CorrectorNormal <> "}"
|
||||||
showCompactCorrectorLoad Load{..} CorrectorNormal | byProportion == 0 = fromMaybe mempty tutorialText
|
showCompactCorrectorLoad Load{..} CorrectorNormal
|
||||||
| otherwise = maybe id (\tt pt -> pt <> " + " <> tt) tutorialText proportionText
|
| byProportion == 0
|
||||||
|
, Just tutorialText' <- tutorialText
|
||||||
|
, Just deficitText' <- deficitText
|
||||||
|
= tutorialText' <> " " <> deficitText' <> " D"
|
||||||
|
| byProportion == 0
|
||||||
|
= fromMaybe mempty $ tutorialText <|> fmap (<> "D") deficitText
|
||||||
|
| otherwise
|
||||||
|
= maybe id (\dt acc -> acc <> " " <> dt <> " D") deficitText $ maybe id (\tt acc -> acc <> " + " <> tt) tutorialText proportionText
|
||||||
where
|
where
|
||||||
proportionText = let propDbl :: Double
|
proportionText = let propDbl :: Double
|
||||||
propDbl = fromRational byProportion
|
propDbl = fromRational byProportion
|
||||||
@ -372,6 +393,9 @@ showCompactCorrectorLoad Load{..} CorrectorNormal | byProportion == 0 = fromMay
|
|||||||
tutorialText = byTutorial <&> \case
|
tutorialText = byTutorial <&> \case
|
||||||
True -> "(T)"
|
True -> "(T)"
|
||||||
False -> "T"
|
False -> "T"
|
||||||
|
deficitText | byDeficit == 1 = Nothing
|
||||||
|
| byDeficit > 1 = Just "+"
|
||||||
|
| otherwise = Just "-"
|
||||||
|
|
||||||
instance Csv.ToField (SheetType epid, Maybe Points) where
|
instance Csv.ToField (SheetType epid, Maybe Points) where
|
||||||
toField (_, Nothing) = mempty
|
toField (_, Nothing) = mempty
|
||||||
|
|||||||
@ -0,0 +1,2 @@
|
|||||||
|
$newline never
|
||||||
|
Das Ausgleichen von Defiziten beim Verteilen von Korrekturen kann nun deaktiviert werden
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
$newline never
|
||||||
|
Consideration of corrector deficits when assigning corrections can now be disabled
|
||||||
@ -1,5 +1,5 @@
|
|||||||
$newline never
|
$newline never
|
||||||
<td colspan=5>
|
<td colspan=6>
|
||||||
#{csrf}
|
#{csrf}
|
||||||
^{fvWidget addView}
|
^{fvWidget addView}
|
||||||
<td>
|
<td>
|
||||||
|
|||||||
@ -11,6 +11,8 @@ $case userIdent
|
|||||||
<td>
|
<td>
|
||||||
#{csrf}
|
#{csrf}
|
||||||
^{fvWidget stateView}
|
^{fvWidget stateView}
|
||||||
|
<td>
|
||||||
|
^{fvWidget deficitView}
|
||||||
<td>
|
<td>
|
||||||
^{fvWidget byTutView}
|
^{fvWidget byTutView}
|
||||||
<td>
|
<td>
|
||||||
|
|||||||
@ -4,6 +4,9 @@ $newline never
|
|||||||
<tr .table__row .table__row--head>
|
<tr .table__row .table__row--head>
|
||||||
<th .table__th colspan="2">_{MsgTableCorrector}
|
<th .table__th colspan="2">_{MsgTableCorrector}
|
||||||
<th .table__th>_{MsgTableCorState}
|
<th .table__th>_{MsgTableCorState}
|
||||||
|
<th .table__th>
|
||||||
|
_{MsgConsiderDeficits}
|
||||||
|
^{messageTooltip =<< messageI Info MsgConsiderDeficitsTip}
|
||||||
<th .table__th>_{MsgCorByTut}
|
<th .table__th>_{MsgCorByTut}
|
||||||
<th .table__th>_{MsgTableCorProportion}
|
<th .table__th>_{MsgTableCorProportion}
|
||||||
<td>
|
<td>
|
||||||
|
|||||||
@ -918,9 +918,9 @@ fillDb = do
|
|||||||
forM_ [fhamann, maxMuster, tinaTester] $ \uid -> do
|
forM_ [fhamann, maxMuster, tinaTester] $ \uid -> do
|
||||||
p <- liftIO getRandom
|
p <- liftIO getRandom
|
||||||
void . insert $ SheetPseudonym shId p uid
|
void . insert $ SheetPseudonym shId p uid
|
||||||
void . insert $ SheetCorrector jost shId (Load (Just True) 0) CorrectorNormal
|
void . insert $ SheetCorrector jost shId (Load (Just True) 0 1) CorrectorNormal
|
||||||
void . insert $ SheetCorrector gkleen shId (Load (Just True) 1) CorrectorNormal
|
void . insert $ SheetCorrector gkleen shId (Load (Just True) 1 1) CorrectorNormal
|
||||||
void . insert $ SheetCorrector svaupel shId (Load (Just True) 1) CorrectorNormal
|
void . insert $ SheetCorrector svaupel shId (Load (Just True) 1 1) CorrectorNormal
|
||||||
void $ insertFile (SheetFileResidual shId SheetHint) "H10-2.hs"
|
void $ insertFile (SheetFileResidual shId SheetHint) "H10-2.hs"
|
||||||
void $ insertFile (SheetFileResidual shId SheetSolution) "H10-3.hs"
|
void $ insertFile (SheetFileResidual shId SheetSolution) "H10-3.hs"
|
||||||
void $ insertFile (SheetFileResidual shId SheetExercise) "ProMo_Uebung10.pdf"
|
void $ insertFile (SheetFileResidual shId SheetExercise) "ProMo_Uebung10.pdf"
|
||||||
|
|||||||
@ -26,7 +26,13 @@ import Database.Persist.Sql (fromSqlKey)
|
|||||||
|
|
||||||
import qualified Database.Esqueleto as E
|
import qualified Database.Esqueleto as E
|
||||||
|
|
||||||
-- import Data.Maybe (fromJust)
|
import Data.Monoid (First(..))
|
||||||
|
|
||||||
|
import Utils (guardOn)
|
||||||
|
|
||||||
|
import Control.Lens.Extras (is)
|
||||||
|
|
||||||
|
import Data.Maybe (fromJust)
|
||||||
|
|
||||||
|
|
||||||
userNumber :: TVar Natural
|
userNumber :: TVar Natural
|
||||||
@ -46,8 +52,8 @@ makeUsers (fromIntegral -> n) = do
|
|||||||
return $ zipWith Entity uids users
|
return $ zipWith Entity uids users
|
||||||
|
|
||||||
distributionExample :: SqlPersistM [(Natural, [Maybe Load])] -- ^ Number of submissions and corrector specification
|
distributionExample :: SqlPersistM [(Natural, [Maybe Load])] -- ^ Number of submissions and corrector specification
|
||||||
-> ([Entity Submission] -> [Entity SheetCorrector] -> SqlPersistM ()) -- ^ Setup hook
|
-> (Natural -> [Entity Submission] -> [Entity SheetCorrector] -> SqlPersistM ()) -- ^ Setup hook
|
||||||
-> (Map (Maybe SheetCorrector) (Set SubmissionId) -> Expectation)
|
-> (Map (Maybe SheetCorrector) (Set (SubmissionId, Maybe Natural)) -> Expectation)
|
||||||
-> YesodExample UniWorX ()
|
-> YesodExample UniWorX ()
|
||||||
distributionExample mkParameters setupHook cont = do
|
distributionExample mkParameters setupHook cont = do
|
||||||
situations <- runDB $ do
|
situations <- runDB $ do
|
||||||
@ -88,11 +94,18 @@ distributionExample mkParameters setupHook cont = do
|
|||||||
|
|
||||||
return (sid, (submissions, sheetCorrectors'))
|
return (sid, (submissions, sheetCorrectors'))
|
||||||
|
|
||||||
mapM_ (uncurry setupHook) $ map snd situations
|
mapM_ (\(n, (subs, corrs)) -> setupHook n subs corrs) . zip [1..] $ map snd situations
|
||||||
|
|
||||||
return situations
|
situations' <- forM situations $ \(sid, (submissions, sheetCorrectors)) -> (sid, ) <$> do
|
||||||
|
submissions' <- mapM (fmap fromJust . getEntity . entityKey) submissions
|
||||||
|
sheetCorrectors' <- mapM (fmap fromJust . getEntity . entityKey) sheetCorrectors
|
||||||
|
return (submissions', sheetCorrectors')
|
||||||
|
|
||||||
let subIds = concatMap (\(_, (subs, _)) -> map entityKey subs) situations
|
return situations'
|
||||||
|
|
||||||
|
let
|
||||||
|
subIds :: [SubmissionId]
|
||||||
|
subIds = concatMap (\(_, (subs, _)) -> mapMaybe (\(Entity subId Submission{..}) -> guardOn (is _Nothing submissionRatingBy) subId) subs) situations
|
||||||
|
|
||||||
results <- runHandler . Yesod.runDB . mapM (\sid -> assignSubmissions sid Nothing) $ map fst situations
|
results <- runHandler . Yesod.runDB . mapM (\sid -> assignSubmissions sid Nothing) $ map fst situations
|
||||||
|
|
||||||
@ -104,15 +117,16 @@ distributionExample mkParameters setupHook cont = do
|
|||||||
cont . Map.fromListWith mappend $ do
|
cont . Map.fromListWith mappend $ do
|
||||||
Entity subId Submission{..} <- submissions
|
Entity subId Submission{..} <- submissions
|
||||||
let key = listToMaybe . filter (\(Entity _ (SheetCorrector uid _ _ _)) -> Just uid == submissionRatingBy) $ concatMap (\(_, (_, corrs)) -> corrs) situations
|
let key = listToMaybe . filter (\(Entity _ (SheetCorrector uid _ _ _)) -> Just uid == submissionRatingBy) $ concatMap (\(_, (_, corrs)) -> corrs) situations
|
||||||
return (entityVal <$> key, Set.singleton subId)
|
sheet = getFirst . foldMap (\(n, (sid, _)) -> First $ guardOn (sid == submissionSheet) n) $ zip [1..] situations
|
||||||
|
return (entityVal <$> key, Set.singleton (subId, sheet))
|
||||||
|
|
||||||
|
|
||||||
spec :: Spec
|
spec :: Spec
|
||||||
spec = withApp . describe "Submission distribution" $ do
|
spec = withApp . describe "Submission distribution" $ do
|
||||||
it "is fair" $
|
it "is fair" $
|
||||||
distributionExample
|
distributionExample
|
||||||
(return [(500, replicate 10 (Just $ Load Nothing 1))])
|
(return [(500, replicate 10 (Just $ Load Nothing 1 1))])
|
||||||
(\_ _ -> return ())
|
(\_ _ _ -> return ())
|
||||||
(\result -> do
|
(\result -> do
|
||||||
let countResult = Map.map Set.size result
|
let countResult = Map.map Set.size result
|
||||||
countResult `shouldNotSatisfy` Map.member Nothing
|
countResult `shouldNotSatisfy` Map.member Nothing
|
||||||
@ -120,20 +134,20 @@ spec = withApp . describe "Submission distribution" $ do
|
|||||||
)
|
)
|
||||||
it "follows distribution" $
|
it "follows distribution" $
|
||||||
distributionExample
|
distributionExample
|
||||||
(return [(500, replicate 6 (Just $ Load Nothing 1) ++ replicate 2 (Just $ Load Nothing 2))])
|
(return [(500, replicate 6 (Just $ Load Nothing 1 1) ++ replicate 2 (Just $ Load Nothing 2 1))])
|
||||||
(\_ _ -> return ())
|
(\_ _ _ -> return ())
|
||||||
(\result -> do
|
(\result -> do
|
||||||
let countResult = Map.map Set.size result
|
let countResult = Map.map Set.size result
|
||||||
countResult `shouldNotSatisfy` Map.member Nothing
|
countResult `shouldNotSatisfy` Map.member Nothing
|
||||||
countResult `shouldSatisfy` all (\(Just (SheetCorrector _ _ (Load _ prop) _), subsSet) -> (== 50 * prop) $ fromIntegral subsSet) . Map.toList
|
countResult `shouldSatisfy` all (\(Just (SheetCorrector _ _ (Load _ prop _) _), subsSet) -> (== 50 * prop) $ fromIntegral subsSet) . Map.toList
|
||||||
)
|
)
|
||||||
it "follows cumulative distribution over multiple sheets" $ do
|
it "follows cumulative distribution over multiple sheets" $ do
|
||||||
ns <- liftIO . replicateM 5 . fmap fromInteger $ getRandomR (0, 100)
|
ns <- liftIO . replicateM 5 . fmap fromInteger $ getRandomR (0, 100)
|
||||||
let ns' = ns ++ [500 - sum ns]
|
let ns' = ns ++ [500 - sum ns]
|
||||||
loads = replicate 6 (Just $ Load Nothing 1) ++ replicate 2 (Just $ Load Nothing 2)
|
loads = replicate 6 (Just $ Load Nothing 1 1) ++ replicate 2 (Just $ Load Nothing 2 1)
|
||||||
distributionExample
|
distributionExample
|
||||||
(return [ (n, loads) | n <- ns' ])
|
(return [ (n, loads) | n <- ns' ])
|
||||||
(\_ _ -> return ())
|
(\_ _ _ -> return ())
|
||||||
(\result -> do
|
(\result -> do
|
||||||
let countResult = Map.map Set.size result
|
let countResult = Map.map Set.size result
|
||||||
countResult' = Map.mapKeysWith (+) (fmap $ \SheetCorrector{..} -> (fromSqlKey sheetCorrectorUser, byProportion sheetCorrectorLoad)) countResult
|
countResult' = Map.mapKeysWith (+) (fmap $ \SheetCorrector{..} -> (fromSqlKey sheetCorrectorUser, byProportion sheetCorrectorLoad)) countResult
|
||||||
@ -144,12 +158,12 @@ spec = withApp . describe "Submission distribution" $ do
|
|||||||
let ns = replicate 4 100
|
let ns = replicate 4 100
|
||||||
loads = do
|
loads = do
|
||||||
(onesBefore, onesAfter) <- zip [0,2..6] [6,4..0]
|
(onesBefore, onesAfter) <- zip [0,2..6] [6,4..0]
|
||||||
return $ replicate onesBefore (Just $ Load Nothing 1)
|
return $ replicate onesBefore (Just $ Load Nothing 1 1)
|
||||||
++ replicate 2 (Just $ Load Nothing 2)
|
++ replicate 2 (Just $ Load Nothing 2 1)
|
||||||
++ replicate onesAfter (Just $ Load Nothing 1)
|
++ replicate onesAfter (Just $ Load Nothing 1 1)
|
||||||
distributionExample
|
distributionExample
|
||||||
(return $ zip ns loads)
|
(return $ zip ns loads)
|
||||||
(\_ _ -> return ())
|
(\_ _ _ -> return ())
|
||||||
(\result -> do
|
(\result -> do
|
||||||
let countResult = Map.map Set.size result
|
let countResult = Map.map Set.size result
|
||||||
countResult' = Map.mapKeysWith (+) (fmap $ \SheetCorrector{..} -> fromSqlKey sheetCorrectorUser) countResult
|
countResult' = Map.mapKeysWith (+) (fmap $ \SheetCorrector{..} -> fromSqlKey sheetCorrectorUser) countResult
|
||||||
@ -159,11 +173,11 @@ spec = withApp . describe "Submission distribution" $ do
|
|||||||
it "handles tutorials with proportion" $ do
|
it "handles tutorials with proportion" $ do
|
||||||
ns <- liftIO . replicateM 5 . fmap fromInteger $ getRandomR (0, 100)
|
ns <- liftIO . replicateM 5 . fmap fromInteger $ getRandomR (0, 100)
|
||||||
let ns' = ns ++ [500 - sum ns]
|
let ns' = ns ++ [500 - sum ns]
|
||||||
loads = replicate 6 (Just $ Load (Just True) 1) ++ replicate 2 (Just $ Load (Just True) 2)
|
loads = replicate 6 (Just $ Load (Just True) 1 1) ++ replicate 2 (Just $ Load (Just True) 2 1)
|
||||||
tutSubIds <- liftIO $ newTVarIO Map.empty
|
tutSubIds <- liftIO $ newTVarIO Map.empty
|
||||||
distributionExample
|
distributionExample
|
||||||
(return [ (n, loads) | n <- ns' ])
|
(return [ (n, loads) | n <- ns' ])
|
||||||
(\subs corrs -> do
|
(\_ subs corrs -> do
|
||||||
tutSubmissions <- liftIO $ getRandomR (5,10)
|
tutSubmissions <- liftIO $ getRandomR (5,10)
|
||||||
subs' <- liftIO $ shuffleM subs
|
subs' <- liftIO $ shuffleM subs
|
||||||
forM_ (take tutSubmissions subs') $ \(Entity subId Submission{..}) -> do
|
forM_ (take tutSubmissions subs') $ \(Entity subId Submission{..}) -> do
|
||||||
@ -192,3 +206,19 @@ spec = withApp . describe "Submission distribution" $ do
|
|||||||
-- HUnit.assertEqual ("Submission " <> show (fromSqlKey subId) <> " assigned to multiple correctors") 1 $ Set.size assignedTo
|
-- HUnit.assertEqual ("Submission " <> show (fromSqlKey subId) <> " assigned to multiple correctors") 1 $ Set.size assignedTo
|
||||||
-- HUnit.assertEqual ("Submission " <> show (fromSqlKey subId) <> " assigned to non-tutors (" <> show (Set.map fromSqlKey tutors) <> ")") Set.empty (Set.map fromSqlKey $ assignedTo `Set.difference` tutors)
|
-- HUnit.assertEqual ("Submission " <> show (fromSqlKey subId) <> " assigned to non-tutors (" <> show (Set.map fromSqlKey tutors) <> ")") Set.empty (Set.map fromSqlKey $ assignedTo `Set.difference` tutors)
|
||||||
)
|
)
|
||||||
|
it "allows disabling deficit consideration" $
|
||||||
|
distributionExample
|
||||||
|
(return . replicate 2 $ (500, replicate 2 (Just $ Load Nothing 1 0)))
|
||||||
|
(\n subs corrs -> if
|
||||||
|
| n < 2
|
||||||
|
, Entity _ SheetCorrector{ sheetCorrectorUser = corrId } : _ <- corrs
|
||||||
|
-> forM_ subs $ \(Entity subId _) ->
|
||||||
|
update subId [SubmissionRatingBy =. Just corrId]
|
||||||
|
| otherwise -> return ()
|
||||||
|
)
|
||||||
|
(\result -> do
|
||||||
|
let secondResult = Map.map (Set.size . Set.filter (views _2 (== Just 1))) result
|
||||||
|
allEqual [] = True
|
||||||
|
allEqual ((_, c) : xs) = all (\(_, c') -> c == c') xs
|
||||||
|
secondResult `shouldSatisfy` allEqual . Map.toList
|
||||||
|
)
|
||||||
|
|||||||
@ -149,6 +149,7 @@ instance Arbitrary Load where
|
|||||||
arbitrary = do
|
arbitrary = do
|
||||||
byTutorial <- arbitrary
|
byTutorial <- arbitrary
|
||||||
byProportion <- getNonNegative <$> arbitrary
|
byProportion <- getNonNegative <$> arbitrary
|
||||||
|
byDeficit <- oneof [ pure 0, pure 1, arbitrary ]
|
||||||
return Load{..}
|
return Load{..}
|
||||||
shrink = genericShrink
|
shrink = genericShrink
|
||||||
|
|
||||||
@ -523,16 +524,29 @@ spec = do
|
|||||||
toPathPiece ExamCloseSeparate `shouldBe` "separate"
|
toPathPiece ExamCloseSeparate `shouldBe` "separate"
|
||||||
toPathPiece (ExamCloseOnFinished False) `shouldBe` "on-finished"
|
toPathPiece (ExamCloseOnFinished False) `shouldBe` "on-finished"
|
||||||
toPathPiece (ExamCloseOnFinished True) `shouldBe` "on-finished-hidden"
|
toPathPiece (ExamCloseOnFinished True) `shouldBe` "on-finished-hidden"
|
||||||
|
describe "Load" $
|
||||||
|
it "decodes some examples from json" . example $ do
|
||||||
|
let t str expect = Aeson.eitherDecode str `shouldBe` Right expect
|
||||||
|
t "{}" $ Load Nothing 0 1
|
||||||
|
t "{\"byTutorial\": true, \"byProportion\": {\"numerator\": 0, \"denominator\": 1}}" $ Load (Just True) 0 1
|
||||||
describe "CompactCorrectorLoad" $ do
|
describe "CompactCorrectorLoad" $ do
|
||||||
it "matches expectations" . example $ do
|
it "matches expectations" . example $ do
|
||||||
showCompactCorrectorLoad Load{ byTutorial = Just False, byProportion = 0 } CorrectorNormal `shouldBe` "T"
|
showCompactCorrectorLoad Load{ byTutorial = Just False, byProportion = 0, byDeficit = 1 } CorrectorNormal `shouldBe` "T"
|
||||||
showCompactCorrectorLoad Load{ byTutorial = Just True, byProportion = 0 } CorrectorNormal `shouldBe` "(T)"
|
showCompactCorrectorLoad Load{ byTutorial = Just True, byProportion = 0, byDeficit = 1 } CorrectorNormal `shouldBe` "(T)"
|
||||||
showCompactCorrectorLoad Load{ byTutorial = Nothing, byProportion = 1 } CorrectorNormal `shouldBe` "1.0"
|
showCompactCorrectorLoad Load{ byTutorial = Nothing, byProportion = 1, byDeficit = 1 } CorrectorNormal `shouldBe` "1.0"
|
||||||
showCompactCorrectorLoad Load{ byTutorial = Just False, byProportion = 1 } CorrectorNormal `shouldBe` "1.0 + T"
|
showCompactCorrectorLoad Load{ byTutorial = Just False, byProportion = 1, byDeficit = 1 } CorrectorNormal `shouldBe` "1.0 + T"
|
||||||
showCompactCorrectorLoad Load{ byTutorial = Just True, byProportion = 1 } CorrectorNormal `shouldBe` "1.0 + (T)"
|
showCompactCorrectorLoad Load{ byTutorial = Just True, byProportion = 1, byDeficit = 1 } CorrectorNormal `shouldBe` "1.0 + (T)"
|
||||||
showCompactCorrectorLoad Load{ byTutorial = Nothing, byProportion = 0 } CorrectorNormal `shouldBe` ""
|
showCompactCorrectorLoad Load{ byTutorial = Nothing, byProportion = 0, byDeficit = 1 } CorrectorNormal `shouldBe` ""
|
||||||
showCompactCorrectorLoad Load{ byTutorial = Nothing, byProportion = 1 } CorrectorMissing `shouldBe` "[1.0]"
|
showCompactCorrectorLoad Load{ byTutorial = Nothing, byProportion = 1, byDeficit = 1 } CorrectorMissing `shouldBe` "[1.0]"
|
||||||
showCompactCorrectorLoad Load{ byTutorial = Nothing, byProportion = 1 } CorrectorExcused `shouldBe` "{1.0}"
|
showCompactCorrectorLoad Load{ byTutorial = Nothing, byProportion = 1, byDeficit = 1 } CorrectorExcused `shouldBe` "{1.0}"
|
||||||
|
showCompactCorrectorLoad Load{ byTutorial = Just False, byProportion = 0, byDeficit = 0 } CorrectorNormal `shouldBe` "T - D"
|
||||||
|
showCompactCorrectorLoad Load{ byTutorial = Just True, byProportion = 0, byDeficit = 0 } CorrectorNormal `shouldBe` "(T) - D"
|
||||||
|
showCompactCorrectorLoad Load{ byTutorial = Nothing, byProportion = 1, byDeficit = 0 } CorrectorNormal `shouldBe` "1.0 - D"
|
||||||
|
showCompactCorrectorLoad Load{ byTutorial = Just False, byProportion = 1, byDeficit = 0 } CorrectorNormal `shouldBe` "1.0 + T - D"
|
||||||
|
showCompactCorrectorLoad Load{ byTutorial = Just True, byProportion = 1, byDeficit = 0 } CorrectorNormal `shouldBe` "1.0 + (T) - D"
|
||||||
|
showCompactCorrectorLoad Load{ byTutorial = Nothing, byProportion = 0, byDeficit = 0 } CorrectorNormal `shouldBe` "-D"
|
||||||
|
showCompactCorrectorLoad Load{ byTutorial = Nothing, byProportion = 1, byDeficit = 0 } CorrectorMissing `shouldBe` "[1.0 - D]"
|
||||||
|
showCompactCorrectorLoad Load{ byTutorial = Nothing, byProportion = 1, byDeficit = 0 } CorrectorExcused `shouldBe` "{1.0 - D}"
|
||||||
|
|
||||||
termExample :: (TermIdentifier, Text) -> Expectation
|
termExample :: (TermIdentifier, Text) -> Expectation
|
||||||
termExample (term, encoded) = example $ do
|
termExample (term, encoded) = example $ do
|
||||||
|
|||||||
Reference in New Issue
Block a user