Major contributions towards #189 and #194

This commit is contained in:
SJost 2018-09-28 17:22:22 +02:00
parent 0a3b09f5ee
commit 66ab0f9be6
21 changed files with 203 additions and 126 deletions

View File

@ -228,6 +228,7 @@ CorrectionsUploaded num@Int64: #{display num} Korrekturen wurden gespeichert:
NoCorrectionsUploaded: In der hochgeladenen Datei wurden keine Korrekturen gefunden. NoCorrectionsUploaded: In der hochgeladenen Datei wurden keine Korrekturen gefunden.
RatingBy: Korrigiert von RatingBy: Korrigiert von
AssignedTime: Zuteilung
AchievedBonusPoints: Erreichte Bonuspunkte AchievedBonusPoints: Erreichte Bonuspunkte
AchievedNormalPoints: Erreichte Punkte AchievedNormalPoints: Erreichte Punkte
AchievedPassPoints: Erreichte Punkte AchievedPassPoints: Erreichte Punkte

3
models
View File

@ -41,7 +41,7 @@ StudyTerms
Primary key Primary key
Term json Term json
name TermIdentifier -- unTermKey :: TermId -> TermIdentifier name TermIdentifier -- unTermKey :: TermId -> TermIdentifier
start Day -- TermKey :: TermIdentifier -< TermId start Day -- TermKey :: TermIdentifier -> TermId
end Day end Day
holidays [Day] holidays [Day]
lectureStart Day lectureStart Day
@ -136,6 +136,7 @@ Submission
ratingPoints Points Maybe -- "Just" does not mean done ratingPoints Points Maybe -- "Just" does not mean done
ratingComment Text Maybe -- "Just" does not mean done ratingComment Text Maybe -- "Just" does not mean done
ratingBy UserId Maybe -- assigned corrector ratingBy UserId Maybe -- assigned corrector
ratingAssigned UTCTime Maybe -- time assigned corrector
ratingTime UTCTime Maybe -- "Just" here indicates done! ratingTime UTCTime Maybe -- "Just" here indicates done!
deriving Show deriving Show
SubmissionEdit SubmissionEdit

2
routes
View File

@ -55,10 +55,12 @@
-- For Pattern Synonyms see Foundation -- For Pattern Synonyms see Foundation
/course/ CourseListR GET !free /course/ CourseListR GET !free
!/course/new CourseNewR GET POST !lecturer !/course/new CourseNewR GET POST !lecturer
!/course/new/#{Maybe TermId}/#{Maybe SchoolId}/#{Maybe CourseShorthand} CourseNewTemplateR GET !lecturer
/course/#TermId/#SchoolId/#CourseShorthand CourseR !lecturer: /course/#TermId/#SchoolId/#CourseShorthand CourseR !lecturer:
/ CShowR GET !free / CShowR GET !free
/register CRegisterR POST !timeANDcapacity /register CRegisterR POST !timeANDcapacity
/edit CEditR GET POST /edit CEditR GET POST
/delete CDeleteR GET POST !lecturerANDempty
/users CUsersR GET /users CUsersR GET
/user/#CryptoUUIDUser CUserR GET /user/#CryptoUUIDUser CUserR GET
/correctors CHiWisR GET /correctors CHiWisR GET

View File

@ -79,7 +79,7 @@ campusLogin conf@LdapConf{..} = AuthPlugin{..}
((loginRes, _), _) <- lift . runFormPost $ renderAForm FormStandard campusForm ((loginRes, _), _) <- lift . runFormPost $ renderAForm FormStandard campusForm
case loginRes of case loginRes of
FormFailure errs -> do FormFailure errs -> do
forM_ errs $ addMessage "error" . toHtml forM_ errs $ addMessage Error . toHtml
redirect LoginR redirect LoginR
FormMissing -> redirect LoginR FormMissing -> redirect LoginR
FormSuccess CampusLogin{..} -> do FormSuccess CampusLogin{..} -> do

View File

