chore(exam): add examiner to occurrence options

we intend to use the exam examiner for adding occurrences through tutorials
This commit is contained in:
Steffen Jost 2024-12-09 17:38:34 +01:00 committed by Sarah Vaupel
parent ae6d3b0fc3
commit 36a3b04ad8
15 changed files with 70 additions and 26 deletions

View File

@ -1,6 +0,0 @@
if [[ ! -d .stack-work-test ]]; then
mv -vT .stack-work .stack-work-test
[[ -d .stack-work-build ]] && mv -vT .stack-work-build .stack-work
else
echo "Directory .stack-work-test exists already."
fi

View File

@ -64,7 +64,7 @@ ExamAutomaticGradingTip: Sollen die Gesamtleistungen der Teilnehmer:innen automa
ExamBonus: Bonuspunkte-System
ExamGradingMode: Bewertungsmodus
ExamGradingModeTip: In welcher Form werden Prüfungsleistungen für diese Prüfung eingetragen?
ExamStaff: Prüfer:innen/Verantwortliche Hochschullehrer:innen
ExamStaff: Prüfer:innen
ExamStaffTip: Geben Sie bitte in jedem Fall einen Namen an, der Prüfer:in/Veranstalter:in/Hochschullehrer:in eindeutig identifiziert! Sollte der Name des Prüfers/der Prüferin allein womöglich nicht eindeutig sein, so geben Sie bitte eindeutig identifizierende Zusatzinfos, wie beispielsweise den Lehrstuhl bzw. die LFE o.Ä., an.
ExamExamOfficeSchools: Zusätzliche Bereiche
ExamExamOfficeSchoolsTip: Prüfungsbeauftragte von Bereichen, die Sie hier angeben, erhalten im System (zusätzlich zum primären Bereich der zugehörigen Kursart) volle Einsicht in sämtliche für diese Prüfung hinterlegten Leistungen, unabhängig von den Studiendaten der Teilnehmer:innen.
@ -123,6 +123,8 @@ ExamOccurrenceStartMustBeAfterExamStart eoName@ExamOccurrenceName: Beginn des Te
ExamOccurrenceEndMustBeBeforeExamEnd eoName@ExamOccurrenceName: Ende des Termins #{eoName} liegt nach dem Ende der Prüfung
ExamOccurrenceDuplicate eoRoom@Text eoRange@Text: Raum #{eoRoom}, Termin #{eoRange} kommt mehrfach mit der selben Beschreibung vor
ExamOccurrenceDuplicateName eoName@ExamOccurrenceName: Interne Terminbezeichnung #{eoName} kommt mehrfach vor
ExamOccurrenceExaminerIsUnset !ident-ok: —
ExamOccurrenceExaminerIsHidden: Prüfer wird nur Teilnehmer:innen angezeigt
ExamOccurrenceRoomIsUnset !ident-ok: —
ExamOccurrenceRoomIsHidden: Raum wird nur Teilnehmer:innen angezeigt
ExamOccurrenceCannotBeDeletedDueToRegistrations eoName@ExamOccurrenceName: Termin #{eoName} kann nicht gelöscht werden, da noch Teilnehmer:innen diesem Termin zugewiesen sind. Über die Liste von Prüfungsteilnehmern können Sie zunächst die entsprechenden Terminzuweisungen entfernen.
@ -264,6 +266,7 @@ ExamAutoOccurrenceExceptionRoomTooSmall: Automatische Verteilung gescheitert. Ei
ExamBonusInfoPoints: Zur Berechnung von Bonuspunkten werden nur jene Blätter herangezogen, deren Aktivitätszeitraum vor Start des jeweiligen Termin/Prüfung begonnen hat
ExamUserCsvSheetName tid@TermId ssh@SchoolId csh@CourseShorthand examn@ExamName: #{foldCase (termToText (unTermKey tid))}-#{foldedCase (unSchoolKey ssh)}-#{foldedCase csh}-#{foldedCase examn} Teilnehmer
ExamRoomExaminerTip: Nur bereits eingetragene Korrektor:innen sind hier erlaubt
ExamRoomCapacityTip: Maximale Anzahl an Prüfungsteilnehmern für diesen Termin/Raum; leer lassen für unbeschränkte Teilnehmeranzahl
ExamRoomMappingRandom: Verteilung
ExamFinishHeading: Prüfungsergebnisse sichtbar schalten

View File

