feat(external-exams): list

This commit is contained in:
Gregor Kleen 2019-11-28 15:55:49 +01:00 committed by Gregor Kleen
parent b7506a03b1
commit fa3521d6db
9 changed files with 224 additions and 2 deletions

View File

@ -1258,6 +1258,7 @@ BreadcrumbExternalExamShow coursen@CourseName examn@ExamName: #{coursen}, #{exam
BreadcrumbExternalExamEdit: Editieren BreadcrumbExternalExamEdit: Editieren
BreadcrumbExternalExamUsers: Teilnehmer BreadcrumbExternalExamUsers: Teilnehmer
BreadcrumbExternalExamGrades: Prüfungsleistungen BreadcrumbExternalExamGrades: Prüfungsleistungen
BreadcrumbExternalExamStaffInvite: Einladung zum Prüfer
TitleMetrics: Metriken TitleMetrics: Metriken
@ -1834,6 +1835,11 @@ MailSchoolFunctionInviteHeading school@SchoolName renderedFunction@Text: #{rende
SchoolFunctionInviteExplanation renderedFunction@Text: Sie wurden eingeladen, als #{renderedFunction} für ein Institut zu wirken. Sie erhalten, nachdem Sie die Einladung annehmen, erweiterte Rechte innerhalb des Instituts. SchoolFunctionInviteExplanation renderedFunction@Text: Sie wurden eingeladen, als #{renderedFunction} für ein Institut zu wirken. Sie erhalten, nachdem Sie die Einladung annehmen, erweiterte Rechte innerhalb des Instituts.
SchoolFunctionInvitationAccepted school@SchoolName renderedFunction@Text: #{renderedFunction}-Einladung zum Dozent für „#{school}“ angenommen SchoolFunctionInvitationAccepted school@SchoolName renderedFunction@Text: #{renderedFunction}-Einladung zum Dozent für „#{school}“ angenommen
MailSubjectExternalExamStaffInvitation coursen@CourseName examn@ExamName: Einladung zum Prüfer für „#{examn}“ in „#{coursen}“
ExternalExamStaffInviteHeading coursen@CourseName examn@ExamName: Einladung zum Prüfer für „#{examn}“ in „#{coursen}“
ExternalExamStaffInviteExplanation: Sie wurden eingeladen als Prüfer für eine Uni2work-externe Prüfung zu wirken. Sie können dann u.A. Noten für die Prüfung hinterlegen.
ExternalExamStaffInvitationAccepted coursen@CourseName examn@ExamName: Sie sind nun als Prüfer für „#{examn}“ in „#{coursen}“ eingetragen.
AllocationActive: Aktiv AllocationActive: Aktiv
AllocationName: Name AllocationName: Name
AllocationAvailableCourses: Kurse AllocationAvailableCourses: Kurse

View File

@ -1255,6 +1255,7 @@ BreadcrumbExternalExamShow coursen@CourseName examn@ExamName: #{coursen}, #{exam
BreadcrumbExternalExamEdit: Edit BreadcrumbExternalExamEdit: Edit
BreadcrumbExternalExamUsers: Participants BreadcrumbExternalExamUsers: Participants
BreadcrumbExternalExamGrades: Exam results BreadcrumbExternalExamGrades: Exam results
BreadcrumbExternalExamStaffInvite: Invitation
TitleMetrics: Metrics TitleMetrics: Metrics
@ -1830,6 +1831,11 @@ MailSchoolFunctionInviteHeading school renderedFunction: Invitation to be #{rend
SchoolFunctionInviteExplanation renderedFunction: You were invited to act as #{renderedFunction} for a department. By accepting the invitation you are granted elevated rights within the department. SchoolFunctionInviteExplanation renderedFunction: You were invited to act as #{renderedFunction} for a department. By accepting the invitation you are granted elevated rights within the department.
SchoolFunctionInvitationAccepted school renderedFunction: Successfully accepted invitation to be #{renderedFunction} for “#{school}” SchoolFunctionInvitationAccepted school renderedFunction: Successfully accepted invitation to be #{renderedFunction} for “#{school}”
MailSubjectExternalExamStaffInvitation coursen examn: Invitation to act as examiner for “#{examn}” of “#{coursen}”
ExternalExamStaffInviteHeading coursen examn: Invitation to act as examiner for “#{examn}” of “#{coursen}”
ExternalExamStaffInviteExplanation: You have been invited to act as an examiner for a uni2work-external exam. After accepting you will be able to upload exam results.
ExternalExamStaffInvitationAccepted coursen examn: You are now registered as an examiner for “#{examn}” of “#{coursen}”.
AllocationActive: Active AllocationActive: Active
AllocationName: Name AllocationName: Name
AllocationAvailableCourses: Courses AllocationAvailableCourses: Courses