@ -81,7 +81,7 @@ import Utils
import Utils.Form import Utils.Form
import Utils.Lens import Utils.Lens
import Data.Aeson import Data.Aeson hiding (Error)
import Data.Aeson.TH import Data.Aeson.TH
import qualified Data.Yaml as Yaml import qualified Data.Yaml as Yaml
@ -308,7 +308,7 @@ knownTags = Map.fromList -- should not throw exceptions, i.e. no getBy404 or req
[("free", trueAP) [("free", trueAP)
,("deprecated", APHandler $ \r _ -> do ,("deprecated", APHandler $ \r _ -> do
$logWarnS "AccessControl" ("deprecated route: " <> tshow r) $logWarnS "AccessControl" ("deprecated route: " <> tshow r)
addMessageI "error" MsgDeprecatedRoute addMessageI Error MsgDeprecatedRoute
allow <- appAllowDeprecated . appSettings <$> getYesod allow <- appAllowDeprecated . appSettings <$> getYesod
return $ bool (Unauthorized "Deprecated Route") Authorized allow return $ bool (Unauthorized "Deprecated Route") Authorized allow
) )
@ -850,12 +850,6 @@ pageActions (CourseListR) =
] ]
pageActions (CourseR tid ssh csh CShowR) = pageActions (CourseR tid ssh csh CShowR) =
[ PageActionPrime $ MenuItem [ PageActionPrime $ MenuItem
{ menuItemLabel = "Kurs Editieren"
, menuItemIcon = Nothing
, menuItemRoute = CourseR tid ssh csh CEditR
, menuItemAccessCallback' = return True
}
, PageActionPrime $ MenuItem
{ menuItemLabel = "Übungsblätter" { menuItemLabel = "Übungsblätter"
, menuItemIcon = Nothing , menuItemIcon = Nothing
, menuItemRoute = CourseR tid ssh csh SheetListR , menuItemRoute = CourseR tid ssh csh SheetListR
@ -877,12 +871,24 @@ pageActions (CourseR tid ssh csh CShowR) =
, menuItemRoute = CourseR tid ssh csh CCorrectionsR , menuItemRoute = CourseR tid ssh csh CCorrectionsR
, menuItemAccessCallback' = return True , menuItemAccessCallback' = return True
} }
, PageActionSecondary $ MenuItem , PageActionPrime $ MenuItem
{ menuItemLabel = "Neues Übungsblatt anlegen" { menuItemLabel = "Neues Übungsblatt anlegen"
, menuItemIcon = Nothing , menuItemIcon = Nothing
, menuItemRoute = CourseR tid ssh csh SheetNewR , menuItemRoute = CourseR tid ssh csh SheetNewR
, menuItemAccessCallback' = return True , menuItemAccessCallback' = return True
} }
, PageActionSecondary $ MenuItem
{ menuItemLabel = "Kurs editieren"
, menuItemIcon = Nothing
, menuItemRoute = CourseR tid ssh csh CEditR
, menuItemAccessCallback' = return True
}
, PageActionSecondary $ MenuItem
{ menuItemLabel = "Neuen Kurs klonen"
, menuItemIcon = Nothing
, menuItemRoute = CourseNewTemplateR (Just tid) (Just ssh) (Just csh)
, menuItemAccessCallback' = return True
}
] ]
pageActions (CourseR tid ssh csh SheetListR) = pageActions (CourseR tid ssh csh SheetListR) =
[ PageActionPrime $ MenuItem [ PageActionPrime $ MenuItem
@ -1152,7 +1158,7 @@ instance YesodAuth UniWorX where
excHandlers excHandlers
| isDummy || isPWFile | isDummy || isPWFile
= [ C.Handler $ \err -> do = [ C.Handler $ \err -> do
addMessage "error" (toHtml $ tshow (err :: CampusUserException)) addMessage Error (toHtml $ tshow (err :: CampusUserException))
$logErrorS "LDAP" $ tshow err $logErrorS "LDAP" $ tshow err
acceptExisting acceptExisting
] ]

View File

@ -54,7 +54,7 @@ postAdminTestR = do
((btnResult,_), _) <- runFormPost $ buttonForm ((btnResult,_), _) <- runFormPost $ buttonForm
case btnResult of case btnResult of
(FormSuccess CreateInf) -> setMessage "Informatik-Knopf gedrückt" (FormSuccess CreateInf) -> setMessage "Informatik-Knopf gedrückt"
(FormSuccess CreateMath) -> addMessage "warning" "Knopf Mathematik erkannt" (FormSuccess CreateMath) -> addMessage Warning "Knopf Mathematik erkannt"
_other -> return () _other -> return ()
getAdminTestR getAdminTestR

View File

@ -148,6 +148,16 @@ colRating = sortable (Just "rating") (i18nCell MsgRating) $ \DBRow{ dbrOutput=(
return $ CSubmissionR tid ssh csh sheetName cid CorrectionR return $ CSubmissionR tid ssh csh sheetName cid CorrectionR
in anchorCellM mkRoute $(widgetFile "widgets/rating") in anchorCellM mkRoute $(widgetFile "widgets/rating")
colAssigned :: IsDBTable m a => Colonnade _ CorrectionTableData (DBCell m a)
colAssigned = sortable (Just "assignedtime") (i18nCell MsgAssignedTime) $ \DBRow{ dbrOutput=(Entity _subId Submission{..}, _sheet, _course, _, _) } ->
maybe mempty timeCell submissionRatingAssigned
colRated :: IsDBTable m a => Colonnade _ CorrectionTableData (DBCell m a)
colRated = sortable (Just "ratingtime") (i18nCell MsgRatingTime) $ \DBRow{ dbrOutput=(Entity _subId Submission{..}, _sheet, _course, _, _) } ->
maybe mempty timeCell submissionRatingTime
type CorrectionTableExpr = (E.SqlExpr (Entity Course) `E.InnerJoin` E.SqlExpr (Entity Sheet) `E.InnerJoin` E.SqlExpr (Entity Submission)) `E.LeftOuterJoin` E.SqlExpr (Maybe (Entity User)) type CorrectionTableExpr = (E.SqlExpr (Entity Course) `E.InnerJoin` E.SqlExpr (Entity Sheet) `E.InnerJoin` E.SqlExpr (Entity Submission)) `E.LeftOuterJoin` E.SqlExpr (Maybe (Entity User))
makeCorrectionsTable :: ( IsDBTable m x, ToSortable h, Functor h ) makeCorrectionsTable :: ( IsDBTable m x, ToSortable h, Functor h )
@ -248,7 +258,7 @@ correctionsR whereClause (formColonnade -> displayColumns) psValidator actions =
Just currentRoute <- getCurrentRoute -- This should never be called from a 404 handler Just currentRoute <- getCurrentRoute -- This should never be called from a 404 handler
case actionRes of case actionRes of
FormFailure errs -> mapM_ (addMessage "danger" . toHtml) errs FormFailure errs -> mapM_ (addMessage Warning . toHtml) errs
FormMissing -> return () FormMissing -> return ()
FormSuccess (CorrDownloadData, subs) -> do FormSuccess (CorrDownloadData, subs) -> do
ids <- Set.fromList <$> forM (Set.toList subs) decrypt -- Set is not traversable ids <- Set.fromList <$> forM (Set.toList subs) decrypt -- Set is not traversable
@ -256,26 +266,32 @@ correctionsR whereClause (formColonnade -> displayColumns) psValidator actions =
sendResponse =<< submissionMultiArchive ids sendResponse =<< submissionMultiArchive ids
FormSuccess (CorrSetCorrectorData (Just uid), subs') -> do FormSuccess (CorrSetCorrectorData (Just uid), subs') -> do
subs <- mapM decrypt $ Set.toList subs' subs <- mapM decrypt $ Set.toList subs'
now <- liftIO getCurrentTime
runDB $ do runDB $ do
alreadyAssigned <- selectList [SubmissionId <-. subs, SubmissionRatingBy !=. Nothing] [] alreadyAssigned <- selectList [SubmissionId <-. subs, SubmissionRatingBy !=. Nothing] []
when (not $ null alreadyAssigned) $ do when (not $ null alreadyAssigned) $ do
mr <- (toHtml . ) <$> getMessageRender mr <- (toHtml . ) <$> getMessageRender
alreadyAssigned' <- forM alreadyAssigned $ \Entity{..} -> (, entityVal) <$> (encrypt entityKey :: DB CryptoFileNameSubmission) alreadyAssigned' <- forM alreadyAssigned $ \Entity{..} -> (, entityVal) <$> (encrypt entityKey :: DB CryptoFileNameSubmission)
addMessage "warning" =<< withUrlRenderer ($(ihamletFile "templates/messages/submissionsAlreadyAssigned.hamlet") mr) addMessage Warning =<< withUrlRenderer ($(ihamletFile "templates/messages/submissionsAlreadyAssigned.hamlet") mr)
let unassigned = Set.fromList subs `Set.difference` Set.fromList (entityKey <$> alreadyAssigned) let unassigned = Set.fromList subs `Set.difference` Set.fromList (entityKey <$> alreadyAssigned)
when (not $ null unassigned) $ do when (not $ null unassigned) $ do
num <- updateWhereCount [SubmissionId <-. Set.toList unassigned] [SubmissionRatingBy =. Just uid] num <- updateWhereCount [SubmissionId <-. Set.toList unassigned]
addMessageI "success" $ MsgUpdatedAssignedCorrectorSingle num [ SubmissionRatingBy =. Just uid
, SubmissionRatingAssigned =. Just now -- save, since only applies to unassigned
]
addMessageI Success $ MsgUpdatedAssignedCorrectorSingle num
redirect currentRoute redirect currentRoute
FormSuccess (CorrSetCorrectorData Nothing, subs') -> do FormSuccess (CorrSetCorrectorData Nothing, subs') -> do
subs <- mapM decrypt $ Set.toList subs' subs <- mapM decrypt $ Set.toList subs'
runDB $ do runDB $ do
num <- updateWhereCount [SubmissionId <-. subs] [ SubmissionRatingPoints =. Nothing num <- updateWhereCount [SubmissionId <-. subs]
, SubmissionRatingComment =. Nothing [ SubmissionRatingPoints =. Nothing
, SubmissionRatingBy =. Nothing , SubmissionRatingComment =. Nothing
, SubmissionRatingTime =. Nothing , SubmissionRatingBy =. Nothing
] , SubmissionRatingAssigned =. Nothing
addMessageI "success" $ MsgRemovedCorrections num , SubmissionRatingTime =. Nothing
]
addMessageI Success $ MsgRemovedCorrections num
redirect currentRoute redirect currentRoute
FormSuccess (CorrAutoSetCorrectorData shid, subs') -> do FormSuccess (CorrAutoSetCorrectorData shid, subs') -> do
subs <- mapM decrypt $ Set.toList subs' subs <- mapM decrypt $ Set.toList subs'
@ -284,16 +300,16 @@ correctionsR whereClause (formColonnade -> displayColumns) psValidator actions =
when (not $ null alreadyAssigned) $ do when (not $ null alreadyAssigned) $ do
mr <- (toHtml . ) <$> getMessageRender mr <- (toHtml . ) <$> getMessageRender
alreadyAssigned' <- forM alreadyAssigned $ \Entity{..} -> (, entityVal) <$> (encrypt entityKey :: DB CryptoFileNameSubmission) alreadyAssigned' <- forM alreadyAssigned $ \Entity{..} -> (, entityVal) <$> (encrypt entityKey :: DB CryptoFileNameSubmission)
addMessage "warning" =<< withUrlRenderer ($(ihamletFile "templates/messages/submissionsAlreadyAssigned.hamlet") mr) addMessage Warning =<< withUrlRenderer ($(ihamletFile "templates/messages/submissionsAlreadyAssigned.hamlet") mr)
let unassigned = Set.fromList subs `Set.difference` Set.fromList (entityKey <$> alreadyAssigned) let unassigned = Set.fromList subs `Set.difference` Set.fromList (entityKey <$> alreadyAssigned)
when (not $ null unassigned) $ do when (not $ null unassigned) $ do
(assigned, unassigned) <- assignSubmissions shid (Just unassigned) (assigned, unassigned) <- assignSubmissions shid (Just unassigned)
when (not $ null assigned) $ when (not $ null assigned) $
addMessageI "success" $ MsgUpdatedAssignedCorrectorsAuto (fromIntegral $ Set.size assigned) addMessageI Success $ MsgUpdatedAssignedCorrectorsAuto (fromIntegral $ Set.size assigned)
when (not $ null unassigned) $ do when (not $ null unassigned) $ do
mr <- (toHtml . ) <$> getMessageRender mr <- (toHtml . ) <$> getMessageRender
unassigned' <- forM (Set.toList unassigned) $ \sid -> (encrypt sid :: DB CryptoFileNameSubmission) unassigned' <- forM (Set.toList unassigned) $ \sid -> (encrypt sid :: DB CryptoFileNameSubmission)
addMessage "warning" =<< withUrlRenderer ($(ihamletFile "templates/messages/submissionsNotAssignedAuto.hamlet") mr) addMessage Warning =<< withUrlRenderer ($(ihamletFile "templates/messages/submissionsNotAssignedAuto.hamlet") mr)
redirect currentRoute redirect currentRoute
fmap toTypedContent . defaultLayout $ do fmap toTypedContent . defaultLayout $ do
@ -346,7 +362,9 @@ postCorrectionsR = do
, colCourse , colCourse
, colSheet , colSheet
, colSubmissionLink , colSubmissionLink
, colAssigned
, colRating , colRating
, colRated
] -- Continue here ] -- Continue here
psValidator = def psValidator = def
& restrictFilter (\name _ -> name /= "corrector") -- We need to be careful to restrict allowed sorting/filter to not expose sensitive information & restrictFilter (\name _ -> name /= "corrector") -- We need to be careful to restrict allowed sorting/filter to not expose sensitive information
@ -360,15 +378,17 @@ getCCorrectionsR = postCCorrectionsR
postCCorrectionsR tid ssh csh = do postCCorrectionsR tid ssh csh = do
Entity cid _ <- runDB $ getBy404 $ TermSchoolCourseShort tid ssh csh Entity cid _ <- runDB $ getBy404 $ TermSchoolCourseShort tid ssh csh
let whereClause = courseIs cid let whereClause = courseIs cid
colonnade = mconcat colonnade = mconcat -- should match getSSubsR for consistent UX
[ colSelect [ colSelect
, dbRow , dbRow
, colSheet , colSheet
, colCorrector
, colSMatrikel , colSMatrikel
, colSubmittors , colSubmittors
, colSubmissionLink , colSubmissionLink
, colRating , colRating
, colRated
, colCorrector
, colAssigned
] -- Continue here ] -- Continue here
psValidator = def psValidator = def
correctionsR whereClause colonnade psValidator $ Map.fromList correctionsR whereClause colonnade psValidator $ Map.fromList
@ -381,14 +401,16 @@ getSSubsR = postSSubsR
postSSubsR tid ssh csh shn = do postSSubsR tid ssh csh shn = do
shid <- runDB $ fetchSheetId tid ssh csh shn shid <- runDB $ fetchSheetId tid ssh csh shn
let whereClause = sheetIs shid let whereClause = sheetIs shid
colonnade = mconcat colonnade = mconcat -- should match getCCorrectionsR for consistent UX
[ colSelect [ colSelect
, dbRow , dbRow
, colSMatrikel , colSMatrikel
, colSubmittors , colSubmittors
, colSubmissionLink , colSubmissionLink
, colRating , colRating
, colRated
, colCorrector , colCorrector
, colAssigned
] ]
psValidator = def psValidator = def
correctionsR whereClause colonnade psValidator $ Map.fromList correctionsR whereClause colonnade psValidator $ Map.fromList
@ -433,7 +455,7 @@ postCorrectionR tid ssh csh shn cid = do
case corrResult of case corrResult of
FormMissing -> return () FormMissing -> return ()
FormFailure errs -> mapM_ (addMessage "error" . toHtml) errs FormFailure errs -> mapM_ (addMessage Error . toHtml) errs
FormSuccess (ratingPoints, ratingComment) -> do FormSuccess (ratingPoints, ratingComment) -> do
runDB $ do runDB $ do
uid <- liftHandlerT requireAuthId uid <- liftHandlerT requireAuthId
@ -442,23 +464,25 @@ postCorrectionR tid ssh csh shn cid = do
let rated = isJust $ void ratingPoints <|> void ratingComment let rated = isJust $ void ratingPoints <|> void ratingComment
update sub [ SubmissionRatingBy =. (uid <$ guard rated) update sub [ SubmissionRatingBy =. (uid <$ guard rated)
-- SJ: I don't think we need to update AssignedTime here, since this is just for correction upload
-- , SubmissionRatingAssigned +=. (Just now) -- TODO: Should submissionRatingAssigned change here if userId changes?
, SubmissionRatingTime =. (now <$ guard rated) , SubmissionRatingTime =. (now <$ guard rated)
, SubmissionRatingPoints =. ratingPoints , SubmissionRatingPoints =. ratingPoints
, SubmissionRatingComment =. ratingComment , SubmissionRatingComment =. ratingComment
] ]
addMessageI "success" $ bool MsgRatingDeleted MsgRatingUpdated rated addMessageI Success $ bool MsgRatingDeleted MsgRatingUpdated rated
redirect $ CSubmissionR tid ssh csh shn cid CorrectionR redirect $ CSubmissionR tid ssh csh shn cid CorrectionR
case uploadResult of case uploadResult of
FormMissing -> return () FormMissing -> return ()
FormFailure errs -> mapM_ (addMessage "error" . toHtml) errs FormFailure errs -> mapM_ (addMessage Error . toHtml) errs
FormSuccess fileSource -> do FormSuccess fileSource -> do
uid <- requireAuthId uid <- requireAuthId
runDB . runConduit $ transPipe lift fileSource .| extractRatingsMsg .| sinkSubmission uid (Right sub) True runDB . runConduit $ transPipe lift fileSource .| extractRatingsMsg .| sinkSubmission uid (Right sub) True
addMessageI "success" MsgRatingFilesUpdated addMessageI Success MsgRatingFilesUpdated
redirect $ CSubmissionR tid ssh csh shn cid CorrectionR redirect $ CSubmissionR tid ssh csh shn cid CorrectionR
defaultLayout $ do defaultLayout $ do
@ -488,16 +512,16 @@ postCorrectionsUploadR = do
case uploadRes of case uploadRes of
FormMissing -> return () FormMissing -> return ()
FormFailure errs -> mapM_ (addMessage "error" . toHtml) errs FormFailure errs -> mapM_ (addMessage Error . toHtml) errs
FormSuccess files -> do FormSuccess files -> do
uid <- requireAuthId uid <- requireAuthId
subs <- runDB . runConduit $ transPipe lift files .| extractRatingsMsg .| sinkMultiSubmission uid True subs <- runDB . runConduit $ transPipe lift files .| extractRatingsMsg .| sinkMultiSubmission uid True
if if
| null subs -> addMessageI "warning" MsgNoCorrectionsUploaded | null subs -> addMessageI Warning MsgNoCorrectionsUploaded
| otherwise -> do | otherwise -> do
subs' <- traverse encrypt $ Set.toList subs :: Handler [CryptoFileNameSubmission] subs' <- traverse encrypt $ Set.toList subs :: Handler [CryptoFileNameSubmission]
mr <- (toHtml .) <$> getMessageRender mr <- (toHtml .) <$> getMessageRender
addMessage "success" =<< withUrlRenderer ($(ihamletFile "templates/messages/correctionsUploaded.hamlet") mr) addMessage Success =<< withUrlRenderer ($(ihamletFile "templates/messages/correctionsUploaded.hamlet") mr)
defaultLayout $ do defaultLayout $ do

View File

@ -304,51 +304,52 @@ postCRegisterR tid ssh csh = do
(FormSuccess codeOk) (FormSuccess codeOk)
| registered -> do | registered -> do
runDB $ deleteBy $ UniqueParticipant aid cid runDB $ deleteBy $ UniqueParticipant aid cid
addMessageI "info" MsgCourseDeregisterOk addMessageI Info MsgCourseDeregisterOk
| codeOk -> do | codeOk -> do
actTime <- liftIO $ getCurrentTime actTime <- liftIO $ getCurrentTime
regOk <- runDB $ insertUnique $ CourseParticipant cid aid actTime regOk <- runDB $ insertUnique $ CourseParticipant cid aid actTime
when (isJust regOk) $ addMessageI "success" MsgCourseRegisterOk when (isJust regOk) $ addMessageI Success MsgCourseRegisterOk
| otherwise -> addMessageI "danger" MsgCourseSecretWrong | otherwise -> addMessageI Warning MsgCourseSecretWrong
(_other) -> return () -- TODO check this! (_other) -> return () -- TODO check this!
redirect $ CourseR tid ssh csh CShowR redirect $ CourseR tid ssh csh CShowR
getCourseNewR :: Handler Html getCourseNewR :: Handler Html -- call via toTextUrl
getCourseNewR = do getCourseNewR = do
uid <- requireAuthId uid <- requireAuthId
params <- runInputGetResult $ (,,) params <- runInputGetResult $ (,,) --TODO: change to AForm, which is used in Course-Copy-Button
<$> ireq ciTextField "csh" <$> iopt termNewField "tid"
<*> iopt textField "tid" <*> iopt ciTextField "ssh"
<*> iopt ciTextField "ssh" <*> iopt ciTextField "csh"
let noTemplateAction = courseEditHandler True Nothing let noTemplateAction = courseEditHandler True Nothing
case params of case params of
FormMissing -> noTemplateAction FormMissing -> noTemplateAction
FormFailure msgs -> forM_ msgs ((addMessage "error") . toHtml) FormFailure msgs -> forM_ msgs ((addMessage Error) . toHtml)
>> noTemplateAction >> noTemplateAction
FormSuccess (csh,mbTid,mbSsh) -> do FormSuccess (mbTid,mbSsh,mbCsh) ->
tid <- ifMaybeM mbTid Nothing $ \tid -> getCourseNewTemplateR (TermKey <$> mbTid) (SchoolKey <$> mbSsh) mbCsh
case termFromText tid of
Left err -> addMessage "error" (toHtml err) >> return Nothing
Right t -> return $ Just $ TermKey t
getCourseNewTemplateR tid (SchoolKey <$> mbSsh) csh
getCourseNewTemplateR :: Maybe TermId -> Maybe SchoolId -> CourseShorthand -> Handler Html getCourseNewTemplateR :: Maybe TermId -> Maybe SchoolId -> Maybe CourseShorthand -> Handler Html
getCourseNewTemplateR mbTid mbSsh csh = do getCourseNewTemplateR mbTid mbSsh mbCsh = do
uid <- requireAuthId uid <- requireAuthId
oldCourses <- runDB $ do oldCourses <- runDB $ do
E.select $ E.from $ \course -> do E.select $ E.from $ \course -> do
E.where_ $ course E.^. CourseShorthand E.==. E.val csh
whenIsJust mbSsh $ \ssh -> E.where_ $ course E.^. CourseSchool E.==. E.val ssh
whenIsJust mbTid $ \tid -> E.where_ $ course E.^. CourseTerm E.==. E.val tid whenIsJust mbTid $ \tid -> E.where_ $ course E.^. CourseTerm E.==. E.val tid
let lecturersSchool = whenIsJust mbSsh $ \ssh -> E.where_ $ course E.^. CourseSchool E.==. E.val ssh
whenIsJust mbCsh $ \csh -> E.where_ $ course E.^. CourseShorthand E.==. E.val csh
let lecturersCourse =
E.exists $ E.from $ \lecturer -> do E.exists $ E.from $ \lecturer -> do
E.where_ $ lecturer E.^. UserLecturerUser E.==. E.val uid E.where_ $ lecturer E.^. LecturerUser E.==. E.val uid
E.&&. lecturer E.^. UserLecturerSchool E.==. course E.^. CourseSchool E.&&. lecturer E.^. LecturerCourse E.==. course E.^. CourseId
let lecturersSchool =
E.exists $ E.from $ \user -> do
E.where_ $ user E.^. UserLecturerUser E.==. E.val uid
E.&&. user E.^. UserLecturerSchool E.==. course E.^. CourseSchool
let courseCreated c = let courseCreated c =
E.sub_select . E.from $ \edit -> do -- oldest edit must be creation E.sub_select . E.from $ \edit -> do -- oldest edit must be creation
E.where_ $ edit E.^. CourseEditCourse E.==. c E.^. CourseId E.where_ $ edit E.^. CourseEditCourse E.==. c E.^. CourseId
return $ E.min_ $ edit E.^. CourseEditTime return $ E.min_ $ edit E.^. CourseEditTime
E.orderBy [ E.desc $ E.case_ [(lecturersSchool, E.val (1 :: Int64))] (E.val 0) -- prefer from schools of lecturer E.orderBy [ E.desc $ E.case_ [(lecturersCourse, E.val (1 :: Int64))] (E.val 0) -- prefer courses from lecturer
, E.desc $ E.case_ [(lecturersSchool, E.val (1 :: Int64))] (E.val 0) -- prefer from schools of lecturer
, E.desc $ courseCreated course] -- most recent created course , E.desc $ courseCreated course] -- most recent created course
E.limit 1 E.limit 1
return course return course
@ -366,11 +367,11 @@ getCourseNewTemplateR mbTid mbSsh csh = do
(tidOk,sshOk,cshOk) <- runDB $ (,,) (tidOk,sshOk,cshOk) <- runDB $ (,,)
<$> ifMaybeM mbTid True existsKey <$> ifMaybeM mbTid True existsKey
<*> ifMaybeM mbSsh True existsKey <*> ifMaybeM mbSsh True existsKey
<*> ((not . null) <$> selectKeysList [CourseShorthand ==. csh] [LimitTo 1]) <*> ifMaybeM mbCsh True (\csh -> (not . null) <$> selectKeysList [CourseShorthand ==. csh] [LimitTo 1])
unless tidOk $ addMessageI "warning" $ MsgNoSuchTerm $ fromJust mbTid -- safe, since tidOk==True otherwise unless tidOk $ addMessageI Warning $ MsgNoSuchTerm $ fromJust mbTid -- safe, since tidOk==True otherwise
unless sshOk $ addMessageI "warning" $ MsgNoSuchSchool $ fromJust mbSsh -- safe, since sshOk==True otherwise unless sshOk $ addMessageI Warning $ MsgNoSuchSchool $ fromJust mbSsh -- safe, since sshOk==True otherwise
unless cshOk $ addMessageI "warning" $ MsgNoSuchCourseShorthand csh unless cshOk $ addMessageI Warning $ MsgNoSuchCourseShorthand $ fromJust mbCsh
when (tidOk && sshOk && cshOk) $ addMessageI "warning" MsgNoSuchCourse when (tidOk && sshOk && cshOk) $ addMessageI Warning MsgNoSuchCourse
return Nothing return Nothing
courseEditHandler True template courseEditHandler True template
@ -389,14 +390,16 @@ pgCEditR isGetReq tid ssh csh = do
courseEditHandler isGetReq $ courseToForm <$> course courseEditHandler isGetReq $ courseToForm <$> course
courseDeleteHandler :: Handler Html -- not called anywhere yet getCDeleteR :: TermId -> SchoolId -> CourseShorthand -> Handler Html
courseDeleteHandler = undefined getCDeleteR = error "TODO: implement getCDeleteR"
postCDeleteR :: TermId -> SchoolId -> CourseShorthand -> Handler Html
postCDeleteR = error "TODO: implement getCDeleteR"
{- TODO {- TODO
| False -- DELETE -- TODO: This no longer works that way!!! See new way in Handler.Term.termEditHandler | False -- DELETE -- TODO: This no longer works that way!!! See new way in Handler.Term.termEditHandler
, Just cid <- cfCourseId res -> do , Just cid <- cfCourseId res -> do
runDB $ deleteCascade cid -- TODO Sicherheitsabfrage einbauen! runDB $ deleteCascade cid -- TODO Sicherheitsabfrage einbauen!
let cti = toPathPiece $ cfTerm res let cti = toPathPiece $ cfTerm res
addMessage "info" [shamlet| Kurs #{cti}/#{cfShort res} wurde gelöscht!|] addMessage Info [shamlet| Kurs #{cti}/#{cfShort res} wurde gelöscht!|]
redirect $ TermCourseListR $ cfTerm res redirect $ TermCourseListR $ cfTerm res
-} -}
@ -435,10 +438,10 @@ courseEditHandler isGet mbCourseForm = do
runDB $ do runDB $ do
insert_ $ CourseEdit aid now cid insert_ $ CourseEdit aid now cid
insert_ $ Lecturer aid cid insert_ $ Lecturer aid cid
addMessageI "info" $ MsgCourseNewOk tid ssh csh addMessageI Info $ MsgCourseNewOk tid ssh csh
redirect $ TermCourseListR tid redirect $ TermCourseListR tid
Nothing -> Nothing ->
addMessageI "danger" $ MsgCourseNewDupShort tid ssh csh addMessageI Warning $ MsgCourseNewDupShort tid ssh csh
(FormSuccess res@( (FormSuccess res@(
CourseForm { cfCourseId = Just cid CourseForm { cfCourseId = Just cid
@ -451,7 +454,7 @@ courseEditHandler isGet mbCourseForm = do
success <- runDB $ do success <- runDB $ do
old <- get cid old <- get cid
case old of case old of
Nothing -> addMessageI "error" MsgInvalidInput $> False Nothing -> addMessageI Error MsgInvalidInput $> False
(Just oldCourse) -> do (Just oldCourse) -> do
updOkay <- myReplaceUnique cid ( -- replaceUnique requires Eq Course, which we cannot have updOkay <- myReplaceUnique cid ( -- replaceUnique requires Eq Course, which we cannot have
Course { courseName = cfName res Course { courseName = cfName res
@ -469,14 +472,14 @@ courseEditHandler isGet mbCourseForm = do
} }
) )
case updOkay of case updOkay of
(Just _) -> addMessageI "danger" (MsgCourseEditDupShort tid ssh csh) $> False (Just _) -> addMessageI Warning (MsgCourseEditDupShort tid ssh csh) $> False
Nothing -> do Nothing -> do
insert_ $ CourseEdit aid now cid insert_ $ CourseEdit aid now cid
addMessageI "success" $ MsgCourseEditOk tid ssh csh addMessageI Success $ MsgCourseEditOk tid ssh csh
return True return True
when success $ redirect $ CourseR tid ssh csh CShowR when success $ redirect $ CourseR tid ssh csh CShowR
(FormFailure _) -> addMessageI "warning" MsgInvalidInput (FormFailure _) -> addMessageI Warning MsgInvalidInput
(FormMissing) -> return () (FormMissing) -> return ()
actionUrl <- fromMaybe CourseNewR <$> getCurrentRoute actionUrl <- fromMaybe CourseNewR <$> getCurrentRoute
defaultLayout $ do defaultLayout $ do
@ -526,6 +529,9 @@ newCourseForm template = identForm FIDcourse $ \html -> do
, map (userAdminSchool . entityVal) <$> selectList [UserAdminUser ==. userId] [] , map (userAdminSchool . entityVal) <$> selectList [UserAdminUser ==. userId] []
] ]
let termsField = case template of let termsField = case template of
--TODO: if Admin, then all
-- if allowed to delete course then allow current and all active term
-- otherwise only keep current term
(Just cform) | (Just _) <- cfCourseId cform -> termsSetField [cfTerm cform] (Just cform) | (Just _) <- cfCourseId cform -> termsSetField [cfTerm cform]
_allOtherCases -> termsActiveField _allOtherCases -> termsActiveField
(result, widget) <- flip (renderAForm FormStandard) html $ CourseForm (result, widget) <- flip (renderAForm FormStandard) html $ CourseForm

View File

@ -107,7 +107,7 @@ homeAnonymous = do
, dbtIdent = "upcomingdeadlines" :: Text , dbtIdent = "upcomingdeadlines" :: Text
} }
let features = $(widgetFile "featureList") let features = $(widgetFile "featureList")
addMessage "danger" "Vorabversion! Die Implementierung von Uni2work ist noch nicht abgeschlossen!" addMessage Warning "Vorabversion! Die Implementierung von Uni2work ist noch nicht abgeschlossen!"
defaultLayout $ do defaultLayout $ do
$(widgetFile "dsgvDisclaimer") $(widgetFile "dsgvDisclaimer")
$(widgetFile "home") $(widgetFile "home")
@ -207,7 +207,7 @@ homeUser uid = do
, dbtStyle = def { dbsEmptyStyle = DBESNoHeading, dbsEmptyMessage = MsgNoUpcomingSheetDeadlines } , dbtStyle = def { dbsEmptyStyle = DBESNoHeading, dbsEmptyMessage = MsgNoUpcomingSheetDeadlines }
, dbtIdent = "upcomingdeadlines" :: Text , dbtIdent = "upcomingdeadlines" :: Text
} }
addMessage "danger" "Vorabversion! Die Implementierung von Uni2work ist noch nicht abgeschlossen." addMessage Warning "Vorabversion! Die Implementierung von Uni2work ist noch nicht abgeschlossen."
defaultLayout $ do defaultLayout $ do
-- setTitle "Willkommen zum Uni2work Test!" -- setTitle "Willkommen zum Uni2work Test!"
$(widgetFile "homeUser") $(widgetFile "homeUser")

View File

@ -87,10 +87,10 @@ getProfileR = do
, OffsetBy $ stgMaxFavourties , OffsetBy $ stgMaxFavourties
] ]
mapM_ delete oldFavs mapM_ delete oldFavs
addMessageI "info" $ MsgSettingsUpdate addMessageI Info $ MsgSettingsUpdate
redirect ProfileR -- TODO: them change does not happen without redirect redirect ProfileR -- TODO: them change does not happen without redirect
(FormFailure msgs) -> forM_ msgs $ (addMessage "warning") . toHtml (FormFailure msgs) -> forM_ msgs $ (addMessage Warning) . toHtml
_ -> return () _ -> return ()

View File

@ -305,7 +305,7 @@ getSShowR tid ssh csh shn = do
return (hasHints, hasSolution) return (hasHints, hasSolution)
cTime <- Just <$> liftIO getCurrentTime cTime <- Just <$> liftIO getCurrentTime
visibleFrom <- traverse (formatTime SelFormatDateTime) $ sheetVisibleFrom sheet visibleFrom <- traverse (formatTime SelFormatDateTime) $ sheetVisibleFrom sheet
when (NTop (sheetVisibleFrom sheet) >= NTop cTime) $ addMessageI "warning" $ when (NTop (sheetVisibleFrom sheet) >= NTop cTime) $ addMessageI Warning $
maybe MsgSheetInvisible MsgSheetInvisibleUntil visibleFrom maybe MsgSheetInvisible MsgSheetInvisibleUntil visibleFrom
defaultLayout $ do defaultLayout $ do
setTitleI $ MsgSheetTitle tid ssh csh shn setTitleI $ MsgSheetTitle tid ssh csh shn
@ -444,21 +444,21 @@ handleSheetEdit tid ssh csh msId template dbAction = do
} }
mbsid <- dbAction newSheet mbsid <- dbAction newSheet
case mbsid of case mbsid of
Nothing -> False <$ addMessageI "error" (MsgSheetNameDup tid ssh csh sfName) Nothing -> False <$ addMessageI Error (MsgSheetNameDup tid ssh csh sfName)
(Just sid) -> do -- save files in DB: (Just sid) -> do -- save files in DB:
whenIsJust sfSheetF $ insertSheetFile' sid SheetExercise whenIsJust sfSheetF $ insertSheetFile' sid SheetExercise
whenIsJust sfHintF $ insertSheetFile' sid SheetHint whenIsJust sfHintF $ insertSheetFile' sid SheetHint
whenIsJust sfSolutionF $ insertSheetFile' sid SheetSolution whenIsJust sfSolutionF $ insertSheetFile' sid SheetSolution
whenIsJust sfMarkingF $ insertSheetFile' sid SheetMarking whenIsJust sfMarkingF $ insertSheetFile' sid SheetMarking
insert_ $ SheetEdit aid actTime sid insert_ $ SheetEdit aid actTime sid
addMessageI "info" $ MsgSheetEditOk tid ssh csh sfName addMessageI Info $ MsgSheetEditOk tid ssh csh sfName
-- Sanity checks generating warnings only, but not errors! -- Sanity checks generating warnings only, but not errors!
warnTermDays tid [sfVisibleFrom, Just sfActiveFrom, Just sfActiveTo, sfHintFrom, sfSolutionFrom] warnTermDays tid [sfVisibleFrom, Just sfActiveFrom, Just sfActiveTo, sfHintFrom, sfSolutionFrom]
return True return True
when saveOkay $ redirect $ case msId of when saveOkay $ redirect $ case msId of
Just _ -> CSheetR tid ssh csh sfName SShowR -- redirect must happen outside of runDB Just _ -> CSheetR tid ssh csh sfName SShowR -- redirect must happen outside of runDB
Nothing -> CSheetR tid ssh csh sfName SCorrR Nothing -> CSheetR tid ssh csh sfName SCorrR
(FormFailure msgs) -> forM_ msgs $ (addMessage "error") . toHtml (FormFailure msgs) -> forM_ msgs $ (addMessage Error) . toHtml
_ -> runDB $ warnTermDays tid $ (join . (flip fmap template)) _ -> runDB $ warnTermDays tid $ (join . (flip fmap template))
<$> [sfVisibleFrom, Just . sfActiveFrom, Just . sfActiveTo, sfHintFrom, sfSolutionFrom] <$> [sfVisibleFrom, Just . sfActiveFrom, Just . sfActiveTo, sfHintFrom, sfSolutionFrom]
@ -481,7 +481,7 @@ getSDelR tid ssh csh shn = do
(FormSuccess BtnDelete) -> do (FormSuccess BtnDelete) -> do
runDB $ fetchSheetId tid ssh csh shn >>= deleteCascade runDB $ fetchSheetId tid ssh csh shn >>= deleteCascade
-- TODO: deleteCascade löscht aber nicht die hochgeladenen Dateien!!! -- TODO: deleteCascade löscht aber nicht die hochgeladenen Dateien!!!
addMessageI "info" $ MsgSheetDelOk tid ssh csh shn addMessageI Info $ MsgSheetDelOk tid ssh csh shn
redirect $ CourseR tid ssh csh SheetListR redirect $ CourseR tid ssh csh SheetListR
_other -> do _other -> do
submissionno <- runDB $ do submissionno <- runDB $ do
@ -572,7 +572,7 @@ correctorForm shid = do
(defaultLoads', currentLoads') <- lift . runDB $ (,) <$> defaultLoads shid <*> currentLoads (defaultLoads', currentLoads') <- lift . runDB $ (,) <$> defaultLoads shid <*> currentLoads
loads' <- fmap (Map.fromList [(uid, (CorrectorNormal, mempty)) | uid <- formCIDs] `Map.union`) $ if loads' <- fmap (Map.fromList [(uid, (CorrectorNormal, mempty)) | uid <- formCIDs] `Map.union`) $ if
| Map.null currentLoads' | Map.null currentLoads'
, null formCIDs -> defaultLoads' <$ when (not $ Map.null defaultLoads') (addMessageI "warning" MsgCorrectorsDefaulted) , null formCIDs -> defaultLoads' <$ when (not $ Map.null defaultLoads') (addMessageI Warning MsgCorrectorsDefaulted)
| otherwise -> return $ Map.fromList (map (, (CorrectorNormal, mempty)) formCIDs) `Map.union` currentLoads' | otherwise -> return $ Map.fromList (map (, (CorrectorNormal, mempty)) formCIDs) `Map.union` currentLoads'
deletions <- lift $ foldM (\dels uid -> maybe (Set.insert uid dels) (const dels) <$> guardNonDeleted uid) Set.empty (Map.keys loads') deletions <- lift $ foldM (\dels uid -> maybe (Set.insert uid dels) (const dels) <$> guardNonDeleted uid) Set.empty (Map.keys loads')
@ -608,11 +608,11 @@ correctorForm shid = do
FormSuccess (Just emails) -> fmap Map.unions . forM emails $ \email -> do FormSuccess (Just emails) -> fmap Map.unions . forM emails $ \email -> do
mUid <- fmap (fmap entityKey) . lift . runDB $ getBy (UniqueEmail email) mUid <- fmap (fmap entityKey) . lift . runDB $ getBy (UniqueEmail email)
case mUid of case mUid of
Nothing -> loads'' <$ addMessageI "error" (MsgEMailUnknown email) Nothing -> loads'' <$ addMessageI Error (MsgEMailUnknown email)
Just uid Just uid
| not (Map.member uid loads') -> return $ Map.insert uid (CorrectorNormal, mempty) loads'' | not (Map.member uid loads') -> return $ Map.insert uid (CorrectorNormal, mempty) loads''
| otherwise -> loads'' <$ addMessageI "warning" (MsgCorrectorExists email) | otherwise -> loads'' <$ addMessageI Warning (MsgCorrectorExists email)
FormFailure errs -> loads'' <$ mapM_ (addMessage "error" . toHtml) errs FormFailure errs -> loads'' <$ mapM_ (addMessage Error . toHtml) errs
_ -> return loads'' _ -> return loads''
let deletions' = deletions `Set.difference` Map.keysSet loads let deletions' = deletions `Set.difference` Map.keysSet loads
@ -703,11 +703,11 @@ getSCorrR tid ssh csh shn = do
((res,formWidget), formEnctype) <- runFormPost . identForm FIDcorrectors . renderAForm FormStandard $ formToAForm (correctorForm shid) <* submitButton ((res,formWidget), formEnctype) <- runFormPost . identForm FIDcorrectors . renderAForm FormStandard $ formToAForm (correctorForm shid) <* submitButton
case res of case res of
FormFailure errs -> mapM_ (addMessage "error" . toHtml) errs FormFailure errs -> mapM_ (addMessage Error . toHtml) errs
FormSuccess res -> runDB $ do FormSuccess res -> runDB $ do
deleteWhere [SheetCorrectorSheet ==. shid] deleteWhere [SheetCorrectorSheet ==. shid]
insertMany_ $ Set.toList res insertMany_ $ Set.toList res
addMessageI "success" MsgCorrectorsUpdated addMessageI Success MsgCorrectorsUpdated
FormMissing -> return () FormMissing -> return ()
let let