@ -64,7 +64,7 @@ ExamAutomaticGradingTip: Should the exam achievement be automatically computed f
ExamBonus: Bonus point system
ExamGradingMode: Grading mode
ExamGradingModeTip: In which format should grades for this exam be entered?
ExamStaff: Examiner/Responsible university teacher
ExamStaff: Examiner
ExamStaffTip: Please always specify a name that uniquely identifies the examiner/organiser/repsonsible university teacher! If there is a possibility that the name alone is ambiguous please also specify some additional information e.g. the professorial chair or the educational and research unit.
ExamExamOfficeSchools: Additional departments
ExamExamOfficeSchoolsTip: Exam offices of departments you specify here will also have full access to all results for this exam disregarding the individual participants' features of study.
@ -123,8 +123,10 @@ ExamOccurrenceStartMustBeAfterExamStart eoName: Start of the occurrence #{eoName
ExamOccurrenceEndMustBeBeforeExamEnd eoName: End of the occurrence #{eoName} must be before the exam end
ExamOccurrenceDuplicate eoRoom eoRange: Combination of room #{eoRoom} and occurrence #{eoRange} occurs multiple times
ExamOccurrenceDuplicateName eoName: Internal name #{eoName} occurs multiple times
ExamOccurrenceExaminerIsUnset !ident-ok: —
ExamOccurrenceExaminerIsHidden: Examiner only displayed to participants registered for this occurrence
ExamOccurrenceRoomIsUnset: —
ExamOccurrenceRoomIsHidden: Room is only displayed to participants registered for this occurrence/room
ExamOccurrenceRoomIsHidden: Room only displayed to participants registered for this occurrence
ExamOccurrenceCannotBeDeletedDueToRegistrations eoName: Occurrence #{eoName} cannot be deleted because participants are registered for it. You can remove the offending registrations via the list of exam participants.
ExamRegistrationMustFollowSchoolSeparationFromStart dayCount: As per school rules there #{pluralEN dayCount "needs" "need"} to be at least #{dayCount} #{pluralEN dayCount "day" "days"} between "Register from" and "Start".
ExamRegistrationMustFollowSchoolDuration dayCount: As per school rules there #{pluralEN dayCount "needs" "need"} to be at least #{dayCount} #{pluralEN dayCount "day" "days"} between "Register from" and "Register to".
@ -262,6 +264,8 @@ ExamAutoOccurrenceExceptionNoUsers: No participants can be distributed with the
ExamAutoOccurrenceExceptionRoomTooSmall: Automatic distribution failed. A different distribution procedure might succeed. Alternatively, minimizing rooms or removing small rooms might help.
ExamBonusInfoPoints: When calculating an exam bonus only those sheets will be considered, for which the submission period started before the start of the relevant occurrence/room
ExamUserCsvSheetName tid ssh csh examn: #{foldCase (termToText (unTermKey tid))}-#{foldedCase (unSchoolKey ssh)}-#{foldedCase csh}-#{foldedCase examn} Participants
ExamRoomExaminerTip: Only correctors allowed here, add beforehand
ExamRoomCapacityTip: Maximum number of participants for this occurrence/room; leave empty for unlimited capacity
ExamRoomMappingRandom: Distribution
ExamFinishHeading: Make results visible

View File

@ -34,4 +34,5 @@ NullDeletes: Zum Löschen NULL eingeben.
SortPriority: Sortierungspriorität
NoProblem: Keine Probleme gefunden
Unknown: ist unbekannt
Ambiguous: ist uneindeutig
UnknownOrNotAllowed: ist unbekannt oder hier nicht erlaubt
Ambiguous: ist uneindeutig

View File

@ -34,4 +34,5 @@ NullDeletes: Enter NULL to delete.
SortPriority: Sort order priority
NoProblem: No Probleme found
Unknown: is unknown
Ambiguous: is ambiguous
UnknownOrNotAllowed: is unknown or not allowed here
Ambiguous: is ambiguous

View File

@ -37,14 +37,15 @@ ExamPart
UniqueExamPartName exam name !force
deriving Read Show Eq Ord Generic
ExamOccurrence
exam ExamId
name ExamOccurrenceName
room RoomReference Maybe
roomHidden Bool default=false
capacity Word64 Maybe
start UTCTime
end UTCTime Maybe
description StoredMarkup Maybe
exam ExamId
name ExamOccurrenceName
examiner UserId Maybe
room RoomReference Maybe
roomHidden Bool default=false
capacity Word64 Maybe
start UTCTime
end UTCTime Maybe
description StoredMarkup Maybe
UniqueExamOccurrence exam name
deriving Generic
ExamRegistration