1
routes
View File

@ -87,6 +87,7 @@
/edit EEEditR GET POST /edit EEEditR GET POST
/users EEUsersR GET POST /users EEUsersR GET POST
/grades EEGradesR GET POST !exam-office /grades EEGradesR GET POST !exam-office
/staff-invite EEStaffInviteR GET POST
/term TermShowR GET !free /term TermShowR GET !free

View File

@ -1995,6 +1995,7 @@ instance YesodBreadcrumbs UniWorX where
EEEditR -> i18nCrumb MsgBreadcrumbExternalExamEdit . Just $ EExamR tid ssh coursen examn EEShowR EEEditR -> i18nCrumb MsgBreadcrumbExternalExamEdit . Just $ EExamR tid ssh coursen examn EEShowR
EEUsersR -> i18nCrumb MsgBreadcrumbExternalExamUsers . Just $ EExamR tid ssh coursen examn EEShowR EEUsersR -> i18nCrumb MsgBreadcrumbExternalExamUsers . Just $ EExamR tid ssh coursen examn EEShowR
EEGradesR -> i18nCrumb MsgBreadcrumbExternalExamGrades . Just $ EExamR tid ssh coursen examn EEShowR EEGradesR -> i18nCrumb MsgBreadcrumbExternalExamGrades . Just $ EExamR tid ssh coursen examn EEShowR
EEStaffInviteR -> i18nCrumb MsgBreadcrumbExternalExamStaffInvite . Just $ EExamR tid ssh coursen examn EEShowR
-- breadcrumb _ = return ("Uni2work", Nothing) -- Default is no breadcrumb at all -- breadcrumb _ = return ("Uni2work", Nothing) -- Default is no breadcrumb at all

View File

@ -7,3 +7,4 @@ import Handler.ExternalExam.New as Handler.ExternalExam
import Handler.ExternalExam.Show as Handler.ExternalExam import Handler.ExternalExam.Show as Handler.ExternalExam
import Handler.ExternalExam.Edit as Handler.ExternalExam import Handler.ExternalExam.Edit as Handler.ExternalExam
import Handler.ExternalExam.Users as Handler.ExternalExam import Handler.ExternalExam.Users as Handler.ExternalExam
import Handler.ExternalExam.StaffInvite as Handler.ExternalExam

View File

@ -3,7 +3,79 @@ module Handler.ExternalExam.List
) where ) where
import Import import Import
import Handler.Utils
import qualified Database.Esqueleto as E
import qualified Database.Esqueleto.Utils as E
import qualified Data.Map as Map
getEExamListR :: Handler Html getEExamListR :: Handler Html
getEExamListR = error "Not implemented" getEExamListR = do
mAuthId <- maybeAuthId
let
examDBTable = DBTable{..}
where
resultEExam = _dbrOutput . _1
resultSchool = _dbrOutput . _2
queryEExam = $(E.sqlIJproj 2 1)
querySchool = $(E.sqlIJproj 2 2)
dbtSQLQuery (eexam `E.InnerJoin` school) = do
E.on $ eexam E.^. ExternalExamSchool E.==. school E.^. SchoolId
let
isStaff
| Just authId <- mAuthId
= E.exists . E.from $ \eexamStaff ->
E.where_ $ eexamStaff E.^. ExternalExamStaffExam E.==. eexam E.^. ExternalExamId
E.&&. eexamStaff E.^. ExternalExamStaffUser E.==. E.val authId
| otherwise
= E.false
isStudent
| Just authId <- mAuthId
= E.exists . E.from $ \eexamResult ->
E.where_ $ eexamResult E.^. ExternalExamResultExam E.==. eexam E.^. ExternalExamId
E.&&. eexamResult E.^. ExternalExamResultUser E.==. E.val authId
| otherwise
= E.false
E.where_ $ isStaff E.||. isStudent
return (eexam, school)
dbtRowKey = queryEExam >>> (E.^. ExternalExamId)
dbtProj x@(view resultEExam -> Entity _ ExternalExam{..}) = do
guardM . hasReadAccessTo $ EExamR externalExamTerm externalExamSchool externalExamCourseName externalExamExamName EEShowR
return x
dbtColonnade = widgetColonnade $ mconcat
[ sortable (Just "term") (i18nCell MsgTerm) $ \(view resultEExam -> Entity _ ExternalExam{..}) -> i18nCell . ShortTermIdentifier $ unTermKey externalExamTerm
, sortable (Just "school") (i18nCell MsgSchool) $ \(view resultSchool -> Entity _ School{..}) -> i18nCell schoolName
, sortable (Just "course") (i18nCell MsgCourse) $ \(view resultEExam -> Entity _ ExternalExam{..}) -> i18nCell externalExamCourseName
, sortable (Just "name") (i18nCell MsgExamName) $ \(view resultEExam -> Entity _ ExternalExam{..}) -> anchorCell (EExamR externalExamTerm externalExamSchool externalExamCourseName externalExamExamName EEShowR) externalExamExamName
]
dbtSorting = Map.fromList
[ ("term", SortColumn $ queryEExam >>> (E.^. ExternalExamTerm))
, ("school", SortColumn $ querySchool >>> (E.^. SchoolName))
, ("course", SortColumn $ queryEExam >>> (E.^. ExternalExamCourseName))
, ("name", SortColumn $ queryEExam >>> (E.^. ExternalExamExamName))
]
dbtFilter = Map.empty
dbtFilterUI = const mempty
dbtStyle = def
dbtParams = def
dbtIdent :: Text
dbtIdent = "external-exams"
dbtCsvEncode = noCsvEncode
dbtCsvDecode = Nothing
examDBTableValidator = def
& defaultSorting [SortDescBy "term", SortAscBy "school", SortAscBy "course", SortAscBy "name"]
examTable <- runDB $ dbTableWidget' examDBTableValidator examDBTable
let heading = MsgMenuExternalExamList
siteLayoutMsg heading $ do
setTitleI heading
examTable