View File

@ -147,7 +147,7 @@ submissionHelper tid ssh csh shn (SubmissionMode mcid) = do
return (csheet, map E.unValue buddies, []) return (csheet, map E.unValue buddies, [])
(E.Value smid:_) -> do (E.Value smid:_) -> do
cID <- encrypt smid cID <- encrypt smid
addMessageI "info" $ MsgSubmissionAlreadyExists addMessageI Info $ MsgSubmissionAlreadyExists
redirect $ CSubmissionR tid ssh csh shn cID SubShowR redirect $ CSubmissionR tid ssh csh shn cID SubShowR
(Just smid) -> do (Just smid) -> do
void $ submissionMatchesSheet tid ssh csh shn (fromJust mcid) void $ submissionMatchesSheet tid ssh csh shn (fromJust mcid)
@ -237,10 +237,11 @@ submissionHelper tid ssh csh shn (SubmissionMode mcid) = do
(Nothing, Nothing) -- new submission, no file upload requested (Nothing, Nothing) -- new submission, no file upload requested
-> insert Submission -> insert Submission
{ submissionSheet = shid { submissionSheet = shid
, submissionRatingPoints = Nothing , submissionRatingPoints = Nothing
, submissionRatingComment = Nothing , submissionRatingComment = Nothing
, submissionRatingBy = Nothing , submissionRatingBy = Nothing
, submissionRatingTime = Nothing , submissionRatingAssigned = Nothing
, submissionRatingTime = Nothing
} }
-- Determine members of pre-registered group -- Determine members of pre-registered group
groupUids <- fmap (setFromList . map E.unValue) . E.select . E.from $ \(submissionGroupUser `E.InnerJoin` submissionGroup `E.InnerJoin` submissionGroupUser') -> do groupUids <- fmap (setFromList . map E.unValue) . E.select . E.from $ \(submissionGroupUser `E.InnerJoin` submissionGroup `E.InnerJoin` submissionGroupUser') -> do
@ -258,7 +259,7 @@ submissionHelper tid ssh csh shn (SubmissionMode mcid) = do
return smid return smid
cID <- encrypt smid cID <- encrypt smid
return $ Just cID return $ Just cID
(FormFailure msgs) -> Nothing <$ forM_ msgs (addMessage "warning" . toHtml) (FormFailure msgs) -> Nothing <$ forM_ msgs (addMessage Warning . toHtml)
_other -> return Nothing _other -> return Nothing
case mCID of case mCID of

