diff --git a/frontend/src/app.sass b/frontend/src/app.sass index 0156fee16..9029b76c5 100644 --- a/frontend/src/app.sass +++ b/frontend/src/app.sass @@ -588,13 +588,35 @@ section padding-bottom: 30px border-bottom: 1px solid #d3d3d3 - + section + & + section, & + .two-column-sections margin-top: 20px &:last-child border-bottom: none padding-bottom: 0px +.two-column-sections + padding-bottom: 30px + border-bottom: 1px solid #d3d3d3 + + & + section, & + .two-column-sections + margin-top: 20px + + &:last-child + border-bottom: none + padding-bottom: 0px + + @media (min-width: 768px) + display: flex + justify-content: space-between + + & > section + padding: 0 + border: none + + margin: 0 auto + width: calc(50% - 7px) + .headline-one margin-bottom: 10px diff --git a/messages/uniworx/misc/de-de-formal.msg b/messages/uniworx/misc/de-de-formal.msg index b575ebe86..e2a58c6f2 100644 --- a/messages/uniworx/misc/de-de-formal.msg +++ b/messages/uniworx/misc/de-de-formal.msg @@ -2551,8 +2551,13 @@ ExamCloseTip: Wenn eine Prüfung abgeschlossen wird, werden Prüfungsbeauftragte ExamCloseReminder: Bitte schließen Sie die Prüfung frühstmöglich, sobald die Prüfungsleistungen sich voraussichtlich nicht mehr ändern werden. Z.B. direkt nach der Klausureinsicht. ExamDidClose: Prüfung erfolgreich abgeschlossen ExamCloseTipOnFinished: Die Prüfung wird automatisch abgeschlossen, also Prüfungsbeauftragte, die im System Note einsehen, benachrichtigt und danach bei Änderungen informiert, sobald die Noten für die Prüfungsteilnehmer veröffentlicht werden. +ExamFinishHeading: Prüfungsergebnisse sichtbar schalten +BtnFinishExam: Prüfungsergebnisse sichtbar schalten +ExamFinishTip: Wenn die Prufungsergebnisse sichtbar geschaltet sind, können Teilnehmende ihre Ergebnisse im System einsehen und werden über diesen Umstand informiert. Es wird die Prüfungfrist „_{MsgExamFinished}“ auf den aktuellen Zeitpunkt gesetzt. +ExamDidFinish: Prüfungsergbnisse sichtbar geschaltet ExamClosedSince time@Text: Prüfung abgeschlossen seit #{time} +ExamFinishedSince time@Text: Prüfungsergebnisse sichtbar seit #{time} LecturerInfoTooltipNew: Neues Feature LecturerInfoTooltipProblem: Feature mit bekannten Problemen diff --git a/messages/uniworx/misc/en-eu.msg b/messages/uniworx/misc/en-eu.msg index 39de22e28..953f884e9 100644 --- a/messages/uniworx/misc/en-eu.msg +++ b/messages/uniworx/misc/en-eu.msg @@ -2551,8 +2551,13 @@ ExamCloseTip: When an exam is closed all relevant exam offices, which pull exam ExamCloseReminder: Please close the exam as soon as possible, when exam achievements are no longer expected to change e.g. after inspection of the exam has concluced. ExamDidClose: Successfully closed exam ExamCloseTipOnFinished: The exam will be closed automatically as soon as exam participants are informed of their exam achievements. That means exam offices will be able notified once and after that each time a grade changes. +ExamFinishHeading: Make results visible +BtnFinishExam: Make results visible +ExamFinishTip: After results are made visible participants are notified and can view their result in Uni2work. The exam timestamp “_{MsgExamFinished}” will be set to the current time. +ExamDidFinish: Successfully made results visible ExamClosedSince time: Exam closed since #{time} +ExamFinishedSince time: Exam results visible since #{time} LecturerInfoTooltipNew: New feature LecturerInfoTooltipProblem: Feature with known issues diff --git a/src/Handler/Exam/Show.hs b/src/Handler/Exam/Show.hs index 1509bfa98..be4c41e55 100644 --- a/src/Handler/Exam/Show.hs +++ b/src/Handler/Exam/Show.hs @@ -7,7 +7,7 @@ import Handler.Exam.Register import Handler.Exam.AutoOccurrence (examAutoOccurrenceCalculateWidget) -import Handler.ExamOffice.Exam (examCloseWidget) +import Handler.ExamOffice.Exam (examCloseWidget, examFinishWidget) import Data.Map ((!?)) import qualified Data.Map as Map @@ -106,6 +106,7 @@ getEShowR tid ssh csh examn = do partNumbersShown = lecturerInfoShown examClosedShown = lecturerInfoShown && isn't _ExamCloseOnFinished' schoolExamCloseMode showCloseWidget = lecturerInfoShown + showFinishWidget = lecturerInfoShown && is _Nothing examFinished showAutoOccurrenceCalculateWidget = lecturerInfoShown showRegisteredCount = lecturerInfoShown examFinishedMsg = if lecturerInfoShown then MsgExamFinished else MsgExamFinishedParticipant @@ -191,6 +192,7 @@ getEShowR tid ssh csh examn = do showOccurrenceMappingColumn = examOccurrenceRuleAutomatic examOccurrenceRule && occurrenceAssignmentsShown && is _Just examExamOccurrenceMapping closeWgt <- examCloseWidget (SomeRoute $ CExamR tid ssh csh examn EUsersR) eId + finishWgt <- examFinishWidget (SomeRoute $ CExamR tid ssh csh examn EUsersR) eId let heading = prependCourseTitle tid ssh csh $ CI.original examName diff --git a/src/Handler/Exam/Users.hs b/src/Handler/Exam/Users.hs index 497cea1bd..c1efbcf8c 100644 --- a/src/Handler/Exam/Users.hs +++ b/src/Handler/Exam/Users.hs @@ -14,7 +14,7 @@ import Handler.Utils.StudyFeatures import Handler.Exam.AutoOccurrence (examAutoOccurrenceCalculateWidget) -import Handler.ExamOffice.Exam (examCloseWidget) +import Handler.ExamOffice.Exam (examCloseWidget, examFinishWidget) import qualified Database.Esqueleto as E import qualified Database.Esqueleto.Utils as E @@ -1131,6 +1131,7 @@ postEUsersR tid ssh csh examn = do redirect $ CExamR tid ssh csh examn EUsersR closeWgt <- examCloseWidget (SomeRoute $ CExamR tid ssh csh examn EUsersR) eId + finishWgt <- examFinishWidget (SomeRoute $ CExamR tid ssh csh examn EUsersR) eId siteLayoutMsg (prependCourseTitle tid ssh csh MsgExamUsersHeading) $ do setTitleI $ prependCourseTitle tid ssh csh MsgExamUsersHeading diff --git a/src/Handler/ExamOffice/Exam.hs b/src/Handler/ExamOffice/Exam.hs index 0c8391fc1..381e2def7 100644 --- a/src/Handler/ExamOffice/Exam.hs +++ b/src/Handler/ExamOffice/Exam.hs @@ -2,7 +2,7 @@ module Handler.ExamOffice.Exam ( getEGradesR, postEGradesR - , examCloseWidget + , examCloseWidget, examFinishWidget ) where import Import @@ -79,6 +79,47 @@ examCloseWidget dest eId = do return $(widgetFile "widgets/exam-close") +data ButtonFinishExam = BtnFinishExam + deriving (Enum, Eq, Ord, Bounded, Read, Show, Generic, Typeable) +instance Universe ButtonFinishExam +instance Finite ButtonFinishExam + +nullaryPathPiece ''ButtonFinishExam $ camelToPathPiece' 1 + +embedRenderMessage ''UniWorX ''ButtonFinishExam id +instance Button UniWorX ButtonFinishExam where + btnClasses BtnFinishExam = [BCIsButton] + + +examFinishWidget :: SomeRoute UniWorX -> ExamId -> Handler Widget +examFinishWidget dest eId = do + Exam{examFinished} <- runDB $ get404 eId + + examFinishedStr <- for examFinished $ formatTime SelFormatDateTime + + ((finishRes, finishView'), finishEnc) <- runFormPost $ identifyForm BtnFinishExam buttonForm + + formResult finishRes $ \case + BtnFinishExam -> do + now <- liftIO getCurrentTime + + unless (is _Nothing examFinished) $ + invalidArgs ["Exam is already finished"] + + runDB $ update eId [ ExamFinished =. Just now ] + addMessageI Success MsgExamDidFinish + redirect dest + + let finishView = wrapForm finishView' def + { formSubmit = FormNoSubmit + , formAction = Just dest + , formEncoding = finishEnc + } + + return $(widgetFile "widgets/exam-finish") + + + type ExamUserTableExpr = ( E.SqlExpr (Entity ExamResult) `E.InnerJoin` E.SqlExpr (Entity User) ) @@ -186,7 +227,7 @@ getEGradesR = postEGradesR postEGradesR tid ssh csh examn = do uid <- requireAuthId now <- liftIO getCurrentTime - ((usersResult, examUsersTable), Entity eId _) <- runDB $ do + ((usersResult, examUsersTable), Entity eId Exam{examFinished}) <- runDB $ do exam@(Entity eid Exam{..}) <- fetchExam tid ssh csh examn Course{..} <- getJust examCourse @@ -431,6 +472,7 @@ postEGradesR tid ssh csh examn = do whenIsJust usersResult join closeWgt <- examCloseWidget (SomeRoute $ CExamR tid ssh csh examn EGradesR) eId + finishWgt <- examFinishWidget (SomeRoute $ CExamR tid ssh csh examn EGradesR) eId hasUsers <- hasReadAccessTo $ CExamR tid ssh csh examn EUsersR siteLayoutMsg (prependCourseTitle tid ssh csh MsgExamOfficeExamUsersHeading) $ do diff --git a/src/Handler/Utils/Submission.hs b/src/Handler/Utils/Submission.hs index adddb8c79..43ecf30db 100644 --- a/src/Handler/Utils/Submission.hs +++ b/src/Handler/Utils/Submission.hs @@ -957,4 +957,4 @@ correctionInvisibleWidget tid ssh csh shn cID (Entity subId sub) = runMaybeT $ d unless (NTop (Just now) >= NTop examFinished) $ tellPoint CorrectionInvisibleExamUnfinished - return $ notificationWidget NotificationBroad Warning $(widgetFile "submission-correction-invisible") + return $ notification NotificationBroad =<< messageIconWidget Warning IconInvisible $(widgetFile "submission-correction-invisible") diff --git a/templates/exam-office/exam-results.hamlet b/templates/exam-office/exam-results.hamlet index 5b077e6d8..329a53cdf 100644 --- a/templates/exam-office/exam-results.hamlet +++ b/templates/exam-office/exam-results.hamlet @@ -1,6 +1,13 @@ $newline never -
- ^{closeWgt} +$if is _Nothing examFinished +
+
+ ^{closeWgt} +
+ ^{finishWgt} +$else +
+ ^{closeWgt}
$if hasUsers ^{examGradesExplanation} diff --git a/templates/exam-show.hamlet b/templates/exam-show.hamlet index 79fc2dc91..c6d59608b 100644 --- a/templates/exam-show.hamlet +++ b/templates/exam-show.hamlet @@ -140,12 +140,31 @@ $maybe desc <- examDescription $if is _Nothing (examRequiredEquipment examExamMode) ^{notificationPersonalIdentification} -$if showCloseWidget && is _Nothing examClosed -
-

