chore: examAutoOccurrence converted to Either

This commit is contained in:
Wolfgang Witt 2021-03-01 15:30:51 +01:00 committed by Wolfgang Witt
parent 59f5bd3591
commit e03326e1ac
7 changed files with 45 additions and 15 deletions

View File

@ -2800,9 +2800,14 @@ BtnExamAutoOccurrenceNudgeUp: +
BtnExamAutoOccurrenceNudgeDown: - BtnExamAutoOccurrenceNudgeDown: -
ExamRoomMappingSurname: Nachnamen beginnend mit ExamRoomMappingSurname: Nachnamen beginnend mit
ExamRoomMappingMatriculation: Matrikelnummern endend in ExamRoomMappingMatriculation: Matrikelnummern endend in
ExamRoomMappingRandom: Zufällige Zuordnung
ExamRoomLoad: Auslastung ExamRoomLoad: Auslastung
ExamRegisteredCount: Anmeldungen ExamRegisteredCount: Anmeldungen
ExamRegisteredCountOf num@Int64 count@Int64: #{num}/#{count} ExamRegisteredCountOf num@Int64 count@Int64: #{num}/#{count}
ExamAutoOccurrenceExceptionRuleNoOp: Keine Automatische Verteilung gewählt
ExamAutoOccurrenceExceptionNotEnoughSpace: Nicht ausreichend Platz
ExamAutoOccurrenceExceptionNoUsers: Keine Nutzer
ExamAutoOccurrenceExceptionRoomTooSmall: Automatische Verteilung gescheitert. Es kann helfen kleine Räume zu entfernen.
NoFilter: Keine Einschränkung NoFilter: Keine Einschränkung

View File

@ -2800,9 +2800,14 @@ BtnExamAutoOccurrenceNudgeUp: +
BtnExamAutoOccurrenceNudgeDown: - BtnExamAutoOccurrenceNudgeDown: -
ExamRoomMappingSurname: Surnames starting with ExamRoomMappingSurname: Surnames starting with
ExamRoomMappingMatriculation: Matriculation numbers ending in ExamRoomMappingMatriculation: Matriculation numbers ending in
ExamRoomMappingRandom: Random assignment
ExamRoomLoad: Utilisation ExamRoomLoad: Utilisation
ExamRegisteredCount: Registrations ExamRegisteredCount: Registrations
ExamRegisteredCountOf num count: #{num}/#{count} ExamRegisteredCountOf num count: #{num}/#{count}
ExamAutoOccurrenceExceptionRuleNoOp: Didn't chose an automatic distribution
ExamAutoOccurrenceExceptionNotEnoughSpace: Not enough space
ExamAutoOccurrenceExceptionNoUsers: No participants
ExamAutoOccurrenceExceptionRoomTooSmall: Automatic distribution failed. Removing small rooms might help.
NoFilter: No restriction NoFilter: No restriction

View File

@ -162,6 +162,8 @@ dependencies:
- nonce - nonce
- IntervalMap - IntervalMap
- haskell-src-meta - haskell-src-meta
- either
other-extensions: other-extensions:
- GeneralizedNewtypeDeriving - GeneralizedNewtypeDeriving
- IncoherentInstances - IncoherentInstances

View File

