refactor(dbtable): move haskell-land filtering to actual filter

This commit is contained in:
Gregor Kleen 2020-03-06 09:00:24 +01:00
parent bb9b4f06ae
commit 17882868d2
28 changed files with 380 additions and 237 deletions

View File

@ -960,6 +960,9 @@ th, td
justify-content: space-between
margin-bottom: 15px
&:empty
margin: 0
// TABLE FOOTER
.table-footer
display: flex
@ -967,6 +970,9 @@ th, td
justify-content: space-between
margin-top: 15px
&:empty
margin: 0
// PAGINATION
.pagination
margin-top: 20px
@ -1193,3 +1199,6 @@ a.breadcrumbs__home
text-align: right
.text--center
text-align: center
.course__registration-status
margin-bottom: 12px

View File

@ -58,5 +58,8 @@
display: block
clear: both
&:empty
margin: 0
.hide-columns--hidden-cell
display: none

View File

@ -0,0 +1,38 @@
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Data.MonoTraversable.Instances
() where
import ClassyPrelude
import Data.Monoid (Any(..), All(..))
type instance Element Any = Bool
type instance Element All = Bool
instance MonoFunctor Any where
omap f = Any . f . getAny
instance MonoFunctor All where
omap f = All . f . getAll
instance MonoPointed Any where
opoint = Any
instance MonoPointed All where
opoint = All
instance MonoFoldable Any where
ofoldMap f = f . getAny
ofoldr f x (Any b) = f b x
ofoldl' f x (Any b) = f x b
ofoldr1Ex _ = getAny
ofoldl1Ex' _ = getAny
instance MonoFoldable All where
ofoldMap f = f . getAll
ofoldr f x (All b) = f b x
ofoldl' f x (All b) = f x b
ofoldr1Ex _ = getAll
ofoldl1Ex' _ = getAll

View File

@ -356,13 +356,13 @@ postAdminFeaturesR = do
dbtSQLQuery = return
dbtRowKey = (E.^. StudyTermsKey)
dbtProj field@(view _dbrOutput -> Entity fId _) = do
fieldSchools <- fmap (setOf $ folded . _Value) . lift . E.select . E.from $ \school -> do
fieldSchools <- fmap (setOf $ folded . _Value) . E.select . E.from $ \school -> do
E.where_ . E.exists . E.from $ \schoolTerms ->
E.where_ $ schoolTerms E.^. SchoolTermsSchool E.==. school E.^. SchoolId
E.&&. schoolTerms E.^. SchoolTermsTerms E.==. E.val fId
E.where_ $ school E.^. SchoolShorthand `E.in_` E.valList (toListOf (folded . _entityKey . _SchoolId) schools)
return $ school E.^. SchoolId
fieldParents <- fmap (setOf folded) . lift . E.select . E.from $ \terms -> do
fieldParents <- fmap (setOf folded) . E.select . E.from $ \terms -> do
E.where_ . E.exists . E.from $ \subTerms ->
E.where_ $ subTerms E.^. StudySubTermsChild E.==. E.val fId
E.&&. subTerms E.^. StudySubTermsParent E.==. terms E.^. StudyTermsId

View File

@ -69,7 +69,7 @@ getAllocationListR = do
<*> view queryAvailable
<*> view (maybe (to . const $ E.val 0) queryApplied muid)
dbtProj :: DBRow _ -> MaybeT (YesodDB UniWorX) AllocationTableData
dbtProj :: DBRow _ -> DB AllocationTableData
dbtProj = return . over (_dbrOutput . _2) E.unValue . over (_dbrOutput . _3) E.unValue
dbtRowKey = view $ queryAllocation . to (E.^. AllocationId)

View File