- _{MsgExamCloseHeading} - \ ^{isVisible False} - ^{closeWgt} +$if (showCloseWidget && is _Nothing examClosed) && (showFinishWidget && is _Nothing examFinished) +
+
+

+ _{MsgExamCloseHeading} + \ ^{isVisible False} + ^{closeWgt} +
+

+ _{MsgExamFinishHeading} + \ ^{isVisible False} + ^{finishWgt} +$else + $if showCloseWidget && is _Nothing examClosed +
+

+ _{MsgExamCloseHeading} + \ ^{isVisible False} + ^{closeWgt} + $if showFinishWidget && is _Nothing examFinished +
+

+ _{MsgExamFinishHeading} + \ ^{isVisible False} + ^{finishWgt} $if examOccurrenceRuleAutomatic examOccurrenceRule && showAutoOccurrenceCalculateWidget

@@ -318,6 +337,8 @@ $if (gradingShown || partsShown) && not (null examParts) $if partNumbersShown + $if showPartSheets + #{showFixed True sumMaxPoints} @@ -327,6 +348,8 @@ $if (gradingShown || partsShown) && not (null examParts) _{MsgExamBonusAchieved} + $if showPartSheets + $if showMaxPoints $if showAchievedPoints @@ -336,6 +359,8 @@ $if (gradingShown || partsShown) && not (null examParts) $if partNumbersShown + $if showPartSheets + $if showMaxPoints $if showAchievedPoints @@ -349,6 +374,8 @@ $if (gradingShown || partsShown) && not (null examParts) $if partNumbersShown + $if showPartSheets + $if showMaxPoints #{showFixed True sumMaxPoints} diff --git a/templates/exam-users.hamlet b/templates/exam-users.hamlet index 9e85b0605..8c49ddc23 100644 --- a/templates/exam-users.hamlet +++ b/templates/exam-users.hamlet @@ -1,8 +1,17 @@ $newline never -
- $if is _Nothing examClosed -

_{MsgExamCloseHeading} - ^{closeWgt} +$if is _Nothing examFinished +
+
+

_{MsgExamCloseHeading} + ^{closeWgt} +
+

_{MsgExamFinishHeading} + ^{finishWgt} +$else +
+ $if is _Nothing examClosed +

_{MsgExamCloseHeading} + ^{closeWgt} $if examOccurrenceRuleAutomatic examOccurrenceRule

_{MsgExamAutoOccurrenceHeading} diff --git a/templates/widgets/exam-finish.hamlet b/templates/widgets/exam-finish.hamlet new file mode 100644 index 000000000..acc0db3d2 --- /dev/null +++ b/templates/widgets/exam-finish.hamlet @@ -0,0 +1,8 @@ +$newline never +$maybe finished <- examFinishedStr +

+ _{MsgExamFinishedSince finished} +$nothing +

+ _{MsgExamFinishTip} + ^{finishView}