chore(tutorial): assigning exam occurrences may check duplicate examiners

towards #2347
This commit is contained in:
Steffen Jost 2025-02-20 14:54:29 +01:00 committed by Sarah Vaupel
parent be18af08c6
commit 9fbab25ecc
5 changed files with 43 additions and 21 deletions

View File

@ -138,7 +138,8 @@ CourseUserNoTutorialsDeregistered: Teilnehmer:in ist zu keinem der gewählten Ku
CourseUserTutorials: Angemeldete Kurse
CourseUserExams: Angemeldete Prüfungen
CourseUserExamOccurrences: Prüfungstermin
CourseUserExamOccurrenceOverride: Ggf. vorhanden Prüfungstermin überschreiben
CourseUserExamOccurrenceOverride: Ggf. vorhandenen Prüfungstermin überschreiben
CourseUserExamOccurrenceAgainExaminer: Ggf. vorherige Prüfer erneut erlauben
CourseUserSheets: Übungsblätter
CsvColumnUserName: Voller Name des/der Teilnehmers/Teilnehmerin
CsvColumnUserMatriculation: AVS Nummer des/der Teilnehmers/Teilnehmerin

View File

@ -139,6 +139,7 @@ CourseUserTutorials: Registered courses
CourseUserExams: Registered exams
CourseUserExamOccurrences: Exam occurrence
CourseUserExamOccurrenceOverride: Override other registrations for this exam, if any
CourseUserExamOccurrenceAgainExaminer: Possibly allow previous examiners again
CourseUserSheets: Exercise sheets
CsvColumnUserName: Participant's full name
CsvColumnUserMatriculation: Participant's AVS number

View File

@ -87,7 +87,7 @@ ExamRoomAlreadyExists: Prüfung ist bereits eingetragen
ExamRoomName: Interne Bezeichnung
ExamRoomCapacity: Kapazität
ExamRoomCapacityNegative: Kapazität darf nicht negativ sein
ExamRommCapacityInsufficient n@Int: Kapazität reicht nicht aus, nur noch #{n} Plätze verfügbar
ExamRoomCapacityInsufficient n@Int: Kapazität reicht nicht aus, #{noneOneMoreDE n "keine Plätze" "nur noch ein Platz" ("nur noch " <> tshow n <> " Plätze")} verfügbar
ExamRoomTime: Termin
ExamRoomStart: Beginn
ExamRoomEnd: Ende

View File

@ -87,7 +87,7 @@ ExamRoomAlreadyExists: Occurrence already configured
ExamRoomName: Internal name
ExamRoomCapacity: Capacity
ExamRoomCapacityNegative: Capacity may not be negative
ExamRommCapacityInsufficient n@Int: Insufficient capacity, only #{n} remaining
ExamRoomCapacityInsufficient n@Int: Insufficient capacity, #{noneOneMoreEN n "none" "just one" ("only " <> tshow n)} remaining
ExamRoomTime: Time
ExamRoomStart: Start
ExamRoomEnd: End

View File