View File

@ -167,12 +167,12 @@ termEditHandler term = do
-- VOR INTERNATIONALISIERUNG: -- VOR INTERNATIONALISIERUNG:
-- let tid = termToText $ termName res -- let tid = termToText $ termName res
-- let msg = "Semester " `T.append` tid `T.append` " erfolgreich editiert." -- let msg = "Semester " `T.append` tid `T.append` " erfolgreich editiert."
-- addMessage "success" [shamlet| #{msg} |] -- addMessage Success [shamlet| #{msg} |]
-- MIT INTERNATIONALISIERUNG: -- MIT INTERNATIONALISIERUNG:
addMessageI "success" $ MsgTermEdited tid addMessageI Success $ MsgTermEdited tid
redirect TermShowR redirect TermShowR
(FormMissing ) -> return () (FormMissing ) -> return ()
(FormFailure _) -> addMessageI "warning" MsgInvalidInput (FormFailure _) -> addMessageI Warning MsgInvalidInput
let actionUrl = TermEditR let actionUrl = TermEditR
defaultLayout $ do defaultLayout $ do
setTitleI MsgTermEditHeading setTitleI MsgTermEditHeading

View File

@ -118,5 +118,5 @@ postAdminHijackUserR cID = do
get404 uid get404 uid
setCredsRedirect $ Creds "dummy" (userPlugin <> ":" <> userIdent) [] setCredsRedirect $ Creds "dummy" (userPlugin <> ":" <> userIdent) []
| otherwise -> error "This should be impossible by definition of `hijackUserForm`" | otherwise -> error "This should be impossible by definition of `hijackUserForm`"
FormFailure errs -> toTypedContent <$> mapM_ (addMessage "error" . toHtml) errs FormFailure errs -> toTypedContent <$> mapM_ (addMessage Error . toHtml) errs
FormMissing -> return $ toTypedContent () FormMissing -> return $ toTypedContent ()

View File

@ -68,7 +68,7 @@ warnTermDays tid times = do
outoftermdays = Set.filter (\d -> d < termStart || d > termEnd ) alldays outoftermdays = Set.filter (\d -> d < termStart || d > termEnd ) alldays
outoflecture = Set.filter (\d -> d < termLectureStart || d > termLectureEnd) alldays outoflecture = Set.filter (\d -> d < termLectureStart || d > termLectureEnd) alldays
`Set.difference` outoftermdays -- out of term implies out of lecture-time `Set.difference` outoftermdays -- out of term implies out of lecture-time
warnI msg d = formatTime SelFormatDate d >>= \dt -> addMessageI "warning" $ msg tid dt warnI msg d = formatTime SelFormatDate d >>= \dt -> addMessageI Warning $ msg tid dt
forM_ warnholidays $ warnI MsgDayIsAHoliday forM_ warnholidays $ warnI MsgDayIsAHoliday
forM_ outoflecture $ warnI MsgDayIsOutOfLecture forM_ outoflecture $ warnI MsgDayIsOutOfLecture
forM_ outoftermdays $ warnI MsgDayIsOutOfTerm forM_ outoftermdays $ warnI MsgDayIsOutOfTerm

View File

@ -222,26 +222,17 @@ termsActiveField :: Field Handler TermId
termsActiveField = selectField $ optionsPersistKey [TermActive ==. True] [Desc TermStart] termName termsActiveField = selectField $ optionsPersistKey [TermActive ==. True] [Desc TermStart] termName
termsSetField :: [TermId] -> Field Handler TermId termsSetField :: [TermId] -> Field Handler TermId
-- termsSetField tids = selectField $ optionsPersistKey [TermId <-. tids] [Desc TermStart] termName termsSetField tids = selectField $ optionsPersistKey [TermName <-. (unTermKey <$> tids)] [Desc TermStart] termName
termsSetField tids = selectFieldList [(unTermKey t, t)| t <- tids ] -- termsSetField tids = selectFieldList [(unTermKey t, t)| t <- tids ]
termsActiveOrSetField :: [TermId] -> Field Handler TermId
termsActiveOrSetField tids = selectField $ optionsPersistKey ([TermActive ==.True] ||. [TermName <-. terms]) [Desc TermStart] termName
where terms = map unTermKey tids
-- termActiveOld :: Field Handler TermIdentifier -- termActiveOld :: Field Handler TermIdentifier
-- termActiveOld = convertField unTermKey TermKey $ selectField $ optionsPersistKey [TermActive ==. True] [Desc TermStart] termName -- termActiveOld = convertField unTermKey TermKey $ selectField $ optionsPersistKey [TermActive ==. True] [Desc TermStart] termName
termNewField :: Field Handler TermIdentifier termNewField :: Field Handler TermIdentifier
termNewField = checkMMap checkTerm termToText textField termNewField = checkMMap (return.termFromText) termToText textField
where
errTextParse :: Text
errTextParse = "Semester: S oder W gefolgt von Jahreszahl"
errTextFreigabe :: TermIdentifier -> Text
errTextFreigabe ti = "Semester " `T.append` (termToText ti) `T.append` " wurde noch nicht freigegeben."
checkTerm :: Text -> HandlerT UniWorX IO (Either Text TermIdentifier)
checkTerm t = case termFromText t of
Left _ -> return $ Left errTextParse
res@(Right _) -> return res
schoolField :: Field Handler SchoolId schoolField :: Field Handler SchoolId
schoolField = selectField $ optionsPersistKey [] [Asc SchoolName] schoolName schoolField = selectField $ optionsPersistKey [] [Asc SchoolName] schoolName

View File

@ -210,7 +210,10 @@ assignSubmissions sid restriction = do
$logDebugS "assignSubmissions" $ tshow smid <> " -> " <> tshow q <> " (tutorial)" $logDebugS "assignSubmissions" $ tshow smid <> " -> " <> tshow q <> " (tutorial)"
assignSubmission (countsToLoad' q) smid q assignSubmission (countsToLoad' q) smid q
forM_ (Map.toList subTutor) $ \(smid, tutid) -> update smid [SubmissionRatingBy =. Just tutid] now <- liftIO getCurrentTime
forM_ (Map.toList subTutor) $
\(smid, tutid) -> update smid [ SubmissionRatingBy =. Just tutid
, SubmissionRatingAssigned =. Just now ]
let assignedSubmissions = Map.keysSet subTutor let assignedSubmissions = Map.keysSet subTutor
unassigendSubmissions = Map.keysSet subTutor' \\ assignedSubmissions unassigendSubmissions = Map.keysSet subTutor' \\ assignedSubmissions
@ -325,7 +328,7 @@ extractRatingsMsg = do
ignored = Right `Set.map` ignored' ignored = Right `Set.map` ignored'
unless (null ignored) $ do unless (null ignored) $ do
mr <- (toHtml . ) <$> getMessageRender mr <- (toHtml . ) <$> getMessageRender
addMessage "warning" =<< withUrlRenderer ($(ihamletFile "templates/messages/submissionFilesIgnored.hamlet") mr) addMessage Warning =<< withUrlRenderer ($(ihamletFile "templates/messages/submissionFilesIgnored.hamlet") mr)
sinkSubmission :: UserId sinkSubmission :: UserId
-> Either SheetId SubmissionId -> Either SheetId SubmissionId
@ -343,11 +346,13 @@ sinkSubmission userId mExists isUpdate = do
sId <- lift $ case mExists of sId <- lift $ case mExists of
Left sheetId -> do Left sheetId -> do
let let
submissionSheet = sheetId submissionSheet = sheetId
submissionRatingPoints = Nothing submissionRatingPoints = Nothing
submissionRatingComment = Nothing submissionRatingComment = Nothing
submissionRatingBy = Nothing submissionRatingBy = Nothing
submissionRatingTime = Nothing submissionRatingAssigned = Nothing
submissionRatingTime = Nothing
sId <- insert Submission{..} sId <- insert Submission{..}
-- now <- liftIO getCurrentTime -- now <- liftIO getCurrentTime
-- insert $ SubmissionEdit userId now sId -- This is done automatically during 'sinkSubmission'' iff the given submission is nonempty -- insert $ SubmissionEdit userId now sId -- This is done automatically during 'sinkSubmission'' iff the given submission is nonempty
@ -466,6 +471,7 @@ sinkSubmission userId mExists isUpdate = do
lift $ case isUpdate of lift $ case isUpdate of
False -> insert_ $ SubmissionEdit userId now submissionId False -> insert_ $ SubmissionEdit userId now submissionId
True -> update submissionId [ SubmissionRatingBy =. Just userId, SubmissionRatingTime =. Just now ] True -> update submissionId [ SubmissionRatingBy =. Just userId, SubmissionRatingTime =. Just now ]
-- TODO: Should submissionRatingAssigned change here if userId changes?
tell $ mempty{ sinkSubmissionTouched = Any True } tell $ mempty{ sinkSubmissionTouched = Any True }
finalize :: SubmissionSinkState -> YesodDB UniWorX () finalize :: SubmissionSinkState -> YesodDB UniWorX ()
@ -586,7 +592,7 @@ sinkMultiSubmission userId isUpdate = do
lift . feed sId $ Left f{ fileTitle = fileTitle' } lift . feed sId $ Left f{ fileTitle = fileTitle' }
when (not $ null ignored) $ do when (not $ null ignored) $ do
mr <- (toHtml .) <$> getMessageRender mr <- (toHtml .) <$> getMessageRender
addMessage "warning" =<< withUrlRenderer ($(ihamletFile "templates/messages/submissionFilesIgnored.hamlet") mr) addMessage Warning =<< withUrlRenderer ($(ihamletFile "templates/messages/submissionFilesIgnored.hamlet") mr)
fmap Map.keysSet . lift . sequence $ flip Map.mapWithKey sinks $ \sId sink -> do fmap Map.keysSet . lift . sequence $ flip Map.mapWithKey sinks $ \sId sink -> do
cID <- encrypt sId cID <- encrypt sId
handle (throwM . SubmissionSinkException cID Nothing) $ handle (throwM . SubmissionSinkException cID Nothing) $

View File

@ -402,7 +402,7 @@ dbTable PSValidator{..} dbtable@(DBTable{ dbtIdent = (toPathPiece -> dbtIdent),
<* E.offset (psPage * psLimit) <* E.offset (psPage * psLimit)
<* Map.foldrWithKey (\key args expr -> E.where_ (filterColumn (dbtFilter ! key) args t) >> expr) (return ()) psFilter <* Map.foldrWithKey (\key args expr -> E.where_ (filterColumn (dbtFilter ! key) args t) >> expr) (return ()) psFilter
mapM_ (addMessageI "warning") errs mapM_ (addMessageI Warning) errs
runDB $ do runDB $ do
rows' <- E.select $ (,) <$> pure (E.unsafeSqlValue "count(*) OVER ()" :: E.SqlExpr (E.Value Int64)) <*> sqlQuery' rows' <- E.select $ (,) <$> pure (E.unsafeSqlValue "count(*) OVER ()" :: E.SqlExpr (E.Value Int64)) <*> sqlQuery'

View File

@ -293,7 +293,7 @@ termFromText t
, Just (review shortened -> year) <- readMaybe ys , Just (review shortened -> year) <- readMaybe ys
, Right season <- seasonFromChar s , Right season <- seasonFromChar s
= Right TermIdentifier{..} = Right TermIdentifier{..}
| otherwise = Left $ "Invalid TermIdentifier: “" <> t <> "" | otherwise = Left $ "Invalid TermIdentifier: “" <> t <> "" -- TODO: Could be improved, I.e. say "W"/"S" from Number
termToRational :: TermIdentifier -> Rational termToRational :: TermIdentifier -> Rational
termToRational TermIdentifier{..} = fromInteger year + seasonOffset termToRational TermIdentifier{..} = fromInteger year + seasonOffset
@ -368,11 +368,13 @@ deriveJSON defaultOptions
{ constructorTagModifier = fromJust . stripPrefix "Theme" { constructorTagModifier = fromJust . stripPrefix "Theme"
} ''Theme } ''Theme
$( return [] ) -- forces order of splices, error otherwise, see https://ghc.haskell.org/trac/ghc/ticket/9813
instance Universe Theme where universe = universeDef instance Universe Theme where universe = universeDef
instance Finite Theme instance Finite Theme
instance PathPiece Theme where instance PathPiece Theme where
toPathPiece = $(nullaryToPathPiece ''Theme [Text.intercalate "-" . map Text.toLower . unsafeTail . splitCamel]) toPathPiece = $(nullaryToPathPiece ''Theme [Text.intercalate "-" . map Text.toLower . unsafeTail . splitCamel])
fromPathPiece = finiteFromPathPiece fromPathPiece = finiteFromPathPiece
$(deriveSimpleWith ''DisplayAble 'display (over Text.packed $ Text.intercalate " " . unsafeTail . splitCamel) ''Theme) -- describe theme to user $(deriveSimpleWith ''DisplayAble 'display (over Text.packed $ Text.intercalate " " . unsafeTail . splitCamel) ''Theme) -- describe theme to user
@ -397,6 +399,8 @@ data SelDateTimeFormat = SelFormatDateTime | SelFormatDate | SelFormatTime
data CorrectorState = CorrectorNormal | CorrectorMissing | CorrectorExcused data CorrectorState = CorrectorNormal | CorrectorMissing | CorrectorExcused
deriving (Eq, Ord, Read, Show, Enum, Bounded) deriving (Eq, Ord, Read, Show, Enum, Bounded)
$( return [] ) -- forces order of splices, error otherwise, see https://ghc.haskell.org/trac/ghc/ticket/9813
deriveJSON defaultOptions deriveJSON defaultOptions
{ constructorTagModifier = fromJust . stripPrefix "Corrector" { constructorTagModifier = fromJust . stripPrefix "Corrector"
} ''CorrectorState } ''CorrectorState

View File

@ -31,7 +31,7 @@ import qualified Data.Text.Encoding as Text
import qualified Ldap.Client as Ldap import qualified Ldap.Client as Ldap
import Utils import Utils hiding (MessageClass(..))
import Control.Lens import Control.Lens
import Data.Maybe (fromJust) import Data.Maybe (fromJust)

35
src/Utils/Message.hs Normal file
View File

@ -0,0 +1,35 @@
{-# LANGUAGE FlexibleInstances, FlexibleContexts #-}
{-# LANGUAGE TemplateHaskell #-}
module Utils.Message
( MessageClass(..)
, addMessage, addMessageI
) where
import Data.Text as Text (toLower)
import Data.Universe
import Utils.PathPiece (finiteFromPathPiece, nullaryToPathPiece)
import qualified ClassyPrelude.Yesod (addMessage, addMessageI)
import ClassyPrelude.Yesod (PathPiece(..),MonadHandler,HandlerSite,RenderMessage,Html)
data MessageClass = Error | Warning | Info | Success
deriving (Eq,Ord,Enum,Bounded,Show,Read)
instance Universe MessageClass
instance Finite MessageClass
$( return [] ) -- forces order of splices, error otherwise, see https://ghc.haskell.org/trac/ghc/ticket/9813
instance PathPiece MessageClass where
toPathPiece = $(nullaryToPathPiece ''MessageClass [Text.toLower])
fromPathPiece = finiteFromPathPiece
addMessage :: MonadHandler m => MessageClass-> Html -> m ()
addMessage mc = ClassyPrelude.Yesod.addMessage (toPathPiece mc)
addMessageI :: (MonadHandler m, RenderMessage (HandlerSite m) msg) => MessageClass -> msg -> m ()
addMessageI mc = ClassyPrelude.Yesod.addMessageI (toPathPiece mc)