feat(labels): actions for setting and removing labels

This commit is contained in:
Sarah Vaupel 2022-01-24 01:09:47 +01:00
parent a2a8957c57
commit 9e81f03742
4 changed files with 74 additions and 12 deletions

View File

@ -69,3 +69,7 @@ ExamOfficeLabelStatus !ident-ok: Status
ExamOfficeLabelPriority: Priorität
ExamOfficeLabelAlreadyExists: Es existiert bereits ein Prüfungs-Label mit diesem Namen!
ExamOfficeExamsNoLabel: Kein Label
ExamSetLabel: Label setzen
ExamLabelsSet n@Int: #{n} Prüfungs-#{pluralDE n "Label" "Labels"} gesetzt
ExamRemoveLabel: Label entfernen
ExamLabelsRemoved n@Int: #{n} Prüfungs-#{pluralDE n "Label" "Labels"} entfernt

View File

@ -67,3 +67,7 @@ ExamOfficeLabelStatus: Status
ExamOfficeLabelPriority: Priority
ExamOfficeLabelAlreadyExists: There already exists an exam label with this name!
ExamOfficeExamsNoLabel: No label
ExamSetLabel: Set label
ExamLabelsSet n: Successfully set #{n} exam #{pluralEN n "label" "labels"}
ExamRemoveLabel: Remove label
ExamLabelsRemoved n: Successfully removed #{n} exam #{pluralEN n "label" "labels"}

2
routes
View File

@ -112,7 +112,7 @@
/user/storage-key StorageKeyR POST !free
/exam-office ExamOfficeR !exam-office:
/ EOExamsR GET !system-exam-office
/ EOExamsR GET POST !system-exam-office
/fields EOFieldsR GET POST
/users EOUsersR GET POST !system-exam-office
/users/invite EOUsersInviteR GET POST !system-exam-office

View File