@ -32,6 +32,7 @@ import qualified Data.ByteString.Lazy as LBS
import Database.Esqueleto.Experimental ((:&)(..))
import qualified Database.Esqueleto.Experimental as E -- needs TypeApplications Lang-Pragma
import qualified Database.Esqueleto.Utils as E
import Handler.Course.Users
@ -61,8 +62,9 @@ data TutorialUserActionData
| TutorialUserSendMailData
| TutorialUserDeregisterData
| TutorialUserAssignExamData
{ tuOccurrenceId :: ExamOccurrenceId
, tuReassign :: Bool
{ tuOccurrenceId :: ExamOccurrenceId
, tuExaminerAgain :: Bool
, tuReassign :: Bool
}
deriving (Eq, Ord, Read, Show, Generic)
@ -160,7 +162,8 @@ postTUsersR tid ssh csh tutn = do
( TutorialUserAssignExam
, TutorialUserAssignExamData
<$> apopt (selectField $ pure $ mkExamOccurrenceOptions exOccs) (fslI MsgCourseUserExamOccurrences) Nothing
<*> apopt checkBoxField (fslI MsgCourseUserExamOccurrenceOverride) (Just False)
<*> apopt checkBoxField (fslI MsgCourseUserExamOccurrenceAgainExaminer) (Just False)
<*> apopt checkBoxField (fslI MsgCourseUserExamOccurrenceOverride) (Just False)
) $
(if null qualifications then mempty else
[ ( TutorialUserRenewQualification
@ -232,23 +235,40 @@ postTUsersR tid ssh csh tutn = do
]
addMessageI Success $ MsgTutorialUsersDeregistered nrDel
reloadKeepGetParams croute
(TutorialUserAssignExamData{..}, selectedUsers)
(TutorialUserAssignExamData{..}, setSelectedUsers)
| (Just (ExamOccurrence{..}, _, (eid,_))) <- Map.lookup tuOccurrenceId exOccs -> do
let n = Set.size selectedUsers
capOk <- ifNothing examOccurrenceCapacity (pure True) $ \(fromIntegral -> totalCap) -> do
usedCap <- runDBRead $ count [ExamRegistrationOccurrence ==. Just tuOccurrenceId, ExamRegistrationUser /<-. Set.toList selectedUsers]
let ok = totalCap - usedCap >= n
unless ok $ addMessageI Error $ MsgExamRommCapacityInsufficient $ totalCap - usedCap
pure ok
when capOk do
let regTemplate uid = ExamRegistration eid uid (Just tuOccurrenceId) now
nrOk <- runDB $ if tuReassign
then putMany [regTemplate uid | uid <- Set.toList selectedUsers] >> pure n
else forM (Set.toList selectedUsers) (insertUnique . regTemplate) <&> (length . catMaybes)
let allok = bool Warning Success $ nrOk == n
addMessageI allok $ MsgTutorialUserExamAssignedFor nrOk n $ ciOriginal examOccurrenceName
reloadKeepGetParams croute
assignRes <- runDB $ do
(Set.toList &&& Set.size -> (selectedUsers, nr_usrs)) <- if -- remove duplicate examiners, if desired
| isJust examOccurrenceExaminer && not tuExaminerAgain -> do
conflictingUsers <- E.select $ do
reg :& occ <- E.from $ E.table @ExamRegistration
`E.innerJoin` E.table @ExamOccurrence
`E.on` (\(reg :& occ) -> occ E.^. ExamOccurrenceId E.=?. reg E.^. ExamRegistrationOccurrence)
E.where_ $ occ E.^. ExamOccurrenceExaminer E.==. E.val examOccurrenceExaminer
E.&&. occ E.^. ExamOccurrenceExam E.!=. E.val examOccurrenceExam
E.&&. (reg E.^. ExamRegistrationUser `E.in_` E.vals setSelectedUsers)
E.orderBy [E.asc $ reg E.^. ExamRegistrationUser]
E.distinct $ pure $ reg E.^. ExamRegistrationUser
return $ setSelectedUsers `Set.difference` Set.fromAscList (E.unValue <$> conflictingUsers)
| otherwise -> return setSelectedUsers
runExceptT $ do
whenIsJust examOccurrenceCapacity $ \(fromIntegral -> totalCap) -> do
usedCap <- lift $ count [ExamRegistrationOccurrence ==. Just tuOccurrenceId, ExamRegistrationUser /<-. selectedUsers]
let remCap = totalCap - usedCap
when (nr_usrs > remCap) $ throwE $ MsgExamRoomCapacityInsufficient remCap
let regTemplate uid = ExamRegistration eid uid (Just tuOccurrenceId) now
lift $ if tuReassign
then putMany [regTemplate uid | uid <- selectedUsers] >> pure nr_usrs
else forM selectedUsers (insertUnique . regTemplate) <&> (length . catMaybes)
case assignRes of
Left errm -> do
addMessageI Error errm
return Nothing
Right nrOk -> do
let total = Set.size setSelectedUsers
allok = bool Warning Success $ nrOk == total
addMessageI allok $ MsgTutorialUserExamAssignedFor nrOk total $ ciOriginal examOccurrenceName
reloadKeepGetParams croute
_other -> addMessageI Error MsgErrorUnknownFormAction >> return Nothing
case tcontent of