View File

@ -4,6 +4,26 @@ module Handler.ExternalExam.Show
import Import import Import
import Handler.Utils
import qualified Data.CaseInsensitive as CI
getEEShowR :: TermId -> SchoolId -> CourseName -> ExamName -> Handler Html getEEShowR :: TermId -> SchoolId -> CourseName -> ExamName -> Handler Html
getEEShowR = error "Not implemented" getEEShowR tid ssh coursen examn = do
mUid <- maybeAuthId
(Entity _ ExternalExam{..}, fmap entityVal -> mResult, School{..}) <- runDB $ do
exam@(Entity eeId ExternalExam{..}) <- getBy404 $ UniqueExternalExam tid ssh coursen examn
school <- getJust externalExamSchool
mResult <- fmap join . for mUid $ getBy . UniqueExternalExamResult eeId
return (exam, mResult, school)
let heading = CI.original examn
siteLayoutMsg heading $ do
setTitleI heading
$(widgetFile "external-exam-show")

View File

@ -0,0 +1,74 @@
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Handler.ExternalExam.StaffInvite
( externalExamStaffInvitationConfig
, getEEStaffInviteR, postEEStaffInviteR
, InvitableJunction(..), InvitationDBData(..), InvitationTokenData(..)
) where
import Import
import Handler.Utils.Invitations
import Text.Hamlet (ihamlet)
import Data.Aeson hiding (Result(..))
instance IsInvitableJunction ExternalExamStaff where
type InvitationFor ExternalExamStaff = ExternalExam
data InvitableJunction ExternalExamStaff = JunctionExternalExamStaff
deriving (Eq, Ord, Read, Show, Generic, Typeable)
data InvitationDBData ExternalExamStaff = InvDBDataExternalExamStaff
deriving (Eq, Ord, Read, Show, Generic, Typeable)
data InvitationTokenData ExternalExamStaff = InvTokenDataExternalExamStaff
deriving (Eq, Ord, Read, Show, Generic, Typeable)
_InvitableJunction = iso
(\ExternalExamStaff{..} -> (externalExamStaffUser, externalExamStaffExam, JunctionExternalExamStaff))
(\(externalExamStaffUser, externalExamStaffExam, JunctionExternalExamStaff{}) -> ExternalExamStaff{..})
instance ToJSON (InvitableJunction ExternalExamStaff) where
toJSON = genericToJSON defaultOptions { constructorTagModifier = camelToPathPiece' 1 }
toEncoding = genericToEncoding defaultOptions { constructorTagModifier = camelToPathPiece' 1 }
instance FromJSON (InvitableJunction ExternalExamStaff) where
parseJSON = genericParseJSON defaultOptions { constructorTagModifier = camelToPathPiece' 1 }
instance ToJSON (InvitationDBData ExternalExamStaff) where
toJSON = genericToJSON defaultOptions { constructorTagModifier = camelToPathPiece' 3 }
toEncoding = genericToEncoding defaultOptions { constructorTagModifier = camelToPathPiece' 3 }
instance FromJSON (InvitationDBData ExternalExamStaff) where
parseJSON = genericParseJSON defaultOptions { constructorTagModifier = camelToPathPiece' 3 }
instance ToJSON (InvitationTokenData ExternalExamStaff) where
toJSON = genericToJSON defaultOptions { constructorTagModifier = camelToPathPiece' 3 }
toEncoding = genericToEncoding defaultOptions { constructorTagModifier = camelToPathPiece' 3 }
instance FromJSON (InvitationTokenData ExternalExamStaff) where
parseJSON = genericParseJSON defaultOptions { constructorTagModifier = camelToPathPiece' 3 }
externalExamStaffInvitationConfig :: InvitationConfig ExternalExamStaff
externalExamStaffInvitationConfig = InvitationConfig{..}
where
invitationRoute (Entity _ ExternalExam{..}) _ = return $ EExamR externalExamTerm externalExamSchool externalExamCourseName externalExamExamName EEStaffInviteR
invitationResolveFor _ = do
cRoute <- getCurrentRoute
case cRoute of
Just (EExamR tid ssh coursen examn EEStaffInviteR) ->
getKeyBy404 $ UniqueExternalExam tid ssh coursen examn
_other -> error "externalExamStaffInvitationConfig called from unsupported route"
invitationSubject (Entity _ ExternalExam{..}) _ = return . SomeMessage $ MsgMailSubjectExternalExamStaffInvitation externalExamCourseName externalExamExamName
invitationHeading (Entity _ ExternalExam{..}) _ = return . SomeMessage $ MsgExternalExamStaffInviteHeading externalExamCourseName externalExamExamName
invitationExplanation _ _ = return [ihamlet|_{SomeMessage MsgExternalExamStaffInviteExplanation}|]
invitationTokenConfig _ _ = do
itAuthority <- Right <$> liftHandler requireAuthId
return $ InvitationTokenConfig itAuthority Nothing (Just Nothing) Nothing
invitationRestriction _ _ = return Authorized
invitationForm _ (InvDBDataExternalExamStaff, _) _ = pure (JunctionExternalExamStaff, ())
invitationInsertHook _ _ _ _ = id
invitationSuccessMsg (Entity _ ExternalExam{..}) (Entity _ ExternalExamStaff{})
= return . SomeMessage $ MsgExternalExamStaffInvitationAccepted externalExamCourseName externalExamExamName
invitationUltDest (Entity _ ExternalExam{..}) _ = return . SomeRoute $ EExamR externalExamTerm externalExamSchool externalExamCourseName externalExamExamName EEShowR
getEEStaffInviteR, postEEStaffInviteR :: TermId -> SchoolId -> CourseName -> ExamName -> Handler Html
getEEStaffInviteR = postEEStaffInviteR
postEEStaffInviteR = invitationR externalExamStaffInvitationConfig

View File

@ -0,0 +1,41 @@
$newline never
$maybe ExternalExamResult{externalExamResultResult} <- mResult
<section>
<h2>
_{MsgExamResult}
<p .result>
$case externalExamResultResult
$of ExamAttended grade
$if externalExamShowGrades
_{grade}
$else
$if view (passingGrade . _Wrapped) grade
_{MsgExamPassed}
$else
_{MsgExamNotPassed}
$of ExamNoShow
_{MsgExamNoShow}
$of ExamVoided
_{MsgExamVoided}
<section>
<dl .deflist>
<dt .deflist__dt>_{MsgTerm}
<dd .deflist__dd>
_{unTermKey externalExamTerm}
<dt .deflist__dt>_{MsgCourseSchool}
<dd .deflist__dd>
#{schoolName}
<dt .deflist__dt>_{MsgCourseName}
<dd .deflist__dd>
#{externalExamCourseName}
<dt .deflist__dt>_{MsgExamName}
<dd .deflist__dd>
#{externalExamExamName}
$maybe examTime <- fmap externalExamResultTime mResult <|> externalExamDefaultTime
<dt .deflist__dt>
_{MsgExamTime}
<dd .deflist__dd>
^{formatTimeW SelFormatDateTime examTime}