feat(allocations): explanations & introduce grade-ordinal-proportion

BREAKING CHANGE: influence of grades on allocation priority now
relative when priorities are ordinal
This commit is contained in:
Gregor Kleen 2020-02-28 20:53:24 +01:00
parent 6bca64cf5f
commit ee2e504ffa
13 changed files with 83 additions and 22 deletions

View File

@ -143,9 +143,10 @@ user-defaults:
# This encodes the weight of the lecturer ratings on the same scale as the
# centrally supplied priorities.
allocation-grade-scale: 25
# This encodes how many ordinal places lecturer ratings may move students up or
# down when central priorities are supplied as ordered list.
allocation-grade-ordinal-places: 3
# This encodes, as a proportion of the number of places, how many
# ordinal places lecturer ratings may move students up or down when
# central priorities are supplied as ordered list.
allocation-grade-ordinal-proportion: 0.075
instance-id: "_env:INSTANCE_ID:instance"
ribbon: "_env:RIBBON:"

View File

@ -472,6 +472,10 @@ ul.list--inline
.deflist__dt
font-weight: 600
.deflist__explanation
color: var(--color-fontsec)
font-size: 0.9rem
.deflist__dd
font-size: 18px
margin-bottom: 10px

View File

@ -2369,4 +2369,5 @@ AllocationPrioritiesOrdinal: Dringlichkeiten durch Sortierung
AllocationPrioritiesTitle tid@TermId ssh@SchoolId ash@AllocationShorthand: #{tid}-#{ssh}-#{ash}: Zentrale Dringlichkeiten
AllocationPrioritiesFile: CSV-Datei
AllocationPrioritiesSunk num@Int64: Zentrale Prioritäten für #{num} Bewerber erfolgreich hinterlegt
AllocationPrioritiesMissing num@Int64: Für #{num} Bewerber ist keine zentrale Priorität hinterlegt, da in der hochgeladenen CSV-Datei die #{pluralDE num "entsprechende Matrikelnummer" "entsprechenden Matrikelnummern"} nicht gefunden #{pluralDE num "wurde" "wurden"}
AllocationPrioritiesMissing num@Int64: Für #{num} Bewerber ist keine zentrale Priorität hinterlegt, da in der hochgeladenen CSV-Datei die #{pluralDE num "entsprechende Matrikelnummer" "entsprechenden Matrikelnummern"} nicht gefunden #{pluralDE num "wurde" "wurden"}
AllocationMissingPrioritiesIgnored: Bewerber, für die keine zentrale Priorität angegeben wird, werden bei der Vergabe ignoriert!

View File

@ -3028,7 +3028,7 @@ pageActions (AllocationR tid ssh ash AUsersR) = return
{ navLabel = MsgMenuAllocationPriorities
, navRoute = AllocationR tid ssh ash APriosR
, navAccess' = return True
, navType = NavTypeLink { navModal = True }
, navType = NavTypeLink { navModal = False }
, navQuick' = mempty
, navForceActive = False
}

View File

@ -49,8 +49,10 @@ postAPriosR tid ssh ash = do
AllocationPrioritiesNumeric -> return $(i18nWidgetFile "allocation-priority-explanation/numeric")
AllocationPrioritiesOrdinal -> return $(i18nWidgetFile "allocation-priority-explanation/ordinal")
ignoreWarningMsg <- messageIconI Warning IconMissingAllocationPriority MsgAllocationMissingPrioritiesIgnored
((priosRes, priosView), priosEnctype) <- runFormPost . renderAForm FormStandard $ (,)
<$> apopt (explainedSelectionField Nothing (explainOptionList optionsFinite explainAllocationPrioMode)) (fslI MsgAllocationPrioritiesMode) (Just $ bool AllocationPrioritiesOrdinal AllocationPrioritiesNumeric doNumericPrios)
<* aformMessage ignoreWarningMsg
<*> areq fileField (fslI MsgAllocationPrioritiesFile) Nothing
formResult priosRes $ \(mode, fInfo) -> do
@ -76,7 +78,11 @@ postAPriosR tid ssh ash = do
siteLayoutMsg MsgMenuAllocationPriorities $ do
setTitleI $ MsgAllocationPrioritiesTitle tid ssh ash
wrapForm priosView def
{ formEncoding = priosEnctype
, formAction = Just . SomeRoute $ AllocationR tid ssh ash APriosR
}
let priosForm = wrapForm priosView def
{ formEncoding = priosEnctype
, formAction = Just . SomeRoute $ AllocationR tid ssh ash APriosR
}
gradeScale <- getsYesod $ view _appAllocationGradeScale
gradeOrdinalProportion <- getsYesod $ view _appAllocationGradeOrdinalProportion
$(i18nWidgetFile "allocation-priorities")