@ -1,7 +1,7 @@
{-# OPTIONS_GHC -fno-warn-redundant-constraints #-}
module Handler.ExamOffice.Exams
( getEOExamsR
( getEOExamsR, postEOExamsR
) where
import Import
@ -16,6 +16,22 @@ import qualified Database.Esqueleto.Utils as E
import qualified Colonnade
import qualified Data.Conduit.Combinators as C
import qualified Data.Map as Map
import qualified Data.Set as Set
data ExamAction = ExamSetLabel | ExamRemoveLabel
deriving (Eq, Ord, Read, Show, Enum, Bounded, Generic, Typeable)
deriving anyclass (Universe, Finite)
nullaryPathPiece ''ExamAction $ camelToPathPiece' 1
embedRenderMessage ''UniWorX ''ExamAction id
data ExamActionData = ExamSetLabelData
{ easlNewLabel :: ExamOfficeLabelId
}
| ExamRemoveLabelData
deriving (Eq, Ord, Read, Show, Generic, Typeable)
data ExamsTableFilterProj = ExamsTableFilterProj
@ -114,8 +130,9 @@ resultIsSynced = to $ (>=) <$> view resultSynchronised <*> view resultResults
-- | List of all exams where the current user may (in her function as
-- exam-office) access users grades
getEOExamsR :: Handler Html
getEOExamsR = do
getEOExamsR, postEOExamsR :: Handler Html
getEOExamsR = postEOExamsR
postEOExamsR = do
(uid, User{..}) <- requireAuthPair
now <- liftIO getCurrentTime
mr <- getMessageRender
@ -130,7 +147,7 @@ getEOExamsR = do
Just "no" -> False
_ -> userExamOfficeGetLabels
examsTable <- runDB $ do
(examsRes, examsTable) <- runDB $ do
let labelFilterNoLabelOption = Option
{ optionDisplay = mr MsgExamOfficeExamsNoLabel
, optionInternalValue = Nothing
@ -160,6 +177,7 @@ getEOExamsR = do
externalExamLink ExternalExam{..}
= SomeRoute $ EExamR externalExamTerm externalExamSchool externalExamCourseName externalExamExamName EEGradesR
examsDBTable = DBTable{..}
where
dbtSQLQuery = runReaderT $ do
@ -203,7 +221,7 @@ getEOExamsR = do
dbtRowKey = views ($(multifocusG 2) queryExam queryExternalExam) (bimap (E.?. ExamId) (E.?. ExternalExamId))
dbtProj :: _ ExamsTableData
dbtProj = (views _dbtProjRow . set _dbrOutput) =<< do -- dbtProjSimple . runReaderT $ do
dbtProj = (views _dbtProjRow . set _dbrOutput) =<< do
exam <- view $ _dbtProjRow . _dbrOutput . _1
course <- view $ _dbtProjRow . _dbrOutput . _2
school <- view $ _dbtProjRow . _dbrOutput . _3
@ -313,18 +331,18 @@ getEOExamsR = do
, emptyOpticColonnade (resultCourse . _entityVal . _courseTerm <> resultExternalExam . _entityVal . _externalExamTerm) colTermShort
]
dbtSorting = mconcat $
(bool mempty
bool mempty
[ singletonMap "label-prio" $
SortProjected . comparing $ (fmap . fmap $ examOfficeLabelPriority . entityVal) <$> preview resultLabel
, singletonMap "label-status" $
SortProjected . comparing $ (fmap . fmap $ examOfficeLabelStatus . entityVal) <$> preview resultLabel
] getLabels) <>
(bool mempty
] getLabels <>
bool mempty
[ singletonMap "synced" $
SortProjected . comparing $ ((/) `on` toRational . fromMaybe 0) <$> view resultSynchronised <*> view resultResults
, singletonMap "is-synced" $
SortProjected . comparing $ (>=) <$> view resultSynchronised <*> view resultResults
] getSynced) <>
] getSynced <>
[ sortExamName (to $ E.unsafeCoalesce . sequence [views queryExam (E.?. ExamName), views queryExternalExam (E.?. ExternalExamExamName)])
, sortExamTime (queryExam . $(multifocusG 2) (to $ E.joinV . (E.?. ExamStart)) (to $ E.joinV . (E.?. ExamEnd)))
, sortExamFinished (queryExam . to (E.joinV . (E.?. ExamFinished)))
@ -347,7 +365,28 @@ getEOExamsR = do
| getSynced ]
dbtStyle = def { dbsFilterLayout = defaultDBSFilterLayout }
dbtParams = def
dbtParams = DBParamsForm
{ dbParamsFormMethod = POST
, dbParamsFormAction = Just . SomeRoute $ ExamOfficeR EOExamsR
, dbParamsFormAttrs = []
, dbParamsFormSubmit = FormSubmit
, dbParamsFormAdditional
= let actions :: Map ExamAction (AForm Handler ExamActionData)
actions = Map.fromList $
bool mempty
[ ( ExamSetLabel, ExamSetLabelData
<$> apopt (selectField' Nothing . fmap (fmap entityKey) $ optionsPersist [ExamOfficeLabelUser ==. uid] [Asc ExamOfficeLabelName] examOfficeLabelName) (fslI MsgExamLabel) Nothing
)
, ( ExamRemoveLabel, pure ExamRemoveLabelData )
] getLabels
in renderAForm FormStandard
$ (, mempty) . First . Just
<$> multiActionA actions (fslI MsgTableAction) Nothing
, dbParamsFormEvaluate = liftHandler . runFormPost
, dbParamsFormResult = id
, dbParamsFormIdent = def
}
dbtIdent :: Text
dbtIdent = "exams"
@ -362,7 +401,22 @@ getEOExamsR = do
& forceFilter "may-access" (Any True)
& forceFilter "has-results" (Any True)
dbTableWidget' examsDBTableValidator examsDBTable
postprocess :: FormResult (First ExamActionData, DBFormResult (Either ExternalExamId ExamId) Bool (DBRow (Either (Entity ExternalExam) (Entity Exam)))) -> FormResult (ExamActionData, Set (Either ExternalExamId ExamId))
postprocess (FormFailure errs) = FormFailure errs
postprocess FormMissing = FormMissing
postprocess (FormSuccess (First mExamActionData, examRes)) = maybe FormMissing (\act -> FormSuccess . (act,) . Map.keysSet . Map.filter id $ getDBFormResult (const False) examRes) mExamActionData
over _1 postprocess <$> dbTable examsDBTableValidator examsDBTable
formResult examsRes $ \(examAction, exams) -> case examAction of
ExamSetLabelData{..} -> do
runDB . forM_ (Set.toList exams) $ either (\eeid -> void $ upsert (ExamOfficeExternalExamLabel eeid easlNewLabel) [ExamOfficeExternalExamLabelLabel =. easlNewLabel]) (\eid -> void $ upsert (ExamOfficeExamLabel eid easlNewLabel) [ExamOfficeExamLabelLabel =. easlNewLabel])
addMessageI Success $ MsgExamLabelsSet (Set.size exams)
redirect $ ExamOfficeR EOExamsR
ExamRemoveLabelData -> do
runDB . forM_ (Set.toList exams) $ either delete delete
addMessageI Success $ MsgExamLabelsRemoved (Set.size exams)
redirect $ ExamOfficeR EOExamsR
siteLayoutMsg MsgHeadingExamList $ do
setTitleI MsgHeadingExamList