feat(exams): exam finish button

This commit is contained in:
Gregor Kleen 2021-03-19 00:05:56 +01:00
parent a6390eccbd
commit 78d0f2522d
11 changed files with 146 additions and 18 deletions

View File

@ -588,13 +588,35 @@ section
padding-bottom: 30px padding-bottom: 30px
border-bottom: 1px solid #d3d3d3 border-bottom: 1px solid #d3d3d3
+ section & + section, & + .two-column-sections
margin-top: 20px margin-top: 20px
&:last-child &:last-child
border-bottom: none border-bottom: none
padding-bottom: 0px 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 .headline-one
margin-bottom: 10px margin-bottom: 10px

View File

@ -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. 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 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. 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} ExamClosedSince time@Text: Prüfung abgeschlossen seit #{time}
ExamFinishedSince time@Text: Prüfungsergebnisse sichtbar seit #{time}
LecturerInfoTooltipNew: Neues Feature LecturerInfoTooltipNew: Neues Feature
LecturerInfoTooltipProblem: Feature mit bekannten Problemen LecturerInfoTooltipProblem: Feature mit bekannten Problemen

View File

@ -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. 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 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. 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} ExamClosedSince time: Exam closed since #{time}
ExamFinishedSince time: Exam results visible since #{time}
LecturerInfoTooltipNew: New feature LecturerInfoTooltipNew: New feature
LecturerInfoTooltipProblem: Feature with known issues LecturerInfoTooltipProblem: Feature with known issues

View File

@ -7,7 +7,7 @@ import Handler.Exam.Register
import Handler.Exam.AutoOccurrence (examAutoOccurrenceCalculateWidget) import Handler.Exam.AutoOccurrence (examAutoOccurrenceCalculateWidget)
import Handler.ExamOffice.Exam (examCloseWidget) import Handler.ExamOffice.Exam (examCloseWidget, examFinishWidget)
import Data.Map ((!?)) import Data.Map ((!?))
import qualified Data.Map as Map import qualified Data.Map as Map
@ -106,6 +106,7 @@ getEShowR tid ssh csh examn = do
partNumbersShown = lecturerInfoShown partNumbersShown = lecturerInfoShown
examClosedShown = lecturerInfoShown && isn't _ExamCloseOnFinished' schoolExamCloseMode examClosedShown = lecturerInfoShown && isn't _ExamCloseOnFinished' schoolExamCloseMode
showCloseWidget = lecturerInfoShown showCloseWidget = lecturerInfoShown
showFinishWidget = lecturerInfoShown && is _Nothing examFinished
showAutoOccurrenceCalculateWidget = lecturerInfoShown showAutoOccurrenceCalculateWidget = lecturerInfoShown
showRegisteredCount = lecturerInfoShown showRegisteredCount = lecturerInfoShown
examFinishedMsg = if lecturerInfoShown then MsgExamFinished else MsgExamFinishedParticipant examFinishedMsg = if lecturerInfoShown then MsgExamFinished else MsgExamFinishedParticipant
@ -191,6 +192,7 @@ getEShowR tid ssh csh examn = do
showOccurrenceMappingColumn = examOccurrenceRuleAutomatic examOccurrenceRule && occurrenceAssignmentsShown && is _Just examExamOccurrenceMapping showOccurrenceMappingColumn = examOccurrenceRuleAutomatic examOccurrenceRule && occurrenceAssignmentsShown && is _Just examExamOccurrenceMapping
closeWgt <- examCloseWidget (SomeRoute $ CExamR tid ssh csh examn EUsersR) eId 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 let heading = prependCourseTitle tid ssh csh $ CI.original examName

View File

@ -14,7 +14,7 @@ import Handler.Utils.StudyFeatures
import Handler.Exam.AutoOccurrence (examAutoOccurrenceCalculateWidget) 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 as E
import qualified Database.Esqueleto.Utils 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 redirect $ CExamR tid ssh csh examn EUsersR
closeWgt <- examCloseWidget (SomeRoute $ CExamR tid ssh csh examn EUsersR) eId 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 siteLayoutMsg (prependCourseTitle tid ssh csh MsgExamUsersHeading) $ do
setTitleI $ prependCourseTitle tid ssh csh MsgExamUsersHeading setTitleI $ prependCourseTitle tid ssh csh MsgExamUsersHeading

View File

@ -2,7 +2,7 @@
module Handler.ExamOffice.Exam module Handler.ExamOffice.Exam
( getEGradesR, postEGradesR ( getEGradesR, postEGradesR
, examCloseWidget , examCloseWidget, examFinishWidget
) where ) where
import Import import Import
@ -79,6 +79,47 @@ examCloseWidget dest eId = do
return $(widgetFile "widgets/exam-close") 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) type ExamUserTableExpr = ( E.SqlExpr (Entity ExamResult)
`E.InnerJoin` E.SqlExpr (Entity User) `E.InnerJoin` E.SqlExpr (Entity User)
) )
@ -186,7 +227,7 @@ getEGradesR = postEGradesR
postEGradesR tid ssh csh examn = do postEGradesR tid ssh csh examn = do
uid <- requireAuthId uid <- requireAuthId
now <- liftIO getCurrentTime 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 exam@(Entity eid Exam{..}) <- fetchExam tid ssh csh examn
Course{..} <- getJust examCourse Course{..} <- getJust examCourse
@ -431,6 +472,7 @@ postEGradesR tid ssh csh examn = do
whenIsJust usersResult join whenIsJust usersResult join
closeWgt <- examCloseWidget (SomeRoute $ CExamR tid ssh csh examn EGradesR) eId 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 hasUsers <- hasReadAccessTo $ CExamR tid ssh csh examn EUsersR
siteLayoutMsg (prependCourseTitle tid ssh csh MsgExamOfficeExamUsersHeading) $ do siteLayoutMsg (prependCourseTitle tid ssh csh MsgExamOfficeExamUsersHeading) $ do