View File

@ -10,7 +10,7 @@ import Handler.Utils
import Handler.Utils.Allocation
import qualified Database.Esqueleto as E
import qualified Database.Esqueleto.Utils.TH as E
import qualified Database.Esqueleto.Utils as E
import qualified Data.Csv as Csv
@ -103,11 +103,15 @@ instance CsvColumnsExplained AllocationUserTableCsv where
getAUsersR, postAUsersR :: TermId -> SchoolId -> AllocationShorthand -> Handler Html
getAUsersR = postAUsersR
postAUsersR tid ssh ash = do
usersTable <- runDB $ do
(usersTable, missingPriorities) <- runDB $ do
Entity aId _ <- getBy404 $ TermSchoolAllocationShort tid ssh ash
now <- liftIO getCurrentTime
resultsDone <- (<= NTop (Just now)) . NTop <$> allocationDone aId
missingPriorities <- E.selectExists . E.from $ \allocationUser ->
E.where_ $ allocationUser E.^. AllocationUserAllocation E.==. E.val aId
E.&&. E.isNothing (allocationUser E.^. AllocationUserPriority)
csvName <- getMessageRender <*> pure (MsgAllocationUsersCsvName tid ssh ash)
let
@ -220,9 +224,12 @@ postAUsersR tid ssh ash = do
& defaultSorting [SortAscBy "priority", SortAscBy "user-matriculation"]
& defaultPagesize PagesizeAll
dbTableDB' allocationUsersDBTableValidator allocationUsersDBTable
usersTable <- dbTableDB' allocationUsersDBTableValidator allocationUsersDBTable
return (usersTable, missingPriorities)
siteLayoutMsg MsgMenuAllocationUsers $ do
setTitleI $ MsgAllocationUsersTitle tid ssh ash
when missingPriorities $
notification NotificationBroad =<< messageIconI Warning IconMissingAllocationPriority MsgAllocationMissingPrioritiesIgnored
usersTable

View File

@ -119,7 +119,13 @@ computeAllocation allocId cRestr = do
return ((courseApplicationUser, courseApplicationCourse), (courseApplicationAllocationPriority, courseApplicationRatingPoints))
gradeScale <- getsYesod $ view _appAllocationGradeScale
gradeOrdinalPlaces <- getsYesod $ view _appAllocationGradeOrdinalPlaces
gradeOrdinalProportion <- getsYesod $ view _appAllocationGradeOrdinalProportion
let ordinalUsers = getSum . flip foldMap users'' $ \(_, prio) -> case prio of
AllocationPriorityOrdinal{} -> Sum 1
_other -> mempty
gradeOrdinalPlaces :: Natural
gradeOrdinalPlaces = round . abs $ ordinalUsers * gradeOrdinalProportion
let centralNudge user cloneIndex grade = case allocationPrio user of
AllocationPriorityNumeric{..}
-> let allocationPriorities' = under vector (sortOn Down) allocationPriorities

View File

@ -125,8 +125,8 @@ data AppSettings = AppSettings
, appTransactionLogIPRetentionTime :: NominalDiffTime
, appAllocationGradeScale :: Rational
, appAllocationGradeOrdinalPlaces :: Natural
, appAllocationGradeScale
, appAllocationGradeOrdinalProportion :: Rational
, appReloadTemplates :: Bool
-- ^ Use the reload version of templates
@ -423,7 +423,7 @@ instance FromJSON AppSettings where
appTransactionLogIPRetentionTime <- o .: "ip-retention-time"
appAllocationGradeScale <- o .: "allocation-grade-scale" <|> fmap toRational (o .: "allocation-grade-scale" :: Aeson.Parser Scientific)
appAllocationGradeOrdinalPlaces <- o .: "allocation-grade-ordinal-places"
appAllocationGradeOrdinalProportion <- o .: "allocation-grade-ordinal-proportion" <|> fmap toRational (o .: "allocation-grade-ordinal-proportion" :: Aeson.Parser Scientific)
appUserDefaults <- o .: "user-defaults"
appAuthPWHash <- o .: "auth-pw-hash"