View File

@ -80,6 +80,7 @@ postEEditR tid ssh csh examn = do
ExamOccurrence
{ examOccurrenceExam = eId
, examOccurrenceName = eofName
, examOccurrenceExaminer = eofExaminer
, examOccurrenceRoom = eofRoom
, examOccurrenceRoomHidden = eofRoomHidden
, examOccurrenceCapacity = eofCapacity
@ -95,6 +96,7 @@ postEEditR tid ssh csh examn = do
lift $ replace eofId' ExamOccurrence
{ examOccurrenceExam = eId
, examOccurrenceName = eofName
, examOccurrenceExaminer = eofExaminer
, examOccurrenceRoom = eofRoom
, examOccurrenceRoomHidden = eofRoomHidden
, examOccurrenceCapacity = eofCapacity
@ -118,7 +120,7 @@ postEEditR tid ssh csh examn = do
when brokenRefs $
throwM ExamEditWouldBreakSheetTypeReference
deleteWhere [ ExamPartExam ==. eId, ExamPartId /<-. pIds ]
forM_ (Set.toList efExamParts) $ \case
ExamPartForm{ epfId = Nothing, .. } -> insert_

View File

@ -62,6 +62,7 @@ data ExamForm = ExamForm
data ExamOccurrenceForm = ExamOccurrenceForm
{ eofId :: Maybe CryptoUUIDExamOccurrence
, eofName :: ExamOccurrenceName
, eofExaminer :: Maybe UserId
, eofRoom :: Maybe RoomReference
, eofRoomHidden :: Bool
, eofCapacity :: Maybe Word64
@ -74,6 +75,7 @@ instance Ord ExamOccurrenceForm where
compare = mconcat
[ comparing eofName
, comparing eofStart
, comparing eofExaminer
, comparing eofRoom
, comparing eofEnd
, comparing eofCapacity
@ -164,7 +166,7 @@ examForm (Entity _ Course{..}) template csrf = hoist liftHandler $ do
(fslI MsgExamAuthorshipStatementContent & setTooltip MsgExamAuthorshipStatementContentForcedTip)
contentField ttipReq
| not schoolSheetExamAuthorshipStatementAllowOther
= fmap (fmap authorshipStatementDefinitionContent) . traverse forcedContentField $ entityVal <$> mSchoolAuthorshipStatement
= fmap (fmap authorshipStatementDefinitionContent) (traverse (forcedContentField . entityVal) mSchoolAuthorshipStatement)
| otherwise
= Just <$> reqContentField ttipReq
in case schoolSheetExamAuthorshipStatementMode of
@ -257,9 +259,18 @@ examOccurrenceForm prev = wFormToAForm $ do
fmap (fmap Set.fromList) . massInputAccumEditW miAdd' miCell' miButtonAction' miLayout' miIdent' (fslI MsgExamOccurrences) False $ Set.toList <$> prev
where
examinerField = knownUserField True $ Just $ E.from $ \usr -> do
E.where_ $
(E.exists . E.from $ \exCorr -> E.where_ $ exCorr E.^. ExamCorrectorUser E.==. usr E.^. UserId
) E.||.
(E.exists . E.from $ \exOccr -> E.where_ $ exOccr E.^. ExamOccurrenceExaminer E.==. E.just (usr E.^. UserId)
)
pure usr
examOccurrenceForm' nudge mPrev csrf = do
(eofIdRes, eofIdView) <- mopt hiddenField ("" & addName (nudge "id")) (Just $ eofId =<< mPrev)
(eofNameRes, eofNameView) <- mpreq (textField & cfStrip & cfCI) (fslI MsgExamRoomName & addName (nudge "name")) (eofName <$> mPrev)
(eofExaminerRes, eofExaminerView) <- mopt examinerField (fslI MsgExamStaff & addName (nudge "examiner")) (eofExaminer <$> mPrev) -- TODO: restrict suggestions!
(eofRoomRes', eofRoomView) <- ($ mempty) . renderAForm FormVertical $ (,)
<$> roomReferenceFormOpt (fslI MsgExamRoomRoom & addName (nudge "room")) (eofRoom <$> mPrev)
<*> apopt checkBoxField (fslI MsgExamRoomRoomHidden & setTooltip MsgExamRoomRoomHiddenTip & addName (nudge "room-hidden")) (eofRoomHidden <$> mPrev)
@ -273,6 +284,7 @@ examOccurrenceForm prev = wFormToAForm $ do
return ( ExamOccurrenceForm
<$> eofIdRes
<*> eofNameRes
<*> eofExaminerRes
<*> eofRoomRes
<*> eofRoomHiddenRes
<*> eofCapacityRes
@ -372,6 +384,7 @@ examFormTemplate (Entity eId Exam{..}) = do
return ExamOccurrenceForm
{ eofId
, eofName = examOccurrenceName
, eofExaminer = examOccurrenceExaminer
, eofRoom = examOccurrenceRoom
, eofRoomHidden = examOccurrenceRoomHidden
, eofCapacity = examOccurrenceCapacity

View File

@ -75,6 +75,7 @@ postCExamNewR tid ssh csh = do
| ExamOccurrenceForm{..} <- Set.toList efOccurrences
, let examOccurrenceExam = examid
examOccurrenceName = eofName
examOccurrenceExaminer = eofExaminer
examOccurrenceRoom = eofRoom
examOccurrenceRoomHidden = eofRoomHidden
examOccurrenceCapacity = eofCapacity

View File

@ -48,7 +48,7 @@ getEShowR tid ssh csh examn = do
let occurrenceAssignmentsVisible = NTop (Just cTime) >= NTop examPublishOccurrenceAssignments || examOccurrenceRule == ExamRoomFifo
occurrenceAssignmentsShown = occurrenceAssignmentsVisible || lecturerInfoShown
sheets <- selectList [ SheetCourse ==. examCourse ] []
sheets <- selectList [ SheetCourse ==. examCourse ] []
let examPartSheets epId = do
let sheets' = flip filter sheets $ \(Entity _ Sheet{..}) -> has (_examPart . re _SqlKey . only epId) sheetType
flip filterM sheets' $ \(Entity _ Sheet{..}) -> hasReadAccessTo $ CSheetR tid ssh csh sheetName SShowR
@ -142,6 +142,11 @@ getEShowR tid ssh csh examn = do
guard $ all (\(Entity _ occ, _, _, _) -> examOccurrenceRoom occ == examOccurrenceRoom primeOcc) occurrences
guard $ andOf (folded . _4) occurrences
examOccurrenceRoom primeOcc
examExaminer = do
(Entity _ primeOcc, _, _, _) <- occurrences ^? _head
guard $ all (\(Entity _ occ, _, _, _) -> examOccurrenceExaminer occ == examOccurrenceExaminer primeOcc) occurrences
guard $ andOf (folded . _4) occurrences
examOccurrenceExaminer primeOcc
registerWidget mOcc
| isRegistered <- is _Just $ join registered
, examOccurrenceRule /= ExamRoomFifo || (isRegistered && not (orOf (folded . _2) occurrences))
@ -204,7 +209,7 @@ getEShowR tid ssh csh examn = do
guard $ evalExamModeDNF schoolExamDiscouragedModes examExamMode
guardM . lift . hasWriteAccessTo $ CExamR tid ssh csh examn EEditR
return $ notification NotificationBroad =<< messageI Warning MsgExamModeSchoolDiscouraged
siteLayoutMsg heading $ do
setTitleI heading
let

View File

@ -185,7 +185,7 @@ commR CommunicationRoute{..} = do
recipientAForm = postProcess <$> massInputA MassInput{..} (fslI MsgCommRecipients & setTooltip MsgCommRecipientsTip) True (Just chosenRecipients')
where
miAdd pos@(BoundedPosition RecipientCustom, 0) dim@1 liveliness nudge submitView = guardOn (miAllowAdd pos dim liveliness) $ \csrf -> do
(addRes, addView) <- mpreq (multiUserField True Nothing) (fslpI MsgUtilEMail (mr MsgUtilEMail) & setTooltip MsgUtilMultiEmailFieldTip & addName (nudge "email")) Nothing
(addRes, addView) <- mpreq (multiUserField False Nothing) (fslpI MsgUtilEMail (mr MsgUtilEMail) & setTooltip MsgUtilMultiEmailFieldTip & addName (nudge "email")) Nothing
let
addRes' = addRes <&> \nEmails ((Map.elems &&& maybe 0 (succ . snd . fst) . Map.lookupMax) . Map.filterWithKey (\(BoundedPosition c, _) _ -> c == RecipientCustom) -> (oEmails, kStart)) -> FormSuccess . Map.fromList . zip (map (BoundedPosition RecipientCustom, ) [kStart..]) . Set.toList $ nEmails `Set.difference` Set.fromList oEmails
return (addRes', $(widgetFile "widgets/communication/recipientAdd"))

View File

@ -2006,7 +2006,7 @@ knownUserField onlySuggested suggestions = Field{..}
let errMsg m = SomeMessage $ SomeMessages [SomeMessage MsgAvsPersonNo, text2message "/", SomeMessage MsgCompanyPersonalNumber, text2message t, m]
case dbRes of
[uid] -> return $ Right $ Just $ E.unValue uid
[] -> return $ Left $ errMsg $ SomeMessage MsgUnknown
[] -> return $ Left $ errMsg $ SomeMessage $ bool MsgUnknown MsgUnknownOrNotAllowed onlySuggested
_ -> return $ Left $ errMsg $ SomeMessage MsgAmbiguous
fieldParse _ _ = return $ Right Nothing

View File

@ -84,6 +84,9 @@ $maybe desc <- examDescription
#{c}
^{notificationPersonalIdentification}
$maybe examinerId <- examExaminer
<dt .deflist__dt>_{MsgExamStaff}
<dd .deflist__dd>^{userIdWidget examinerId}
$maybe room <- examRoom
<dt .deflist__dt>_{MsgExamRoom}
<dd .deflist__dd>^{roomReferenceShortWidget room}
@ -194,6 +197,8 @@ $if not (null occurrences)
<th .table__th>
_{MsgExamRoomName}
\ ^{isVisible False}
$if is _Nothing examExaminer
<th .table__th>_{MsgExamStaff}
$if is _Nothing examRoom
<th .table__th>_{MsgExamRoom}
$if not examTimes
@ -234,11 +239,21 @@ $if not (null occurrences)
<th .table__th>_{MsgExamRoomDescription}
<tbody>
$forall (occurrence, registered, rCount, showRoom) <- occurrences
$with Entity _occId ExamOccurrence{examOccurrenceName, examOccurrenceRoom, examOccurrenceStart, examOccurrenceEnd, examOccurrenceDescription} <- occurrence
$with Entity _occId ExamOccurrence{examOccurrenceName, examOccurrenceExaminer, examOccurrenceRoom, examOccurrenceStart, examOccurrenceEnd, examOccurrenceDescription} <- occurrence
$with registerWdgt <- registerWidget (Just occurrence)
<tr .table__row :markUnregisteredOccurrences (Just occurrence) && not registered:.occurrence--not-registered>
$if occurrenceNamesShown
<td .table__td #exam-occurrence__#{examOccurrenceName}>#{examOccurrenceName}
$if is _Nothing examExaminer
$if showRoom
<td .table__td>
$maybe examinerId <- examOccurrenceExaminer
^{userIdWidget examinerId}
$nothing
_{MsgExamOccurrenceExaminerIsUnset}
$else
<td .table__td .explanation>
_{MsgExamOccurrenceExaminerIsHidden}
$if is _Nothing examRoom
$if showRoom
<td .table__td>

View File

@ -5,6 +5,7 @@ $#
$# SPDX-License-Identifier: AGPL-3.0-or-later
<td .form--vertical__cell>#{csrf}^{fvInput eofIdView}^{fvWidget eofNameView}
<td .form--vertical__cell>^{fvWidget eofExaminerView}
<td .form--vertical__cell>^{eofRoomView}
<td .form--vertical__cell>^{fvWidget eofCapacityView}
<td .form--vertical__cell>^{fvWidget eofStartView}

View File

@ -10,6 +10,8 @@ $# SPDX-License-Identifier: AGPL-3.0-or-later
<th>
_{MsgExamRoomName} #
<span .form-group__required-marker>
<th>
_{MsgExamStaff}
<th>
_{MsgExamRoom}
<th>
@ -22,6 +24,7 @@ $# SPDX-License-Identifier: AGPL-3.0-or-later
<td>
<tr>
<td>
<td .explanation>_{MsgExamRoomExaminerTip}
<td>
<td .explanation>_{MsgExamRoomCapacityTip}
<td>