feat(allocations): improve display

This commit is contained in:
Gregor Kleen 2020-03-12 11:56:26 +01:00
parent bb20062d9f
commit 26f8f392a9
7 changed files with 175 additions and 22 deletions

1
.gitignore vendored
View File

@ -40,3 +40,4 @@ tunnel.log
/well-known
/.well-known-cache
/**/tmp-*
/testdata/bigAlloc_*.csv

View File

@ -1176,7 +1176,7 @@ a.breadcrumbs__home
font-size: 14px
font-family: monospace
.func-field__wrapper
.func-field__wrapper, .allocation-missing-prios
max-height: 75vh
overflow: auto

View File

@ -156,7 +156,7 @@ postAUsersR tid ssh ash = do
, guardOn resultsDone . coursesModalAssigned . bool id assignedHeated resultsDone $ colAllocationAssigned resultAssignedCourses
, coursesModalNewAssigned . assignedHeated <$> do
allocMatching' <- allocMatching
pure . sortable Nothing (i18nCell MsgAllocationUserNewMatches) .
pure . sortable (Just "new-assigned") (i18nCell MsgAllocationUserNewMatches) .
views (resultUser . _entityKey) $ \uid -> cell . toWidget . toMarkup . maybe 0 olength $ allocMatching' !? uid
, pure $ emptyOpticColonnade' emptyPriorityCell (resultAllocationUser . _entityVal . _allocationUserPriority . _Just) colAllocationPriority
]
@ -211,6 +211,8 @@ postAUsersR tid ssh ash = do
, sortAllocationRequested $ queryAllocationUser . to (E.^. AllocationUserTotalCourses)
, sortAllocationVetoed queryVetoedCourses
, sortAllocationPriority $ queryAllocationUser . to (E.^. AllocationUserPriority)
, singletonMap "new-assigned" $
SortProjected . comparing $ (\uid -> maybe 0 olength $ Map.lookup uid =<< allocMatching) . view (resultUser . _entityKey)
]
dbtFilter = mconcat
[ fltrUserName' $ queryUser . to (E.^. UserDisplayName)
@ -237,7 +239,7 @@ postAUsersR tid ssh ash = do
dbtCsvDecode = Nothing
allocationUsersDBTableValidator = def
& defaultSorting [SortAscBy "priority", SortAscBy "user-matriculation"]
& defaultPagesize PagesizeAll
& defaultPagesize (PagesizeLimit 500)
usersTable <- dbTableDB' allocationUsersDBTableValidator allocationUsersDBTable

View File

@ -1021,6 +1021,7 @@ dbTable PSValidator{..} dbtable@DBTable{ dbtIdent = dbtIdent'@(toPathPiece -> db
case previousKeys of
Nothing
| PagesizeLimit l <- psLimit'
, selectPagesize
-> do
E.limit l
E.offset (psPage * l)
@ -1236,7 +1237,9 @@ dbTable PSValidator{..} dbtable@DBTable{ dbtIdent = dbtIdent'@(toPathPiece -> db
_other -> return ()
let
rowCount = olength64 rows
rowCount
| selectPagesize = fromMaybe 0 $ rows' ^? _head . _1 . _Value
| otherwise = olength64 rows
rawAction = tblLink
$ setParam (wIdent "sorting") Nothing

View File

@ -4,6 +4,8 @@ module Model.Types.Allocation
, AllocationPriorityComparison(..)
, AllocationFingerprint
, module Utils.Allocation
, AllocationPriorityNumericRecord(..)
, allocationPriorityNumericMap
) where
import Import.NoModel
@ -44,13 +46,36 @@ deriving via E.JSONB AllocationPriority instance E.PersistFieldSql AllocationPri
instance Binary AllocationPriority
instance Csv.FromRecord (Map UserMatriculation AllocationPriority) where
data AllocationPriorityNumericRecord = AllocationPriorityNumericRecord
{ apmrMatrikelnummer :: UserMatriculation
, apmrPriority :: Vector Integer
} deriving (Eq, Ord, Read, Show, Generic, Typeable)
allocationPriorityNumericMap :: Prism' (Map UserMatriculation AllocationPriority) AllocationPriorityNumericRecord
allocationPriorityNumericMap = prism' fromPrioRecord toPrioRecord
where
fromPrioRecord AllocationPriorityNumericRecord{..}
= Map.singleton apmrMatrikelnummer $ AllocationPriorityNumeric apmrPriority
toPrioRecord recordMap = do
[(matr, AllocationPriorityNumeric{..})] <- pure $ Map.toList recordMap
return $ AllocationPriorityNumericRecord matr allocationPriorities
instance Csv.FromRecord AllocationPriorityNumericRecord where
parseRecord v = parseNumeric
where
parseNumeric
| Vector.length v >= 1 = Map.singleton <$> v Csv..! 0 <*> (AllocationPriorityNumeric <$> mapM Csv.parseField (Vector.tail v))
| Vector.length v >= 1 = AllocationPriorityNumericRecord <$> v Csv..! 0 <*> mapM Csv.parseField (Vector.tail v)
| otherwise = mzero
instance Csv.ToRecord AllocationPriorityNumericRecord where
toRecord AllocationPriorityNumericRecord{..} = Csv.record $
Csv.toField apmrMatrikelnummer
: map Csv.toField (otoList apmrPriority)
instance Csv.FromRecord (Map UserMatriculation AllocationPriority) where
parseRecord = fmap (review allocationPriorityNumericMap) . Csv.parseRecord
instance Csv.ToField AllocationPriority where
toField (AllocationPriorityOrdinal n ) = Csv.toField n

View File

@ -2,12 +2,13 @@ $newline never
<p>
Die folgenden Benutzer nehmen nicht an der Zentralvergabe teil, da #
ihnen keine zentrale Dringlichkeit zugeordnet wurde:
<ul>
$forall User{userDisplayName, userSurname, userMatrikelnummer} <- usersWithoutPrio
<li>
^{nameWidget userDisplayName userSurname}
$maybe matrikel <- userMatrikelnummer
\ (#{matrikel})
<div .allocation-missing-prios>
<ul>
$forall User{userDisplayName, userSurname, userMatrikelnummer} <- usersWithoutPrio
<li>
^{nameWidget userDisplayName userSurname}
$maybe matrikel <- userMatrikelnummer
\ (#{matrikel})
^{checkBoxFieldView}
<p>
Benutzern, die nicht an der Zentralvergabe teilnehmen, werden #

View File

@ -5,8 +5,10 @@ module Database.Fill
import "uniworx" Import hiding (Option(..), currentYear)
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as LBS
import qualified Data.Set as Set
import qualified Data.Map as Map
import Data.Time.Calendar.OrdinalDate
import Data.Time.Calendar.WeekDate
@ -15,14 +17,21 @@ import Control.Applicative (ZipList(..))
import Handler.Utils.DateTime
import Control.Monad.Random.Class (weighted)
import System.Random.Shuffle (shuffleM)
import qualified Data.CaseInsensitive as CI
import qualified Data.Csv as Csv
testdataDir :: FilePath
testdataDir = "testdata"
insertFile :: FilePath -> DB FileId
insertFile fileTitle = do
fileContent <- liftIO . fmap Just . BS.readFile $ "testdata" </> fileTitle
fileContent <- liftIO . fmap Just . BS.readFile $ testdataDir </> fileTitle
fileModified <- liftIO getCurrentTime
insert File{..}
@ -267,6 +276,7 @@ fillDb = do
, "Clark", "Lewis", "Robinson", "Walker"
, "Perez", "Hall", "Young", "Allen"
]
middlenames = [ Nothing, Just "Jamesson" ]
toMatrikel :: [Int] -> [Text]
toMatrikel ns
| (cs, rest) <- splitAt 8 ns
@ -274,7 +284,7 @@ fillDb = do
= foldMap tshow cs : toMatrikel rest
| otherwise
= []
manyUser (userFirstName, userSurname) (Just -> userMatrikelnummer) = User
manyUser (firstName, middleName, userSurname) (Just -> userMatrikelnummer) = User
{ userIdent
, userAuthentication = AuthLDAP
, userLastAuthentication = Nothing
@ -282,9 +292,11 @@ fillDb = do
, userMatrikelnummer
, userEmail = userIdent
, userDisplayEmail = userIdent
, userDisplayName = [st|#{userFirstName} #{userSurname}|]
, userDisplayName = case middleName of
Just middleName' -> [st|#{firstName} #{middleName'} #{userSurname}|]
Nothing -> [st|#{firstName} #{userSurname}|]
, userSurname
, userFirstName
, userFirstName = maybe id (\m f -> f <> " " <> m) middleName firstName
, userTitle = Nothing
, userMaxFavourites = userDefaultMaxFavourites
, userMaxFavouriteTerms = userDefaultMaxFavourites
@ -304,9 +316,11 @@ fillDb = do
}
where
userIdent :: IsString t => t
userIdent = fromString $ repack [st|#{userFirstName}.#{userSurname}@campus.lmu.de|]
userIdent = fromString $ case middleName of
Just middleName' -> repack [st|#{firstName}.#{middleName'}.#{userSurname}@example.invalid|]
Nothing -> repack [st|#{firstName}.#{userSurname}@example.invalid|]
matrikel <- toMatrikel <$> getRandomRs (0 :: Int, 9 :: Int)
manyUsers <- insertMany . getZipList $ manyUser <$> ZipList ((,) <$> firstNames <*> surnames) <*> ZipList matrikel
manyUsers <- insertMany . getZipList $ manyUser <$> ZipList ((,,) <$> firstNames <*> middlenames <*> surnames) <*> ZipList matrikel
forM_ [prevPrevTerm, prevTerm, currentTerm, nextTerm] $ \term@TermIdentifier{..} -> case season of
Summer -> let (wYearStart, wWeekStart, _) = toWeekDate $ fromGregorian year 04 01
@ -933,7 +947,7 @@ fillDb = do
insert_ $ CourseEdit gkleen now bs
void . insert' $ Lecturer gkleen bs CourseLecturer
void . insertMany $ do
uid <- manyUsers
uid <- take 1024 manyUsers
return $ CourseParticipant bs uid now Nothing Nothing
forM_ [1..14] $ \shNr -> do
shId <- insert Sheet
@ -951,7 +965,7 @@ fillDb = do
, sheetSolutionFrom = Nothing
, sheetAutoDistribute = False
}
manyUsers' <- shuffleM manyUsers
manyUsers' <- shuffleM $ take 1024 manyUsers
groupSizes <- getRandomRs (1, 3)
let groups = go groupSizes manyUsers'
where go [] _ = []
@ -998,9 +1012,116 @@ fillDb = do
, courseApplicationsRatingsVisible = False
}
insert_ $ CourseEdit gkleen now cid
void . insert' $ Lecturer gkleen cid CourseLecturer
-- void . insert' $ Lecturer gkleen cid CourseLecturer
participants <- getRandomR (0, 50)
manyUsers' <- shuffleM manyUsers
manyUsers' <- shuffleM $ take 1024 manyUsers
forM_ (take participants manyUsers') $ \uid ->
void . insert $ CourseParticipant cid uid now Nothing Nothing
bigAlloc <- insert' Allocation
{ allocationName = "Große Zentralanmeldung"
, allocationShorthand = "big"
, allocationTerm = TermKey $ seasonTerm True Summer
, allocationSchool = ifi
, allocationDescription = Nothing
, allocationStaffDescription = Nothing
, allocationStaffRegisterFrom = Nothing
, allocationStaffRegisterTo = Nothing
, allocationStaffAllocationFrom = Nothing
, allocationStaffAllocationTo = Nothing
, allocationRegisterFrom = Nothing
, allocationRegisterTo = Just now
, allocationRegisterByStaffFrom = Nothing
, allocationRegisterByStaffTo = Nothing
, allocationRegisterByCourse = Nothing
, allocationOverrideDeregister = Just $ termTime True Summer 1 False Monday toMidnight
}
bigAllocCourses <- forM ([1..40] :: [Int]) $ \n -> do
csh <- pack . take 3 <$> getRandomRs ('A', 'Z')
cap <- getRandomR (10,50)
minCap <- round . (* fromIntegral cap) <$> getRandomR (0, 0.5 :: Double)
cid <- insert' Course
{ courseName = CI.mk [st|Zentralanmeldungskurs #{n} (#{csh})|]
, courseDescription = Nothing
, courseLinkExternal = Nothing
, courseShorthand = CI.mk csh
, courseTerm = TermKey $ seasonTerm False Winter
, courseSchool = ifi
, courseCapacity = Just cap
, courseRegisterFrom = Nothing
, courseRegisterTo = Nothing
, courseDeregisterUntil = Nothing
, courseRegisterSecret = Nothing
, courseMaterialFree = True
, courseApplicationsRequired = False
, courseApplicationsInstructions = Nothing
, courseApplicationsText = False
, courseApplicationsFiles = NoUpload
, courseApplicationsRatingsVisible = False
}
insert_ $ CourseEdit gkleen now cid
insert_ $ AllocationCourse bigAlloc cid minCap
-- void . insert' $ Lecturer gkleen cid CourseLecturer
return cid
forM_ manyUsers $ \uid -> do
totalCourses <- weighted $ do
n <- [1..10]
return (n, fromIntegral $ (1 - 10) ^ 2 - (1 - n) ^ 2)
void . insert $ AllocationUser bigAlloc uid (fromIntegral totalCourses) Nothing
appliedCourses <- weighted $ do
n <- [totalCourses - 2..totalCourses + 5]
return (n, fromIntegral $ (totalCourses + 1 - totalCourses - 5) ^ 2 - (totalCourses + 1 - n) ^ 2)
appliedCourses' <- take appliedCourses <$> shuffleM bigAllocCourses
forM_ (zip [0..] appliedCourses') $ \(prio, cid) -> do
rating <- weighted . Map.toList . Map.fromListWith (+) $ do
veto <- universeF :: [Bool]
grade <- universeF :: [ExamGrade]
rated <- universeF
return ( bool Nothing (Just (veto, grade)) rated
, bool 5 1 veto * bool 5 1 rated
)
void $ insert CourseApplication
{ courseApplicationCourse = cid
, courseApplicationUser = uid
, courseApplicationField = Nothing
, courseApplicationText = Nothing
, courseApplicationRatingVeto = maybe False (view _1) rating
, courseApplicationRatingPoints = view _2 <$> rating
, courseApplicationRatingComment = Nothing
, courseApplicationAllocation = Just bigAlloc
, courseApplicationAllocationPriority = Just prio
, courseApplicationTime = now
, courseApplicationRatingTime = now <$ rating
}
numericPriorities <- flip foldMapM manyUsers $ \uid -> do
uRec <- get uid
case uRec of
Just User{ userMatrikelnummer = Just matr } -> do
prios <- replicateM 3 $ getRandomR (0, 300)
return . pure . AllocationPriorityNumericRecord matr . fromList $ sortOn Down prios
_other -> return mempty
liftIO . LBS.writeFile (testdataDir </> "bigAlloc_numeric.csv") $ Csv.encode numericPriorities
ordinalPriorities <- do
manyUsers' <- shuffleM manyUsers
flip foldMapM manyUsers' $ \uid -> do
uRec <- get uid
case uRec of
Just User{ userMatrikelnummer = Just matr } ->
return . pure $ Csv.Only matr
_other -> return mempty
liftIO . LBS.writeFile (testdataDir </> "bigAlloc_ordinal.csv") $ Csv.encode ordinalPriorities