View File

@ -82,6 +82,7 @@ data Icon
| IconMenuAdmin
| IconPageActionPrimaryExpand | IconPageActionSecondary
| IconBreadcrumbSeparator
| IconMissingAllocationPriority
deriving (Eq, Ord, Enum, Bounded, Show, Read, Generic, Typeable)
iconText :: Icon -> Text
@ -142,6 +143,7 @@ iconText = \case
IconPageActionPrimaryExpand -> "bars"
IconPageActionSecondary -> "ellipsis-h"
IconBreadcrumbSeparator -> "angle-right"
IconMissingAllocationPriority -> "empty-set"
instance Universe Icon
instance Finite Icon

View File

@ -6,7 +6,7 @@ module Utils.Message
, addMessage, addMessageI, addMessageIHamlet, addMessageFile, addMessageWidget
, statusToUrgencyClass
, Message(..)
, messageIconI, messageIconIHamlet, messageIconWidget
, messageIconI, messageIconIHamlet, messageIconFile, messageIconWidget
, messageI, messageIHamlet, messageFile, messageWidget, messageTooltip
) where
@ -178,6 +178,9 @@ addMessageFile mc tPath = [e|addMessageIHamlet mc $(ihamletFile tPath)|]
messageFile :: MessageStatus -> FilePath -> ExpQ
messageFile mc tPath = [e|messageIHamlet mc $(ihamletFile tPath)|]
messageIconFile :: MessageStatus -> Icon -> FilePath -> ExpQ
messageIconFile mc mi tPath = [e|messageIHamlet mc mi $(ihamletFile tPath)|]
addMessageWidget :: forall m site.
( MonadHandler m
, HandlerSite m ~ site

View File

@ -0,0 +1,23 @@
$newline never
<section>
^{priosForm}
<section>
<dl .deflist>
<dt .deflist__dt>
Einfluss von Bewertungen bei numerischen Dringlichkeiten
<p .deflist__explanation>
Dozenten können, durch Hinterlegen von Bewertungen, die Vergabe #
in einem Maße beeinflussen, das einer Abweichung der #
zentralen Dringlichkeit um plus bzw. minus diesen Wert, #
entspricht.
<dd .deflist__dd>
#{rationalToFixed2 gradeScale}
<dt .deflist__dt>
Einfluss von Bewertungen bei Dringlichkeiten durch Sortierung
<p .deflist__explanation>
Dozenten können, durch Hinterlegen von Bewertungen, die #
Vergabe in einem Maße beeinflussen, das einer Verschiebung des #
Bewerbers (auf- bzw. abwärts) um diese Proportion, in der nach #
Dringlichkeit sortierten Bewerberliste, entspricht.
<dd .deflist__dd>
#{textPercent gradeOrdinalProportion 1}

View File

@ -9,10 +9,15 @@ Alle weiteren Spalten werden als ganze Zahlen interpretiert und #
kodieren die jeweilige zentrale Dringlichkeit bei der Vergabe der #
Plätze. #
Hierbei wird die erste Dringlichkeits-Spalte verwendet zur Vergabe des #
jeweils ersten Platzes, die zweite Spalte für den zweiten Platz, usw. #
Größere Zahlen kodieren eine höhere Dringlichkeit. #
Größere Zahlen kodieren eine höhere Dringlichkeit.
Hierbei wird die höchste der jeweiligen Dringlichkeiten verwendet zur #
Vergabe des jeweils ersten Platzes, die nächst niedrigere für den #
zweiten Platz, usw.. #
Werden für einen Bewerber mehr Plätze in Betracht gezogen, als #
Dringlichkeiten für ihn angegeben wurden, so wird hierfür die #
niedrigste Dringlichkeit verwendet.
<br />

View File

@ -9,8 +9,11 @@ Die zentrale Dringlichkeit ergibt sich ausschließlich anhand der #
Sortierung der CSV-Datei. #
Bewerber, deren Matrikelnummer später in der Datei vorkommt, erhalten #
für alle ihre Plätze eine höhere Dringlichkeit, als Bewerber, deren #
Matrikelnummern in der Datei früher vorkommen. #
eine höhere Dringlichkeit, als Bewerber, deren Matrikelnummern in der #
Datei früher vorkommen. #
Für ihren zweiten Platz haben alle Bewerber eine niedrigere #
Dringlichkeit als andere Bewerber für ihren ersten Platz, usw..
<br />