@ -47,6 +47,8 @@ import qualified Data.Array.ST as ST
import Data.List (findIndex, unfoldr) import Data.List (findIndex, unfoldr)
import qualified Data.List as List import qualified Data.List as List
import Data.Either.Combinators (maybeToRight)
import Data.ExtendedReal import Data.ExtendedReal
import Data.Ratio (Ratio) import Data.Ratio (Ratio)
@ -256,6 +258,16 @@ deriveJSON defaultOptions
{ fieldLabelModifier = camelToPathPiece' 1 { fieldLabelModifier = camelToPathPiece' 1
} ''ExamAutoOccurrenceConfig } ''ExamAutoOccurrenceConfig
data ExamAutoOccurrenceException
= ExamAutoOccurrenceExceptionRuleNoOp
| ExamAutoOccurrenceExceptionNotEnoughSpace
| ExamAutoOccurrenceExceptionNoUsers
| ExamAutoOccurrenceExceptionRoomTooSmall
deriving (Show, Generic, Typeable)
instance Exception ExamAutoOccurrenceException
embedRenderMessage ''UniWorX ''ExamAutoOccurrenceException id
examAutoOccurrence :: forall seed. examAutoOccurrence :: forall seed.
Hashable seed Hashable seed
@ -264,16 +276,20 @@ examAutoOccurrence :: forall seed.
-> ExamAutoOccurrenceConfig -> ExamAutoOccurrenceConfig
-> Map ExamOccurrenceId Natural -> Map ExamOccurrenceId Natural
-> Map UserId (User, Maybe ExamOccurrenceId) -> Map UserId (User, Maybe ExamOccurrenceId)
-> (Maybe (ExamOccurrenceMapping ExamOccurrenceId), Map UserId (Maybe ExamOccurrenceId)) -> Either ExamAutoOccurrenceException (ExamOccurrenceMapping ExamOccurrenceId, Map UserId (Maybe ExamOccurrenceId))
examAutoOccurrence (hash -> seed) rule ExamAutoOccurrenceConfig{..} occurrences users examAutoOccurrence (hash -> seed) rule ExamAutoOccurrenceConfig{..} occurrences users
| sum occurrences' < usersCount | sum occurrences' < usersCount
|| sum occurrences' <= 0 || sum occurrences' <= 0
|| Map.null users' = Left ExamAutoOccurrenceExceptionNotEnoughSpace
= nullResult | Map.null users'
= Left ExamAutoOccurrenceExceptionNoUsers
| otherwise | otherwise
= case rule of = case rule of
ExamRoomRandom ExamRoomRandom
-> ( Nothing -> Right ( ExamOccurrenceMapping {
examOccurrenceMappingRule=rule,
examOccurrenceMappingMapping=Map.fromList $ (set _2 $ Set.singleton ExamOccurrenceMappingRandom) <$> occurrences''
}
, Map.union (view _2 <$> assignedUsers) randomlyAssignedUsers , Map.union (view _2 <$> assignedUsers) randomlyAssignedUsers
) )
where where
@ -307,13 +323,8 @@ examAutoOccurrence (hash -> seed) rule ExamAutoOccurrenceConfig{..} occurrences
where where
newUsers, remainingUsers :: [UserId] newUsers, remainingUsers :: [UserId]
(newUsers, remainingUsers) = List.genericSplitAt roomSize userList (newUsers, remainingUsers) = List.genericSplitAt roomSize userList
_ | Just (postprocess -> (resMapping, result)) <- bestOption _ -> bimap (ExamOccurrenceMapping rule) (Map.unionWith (<|>) (view _2 <$> users)) . postprocess <$> bestOption
-> ( Just $ ExamOccurrenceMapping rule resMapping
, Map.unionWith (<|>) (view _2 <$> users) result
)
_ -> nullResult
where where
nullResult = (Nothing, view _2 <$> users)
usersCount :: forall a. Num a => a usersCount :: forall a. Num a => a
usersCount = getSum $ foldMap (Sum . fromIntegral . Set.size) users' usersCount = getSum $ foldMap (Sum . fromIntegral . Set.size) users'
@ -519,13 +530,13 @@ examAutoOccurrence (hash -> seed) rule ExamAutoOccurrenceConfig{..} occurrences
lineNudges = fromMaybe 0 . flip Map.lookup eaocNudge lineNudges = fromMaybe 0 . flip Map.lookup eaocNudge
bestOption :: Maybe [(ExamOccurrenceId, [[CI Char]])] bestOption :: Either ExamAutoOccurrenceException [(ExamOccurrenceId, [[CI Char]])]
bestOption = case rule of bestOption = case rule of
ExamRoomSurname -> do ExamRoomSurname -> maybeToRight ExamAutoOccurrenceExceptionRoomTooSmall $ do
(_cost, res) <- distribute (sortBy (RFC5051.compareUnicode `on` (pack . toListOf (_1 . folded . to CI.foldedCase))) . Map.toAscList $ fromIntegral . Set.size <$> users') occurrences'' lineNudges charCost (_cost, res) <- distribute (sortBy (RFC5051.compareUnicode `on` (pack . toListOf (_1 . folded . to CI.foldedCase))) . Map.toAscList $ fromIntegral . Set.size <$> users') occurrences'' lineNudges charCost
-- traceM $ show cost -- traceM $ show cost
return res return res
ExamRoomMatriculation -> do ExamRoomMatriculation -> maybeToRight ExamAutoOccurrenceExceptionRoomTooSmall $ do
let usersFineness n = Map.toAscList $ fromIntegral . Set.size <$> Map.mapKeysWith Set.union (reverse . take (fromIntegral n) . reverse) users' let usersFineness n = Map.toAscList $ fromIntegral . Set.size <$> Map.mapKeysWith Set.union (reverse . take (fromIntegral n) . reverse) users'
-- finenessCost n = Finite (max 1 $ fromIntegral n * eaocFinenessCost * fromIntegral longestLine) ^ 2 * length occurrences' -- finenessCost n = Finite (max 1 $ fromIntegral n * eaocFinenessCost * fromIntegral longestLine) ^ 2 * length occurrences'
@ -556,7 +567,7 @@ examAutoOccurrence (hash -> seed) rule ExamAutoOccurrenceConfig{..} occurrences
(_cost, res) <- fmap (minimumBy . comparing $ view _1) . fromNullable $ genResults 1 (_cost, res) <- fmap (minimumBy . comparing $ view _1) . fromNullable $ genResults 1
return res return res
_other -> Nothing _other -> Left ExamAutoOccurrenceExceptionRuleNoOp
postprocess :: [(ExamOccurrenceId, [[CI Char]])] postprocess :: [(ExamOccurrenceId, [[CI Char]])]
-> ( Map ExamOccurrenceId (Set ExamOccurrenceMappingDescription) -> ( Map ExamOccurrenceId (Set ExamOccurrenceMappingDescription)
@ -690,6 +701,7 @@ examAutoOccurrence (hash -> seed) rule ExamAutoOccurrenceConfig{..} occurrences
checkSpecial = case rule of checkSpecial = case rule of
ExamRoomMatriculation -> isSuffixOf ExamRoomMatriculation -> isSuffixOf
_rule -> isPrefixOf _rule -> isPrefixOf
ExamOccurrenceMappingRandom -> False -- Something went wrong, throw an error instead?
resultUsers :: Map UserId (Maybe ExamOccurrenceId) resultUsers :: Map UserId (Maybe ExamOccurrenceId)
resultUsers = Map.fromList $ do resultUsers = Map.fromList $ do

View File

@ -191,6 +191,7 @@ examOccurrenceRuleAutomatic x = any ($ x)
data ExamOccurrenceMappingDescription data ExamOccurrenceMappingDescription
= ExamOccurrenceMappingRange { eaomrStart, eaomrEnd :: [CI Char] } = ExamOccurrenceMappingRange { eaomrStart, eaomrEnd :: [CI Char] }
| ExamOccurrenceMappingSpecial { eaomrSpecial :: [CI Char] } | ExamOccurrenceMappingSpecial { eaomrSpecial :: [CI Char] }
| ExamOccurrenceMappingRandom
deriving (Eq, Ord, Read, Show, Generic, Typeable) deriving (Eq, Ord, Read, Show, Generic, Typeable)
deriveJSON defaultOptions deriveJSON defaultOptions
{ fieldLabelModifier = camelToPathPiece' 1 { fieldLabelModifier = camelToPathPiece' 1

View File

@ -13,3 +13,5 @@ $newline never
#{titleCase special}… #{titleCase special}…
$else $else
…#{titleCase special} …#{titleCase special}
$of ExamOccurrenceMappingRandom
Random

View File

@ -14,6 +14,9 @@ $newline never
$of ExamRoomMatriculation $of ExamRoomMatriculation
<th .table__th> <th .table__th>
_{MsgExamRoomMappingMatriculation} _{MsgExamRoomMappingMatriculation}
$of ExamRoomRandom
<th .table__th>
_{MsgExamRoomMappingRandom}
$of _ $of _
<th .table__td> <th .table__td>
<th .table__th> <th .table__th>