View File

@ -957,4 +957,4 @@ correctionInvisibleWidget tid ssh csh shn cID (Entity subId sub) = runMaybeT $ d
unless (NTop (Just now) >= NTop examFinished) $ unless (NTop (Just now) >= NTop examFinished) $
tellPoint CorrectionInvisibleExamUnfinished tellPoint CorrectionInvisibleExamUnfinished
return $ notificationWidget NotificationBroad Warning $(widgetFile "submission-correction-invisible") return $ notification NotificationBroad =<< messageIconWidget Warning IconInvisible $(widgetFile "submission-correction-invisible")

View File

@ -1,6 +1,13 @@
$newline never $newline never
<section> $if is _Nothing examFinished
^{closeWgt} <div .two-column-sections>
<section>
^{closeWgt}
<section>
^{finishWgt}
$else
<section>
^{closeWgt}
<section> <section>
$if hasUsers $if hasUsers
^{examGradesExplanation} ^{examGradesExplanation}

View File

@ -140,12 +140,31 @@ $maybe desc <- examDescription
$if is _Nothing (examRequiredEquipment examExamMode) $if is _Nothing (examRequiredEquipment examExamMode)
^{notificationPersonalIdentification} ^{notificationPersonalIdentification}
$if showCloseWidget && is _Nothing examClosed $if (showCloseWidget && is _Nothing examClosed) && (showFinishWidget && is _Nothing examFinished)
<section> <div .two-column-sections>
<h2> <section>
_{MsgExamCloseHeading} <h2>
\ ^{isVisible False} _{MsgExamCloseHeading}
^{closeWgt} \ ^{isVisible False}
^{closeWgt}
<section>
<h2>
_{MsgExamFinishHeading}
\ ^{isVisible False}
^{finishWgt}
$else
$if showCloseWidget && is _Nothing examClosed
<section>
<h2>
_{MsgExamCloseHeading}
\ ^{isVisible False}
^{closeWgt}
$if showFinishWidget && is _Nothing examFinished
<section>
<h2>
_{MsgExamFinishHeading}
\ ^{isVisible False}
^{finishWgt}
$if examOccurrenceRuleAutomatic examOccurrenceRule && showAutoOccurrenceCalculateWidget $if examOccurrenceRuleAutomatic examOccurrenceRule && showAutoOccurrenceCalculateWidget
<section> <section>
<h2> <h2>
@ -318,6 +337,8 @@ $if (gradingShown || partsShown) && not (null examParts)
$if partNumbersShown $if partNumbersShown
<td> <td>
<td> <td>
$if showPartSheets
<td>
<td .table__td> <td .table__td>
#{showFixed True sumMaxPoints} #{showFixed True sumMaxPoints}
<td> <td>
@ -327,6 +348,8 @@ $if (gradingShown || partsShown) && not (null examParts)
<td .table__td> <td .table__td>
<a href=@{CourseR tid ssh csh SheetListR}> <a href=@{CourseR tid ssh csh SheetListR}>
_{MsgExamBonusAchieved} _{MsgExamBonusAchieved}
$if showPartSheets
<td>
$if showMaxPoints $if showMaxPoints
<td .table__td> <td .table__td>
$if showAchievedPoints $if showAchievedPoints
@ -336,6 +359,8 @@ $if (gradingShown || partsShown) && not (null examParts)
$if partNumbersShown $if partNumbersShown
<td> <td>
<td> <td>
$if showPartSheets
<td>
$if showMaxPoints $if showMaxPoints
<td> <td>
$if showAchievedPoints $if showAchievedPoints
@ -349,6 +374,8 @@ $if (gradingShown || partsShown) && not (null examParts)
$if partNumbersShown $if partNumbersShown
<td> <td>
<td> <td>
$if showPartSheets
<td>
$if showMaxPoints $if showMaxPoints
<td .table__td> <td .table__td>
#{showFixed True sumMaxPoints} #{showFixed True sumMaxPoints}

View File

@ -1,8 +1,17 @@
$newline never $newline never
<section> $if is _Nothing examFinished
$if is _Nothing examClosed <div .two-column-sections>
<h2>_{MsgExamCloseHeading} <section>
^{closeWgt} <h2>_{MsgExamCloseHeading}
^{closeWgt}
<section>
<h2>_{MsgExamFinishHeading}
^{finishWgt}
$else
<section>
$if is _Nothing examClosed
<h2>_{MsgExamCloseHeading}
^{closeWgt}
$if examOccurrenceRuleAutomatic examOccurrenceRule $if examOccurrenceRuleAutomatic examOccurrenceRule
<section> <section>
<h2>_{MsgExamAutoOccurrenceHeading} <h2>_{MsgExamAutoOccurrenceHeading}

View File

@ -0,0 +1,8 @@
$newline never
$maybe finished <- examFinishedStr
<p>
_{MsgExamFinishedSince finished}
$nothing
<p .explanation>
_{MsgExamFinishTip}
^{finishView}