@ -1,4 +1,14 @@
module Handler.Corrections where
module Handler.Corrections
( getCorrectionsR, postCorrectionsR
, getCCorrectionsR, postCCorrectionsR
, getSSubsR, postSSubsR
, getCorrectionR, postCorrectionR
, getCorrectionsUploadR, postCorrectionsUploadR
, getCorrectionsCreateR, postCorrectionsCreateR
, getCorrectionsGradeR, postCorrectionsGradeR
, getCAssignR, postCAssignR
, getSAssignR, postSAssignR
) where
import Import hiding (link)
-- import System.FilePath (takeFileName)
@ -68,12 +78,6 @@ lastEditQuery submission = E.subSelectMaybe $ E.from $ \edit -> do
E.where_ $ edit E.^. SubmissionEditSubmission E.==. submission E.^. SubmissionId
return $ E.max_ $ edit E.^. SubmissionEditTime
queryCourse :: CorrectionTableExpr -> E.SqlExpr (Entity Course)
queryCourse = $(sqlIJproj 3 1) . $(sqlLOJproj 2 1)
querySheet :: CorrectionTableExpr -> E.SqlExpr (Entity Sheet)
querySheet = $(sqlIJproj 3 2) . $(sqlLOJproj 2 1)
querySubmission :: CorrectionTableExpr -> E.SqlExpr (Entity Submission)
querySubmission = $(sqlIJproj 3 3) . $(sqlLOJproj 2 1)
@ -116,11 +120,6 @@ colSheet = sortable (Just "sheet") (i18nCell MsgSheet) $ \row ->
shn = sheetName $ entityVal sheet
in anchorCell (CSheetR tid ssh csh shn SShowR) [whamlet|_{shn}|]
colSheetType :: IsDBTable m a => Colonnade Sortable CorrectionTableData (DBCell m a)
colSheetType = sortable (toNothing "sheetType") (i18nCell MsgSheetType) $
i18nCell . sheetType <$> view (_dbrOutput . _2 . _entityVal)
-- \DBRow{ dbrOutput=(_, sheet, _, _, _, _) } -> i18nCell . sheetType $ entityVal sheet
colCorrector :: IsDBTable m a => Colonnade Sortable CorrectionTableData (DBCell m a)
colCorrector = sortable (Just "corrector") (i18nCell MsgCorrector) $ \case
DBRow{ dbrOutput = (_, _, _, Nothing , _, _) } -> cell mempty
@ -220,8 +219,8 @@ colLastEdit = sortable (Just "last-edit") (i18nCell MsgLastEdit) $
makeCorrectionsTable :: ( IsDBTable m x, ToSortable h, Functor h )
=> CorrectionTableWhere -> Colonnade h CorrectionTableData (DBCell m x) -> _ -> PSValidator m x -> _ -> DBParams m x -> DB (DBResult m x)
makeCorrectionsTable whereClause dbtColonnade dbtFilterUI psValidator dbtProj' dbtParams = do
=> CorrectionTableWhere -> Colonnade h CorrectionTableData (DBCell m x) -> _ -> PSValidator m x -> DBParams m x -> DB (DBResult m x)
makeCorrectionsTable whereClause dbtColonnade dbtFilterUI psValidator dbtParams = do
let dbtSQLQuery :: CorrectionTableExpr -> E.SqlQuery _
dbtSQLQuery = correctionsTableQuery whereClause
(\((course `E.InnerJoin` sheet `E.InnerJoin` submission) `E.LeftOuterJoin` corrector) ->
@ -232,9 +231,9 @@ makeCorrectionsTable whereClause dbtColonnade dbtFilterUI psValidator dbtProj' d
)
in (submission, sheet, crse, corrector, lastEditQuery submission)
)
dbtProj :: DBRow _ -> MaybeT (ReaderT SqlBackend (HandlerFor UniWorX)) CorrectionTableData
dbtProj :: DBRow _ -> DB CorrectionTableData
dbtProj = traverse $ \(submission@(Entity sId _), sheet@(Entity shId _), (E.Value courseName, E.Value courseShorthand, E.Value courseTerm, E.Value courseSchool), mCorrector, E.Value mbLastEdit) -> do
submittors <- lift . E.select . E.from $ \((submissionUser `E.InnerJoin` user) `E.LeftOuterJoin` pseudonym) -> do
submittors <- E.select . E.from $ \((submissionUser `E.InnerJoin` user) `E.LeftOuterJoin` pseudonym) -> do
E.on $ pseudonym E.?. SheetPseudonymUser E.==. E.just (user E.^. UserId)
E.&&. pseudonym E.?. SheetPseudonymSheet E.==. E.just (E.val shId)
E.on $ submissionUser E.^. SubmissionUserUser E.==. user E.^. UserId
@ -243,7 +242,7 @@ makeCorrectionsTable whereClause dbtColonnade dbtFilterUI psValidator dbtProj' d
return (user, pseudonym E.?. SheetPseudonymPseudonym)
let
submittorMap = List.foldr (\(Entity userId user, E.Value pseudo) -> Map.insert userId (user, pseudo)) Map.empty submittors
dbtProj' (submission, sheet, (courseName, courseShorthand, courseTerm, courseSchool), mCorrector, mbLastEdit, submittorMap)
return (submission, sheet, (courseName, courseShorthand, courseTerm, courseSchool), mCorrector, mbLastEdit, submittorMap)
dbTable psValidator DBTable
{ dbtSQLQuery
, dbtRowKey = \((_ `E.InnerJoin` _ `E.InnerJoin` submission) `E.LeftOuterJoin` _ :: CorrectionTableExpr) -> submission E.^. SubmissionId
@ -420,7 +419,7 @@ correctionsR whereClause displayColumns dbtFilterUI psValidator actions = do
}
((actionRes', statistics), table) <- runDB $
makeCorrectionsTable whereClause displayColumns dbtFilterUI psValidator return DBParamsForm
makeCorrectionsTable whereClause displayColumns dbtFilterUI psValidator DBParamsForm
{ dbParamsFormMethod = POST
, dbParamsFormAction = Just $ SomeRoute currentRoute
, dbParamsFormAttrs = []
@ -1055,12 +1054,8 @@ postCorrectionsGradeR = do
psValidator = def
& defaultSorting [SortDescBy "ratingtime"] :: PSValidator (MForm (HandlerFor UniWorX)) (FormResult (DBFormResult SubmissionId (Bool, Maybe Points, Maybe Text) CorrectionTableData))
unFormResult = getDBFormResult $ \DBRow{ dbrOutput = (Entity _ sub@Submission{..}, _, _, _, _, _) } -> (submissionRatingDone sub, submissionRatingPoints, submissionRatingComment)
dbtProj' i@(Entity subId _, Entity _ Sheet{ sheetName = shn }, (_, csh, tid, ssh), _, _, _) = do
cID <- encrypt subId
void . assertM (== Authorized) . lift $ evalAccessDB (CSubmissionR tid ssh csh shn cID CorrectionR) True
return i
(fmap unFormResult -> tableRes, table) <- runDB $ makeCorrectionsTable whereClause displayColumns filterUI psValidator dbtProj' $ def
(fmap unFormResult -> tableRes, table) <- runDB $ makeCorrectionsTable whereClause displayColumns filterUI psValidator $ def
{ dbParamsFormAction = Just $ SomeRoute CorrectionsGradeR
}
@ -1099,9 +1094,6 @@ embedRenderMessage ''UniWorX ''ButtonSubmissionsAssign id
instance Button UniWorX ButtonSubmissionsAssign where
btnClasses BtnSubmissionsAssign = [BCIsButton, BCPrimary]
-- | DEPRECATED use CorrectorInfo instead. Gather info about corrector assignment per sheet
data SubAssignInfo = SubAssignInfo { saiName :: SheetName, saiSubmissionNr, saiCorrectorNr, saiUnassignedNr :: Int }
getCAssignR, postCAssignR :: TermId -> SchoolId -> CourseShorthand -> Handler Html
getCAssignR = postCAssignR
postCAssignR tid ssh csh = do

View File

@ -257,6 +257,7 @@ getCApplicationsR, postCApplicationsR :: TermId -> SchoolId -> CourseShorthand -
getCApplicationsR = postCApplicationsR
postCApplicationsR tid ssh csh = do
(table, allocationsBounds, mayAccept) <- runDB $ do
now <- liftIO getCurrentTime
Entity cid Course{..} <- getBy404 $ TermSchoolCourseShort tid ssh csh
csvName <- getMessageRender <*> pure (MsgCourseApplicationsTableCsvName tid ssh csh)
@ -295,6 +296,8 @@ postCApplicationsR tid ssh csh = do
E.on $ user E.^. UserId E.==. courseApplication E.^. CourseApplicationUser
E.&&. courseApplication E.^. CourseApplicationCourse E.==. E.val cid
E.where_ $ E.maybe E.true (E.maybe E.false (E.<=. E.val now)) (allocation E.?. AllocationStaffAllocationFrom)
return ( courseApplication
, user
, hasFiles
@ -305,14 +308,8 @@ postCApplicationsR tid ssh csh = do
, E.not_ . E.isNothing $ courseParticipant E.?. CourseParticipantId
)
dbtProj :: DBRow _ -> MaybeT (YesodDB UniWorX) CourseApplicationsTableData
dbtProj = runReaderT $ do
appId <- view $ _dbrOutput . _1 . _entityKey
cID <- encrypt appId
guardM . hasReadAccessTo $ CApplicationR tid ssh csh cID CAEditR
asks $ over (_dbrOutput . _3) E.unValue . over (_dbrOutput . _8) E.unValue
dbtProj :: DBRow _ -> DB CourseApplicationsTableData
dbtProj = traverse $ return . over _3 E.unValue . over _8 E.unValue
dbtRowKey = view $ queryCourseApplication . to (E.^. CourseApplicationId)
@ -431,7 +428,6 @@ postCApplicationsR tid ssh csh = do
CourseApplicationsTableCsvSetCommentData{} -> CourseApplicationsTableCsvSetComment
, dbtCsvCoarsenActionClass = const DBCsvActionExisting
, dbtCsvExecuteActions = do
now <- liftIO getCurrentTime
C.mapM_ $ \case
CourseApplicationsTableCsvSetFieldData{..} -> do
CourseApplication{..} <- updateGet caCsvActApplication [ CourseApplicationField =. caCsvActField

View File

@ -83,10 +83,10 @@ makeCourseTable whereClause colChoices psValidator = do
E.on $ user E.^. UserId E.==. lecturer E.^. LecturerUser
E.where_ $ cid E.==. lecturer E.^. LecturerCourse E.&&. lecturer E.^. LecturerType E.==. E.val CourseLecturer
return user
dbtProj :: DBRow _ -> MaybeT DB CourseTableData
dbtProj :: DBRow _ -> DB CourseTableData
dbtProj = traverse $ \(course, E.Value participants, E.Value registered, school) -> do
lecturerList <- lift $ E.select $ E.from $ lecturerQuery $ E.val $ entityKey course
courseAlloc <- lift $ getBy (UniqueAllocationCourse $ entityKey course)
lecturerList <- E.select $ E.from $ lecturerQuery $ E.val $ entityKey course
courseAlloc <- getBy (UniqueAllocationCourse $ entityKey course)
>>= traverse (getJustEntity . allocationCourseAllocation . entityVal)
return (course, participants, registered, school, lecturerList, courseAlloc)
snd <$> dbTable psValidator DBTable

View File

@ -20,6 +20,8 @@ import Handler.Course.Register
import qualified Data.Conduit.List as C
import Handler.Exam.List (mkExamTable)
getCShowR :: TermId -> SchoolId -> CourseShorthand -> Handler Html
getCShowR tid ssh csh = do
@ -188,73 +190,7 @@ getCShowR tid ssh csh = do
& defaultSorting [SortAscBy "type", SortAscBy "name"]
(Any hasTutorials, tutorialTable) <- runDB $ dbTable tutorialDBTableValidator tutorialDBTable
let
examDBTable = DBTable{..}
where
dbtSQLQuery exam = do
E.where_ $ exam E.^. ExamCourse E.==. E.val cid
return exam
dbtRowKey = (E.^. ExamId)
dbtProj r@DBRow{ dbrOutput = Entity _ Exam{..} } = do
guardM . hasReadAccessTo $ CExamR tid ssh csh examName EShowR
return r
dbtColonnade = dbColonnade $ mconcat
[ sortable (Just "name") (i18nCell MsgExamName) $ \DBRow{ dbrOutput = Entity _ Exam{..} } -> indicatorCell <> anchorCell (CExamR tid ssh csh examName EShowR) examName
, sortable (Just "register-from") (i18nCell MsgExamRegisterFrom) $ \DBRow { dbrOutput = Entity _ Exam{..} } -> maybe mempty dateTimeCell examRegisterFrom
, sortable (Just "register-to") (i18nCell MsgExamRegisterTo) $ \DBRow { dbrOutput = Entity _ Exam{..} } -> maybe mempty dateTimeCell examRegisterTo
, sortable (Just "time") (i18nCell MsgExamTime) $ \DBRow{ dbrOutput = Entity _ Exam{..} } -> maybe mempty (cell . flip (formatTimeRangeW SelFormatDateTime) examEnd) examStart
, sortable (Just "registered") (i18nCell MsgExamRegistration ) $ \DBRow{ dbrOutput = Entity eId Exam{..} } -> sqlCell $ do
mayRegister <- (== Authorized) <$> evalAccessDB (CExamR tid ssh csh examName ERegisterR) True
isRegistered <- case mbAid of
Nothing -> return False
Just uid -> existsBy $ UniqueExamRegistration eId uid
let label = bool MsgExamNotRegistered MsgExamRegistered isRegistered
examUrl = CExamR tid ssh csh examName EShowR
if | mayRegister -> return $ simpleLinkI (SomeMessage label) examUrl
| otherwise -> return [whamlet|_{label}|]
-- , sortable Nothing mempty $ \DBRow{ dbrOutput = Entity eId Exam{..} } -> sqlCell $ do
-- mayRegister <- (== Authorized) <$> evalAccessDB (CExamR tid ssh csh examName ERegisterR) True
-- isRegistered <- case mbAid of
-- Nothing -> return False
-- Just uid -> existsBy $ UniqueExamRegistration eId uid
-- if
-- | mayRegister -> do
-- (examRegisterForm, examRegisterEnctype) <- liftHandler . generateFormPost . buttonForm' $ bool [BtnExamRegister] [BtnExamDeregister] isRegistered
-- return $ wrapForm examRegisterForm def
-- { formAction = Just . SomeRoute $ CExamR tid ssh csh examName ERegisterR
-- , formEncoding = examRegisterEnctype
-- , formSubmit = FormNoSubmit
-- }
-- | isRegistered -> return [whamlet|_{MsgExamRegistered}|]
-- | otherwise -> return mempty
]
dbtSorting = Map.fromList
[ ("name", SortColumn $ \exam -> exam E.^. ExamName )
, ("time", SortColumn $ \exam -> exam E.^. ExamStart )
, ("register-from", SortColumn $ \exam -> exam E.^. ExamRegisterFrom )
, ("register-to", SortColumn $ \exam -> exam E.^. ExamRegisterTo )
, ("visible", SortColumn $ \exam -> exam E.^. ExamVisibleFrom )
, ("registered", SortColumn $ \exam ->
case mbAid of
Nothing -> E.false
Just uid ->
E.exists $ E.from $ \reg -> do
E.where_ $ reg E.^. ExamRegistrationUser E.==. E.val uid
E.where_ $ reg E.^. ExamRegistrationExam E.==. exam E.^. ExamId
)
]
dbtFilter = Map.empty
dbtFilterUI = const mempty
dbtStyle = def
dbtParams = def
dbtIdent :: Text
dbtIdent = "exams"
dbtCsvEncode = noCsvEncode
dbtCsvDecode = Nothing
examDBTableValidator = def
& defaultSorting [SortAscBy "time"]
(Any hasExams, examTable) <- runDB $ dbTable examDBTableValidator examDBTable
(Any hasExams, examTable) <- runDB . mkExamTable $ Entity cid course
let visibleNews = any (view _3) news
showNewsFiles fs = and

View File

@ -299,8 +299,8 @@ makeCourseUserTable cid acts restrict colChoices psValidator csvColumns = do
dbtSQLQuery q = userTableQuery cid q <* E.where_ (restrict q)
dbtRowKey = queryUser >>> (E.^. UserId)
dbtProj = traverse $ \(user, E.Value registrationTime , E.Value userNoteId, (feature,degree,terms)) -> do
tuts'' <- lift $ selectList [ TutorialParticipantUser ==. entityKey user, TutorialParticipantTutorial <-. map entityKey tutorials ] []
exams' <- lift $ selectList [ ExamRegistrationUser ==. entityKey user ] []
tuts'' <- selectList [ TutorialParticipantUser ==. entityKey user, TutorialParticipantTutorial <-. map entityKey tutorials ] []
exams' <- selectList [ ExamRegistrationUser ==. entityKey user ] []
let
regGroups = setOf (folded . _entityVal . _tutorialRegGroup . _Just) tutorials
tuts' = filter (\(Entity tutId _) -> any ((== tutId) . tutorialParticipantTutorial . entityVal) tuts'') tutorials

View File

@ -1,5 +1,6 @@
module Handler.Exam.List
( getCExamListR
( mkExamTable
, getCExamListR
) where
import Import
@ -9,12 +10,16 @@ import Handler.Utils
import qualified Data.Map as Map
import qualified Database.Esqueleto as E
import qualified Database.Esqueleto.Utils as E
getCExamListR :: TermId -> SchoolId -> CourseShorthand -> Handler Html
getCExamListR tid ssh csh = do
Entity cid Course{..} <- runDB . getBy404 $ TermSchoolCourseShort tid ssh csh
mkExamTable :: Entity Course -> DB (Any, Widget)
mkExamTable (Entity cid Course{..}) = do
let tid = courseTerm
ssh = courseSchool
csh = courseShorthand
now <- liftIO getCurrentTime
mbAid <- maybeAuthId
mayCreate <- hasWriteAccessTo $ CourseR tid ssh csh CExamNewR
let
@ -24,15 +29,22 @@ getCExamListR tid ssh csh = do
E.where_ $ exam E.^. ExamCourse E.==. E.val cid
return exam
dbtRowKey = (E.^. ExamId)
dbtProj x@DBRow{ dbrOutput = Entity _ Exam{..} } = do
guardM . hasReadAccessTo $ CExamR tid ssh csh examName EShowR
return x
dbtProj = return
dbtColonnade = dbColonnade . mconcat $ catMaybes
[ Just . sortable (Just "name") (i18nCell MsgExamName) $ \DBRow{ dbrOutput = Entity _ Exam{..} } -> anchorCell (CExamR tid ssh csh examName EShowR) examName
[ Just . sortable (Just "name") (i18nCell MsgExamName) $ \DBRow{ dbrOutput = Entity _ Exam{..} } -> indicatorCell <> anchorCell (CExamR tid ssh csh examName EShowR) examName
, (<$ guard mayCreate) . sortable (Just "visible") (i18nCell MsgExamVisibleFrom) $ \DBRow { dbrOutput = Entity _ Exam{..} } -> maybe mempty (dateTimeCellVisible now) examVisibleFrom
, Just . sortable (Just "register-from") (i18nCell MsgExamRegisterFrom) $ \DBRow { dbrOutput = Entity _ Exam{..} } -> maybe mempty dateTimeCell examRegisterFrom
, Just . sortable (Just "register-to") (i18nCell MsgExamRegisterTo) $ \DBRow { dbrOutput = Entity _ Exam{..} } -> maybe mempty dateTimeCell examRegisterTo
, Just . sortable (Just "time") (i18nCell MsgExamTime) $ \DBRow{ dbrOutput = Entity _ Exam{..} } -> maybe mempty (cell . flip (formatTimeRangeW SelFormatDateTime) examEnd) examStart
, Just . sortable (Just "registered") (i18nCell MsgExamRegistration ) $ \DBRow{ dbrOutput = Entity eId Exam{..} } -> sqlCell $ do
mayRegister <- (== Authorized) <$> evalAccessDB (CExamR tid ssh csh examName ERegisterR) True
isRegistered <- case mbAid of
Nothing -> return False
Just uid -> existsBy $ UniqueExamRegistration eId uid
let label = bool MsgExamNotRegistered MsgExamRegistered isRegistered
examUrl = CExamR tid ssh csh examName EShowR
if | mayRegister -> return $ simpleLinkI (SomeMessage label) examUrl
| otherwise -> return [whamlet|_{label}|]
]
dbtSorting = Map.fromList
[ ("name", SortColumn $ \exam -> exam E.^. ExamName )
@ -40,8 +52,18 @@ getCExamListR tid ssh csh = do
, ("register-from", SortColumn $ \exam -> exam E.^. ExamRegisterFrom )
, ("register-to", SortColumn $ \exam -> exam E.^. ExamRegisterTo )
, ("visible", SortColumn $ \exam -> exam E.^. ExamVisibleFrom )
, ("registered", SortColumn $ \exam ->
case mbAid of
Nothing -> E.false
Just uid ->
E.exists $ E.from $ \reg -> do
E.where_ $ reg E.^. ExamRegistrationUser E.==. E.val uid
E.where_ $ reg E.^. ExamRegistrationExam E.==. exam E.^. ExamId
)
]
dbtFilter = Map.empty
dbtFilter = singletonMap "may-read" . FilterProjected $
\(Any b) DBRow{ dbrOutput = Entity _ Exam{..} }
-> (== b) <$> hasReadAccessTo (CExamR tid ssh csh examName EShowR) :: DB Bool
dbtFilterUI = const mempty
dbtStyle = def
dbtParams = def
@ -52,7 +74,17 @@ getCExamListR tid ssh csh = do
examDBTableValidator = def
& defaultSorting [SortAscBy "time"]
((), examTable) <- runDB $ dbTable examDBTableValidator examDBTable
& forceFilter "may-read" (Any True)
dbTable examDBTableValidator examDBTable
getCExamListR :: TermId -> SchoolId -> CourseShorthand -> Handler Html
getCExamListR tid ssh csh = do
(Entity _ Course{..}, examTable) <- runDB $ do
c <- getBy404 $ TermSchoolCourseShort tid ssh csh
(_, examTable) <- mkExamTable c
return (c, examTable)
siteLayoutMsg (prependCourseTitle tid ssh csh MsgExamsHeading) $ do
setTitleI $ prependCourseTitle tid ssh csh MsgExamsHeading

View File

@ -485,10 +485,10 @@ postEUsersR tid ssh csh examn = do
<*> getExamParts
<*> view _9
where
getExamParts :: ReaderT _ (MaybeT (YesodDB UniWorX)) (Map ExamPartId (ExamPart, Maybe (Entity ExamPartResult)))
getExamParts :: ReaderT _ DB (Map ExamPartId (ExamPart, Maybe (Entity ExamPartResult)))
getExamParts = do
uid <- view $ _2 . _entityKey
rawResults <- lift . lift . E.select . E.from $ \(examPart `E.LeftOuterJoin` examPartResult) -> do
rawResults <- lift . E.select . E.from $ \(examPart `E.LeftOuterJoin` examPartResult) -> do
E.on $ examPartResult E.?. ExamPartResultExamPart E.==. E.just (examPart E.^. ExamPartId)
E.&&. examPartResult E.?. ExamPartResultUser E.==. E.just (E.val uid)
E.where_ $ examPart E.^. ExamPartExam E.==. E.val eid

View File

@ -276,16 +276,16 @@ postEGradesR tid ssh csh examn = do
return (examResult, user, occurrence, studyFeatures, studyDegree, studyField, examRegistration, isSynced)
dbtRowKey = views queryExamResult (E.^. ExamResultId)
dbtProj :: DBRow _ -> MaybeT (YesodDB UniWorX) ExamUserTableData
dbtProj :: DBRow _ -> DB ExamUserTableData
dbtProj = runReaderT $ (asks . set _dbrOutput) <=< magnify _dbrOutput $
(,,,,,,,,)
<$> view _1 <*> view _2 <*> view _3 <*> view _4 <*> view _5 <*> view _6 <*> view _7 <*> view (_8 . _Value)
<*> getSynchronised
where
getSynchronised :: ReaderT _ (MaybeT (YesodDB UniWorX)) [(UserDisplayName, UserSurname, UTCTime, Set SchoolShorthand)]
getSynchronised :: ReaderT _ DB [(UserDisplayName, UserSurname, UTCTime, Set SchoolShorthand)]
getSynchronised = do
resId <- view $ _1 . _entityKey
syncs <- lift . lift . E.select . E.from $ \(examOfficeResultSynced `E.InnerJoin` user) -> do
syncs <- lift . E.select . E.from $ \(examOfficeResultSynced `E.InnerJoin` user) -> do
E.on $ examOfficeResultSynced E.^. ExamOfficeResultSyncedOffice E.==. user E.^. UserId
E.where_ $ examOfficeResultSynced E.^. ExamOfficeResultSyncedResult E.==. E.val resId
return ( examOfficeResultSynced E.^. ExamOfficeResultSyncedOffice

View File

@ -142,21 +142,17 @@ getEOExamsR = do
return (exam, course, externalExam, synchronised, results)
dbtRowKey = views ($(multifocusG 2) queryExam queryExternalExam) (bimap (E.?. ExamId) (E.?. ExternalExamId))
dbtProj :: DBRow _ -> MaybeT (YesodDB UniWorX) ExamsTableData
dbtProj :: DBRow _ -> DB ExamsTableData
dbtProj = runReaderT $ (asks . set _dbrOutput) <=< magnify _dbrOutput $ do
exam <- view _1
course <- view _2
externalExam <- view _3
case (exam, course, externalExam) of
(Just exam', Just course', Nothing) -> do
guard =<< hasReadAccessTo (urlRoute $ examLink (entityVal course') (entityVal exam'))
(Just exam', Just course', Nothing) ->
(,,)
<$> pure (Right (exam', course')) <*> view (_4 . _Value) <*> view (_5 . _Value)
(Nothing, Nothing, Just externalExam') -> do
guard =<< hasReadAccessTo (urlRoute $ externalExamLink (entityVal externalExam'))
(Nothing, Nothing, Just externalExam') ->
(,,)
<$> pure (Left externalExam') <*> view (_4 . _Value) <*> view (_5 . _Value)
_other -> return $ error "Got exam & externalExam in same result"
@ -216,7 +212,14 @@ getEOExamsR = do
]
dbtFilter = mconcat
[
[ singletonMap "may-access" . FilterProjected $ \(Any b) r -> (== b) <$> if
| Just exam <- r ^? resultExam . _entityVal
, Just course <- r ^? resultCourse . _entityVal
-> hasReadAccessTo . urlRoute $ examLink course exam
| Just eexam <- r ^? resultExternalExam . _entityVal
-> hasReadAccessTo . urlRoute $ externalExamLink eexam :: DB Bool
| otherwise
-> return $ error "Got neither exam nor externalExam in result"
]
dbtFilterUI = mconcat
[
@ -231,7 +234,9 @@ getEOExamsR = do
dbtCsvEncode = noCsvEncode
dbtCsvDecode = Nothing
examsDBTableValidator = def & defaultSorting [SortAscBy "is-synced", SortAscBy "exam-time"]
examsDBTableValidator = def
& defaultSorting [SortAscBy "is-synced", SortAscBy "exam-time"]
& forceFilter "may-access" (Any True)
dbTableWidget' examsDBTableValidator examsDBTable

View File

@ -46,9 +46,7 @@ getEExamListR = do
return (eexam, school)
dbtRowKey = queryEExam >>> (E.^. ExternalExamId)
dbtProj x@(view resultEExam -> Entity _ ExternalExam{..}) = do
guardM . hasReadAccessTo $ EExamR externalExamTerm externalExamSchool externalExamCourseName externalExamExamName EEShowR
return x
dbtProj = return
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
@ -61,7 +59,10 @@ getEExamListR = do
, ("course", SortColumn $ queryEExam >>> (E.^. ExternalExamCourseName))
, ("name", SortColumn $ queryEExam >>> (E.^. ExternalExamExamName))
]
dbtFilter = Map.empty
dbtFilter = mconcat
[ singletonMap "may-access" . FilterProjected $ \(Any b) (view resultEExam -> Entity _ ExternalExam{..}) -> (==b) <$>
hasReadAccessTo (EExamR externalExamTerm externalExamSchool externalExamCourseName externalExamExamName EEShowR) :: DB Bool
]
dbtFilterUI = const mempty
dbtStyle = def
dbtParams = def
@ -71,6 +72,7 @@ getEExamListR = do
dbtCsvDecode = Nothing
examDBTableValidator = def
& defaultSorting [SortDescBy "term", SortAscBy "school", SortAscBy "course", SortAscBy "name"]
& forceFilter "may-access" (Any True)
examTable <- runDB $ dbTableWidget' examDBTableValidator examDBTable

View File

@ -103,6 +103,7 @@ getMaterialListR tid ssh csh = do
cid <- getKeyBy404 $ TermSchoolCourseShort tid ssh csh
let row2material = view $ _dbrOutput . _1 . _entityVal
psValidator = def & defaultSorting [SortDescBy "last-edit"]
& forceFilter "may-access" (Any True)
dbTableWidget' psValidator DBTable
{ dbtIdent = "material-list" :: Text
, dbtStyle = def
@ -114,8 +115,7 @@ getMaterialListR tid ssh csh = do
E.where_ $ materialFile E.^. MaterialFileMaterial E.==. material E.^. MaterialId
return (material, filesNum)
, dbtRowKey = (E.^. MaterialId)
-- , dbtProj = \dbr -> guardAuthorizedFor (matLink . materialName $ dbr ^. _dbrOutput . _entityVal) dbr
, dbtProj = guardAuthorizedFor =<< matLink . materialName . row2material -- Moand: (a ->)
, dbtProj = return
, dbtColonnade = widgetColonnade $ mconcat
[ -- dbRow,
sortable (Just "type") (i18nCell MsgMaterialType)
@ -141,7 +141,10 @@ getMaterialListR tid ssh csh = do
, ( "visible-from" , SortColumn (E.^. MaterialVisibleFrom) )
, ( "last-edit" , SortColumn (E.^. MaterialLastEdit) )
]
, dbtFilter = mempty
, dbtFilter = mconcat
[ singletonMap "may-access" . FilterProjected $ \(Any b) dbr
-> (== b) <$> hasReadAccessTo (matLink . materialName $ row2material dbr) :: DB Bool
]
, dbtFilterUI = mempty
, dbtCsvEncode = noCsvEncode
, dbtCsvDecode = Nothing
@ -205,7 +208,7 @@ getMShowR tid ssh csh mnm = do
, colFilePathSimple (view $ _dbrOutput . _1) matLink
, materialModDateCol (view $ _dbrOutput . _2)
]
, dbtProj = \dbr -> guardAuthorizedFor (matLink $ dbr ^. _dbrOutput . _1 . _Value) dbr
, dbtProj = return
, dbtStyle = def
, dbtParams = def
, dbtFilter = mempty
@ -219,6 +222,8 @@ getMShowR tid ssh csh mnm = do
, dbtCsvDecode = Nothing
}
return (matEnt,fileTable')
-- File table has no filtering by access, because we assume that
-- access rights to material and material-files are identical.
let matLastEdit = formatTimeW SelFormatDateTime $ materialLastEdit material
let matVisibleFromMB = visibleUTCTime SelFormatDateTime <$> materialVisibleFrom material

View File

@ -86,12 +86,12 @@ newsUpcomingSheets uid = do
(hasTickmark True)
]
let validator = def & defaultSorting [SortDescBy "done", SortAscBy "deadline"]
& forceFilter "may-access" (Any True)
sheetTable <- liftHandler . runDB $ dbTableWidget' validator DBTable
{ dbtSQLQuery = tableData
, dbtRowKey = \((_ `E.InnerJoin` _ `E.InnerJoin` sheet) `E.LeftOuterJoin` _) -> sheet E.^. SheetId
, dbtColonnade = colonnade
, dbtProj = \row@DBRow{ dbrOutput = (E.Value tid, E.Value ssh, E.Value csh, E.Value shn, _, _) }
-> row <$ guardM (lift $ (== Authorized) <$> evalAccessDB (CSheetR tid ssh csh shn SShowR) False)
, dbtProj = return
, dbtSorting = Map.fromList
[ ( "term"
, SortColumn $ \(_ `E.InnerJoin` course `E.InnerJoin` _ `E.LeftOuterJoin` _) -> course E.^. CourseTerm
@ -112,12 +112,17 @@ newsUpcomingSheets uid = do
, SortColumn $ \(_ `E.InnerJoin` _ `E.InnerJoin` _ `E.LeftOuterJoin` (subm `E.InnerJoin` _)) -> E.isNothing $ subm E.?. SubmissionId
)
]
, dbtFilter = mempty {- [ ( "term"
, FilterColumn $ \(course `E.InnerJoin` _ `E.InnerJoin` _ ) tids -> if
| Set.null tids -> E.val True :: E.SqlExpr (E.Value Bool)
| otherwise -> course E.^. CourseTerm `E.in_` E.valList (Set.toList tids)
)
] -}
, dbtFilter = mconcat
[ singletonMap "may-access" . FilterProjected $ \(Any b) DBRow{..} ->
let (E.Value tid, E.Value ssh, E.Value csh, E.Value shn, _, _) = dbrOutput :: ( E.Value (Key Term)
, E.Value SchoolId
, E.Value CourseShorthand
, E.Value SheetName
, E.Value (Maybe UTCTime)
, E.Value (Maybe SubmissionId)
)
in (==b) <$> hasReadAccessTo (CSheetR tid ssh csh shn SShowR) :: DB Bool
]
, dbtFilterUI = mempty
, dbtStyle = def { dbsEmptyStyle = DBESNoHeading, dbsEmptyMessage = MsgNoUpcomingSheetDeadlines }
, dbtParams = def
@ -172,11 +177,7 @@ newsUpcomingExams uid = do
E.where_ $ regToWithinFortnight E.||. startExamFortnight E.||. startOccurFortnight E.||. startEarliest
return (course, exam, register, occurrence)
dbtRowKey = queryExam >>> (E.^. ExamId)
dbtProj r@DBRow{ dbrOutput } = do
let Entity _ Exam{..} = view lensExam dbrOutput
Entity _ Course{..} = view lensCourse dbrOutput
guardM . hasReadAccessTo $ CExamR courseTerm courseSchool courseShorthand examName EShowR -- check access rights
return r
dbtProj = return
dbtColonnade = dbColonnade $ mconcat
[ sortable (Just "term") (i18nCell MsgTerm) $ \DBRow{ dbrOutput = view lensCourse -> Entity _ Course{..} } ->
msgCell courseTerm
@ -245,7 +246,12 @@ newsUpcomingExams uid = do
E.where_ $ registration E.^. ExamRegistrationExam E.==. exam E.^. ExamId
))
]
dbtFilter = Map.empty
dbtFilter = mconcat
[ singletonMap "may-access" . FilterProjected $ \(Any b) DBRow{..} ->
let Entity _ Exam{..} = view lensExam dbrOutput
Entity _ Course{..} = view lensCourse dbrOutput
in (==b) <$> hasReadAccessTo (CExamR courseTerm courseSchool courseShorthand examName EShowR) :: DB Bool
]
dbtFilterUI = const mempty
dbtStyle = def
dbtParams = def
@ -256,6 +262,7 @@ newsUpcomingExams uid = do
examDBTableValidator = def
& defaultSorting [SortAscBy "time"]
& forceFilter "may-access" (Any True)
(, userWarningDays) <$> dbTable examDBTableValidator examDBTable

View File

@ -19,7 +19,7 @@ getSchoolListR = do
dbtSQLQuery :: E.SqlExpr (Entity School) -> E.SqlQuery _
dbtSQLQuery = return
dbtProj :: DBRow _ -> MaybeT (YesodDB UniWorX) (DBRow (Entity School))
dbtProj :: DBRow _ -> DB (DBRow (Entity School))
dbtProj = return
dbtRowKey = (E.^. SchoolId)

View File

@ -269,6 +269,7 @@ getSheetListR tid ssh csh = do
psValidator = def
& defaultSorting [SortDescBy "submission-until", SortDescBy "submission-since"]
& forceFilter "may-access" (Any True)
(raw_statistics,table) <- runDB $ dbTableWidget psValidator DBTable
{ dbtColonnade = sheetCol
@ -282,8 +283,7 @@ getSheetListR tid ssh csh = do
)
return (sheet, lastSheetEdit sheet, submission, existFiles)
, dbtRowKey = \(sheet `E.LeftOuterJoin` _) -> sheet E.^. SheetId
, dbtProj = \dbr@DBRow{ dbrOutput=(Entity _ Sheet{..}, _, _, _) }
-> dbr <$ guardM (lift $ sheetFilter sheetName)
, dbtProj = return
, dbtSorting = Map.fromList
[ ( "name"
, SortColumn $ \(sheet `E.LeftOuterJoin` _) -> sheet E.^. SheetName
@ -310,7 +310,11 @@ getSheetListR tid ssh csh = do
-- (submission E.?. SubmissionRatingPoints) E./. (sheet E.^. SheetType)
-- )
]
, dbtFilter = mempty
, dbtFilter = mconcat
[ singletonMap "may-access" . FilterProjected $ \(Any b) DBRow{..} ->
let (Entity _ Sheet{..}, _, _, _) = dbrOutput :: (Entity Sheet, E.Value (Maybe UTCTime), Maybe (Entity Submission), (E.Value Bool, E.Value Bool, E.Value Bool, E.Value Bool))
in (==b) <$> sheetFilter sheetName :: DB Bool
]
, dbtFilterUI = mempty
, dbtStyle = def
, dbtParams = def
@ -375,14 +379,18 @@ getSShowR tid ssh csh shn = do
-- , colFileModification (view _2)
]
let psValidator = def & defaultSorting [SortAscBy "type", SortAscBy "path"]
& forceFilter "may-access" (Any True)
(Any hasFiles, fileTable) <- runDB $ dbTable psValidator DBTable
{ dbtSQLQuery = fileData
, dbtRowKey = \(_ `E.InnerJoin` file) -> file E.^. FileId
, dbtColonnade = colonnadeFiles
, dbtProj = \DBRow{ dbrOutput = dbrOutput@(E.Value fName, _, E.Value fType) }
-> guardAuthorizedFor (CSheetR tid ssh csh shn $ SFileR fType fName) dbrOutput
, dbtProj = return . dbrOutput :: DBRow _ -> DB (E.Value FilePath, E.Value UTCTime, E.Value SheetFileType)
, dbtStyle = def
, dbtFilter = mempty
, dbtFilter = mconcat
[ singletonMap "may-access" . FilterProjected $ \(Any b) r ->
let (E.Value fName, _, E.Value fType) = r :: (E.Value FilePath, E.Value UTCTime, E.Value SheetFileType)
in (==b) <$> hasReadAccessTo (CSheetR tid ssh csh shn $ SFileR fType fName) :: DB Bool
]
, dbtFilterUI = mempty
, dbtIdent = "files" :: Text
, dbtSorting = Map.fromList

View File

@ -175,7 +175,7 @@ postMessageListR = do
in cell . toWidget $ fromMaybe content summary
]
dbtProj DBRow{ dbrOutput = smE@(Entity smId _), .. } = do
Just (_, smT) <- lift $ getSystemMessage appLanguages smId
smT <- (>>= view _2) <$> getSystemMessage appLanguages smId
return DBRow
{ dbrOutput = (smE, smT)
, ..

View File

@ -194,16 +194,16 @@ makeExternalExamUsersTable mode (Entity eeId ExternalExam{..}) = do
return (result, user, isSynced)
dbtRowKey = views queryResult (E.^. ExternalExamResultId)
dbtProj :: DBRow _ -> MaybeT (YesodDB UniWorX) ExternalExamUserTableData
dbtProj :: DBRow _ -> DB ExternalExamUserTableData
dbtProj = runReaderT $ (asks . set _dbrOutput) <=< magnify _dbrOutput $
(,,,)
<$> view _1 <*> view _2 <*> view (_3 . _Value)
<*> getSynchronised
where
getSynchronised :: ReaderT _ (MaybeT (YesodDB UniWorX)) [(UserDisplayName, UserSurname, UTCTime, Set SchoolShorthand)]
getSynchronised :: ReaderT _ DB [(UserDisplayName, UserSurname, UTCTime, Set SchoolShorthand)]
getSynchronised = do
resId <- view $ _1 . _entityKey
syncs <- lift . lift . E.select . E.from $ \(examOfficeExternalResultSynced `E.InnerJoin` user) -> do
syncs <- lift . E.select . E.from $ \(examOfficeExternalResultSynced `E.InnerJoin` user) -> do
E.on $ examOfficeExternalResultSynced E.^. ExamOfficeExternalResultSyncedOffice E.==. user E.^. UserId
E.where_ $ examOfficeExternalResultSynced E.^. ExamOfficeExternalResultSyncedResult E.==. E.val resId
return ( examOfficeExternalResultSynced E.^. ExamOfficeExternalResultSyncedOffice

View File

@ -58,10 +58,10 @@ type OpticColonnade focus
-> Colonnade h r' (DBCell m x)
type OpticSortColumn' focus
= forall t sortingMap.
= forall t r' sortingMap.
( IsMap sortingMap
, ContainerKey sortingMap ~ SortingKey
, MapValue sortingMap ~ SortColumn t
, MapValue sortingMap ~ SortColumn t r'
)
=> (forall focus'. Getting focus' t focus)
-> sortingMap
@ -69,10 +69,10 @@ type OpticSortColumn' focus
type OpticSortColumn val = OpticSortColumn' (E.SqlExpr (E.Value val))
type OpticFilterColumn' t inp focus
= forall filterMap.
= forall r' filterMap.
( IsMap filterMap
, ContainerKey filterMap ~ FilterKey
, MapValue filterMap ~ FilterColumn t
, MapValue filterMap ~ FilterColumn t r'
, IsFilterColumn t (t -> inp -> E.SqlExpr (E.Value Bool))
)
=> (forall focus'. Getting focus' t focus)
@ -425,10 +425,10 @@ colFileModificationWhen condition row2time = sortable (Just "time") (i18nCell Ms
where conDTCell = ifCell condition dateTimeCell $ const mempty
sortFilePath :: IsString s => (r -> E.SqlExpr (Entity File)) -> (s, SortColumn r)
sortFilePath :: IsString s => (t -> E.SqlExpr (Entity File)) -> (s, SortColumn t r')
sortFilePath queryPath = ("path", SortColumn $ queryPath >>> (E.^. FileTitle))
sortFileModification :: IsString s => (r -> E.SqlExpr (Entity File)) -> (s, SortColumn r)
sortFileModification :: IsString s => (t -> E.SqlExpr (Entity File)) -> (s, SortColumn t r')
sortFileModification queryModification = ("time", SortColumn $ queryModification >>> (E.^. FileModified))
defaultSortingByFileTitle :: PSValidator m x -> PSValidator m x
@ -484,7 +484,7 @@ colUserNameLink :: (IsDBTable m c, HasEntity a User) => (CryptoUUIDUser -> Route
colUserNameLink userLink = sortable (Just "user-name") (i18nCell MsgCourseMembers) (cellHasUserLink userLink)
-- | Intended to work with @nameWidget@, showing highlighter Surname within Displayname
sortUserName :: IsString a => (t -> E.SqlExpr (Entity User)) -> (a, SortColumn t)
sortUserName :: IsString a => (t -> E.SqlExpr (Entity User)) -> (a, SortColumn t r')
sortUserName queryUser = ("user-name", SortColumns $ queryUser >>> \user ->
[ SomeExprValue $ user E.^. UserSurname
, SomeExprValue $ user E.^. UserDisplayName
@ -492,13 +492,13 @@ sortUserName queryUser = ("user-name", SortColumns $ queryUser >>> \user ->
)
-- | Alias for sortUserName for consistency, since column comes in two variants
sortUserNameLink :: IsString a => (t -> E.SqlExpr (Entity User)) -> (a, SortColumn t)
sortUserNameLink :: IsString a => (t -> E.SqlExpr (Entity User)) -> (a, SortColumn t r')
sortUserNameLink = sortUserName
sortUserSurname :: IsString a => (t -> E.SqlExpr (Entity User)) -> (a, SortColumn t)
sortUserSurname :: IsString a => (t -> E.SqlExpr (Entity User)) -> (a, SortColumn t r')
sortUserSurname queryUser = ("user-surname", SortColumn $ queryUser >>> (E.^. UserSurname))
sortUserDisplayName :: IsString a => (t -> E.SqlExpr (Entity User)) -> (a, SortColumn t)
sortUserDisplayName :: IsString a => (t -> E.SqlExpr (Entity User)) -> (a, SortColumn t r')
sortUserDisplayName queryUser = ("user-display-name", SortColumn $ queryUser >>> (E.^. UserDisplayName))
defaultSortingByName :: PSValidator m x -> PSValidator m x
@ -507,37 +507,37 @@ defaultSortingByName =
defaultSorting [SortAscBy "user-name"] -- new way, working with single sorter
-- | Alias for sortUserName for consistency
fltrUserNameLink :: (IsFilterColumn t (a -> Set Text -> E.SqlExpr (E.Value Bool)), IsString d) => (a -> E.SqlExpr (Entity User)) -> (d, FilterColumn t)
fltrUserNameLink :: (IsFilterColumn t (a -> Set Text -> E.SqlExpr (E.Value Bool)), IsString d) => (a -> E.SqlExpr (Entity User)) -> (d, FilterColumn t r')
fltrUserNameLink = fltrUserName
fltrUserName :: (IsFilterColumn t (a -> Set Text -> E.SqlExpr (E.Value Bool)), IsString d)
=> (a -> E.SqlExpr (Entity User))
-> (d, FilterColumn t)
-> (d, FilterColumn t r')
fltrUserName queryUser = ( "user-name", FilterColumn $ mkContainsFilter queryName )
where
queryName = queryUser >>> (E.^. UserDisplayName)
fltrUserNameExact :: (IsFilterColumn t (a -> Set Text -> E.SqlExpr (E.Value Bool)), IsString d)
=> (a -> E.SqlExpr (Entity User))
-> (d, FilterColumn t)
-> (d, FilterColumn t r')
fltrUserNameExact queryUser = ( "user-name", FilterColumn $ mkExactFilter queryName )
where
queryName = queryUser >>> (E.^. UserDisplayName)
fltrUserSurname :: (IsFilterColumn t (a -> Set Text -> E.SqlExpr (E.Value Bool)), IsString d)
=> (a -> E.SqlExpr (Entity User))
-> (d, FilterColumn t)
-> (d, FilterColumn t r')
fltrUserSurname queryUser = ( "user-surname", FilterColumn $ mkContainsFilter $ queryUser >>> (E.^. UserSurname))
fltrUserDisplayName :: (IsFilterColumn t (a -> Set Text -> E.SqlExpr (E.Value Bool)), IsString d)
=> (a -> E.SqlExpr (Entity User))
-> (d, FilterColumn t)
-> (d, FilterColumn t r')
fltrUserDisplayName queryUser = ( "user-display-name", FilterColumn $ mkContainsFilter $ queryUser >>> (E.^. UserDisplayName))
-- | Search all names, i.e. DisplayName, Surname, EMail
fltrUserNameEmail :: (IsFilterColumn t (a -> Set Text -> E.SqlExpr (E.Value Bool)), IsString d)
=> (a -> E.SqlExpr (Entity User))
-> (d, FilterColumn t)
-> (d, FilterColumn t r')
fltrUserNameEmail queryUser = ( "user-name-email", FilterColumn $ anyFilter
[ mkContainsFilter $ queryUser >>> (E.^. UserDisplayName)
, mkContainsFilter $ queryUser >>> (E.^. UserSurname)
@ -579,14 +579,14 @@ fltrUserMatriculationUI mPrev = prismAForm (singletonFilter "user-matriculation"
colUserMatriclenr :: (IsDBTable m c, HasUser a) => Colonnade Sortable a (DBCell m c)
colUserMatriclenr = sortable (Just "user-matriclenumber") (i18nCell MsgMatrikelNr) cellHasMatrikelnummer
sortUserMatriclenr :: IsString d => (t -> E.SqlExpr (Entity User)) -> (d, SortColumn t)
sortUserMatriclenr :: IsString d => (t -> E.SqlExpr (Entity User)) -> (d, SortColumn t r')
sortUserMatriclenr queryUser = ("user-matriclenumber", SortColumn $ queryUser >>> (E.^. UserMatrikelnummer))
fltrUserMatriclenr :: ( IsFilterColumn t (a -> Set Text -> E.SqlExpr (E.Value Bool))
, IsString d
)
=> (a -> E.SqlExpr (Entity User))
-> (d, FilterColumn t)
-> (d, FilterColumn t r')
fltrUserMatriclenr queryUser = ("user-matriclenumber", FilterColumn . mkContainsFilterWith Just $ queryUser >>> (E.^. UserMatrikelnummer))
fltrUserMatriclenrUI :: Maybe (Map FilterKey [Text]) -> AForm (YesodDB UniWorX) (Map FilterKey [Text])
@ -599,14 +599,14 @@ fltrUserMatriclenrUI mPrev =
colUserEmail :: (IsDBTable m c, HasUser a) => Colonnade Sortable a (DBCell m c)
colUserEmail = sortable (Just "user-email") (i18nCell MsgEMail) cellHasEMail
sortUserEmail :: IsString d => (t -> E.SqlExpr (Entity User)) -> (d, SortColumn t)
sortUserEmail :: IsString d => (t -> E.SqlExpr (Entity User)) -> (d, SortColumn t r')
sortUserEmail queryUser = ( "user-email", SortColumn $ queryUser >>> (E.^. UserEmail))
fltrUserEmail :: ( IsFilterColumn t (a -> Set (CI Text) -> E.SqlExpr (E.Value Bool))
, IsString d
)
=> (a -> E.SqlExpr (Entity User))
-> (d, FilterColumn t)
-> (d, FilterColumn t r')
fltrUserEmail queryUser = ("user-email", FilterColumn . mkContainsFilter $ queryUser >>> (E.^. UserEmail))
fltrUserEmailUI :: Maybe (Map FilterKey [Text]) -> AForm (YesodDB UniWorX) (Map FilterKey [Text])
@ -724,14 +724,14 @@ fltrStudyFeaturesSemesterUI mPrev = prismAForm (singletonFilter "features-semest
colFeaturesSemester :: (IsDBTable m c, HasStudyFeatures x) => Getting (Leftmost x) a x -> Colonnade Sortable a (DBCell m c)
colFeaturesSemester feature = sortable (Just "features-semester") (i18nCell MsgStudyFeatureAge) $ maybe mempty cellHasSemester . firstOf feature
sortFeaturesSemester :: IsString d => (t -> E.SqlExpr (Maybe (Entity StudyFeatures))) -> (d, SortColumn t)
sortFeaturesSemester :: IsString d => (t -> E.SqlExpr (Maybe (Entity StudyFeatures))) -> (d, SortColumn t r')
sortFeaturesSemester queryFeatures = ("features-semester", SortColumn $ queryFeatures >>> (E.?. StudyFeaturesSemester))
fltrFeaturesSemester :: ( IsFilterColumn t (a -> Set Int -> E.SqlExpr (E.Value Bool))
, IsString d
)
=> (a -> E.SqlExpr (Maybe (Entity StudyFeatures)))
-> (d, FilterColumn t)
-> (d, FilterColumn t r')
fltrFeaturesSemester queryFeatures = ("features-semester", FilterColumn . mkExactFilterWith Just $ queryFeatures >>> (E.?. StudyFeaturesSemester))
fltrFeaturesSemesterUI :: Maybe (Map FilterKey [Text]) -> AForm (YesodDB UniWorX) (Map FilterKey [Text])
@ -742,14 +742,14 @@ fltrFeaturesSemesterUI mPrev =
colField :: (IsDBTable m c, HasStudyTerms x) => Getting (Leftmost x) a x -> Colonnade Sortable a (DBCell m c)
colField terms = sortable (Just "terms") (i18nCell MsgStudyTerm) $ maybe mempty cellHasField . firstOf terms
sortField :: IsString d => (t -> E.SqlExpr (Maybe (Entity StudyTerms))) -> (d, SortColumn t)
sortField :: IsString d => (t -> E.SqlExpr (Maybe (Entity StudyTerms))) -> (d, SortColumn t r')
sortField queryTerms = ("terms", SortColumn $ queryTerms >>> (E.?. StudyTermsName))
fltrField :: ( IsFilterColumn t (a -> Set Text -> E.SqlExpr (E.Value Bool))
, IsString d
)
=> (a -> E.SqlExpr (Maybe (Entity StudyTerms)))
-> (d, FilterColumn t)
-> (d, FilterColumn t r')
fltrField queryFeatures = ( "terms"
, FilterColumn $ anyFilter
[ mkContainsFilterWith Just $ queryFeatures >>> E.joinV . (E.?. StudyTermsName)
@ -766,14 +766,14 @@ fltrFieldUI mPrev =
colDegreeShort :: (IsDBTable m c, HasStudyDegree x) => Getting (Leftmost x) a x -> Colonnade Sortable a (DBCell m c)
colDegreeShort terms = sortable (Just "degree-short") (i18nCell MsgDegreeShort) $ maybe mempty cellHasDegreeShort . firstOf terms
sortDegreeShort :: IsString d => (t -> E.SqlExpr (Maybe (Entity StudyDegree))) -> (d, SortColumn t)
sortDegreeShort :: IsString d => (t -> E.SqlExpr (Maybe (Entity StudyDegree))) -> (d, SortColumn t r')
sortDegreeShort queryTerms = ("degree-short", SortColumn $ queryTerms >>> (E.?. StudyDegreeShorthand))
fltrDegree :: ( IsFilterColumn t (a -> Set Text -> E.SqlExpr (E.Value Bool))
, IsString d
)
=> (a -> E.SqlExpr (Maybe (Entity StudyDegree)))
-> (d, FilterColumn t)
-> (d, FilterColumn t r')
fltrDegree queryFeatures = ( "degree"
, FilterColumn $ anyFilter
[ mkContainsFilterWith Just $ queryFeatures >>> E.joinV . (E.?. StudyDegreeName)

View File

@ -26,6 +26,7 @@ module Handler.Utils.Table.Pagination
, defaultPagesize
, defaultFilter, defaultSorting
, restrictFilter, restrictSorting
, forceFilter
, ToSortable(..), Sortable(..)
, dbTable
, dbTableWidget, dbTableWidget'
@ -145,8 +146,9 @@ dbFilterKey ident = toPathPiece . WithIdent ident
data SomeExprValue = forall a. PersistField a => SomeExprValue { getSomeExprValue :: E.SqlExpr (E.Value a) }
data SortColumn t = forall a. PersistField a => SortColumn { getSortColumn :: t -> E.SqlExpr (E.Value a) }
| SortColumns { getSortColumns :: t -> [SomeExprValue] }
data SortColumn t r' = forall a. PersistField a => SortColumn { getSortColumn :: t -> E.SqlExpr (E.Value a) }
| SortColumns { getSortColumns :: t -> [SomeExprValue] }
| SortProjected { sortProjected :: r' -> r' -> Ordering }
data SortDirection = SortAsc | SortDesc
deriving (Eq, Ord, Enum, Bounded, Show, Read, Generic)
@ -157,11 +159,18 @@ instance Finite SortDirection
nullaryPathPiece ''SortDirection $ camelToPathPiece' 1
pathPieceJSON ''SortDirection
sqlSortDirection :: t -> (SortColumn t, SortDirection) -> [E.SqlExpr E.OrderBy]
sqlSortDirection t (SortColumn e , SortAsc ) = pure . E.asc $ e t
sqlSortDirection t (SortColumn e , SortDesc) = pure . E.desc $ e t
sqlSortDirection t (SortColumns es, SortAsc ) = es t <&> \(SomeExprValue v) -> E.asc v
sqlSortDirection t (SortColumns es, SortDesc) = es t <&> \(SomeExprValue v) -> E.desc v
sqlSortDirection :: SortColumn t r' -> Maybe (SortDirection -> t -> [E.SqlExpr E.OrderBy])
sqlSortDirection (SortColumn e ) = Just $ \case
SortAsc -> pure . E.asc . e
SortDesc -> pure . E.desc . e
sqlSortDirection (SortColumns es) = Just $ \case
SortAsc -> fmap (\(SomeExprValue v) -> E.asc v) . es
SortDesc -> fmap (\(SomeExprValue v) -> E.desc v) . es
sqlSortDirection _ = Nothing
sortDirectionProjected :: SortColumn t r' -> r' -> r' -> Ordering
sortDirectionProjected SortProjected{..} = sortProjected
sortDirectionProjected _ = \_ _ -> EQ
data SortingSetting = SortingSetting
@ -188,10 +197,16 @@ pattern SortDescBy :: SortingKey -> SortingSetting
pattern SortDescBy key = SortingSetting key SortDesc
data FilterColumn t = forall a. IsFilterColumn t a => FilterColumn a
data FilterColumn t r' = forall a. IsFilterColumn t a => FilterColumn a
| forall a. IsFilterProjected r' a => FilterProjected a
filterColumn :: FilterColumn t -> [Text] -> t -> E.SqlExpr (E.Value Bool)
filterColumn (FilterColumn f) = filterColumn' f
filterColumn :: FilterColumn t r' -> Maybe ([Text] -> t -> E.SqlExpr (E.Value Bool))
filterColumn (FilterColumn f) = Just $ filterColumn' f
filterColumn _ = Nothing
filterProjected :: FilterColumn t r' -> r' -> [Text] -> DB Bool
filterProjected (FilterProjected f) = flip $ filterProjected' f
filterProjected _ = \_ _ -> return True
class IsFilterColumn t a where
filterColumn' :: a -> [Text] -> t -> E.SqlExpr (E.Value Bool)
@ -203,13 +218,22 @@ instance IsFilterColumn t cont => IsFilterColumn t (t -> cont) where
filterColumn' cont is' t = filterColumn' (cont t) is' t
instance {-# OVERLAPPABLE #-} (PathPiece (Element l), IsFilterColumn t cont, MonoPointed l, Monoid l) => IsFilterColumn t (l -> cont) where
filterColumn' cont is0 = filterColumn' (cont input) is'
where
(input, ($ []) -> is') = go (mempty, id) is0
go acc [] = acc
go (acc, is3) (i:is2)
| Just i' <- fromPathPiece i = go (acc `mappend` singleton i', is3) is2
| otherwise = go (acc, is3 . (i:)) is2
filterColumn' cont is' = filterColumn' (cont $ is' ^. mono' _PathPiece) is'
class IsFilterProjected r' a where
filterProjected' :: a -> [Text] -> r' -> DB Bool
instance IsFilterProjected r' (ReaderT SqlBackend (HandlerFor UniWorX) Bool) where
filterProjected' fin _ _ = fin
instance IsFilterProjected r' Bool where
filterProjected' fin _ _ = return fin
instance IsFilterProjected r' cont => IsFilterProjected r' (r' -> cont) where
filterProjected' cont is' r = filterProjected' (cont r) is' r
instance {-# OVERLAPPABLE #-} (PathPiece (Element l), IsFilterProjected r' cont, MonoPointed l, Monoid l) => IsFilterProjected r' (l -> cont) where
filterProjected' cont is' = filterProjected' (cont $ is' ^. mono' _PathPiece) is'
data PagesizeLimit = PagesizeLimit !Int64 | PagesizeAll
@ -447,6 +471,16 @@ restrictFilter restrict (runPSValidator -> f) = PSValidator $ \dbTable' ps -> ov
where
restrict' p = p { psFilter = Map.filterWithKey restrict $ psFilter p }
forceFilter :: ( MonoFoldable mono
, MonoPointed mono
, Monoid mono
, PathPiece (Element mono)
)
=> FilterKey -> mono -> PSValidator m x -> PSValidator m x
forceFilter key args (runPSValidator -> f) = PSValidator $ \dbTable' ps -> over _2 inject $ f dbTable' ps
where
inject p = p { psFilter = psFilter p <> Map.singleton key (review monoPathPieces args) }
restrictSorting :: (SortingKey -> SortDirection -> Bool) -> PSValidator m x -> PSValidator m x
restrictSorting restrict (runPSValidator -> f) = PSValidator $ \dbTable' ps -> over _2 restrict' $ f dbTable' ps
where
@ -520,9 +554,9 @@ data DBTCsvEncode r' k' csv = forall exportData.
, DBTableKey k'
, Typeable exportData
) => DBTCsvEncode
{ dbtCsvExportForm :: AForm (YesodDB UniWorX) exportData
, dbtCsvHeader :: Maybe exportData -> YesodDB UniWorX Csv.Header -- ^ @exportData@ is @Nothing@, if we're reporting an error
, dbtCsvDoEncode :: exportData -> ConduitT (k', r') csv (YesodDB UniWorX) ()
{ dbtCsvExportForm :: AForm DB exportData
, dbtCsvHeader :: Maybe exportData -> DB Csv.Header -- ^ @exportData@ is @Nothing@, if we're reporting an error
, dbtCsvDoEncode :: exportData -> ConduitT (k', r') csv DB ()
, dbtCsvName :: FilePath
, dbtCsvNoExportData :: Maybe (AnIso' exportData ())
}
@ -535,14 +569,14 @@ data DBTCsvDecode r' k' csv = forall route csvAction csvActionClass csvException
, Ord csvActionClass
, Exception csvException
) => DBTCsvDecode
{ dbtCsvRowKey :: csv -> MaybeT (YesodDB UniWorX) k'
, dbtCsvComputeActions :: DBCsvDiff r' csv k' -> ConduitT () csvAction (YesodDB UniWorX) ()
{ dbtCsvRowKey :: csv -> MaybeT DB k'
, dbtCsvComputeActions :: DBCsvDiff r' csv k' -> ConduitT () csvAction DB ()
, dbtCsvClassifyAction :: csvAction -> csvActionClass
, dbtCsvCoarsenActionClass :: csvActionClass -> DBCsvActionMode
, dbtCsvExecuteActions :: ConduitT csvAction Void (YesodJobDB UniWorX) route
, dbtCsvRenderKey :: Map k' r' -> csvAction -> Widget
, dbtCsvRenderActionClass :: csvActionClass -> Widget
, dbtCsvRenderException :: csvException -> YesodDB UniWorX Text
, dbtCsvRenderException :: csvException -> DB Text
}
data DBTable m x = forall a r r' h i t k k' csv.
@ -553,10 +587,10 @@ data DBTable m x = forall a r r' h i t k k' csv.
) => DBTable
{ dbtSQLQuery :: t -> E.SqlQuery a
, dbtRowKey :: t -> k -- ^ required for table forms; always same key for repeated requests. For joins: return unique tuples.
, dbtProj :: DBRow r -> MaybeT (YesodDB UniWorX) r'
, dbtProj :: DBRow r -> DB r'
, dbtColonnade :: Colonnade h r' (DBCell m x)
, dbtSorting :: Map SortingKey (SortColumn t)
, dbtFilter :: Map FilterKey (FilterColumn t)
, dbtSorting :: Map SortingKey (SortColumn t r')
, dbtFilter :: Map FilterKey (FilterColumn t r')
, dbtFilterUI :: DBFilterUI
, dbtStyle :: DBStyle r'
, dbtParams :: DBParams m x
@ -565,7 +599,7 @@ data DBTable m x = forall a r r' h i t k k' csv.
, dbtIdent :: i
}
type DBFilterUI = Maybe (Map FilterKey [Text]) -> AForm (YesodDB UniWorX) (Map FilterKey [Text])
type DBFilterUI = Maybe (Map FilterKey [Text]) -> AForm DB (Map FilterKey [Text])
noCsvEncode :: Maybe (DBTCsvEncode r' k' Void)
noCsvEncode = Nothing
@ -589,7 +623,7 @@ simpleCsvEncodeM :: forall fp r' k' csv.
, DBTableKey k'
, Textual fp
)
=> fp -> ReaderT r' (YesodDB UniWorX) csv -> Maybe (DBTCsvEncode r' k' csv)
=> fp -> ReaderT r' DB csv -> Maybe (DBTCsvEncode r' k' csv)
simpleCsvEncodeM fName f = Just DBTCsvEncode
{ dbtCsvExportForm = pure ()
, dbtCsvDoEncode = \() -> C.mapM (runReaderT f . view _2)
@ -938,29 +972,45 @@ dbTable PSValidator{..} dbtable@DBTable{ dbtIdent = dbtIdent'@(toPathPiece -> db
Just csvColExplanations'' -> modal [whamlet|_{MsgCsvColumnsExplanationsLabel}|] $ Right $(widgetFile "table/csv-column-explanations")
Nothing -> mempty
psFilter' = imap (\key args -> (, args) $ Map.findWithDefault (error $ "Invalid filter key: " <> show key) key dbtFilter) psFilter
sortSql :: Maybe (_ -> [E.SqlExpr E.OrderBy])
sortSql = do
sqlSorting <- mapM (\(c, d) -> (, d) <$> sqlSortDirection c) psSorting'
return $ \t -> concatMap (\(f, d) -> f d t) sqlSorting
filterSql :: Map FilterKey (Maybe (_ -> E.SqlExpr (E.Value Bool)))
filterSql = map (\(fc, args) -> ($ args) <$> filterColumn fc) $ psFilter'
selectPagesize = is _Just sortSql
&& all (is _Just) filterSql
psLimit' = bool PagesizeAll psLimit selectPagesize
rows' <- E.select . E.from $ \t -> do
res <- dbtSQLQuery t
E.orderBy $ concatMap (sqlSortDirection t) psSorting'
whenIsJust sortSql $ \mkSorting ->
E.orderBy $ mkSorting t
case csvMode of
FormSuccess DBCsvExport{} -> return ()
FormSuccess DBCsvImport{} -> return ()
_other -> do
case previousKeys of
Nothing
| PagesizeLimit l <- psLimit
| PagesizeLimit l <- psLimit'
-> do
E.limit l
E.offset (psPage * l)
Just ps -> E.where_ $ dbtRowKey t `E.sqlIn` ps
_other -> return ()
Map.foldrWithKey (\key args expr -> E.where_ (filterColumn (Map.findWithDefault (error $ "Invalid filter key: " <> show key) key dbtFilter) args t) >> expr) (return ()) psFilter
Map.foldr (\fc expr -> maybe (return ()) (E.where_ . ($ t)) fc >> expr) (return ()) filterSql
return (E.unsafeSqlValue "count(*) OVER ()" :: E.SqlExpr (E.Value Int64), dbtRowKey t, res)
let mapMaybeM' f = mapMaybeM $ \(k, v) -> (,) <$> pure k <*> f v
firstRow :: Int64
firstRow
| PagesizeLimit l <- psLimit
| PagesizeLimit l <- psLimit'
= succ (psPage * l)
| otherwise
= 1
@ -970,7 +1020,20 @@ dbTable PSValidator{..} dbtable@DBTable{ dbtIdent = dbtIdent'@(toPathPiece -> db
| otherwise
= id
(currentKeys, rows) <- fmap unzip . mapMaybeM' dbtProj . map (\(dbrIndex, (E.Value dbrCount, dbrKey, dbrOutput)) -> (dbrKey, DBRow{..})) . zip [firstRow..] $ reproduceSorting rows'
allFilterProjected r' = lift $ getAll <$> foldMapM (\(f, args) -> All <$> filterProjected f r' args) psFilter'
sortProjected
| is _Just previousKeys
= id
| otherwise
= sortBy $ concatMap (\(c, d) (_, r) (_, r') -> adjustOrder d $ sortDirectionProjected c r r') psSorting'
where
adjustOrder SortAsc x = x
adjustOrder SortDesc LT = GT
adjustOrder SortDesc EQ = EQ
adjustOrder SortDesc GT = LT
(currentKeys, rows) <- fmap (unzip . sortProjected) . mapMaybeM' (assertMM allFilterProjected . lift . dbtProj) . map (\(dbrIndex, (E.Value dbrCount, dbrKey, dbrOutput)) -> (dbrKey, DBRow{..})) . zip [firstRow..] $ reproduceSorting rows'
formResult csvMode $ \case
@ -988,10 +1051,10 @@ dbTable PSValidator{..} dbtable@DBTable{ dbtIdent = dbtIdent'@(toPathPiece -> db
, ..
} :: DBTCsvDecode r' k' csv) <- dbtCsvDecode -> do
let existing = Map.fromList $ zip currentKeys rows
sourceDiff :: ConduitT () (DBCsvDiff r' csv k') (StateT (Map k' csv) (YesodDB UniWorX)) ()
sourceDiff :: ConduitT () (DBCsvDiff r' csv k') (StateT (Map k' csv) DB) ()
sourceDiff = do
let
toDiff :: csv -> StateT (Map k' csv) (YesodDB UniWorX) (DBCsvDiff r' csv k')
toDiff :: csv -> StateT (Map k' csv) DB (DBCsvDiff r' csv k')
toDiff row = do
rowKey <- lift $
handle (throwM . (DBCsvException (toNamedRecord row) :: Text -> DBCsvException k') <=< dbtCsvRenderException) . runMaybeT $ dbtCsvRowKey row
@ -1017,7 +1080,7 @@ dbTable PSValidator{..} dbtable@DBTable{ dbtIdent = dbtIdent'@(toPathPiece -> db
importCsv = do
let
dbtCsvComputeActions' :: ConduitT (DBCsvDiff r' csv k') Void (YesodDB UniWorX) (Map csvActionClass (Set csvAction))
dbtCsvComputeActions' :: ConduitT (DBCsvDiff r' csv k') Void DB (Map csvActionClass (Set csvAction))
dbtCsvComputeActions' = do
let innerAct = awaitForever $ \x
-> let doHandle
@ -1152,7 +1215,7 @@ dbTable PSValidator{..} dbtable@DBTable{ dbtIdent = dbtIdent'@(toPathPiece -> db
return $(widgetFile "table/colonnade")
pageCount
| PagesizeLimit l <- psLimit
| PagesizeLimit l <- psLimit'
= max 1 . ceiling $ rowCount % l
| otherwise
= 1
@ -1166,6 +1229,8 @@ dbTable PSValidator{..} dbtable@DBTable{ dbtIdent = dbtIdent'@(toPathPiece -> db
, formSubmit = FormAutoSubmit
, formAnchor = Just $ wIdent "pagesize-form"
}
showPagesizeWdgt = toEnum (fromIntegral rowCount) > minimum (pagesizeOptions referencePagesize)
&& selectPagesize
csvWdgt = $(widgetFile "table/csv-transcode")

View File

@ -154,6 +154,7 @@ import Data.Bool.Instances as Import ()
import Data.Encoding.Instances as Import ()
import Prometheus.Instances as Import ()
import Yesod.Form.Fields.Instances as Import ()
import Data.MonoTraversable.Instances as Import ()
import Crypto.Hash as Import (Digest, SHA3_256)

View File

@ -621,6 +621,12 @@ guardM f = guard =<< f
assertM :: MonadPlus m => (a -> Bool) -> m a -> m a
assertM f x = x >>= assertM' f
assertMM :: MonadPlus m => (a -> m Bool) -> m a -> m a
assertMM f x = do
x' <- x
guardM $ f x'
return x'
assertM_ :: MonadPlus m => (a -> Bool) -> m a -> m ()
assertM_ f x = guard . f =<< x

View File

@ -248,3 +248,37 @@ instance HasHttpManager s Manager => Yesod.HasHttpManager s where
class HasJSONWebKeySet s a | s -> a where
jsonWebKeySet :: Lens' s a
---------------
-- PathPiece --
---------------
mono :: forall mono mono'.
( MonoPointed mono
, MonoFoldable mono
, Monoid mono
, MonoPointed mono'
, MonoFoldable mono'
, Monoid mono'
) => Prism' (Element mono) (Element mono') -> Iso' mono mono'
mono p = iso (view $ mono' p) (view . mono' $ re p)
mono' :: forall mono mono'.
( MonoFoldable mono
, MonoPointed mono'
, Monoid mono'
)
=> Getting (First (Element mono')) (Element mono) (Element mono')
-> Getter mono mono'
mono' p' = to $ foldMap (maybe mempty opoint . preview p')
monoPathPieces :: ( PathPiece (Element mono')
, MonoPointed mono'
, Monoid mono'
, MonoFoldable mono'
, Element mono ~ Text
, MonoFoldable mono
, MonoPointed mono
, Monoid mono
) => Iso' mono mono'
monoPathPieces = mono _PathPiece

View File

@ -169,6 +169,12 @@ $# $if NTop (Just 0) < NTop (courseCapacity course)
<dt .deflist__dt>
_{MsgCourseRegistration}
<dd .deflist__dd>
<div .course__registration-status>
$maybe CourseParticipant{courseParticipantRegistration} <- registration
_{MsgRegisteredSince}
\ ^{formatTimeW SelFormatDateTime courseParticipantRegistration}
$nothing
_{MsgNotRegistered}
<div .course__registration>
$if registrationOpen
$# regForm is defined through templates/widgets/registerForm
@ -180,11 +186,6 @@ $# $if NTop (Just 0) < NTop (courseCapacity course)
$if isJust registration
<p>
_{MsgCourseRegistrationDeleteToEdit}
$maybe CourseParticipant{courseParticipantRegistration} <- registration
_{MsgRegisteredSince}
\ ^{formatTimeW SelFormatDateTime courseParticipantRegistration}
$nothing
_{MsgNotRegistered}
<dt .deflist__dt>
_{MsgCourseMaterial}
<dd .deflist__dd>
@ -197,7 +198,10 @@ $# $if NTop (Just 0) < NTop (courseCapacity course)
<dd .deflist__dd>
^{examTable}
$if not (null events) || mayCreateEvents
<dt .deflist__dt>_{MsgCourseEvents}
<dt .deflist__dt>
_{MsgCourseEvents}
$if null events
\ #{iconInvisible}
<dd .deflist__dd>
<div .scrolltable .scrolltable--bordered>
<table .table .table--striped .table--hover>

View File

@ -14,7 +14,7 @@ $else
<div .table__row-count>
_{MsgRowCount rowCount}
$# Since the current pagesize is always a member of pagesizeOptions we don't need to check `pageCount > 1`
$if toEnum (fromIntegral rowCount) > minimum (pagesizeOptions referencePagesize)
$if showPagesizeWdgt
^{pagesizeWdgt'}
$if pageCount > 1