feat: external exams in exam office exams table
This commit is contained in:
parent
553c117626
commit
3b739f751d
@ -17,7 +17,7 @@ module Database.Esqueleto.Utils
|
|||||||
, selectExists
|
, selectExists
|
||||||
, SqlHashable
|
, SqlHashable
|
||||||
, sha256
|
, sha256
|
||||||
, maybe
|
, maybe, unsafeCoalesce
|
||||||
, SqlProject(..)
|
, SqlProject(..)
|
||||||
, (->.)
|
, (->.)
|
||||||
, fromSqlKey
|
, fromSqlKey
|
||||||
@ -236,6 +236,9 @@ maybe onNothing onJust val = E.case_
|
|||||||
]
|
]
|
||||||
(E.else_ onNothing)
|
(E.else_ onNothing)
|
||||||
|
|
||||||
|
unsafeCoalesce :: E.PersistField a => [E.SqlExpr (E.Value (Maybe a))] -> E.SqlExpr (E.Value a)
|
||||||
|
unsafeCoalesce = E.veryUnsafeCoerceSqlExprValue . E.coalesce
|
||||||
|
|
||||||
|
|
||||||
class (PersistEntity entity, PersistField value) => SqlProject entity value entity' value' | entity value entity' -> value', entity value value' -> entity' where
|
class (PersistEntity entity, PersistField value) => SqlProject entity value entity' value' | entity value entity' -> value', entity value value' -> entity' where
|
||||||
sqlProject :: E.SqlExpr entity' -> EntityField entity value -> E.SqlExpr (E.Value value')
|
sqlProject :: E.SqlExpr entity' -> EntityField entity value -> E.SqlExpr (E.Value value')
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import Import
|
|||||||
|
|
||||||
import Handler.Utils
|
import Handler.Utils
|
||||||
import qualified Handler.Utils.ExamOffice.Exam as Exam
|
import qualified Handler.Utils.ExamOffice.Exam as Exam
|
||||||
|
import qualified Handler.Utils.ExamOffice.ExternalExam as ExternalExam
|
||||||
|
|
||||||
import qualified Database.Esqueleto as E
|
import qualified Database.Esqueleto as E
|
||||||
import qualified Database.Esqueleto.Utils as E
|
import qualified Database.Esqueleto.Utils as E
|
||||||
@ -15,60 +16,81 @@ import qualified Database.Esqueleto.Utils as E
|
|||||||
import qualified Colonnade
|
import qualified Colonnade
|
||||||
|
|
||||||
|
|
||||||
type ExamsTableExpr = E.SqlExpr (Entity Exam)
|
type ExamsTableExpr = ( E.SqlExpr (Maybe (Entity Exam))
|
||||||
`E.InnerJoin` E.SqlExpr (Entity Course)
|
`E.InnerJoin` E.SqlExpr (Maybe (Entity Course))
|
||||||
|
)
|
||||||
|
`E.FullOuterJoin` E.SqlExpr (Maybe (Entity ExternalExam))
|
||||||
|
|
||||||
type ExamsTableData = DBRow ( Entity Exam
|
type ExamsTableData = DBRow ( Either (Entity ExternalExam) (Entity Exam, Entity Course)
|
||||||
, Entity Course
|
|
||||||
, Natural, Natural
|
, Natural, Natural
|
||||||
)
|
)
|
||||||
|
|
||||||
queryExam :: Getter ExamsTableExpr (E.SqlExpr (Entity Exam))
|
queryExam :: Getter ExamsTableExpr (E.SqlExpr (Maybe (Entity Exam)))
|
||||||
queryExam = to $(E.sqlIJproj 2 1)
|
queryExam = to $ $(E.sqlIJproj 2 1) . $(E.sqlFOJproj 2 1)
|
||||||
|
|
||||||
queryCourse :: Getter ExamsTableExpr (E.SqlExpr (Entity Course))
|
queryCourse :: Getter ExamsTableExpr (E.SqlExpr (Maybe (Entity Course)))
|
||||||
queryCourse = to $(E.sqlIJproj 2 2)
|
queryCourse = to $ $(E.sqlIJproj 2 2) . $(E.sqlFOJproj 2 1)
|
||||||
|
|
||||||
|
queryExternalExam :: Getter ExamsTableExpr (E.SqlExpr (Maybe (Entity ExternalExam)))
|
||||||
|
queryExternalExam = to $(E.sqlFOJproj 2 2)
|
||||||
|
|
||||||
querySynchronised :: E.SqlExpr (E.Value UserId) -> Getter ExamsTableExpr (E.SqlExpr (E.Value Natural))
|
querySynchronised :: E.SqlExpr (E.Value UserId) -> Getter ExamsTableExpr (E.SqlExpr (E.Value Natural))
|
||||||
querySynchronised office = to . runReader $ do
|
querySynchronised office = to . runReader $ do
|
||||||
exam <- view queryExam
|
exam' <- view queryExam
|
||||||
|
externalExam' <- view queryExternalExam
|
||||||
let
|
let
|
||||||
synchronised = E.subSelectCount . E.from $ \examResult -> do
|
examSynchronised examId = E.subSelectCount . E.from $ \examResult -> do
|
||||||
E.where_ $ examResult E.^. ExamResultExam E.==. exam E.^. ExamId
|
E.where_ $ examResult E.^. ExamResultExam E.==. examId
|
||||||
E.where_ $ Exam.examOfficeExamResultAuth office examResult
|
E.where_ $ Exam.examOfficeExamResultAuth office examResult
|
||||||
E.where_ $ Exam.resultIsSynced office examResult
|
E.where_ $ Exam.resultIsSynced office examResult
|
||||||
return synchronised
|
externalExamSynchronised externalExamId = E.subSelectCount . E.from $ \externalExamResult -> do
|
||||||
|
E.where_ $ externalExamResult E.^. ExternalExamResultExam E.==. externalExamId
|
||||||
|
E.where_ $ ExternalExam.examOfficeExternalExamResultAuth office externalExamResult
|
||||||
|
E.where_ $ ExternalExam.resultIsSynced office externalExamResult
|
||||||
|
return $ E.maybe (E.val 0) examSynchronised (exam' E.?. ExamId) E.+. E.maybe (E.val 0) externalExamSynchronised (externalExam' E.?. ExternalExamId)
|
||||||
|
|
||||||
queryResults :: E.SqlExpr (E.Value UserId) -> Getter ExamsTableExpr (E.SqlExpr (E.Value Natural))
|
queryResults :: E.SqlExpr (E.Value UserId) -> Getter ExamsTableExpr (E.SqlExpr (E.Value Natural))
|
||||||
queryResults office = to . runReader $ do
|
queryResults office = to . runReader $ do
|
||||||
exam <- view queryExam
|
exam' <- view queryExam
|
||||||
|
externalExam' <- view queryExternalExam
|
||||||
let
|
let
|
||||||
results = E.subSelectCount . E.from $ \examResult -> do
|
results examId = E.subSelectCount . E.from $ \examResult -> do
|
||||||
E.where_ $ examResult E.^. ExamResultExam E.==. exam E.^. ExamId
|
E.where_ $ examResult E.^. ExamResultExam E.==. examId
|
||||||
E.where_ $ Exam.examOfficeExamResultAuth office examResult
|
E.where_ $ Exam.examOfficeExamResultAuth office examResult
|
||||||
return results
|
externalResults externalExamId = E.subSelectCount . E.from $ \externalExamResult -> do
|
||||||
|
E.where_ $ externalExamResult E.^. ExternalExamResultExam E.==. externalExamId
|
||||||
|
E.where_ $ ExternalExam.examOfficeExternalExamResultAuth office externalExamResult
|
||||||
|
return $ E.maybe (E.val 0) results (exam' E.?. ExamId) E.+. E.maybe (E.val 0) externalResults (externalExam' E.?. ExternalExamId)
|
||||||
|
|
||||||
queryIsSynced :: UTCTime -> E.SqlExpr (E.Value UserId) -> Getter ExamsTableExpr (E.SqlExpr (E.Value Bool))
|
queryIsSynced :: UTCTime -> E.SqlExpr (E.Value UserId) -> Getter ExamsTableExpr (E.SqlExpr (E.Value Bool))
|
||||||
queryIsSynced now office = to . runReader $ do
|
queryIsSynced now office = to . runReader $ do
|
||||||
exam <- view queryExam
|
exam' <- view queryExam
|
||||||
|
externalExam' <- view queryExternalExam
|
||||||
let
|
let
|
||||||
synchronised = E.not_ . E.exists . E.from $ \examResult -> do
|
examSynchronised examId = E.not_ . E.exists . E.from $ \examResult -> do
|
||||||
E.where_ $ examResult E.^. ExamResultExam E.==. exam E.^. ExamId
|
E.where_ $ examResult E.^. ExamResultExam E.==. examId
|
||||||
E.where_ $ Exam.examOfficeExamResultAuth office examResult
|
E.where_ $ Exam.examOfficeExamResultAuth office examResult
|
||||||
E.where_ . E.not_ $ Exam.resultIsSynced office examResult
|
E.where_ . E.not_ $ Exam.resultIsSynced office examResult
|
||||||
open = E.maybe E.true (E.>. E.val now) $ exam E.^. ExamClosed
|
externalExamSynchronised externalExamId = E.not_ . E.exists . E.from $ \externalExamResult -> do
|
||||||
return $ synchronised E.||. open
|
E.where_ $ externalExamResult E.^. ExternalExamResultExam E.==. externalExamId
|
||||||
|
E.where_ $ ExternalExam.examOfficeExternalExamResultAuth office externalExamResult
|
||||||
|
E.where_ . E.not_ $ ExternalExam.resultIsSynced office externalExamResult
|
||||||
|
open examClosed' = E.maybe E.true (E.>. E.val now) $ examClosed'
|
||||||
|
return $ E.maybe E.false examSynchronised (exam' E.?. ExamId) E.||. E.maybe E.false open (exam' E.?. ExamClosed) E.||. E.maybe E.false externalExamSynchronised (externalExam' E.?. ExternalExamId)
|
||||||
|
|
||||||
|
|
||||||
resultExam :: Lens' ExamsTableData (Entity Exam)
|
resultExam :: Traversal' ExamsTableData (Entity Exam)
|
||||||
resultExam = _dbrOutput . _1
|
resultExam = _dbrOutput . _1 . _Right . _1
|
||||||
|
|
||||||
resultCourse :: Lens' ExamsTableData (Entity Course)
|
resultCourse :: Traversal' ExamsTableData (Entity Course)
|
||||||
resultCourse = _dbrOutput . _2
|
resultCourse = _dbrOutput . _1 . _Right . _2
|
||||||
|
|
||||||
|
resultExternalExam :: Traversal' ExamsTableData (Entity ExternalExam)
|
||||||
|
resultExternalExam = _dbrOutput . _1 . _Left
|
||||||
|
|
||||||
resultSynchronised, resultResults :: Lens' ExamsTableData Natural
|
resultSynchronised, resultResults :: Lens' ExamsTableData Natural
|
||||||
resultSynchronised = _dbrOutput . _3
|
resultSynchronised = _dbrOutput . _2
|
||||||
resultResults = _dbrOutput . _4
|
resultResults = _dbrOutput . _3
|
||||||
|
|
||||||
resultIsSynced :: Getter ExamsTableData Bool
|
resultIsSynced :: Getter ExamsTableData Bool
|
||||||
resultIsSynced = to $ (>=) <$> view resultSynchronised <*> view resultResults
|
resultIsSynced = to $ (>=) <$> view resultSynchronised <*> view resultResults
|
||||||
@ -90,6 +112,10 @@ getEOExamsR = do
|
|||||||
courseLink :: Course -> SomeRoute UniWorX
|
courseLink :: Course -> SomeRoute UniWorX
|
||||||
courseLink Course{..}
|
courseLink Course{..}
|
||||||
= SomeRoute $ CourseR courseTerm courseSchool courseShorthand CShowR
|
= SomeRoute $ CourseR courseTerm courseSchool courseShorthand CShowR
|
||||||
|
|
||||||
|
externalExamLink :: ExternalExam -> SomeRoute UniWorX
|
||||||
|
externalExamLink ExternalExam{..}
|
||||||
|
= SomeRoute $ EExamR externalExamTerm externalExamSchool externalExamCourseName externalExamExamName EEGradesR
|
||||||
|
|
||||||
querySynchronised' = querySynchronised $ E.val uid
|
querySynchronised' = querySynchronised $ E.val uid
|
||||||
queryResults' = queryResults $ E.val uid
|
queryResults' = queryResults $ E.val uid
|
||||||
@ -100,34 +126,48 @@ getEOExamsR = do
|
|||||||
dbtSQLQuery = runReaderT $ do
|
dbtSQLQuery = runReaderT $ do
|
||||||
exam <- view queryExam
|
exam <- view queryExam
|
||||||
course <- view queryCourse
|
course <- view queryCourse
|
||||||
|
externalExam <- view queryExternalExam
|
||||||
|
|
||||||
synchronised <- view querySynchronised'
|
synchronised <- view querySynchronised'
|
||||||
results <- view queryResults'
|
results <- view queryResults'
|
||||||
|
|
||||||
lift $ do
|
lift $ do
|
||||||
E.on $ exam E.^. ExamCourse E.==. course E.^. CourseId
|
E.on E.false
|
||||||
|
E.on $ exam E.?. ExamCourse E.==. course E.?. CourseId
|
||||||
|
|
||||||
E.where_ $ results E.>. E.val 0
|
E.where_ $ results E.>. E.val 0
|
||||||
|
E.where_ $ (E.not_ (E.isNothing $ exam E.?. ExamId) E.&&. E.not_ (E.isNothing $ course E.?. CourseId) E.&&. E.isNothing (externalExam E.?. ExternalExamId))
|
||||||
|
E.||. ( E.isNothing (exam E.?. ExamId) E.&&. E.isNothing (course E.?. CourseId) E.&&. E.not_ (E.isNothing $ externalExam E.?. ExternalExamId))
|
||||||
|
|
||||||
return (exam, course, synchronised, results)
|
return (exam, course, externalExam, synchronised, results)
|
||||||
dbtRowKey = views queryExam (E.^. ExamId)
|
dbtRowKey = views ($(multifocusG 2) queryExam queryExternalExam) (bimap (E.?. ExamId) (E.?. ExternalExamId))
|
||||||
|
|
||||||
dbtProj :: DBRow _ -> MaybeT (YesodDB UniWorX) ExamsTableData
|
dbtProj :: DBRow _ -> MaybeT (YesodDB UniWorX) ExamsTableData
|
||||||
dbtProj = runReaderT $ (asks . set _dbrOutput) <=< magnify _dbrOutput $ do
|
dbtProj = runReaderT $ (asks . set _dbrOutput) <=< magnify _dbrOutput $ do
|
||||||
exam <- view $ _1 . _entityVal
|
exam <- view _1
|
||||||
course <- view $ _2 . _entityVal
|
course <- view _2
|
||||||
|
externalExam <- view _3
|
||||||
|
|
||||||
guard =<< hasReadAccessTo (urlRoute $ examLink course exam)
|
case (exam, course, externalExam) of
|
||||||
|
(Just exam', Just course', Nothing) -> do
|
||||||
|
guard =<< hasReadAccessTo (urlRoute $ examLink (entityVal course') (entityVal exam'))
|
||||||
|
|
||||||
(,,,)
|
(,,)
|
||||||
<$> view _1 <*> view _2 <*> view (_3 . _Value) <*> view (_4 . _Value)
|
<$> pure (Right (exam', course')) <*> view (_4 . _Value) <*> view (_5 . _Value)
|
||||||
|
(Nothing, Nothing, Just externalExam') -> do
|
||||||
|
guard =<< hasReadAccessTo (urlRoute $ externalExamLink (entityVal externalExam'))
|
||||||
|
|
||||||
|
(,,)
|
||||||
|
<$> pure (Left externalExam') <*> view (_4 . _Value) <*> view (_5 . _Value)
|
||||||
|
_other -> return $ error "Got exam & externalExam in same result"
|
||||||
|
|
||||||
|
|
||||||
colSynced = Colonnade.singleton (fromSortable . Sortable (Just "synced") $ i18nCell MsgExamSynchronised) $ \x -> flip runReader x $ do
|
colSynced = Colonnade.singleton (fromSortable . Sortable (Just "synced") $ i18nCell MsgExamSynchronised) $ \x -> flip runReader x $ do
|
||||||
Entity _ Exam{examClosed} <- view resultExam
|
mExam <- preview resultExam
|
||||||
|
|
||||||
if
|
if
|
||||||
| NTop examClosed > NTop (Just now)
|
| Just (Entity _ Exam{examClosed}) <- mExam
|
||||||
|
, NTop examClosed > NTop (Just now)
|
||||||
-> return . cell $ toWidget iconNew
|
-> return . cell $ toWidget iconNew
|
||||||
| otherwise
|
| otherwise
|
||||||
-> do
|
-> do
|
||||||
@ -151,26 +191,28 @@ getEOExamsR = do
|
|||||||
dbtColonnade :: Colonnade Sortable _ _
|
dbtColonnade :: Colonnade Sortable _ _
|
||||||
dbtColonnade = mconcat
|
dbtColonnade = mconcat
|
||||||
[ colSynced
|
[ colSynced
|
||||||
, anchorColonnade (views ($(multifocusG 2) (resultCourse . _entityVal) (resultExam . _entityVal)) (uncurry examLink))
|
, maybeAnchorColonnade ( runMaybeT $ mpreview ($(multifocusG 2) (pre $ resultCourse . _entityVal) (pre $ resultExam . _entityVal) . to (uncurry $ liftA2 examLink) . _Just)
|
||||||
$ colExamName (resultExam . _entityVal . _examName)
|
<|> mpreviews (resultExternalExam . _entityVal) externalExamLink
|
||||||
, colExamTime (resultExam . _entityVal . $(multifocusG 2) _examStart _examEnd)
|
)
|
||||||
, colExamFinishedOffice (resultExam . _entityVal . _examFinished)
|
$ emptyOpticColonnade (resultExam . _entityVal . _examName <> resultExternalExam . _entityVal . _externalExamExamName) colExamName
|
||||||
, colExamClosed (resultExam . _entityVal . _examClosed)
|
, emptyOpticColonnade (resultExam . _entityVal . $(multifocusG 2) _examStart _examEnd) colExamTime
|
||||||
, anchorColonnade (views (resultCourse . _entityVal) courseLink)
|
, emptyOpticColonnade (resultExam . _entityVal . _examFinished) colExamFinishedOffice
|
||||||
$ colCourseName (resultCourse . _entityVal . _courseName)
|
, emptyOpticColonnade (resultExam . _entityVal . _examClosed) colExamClosed
|
||||||
, colSchool (resultCourse . _entityVal . _courseSchool)
|
, maybeAnchorColonnade (previews (resultCourse . _entityVal) courseLink)
|
||||||
, colTermShort (resultCourse . _entityVal . _courseTerm)
|
$ emptyOpticColonnade (resultCourse . _entityVal . _courseName <> resultExternalExam . _entityVal . _externalExamCourseName) colCourseName
|
||||||
|
, emptyOpticColonnade (resultCourse . _entityVal . _courseSchool <> resultExternalExam . _entityVal . _externalExamSchool) colSchool
|
||||||
|
, emptyOpticColonnade (resultCourse . _entityVal . _courseTerm <> resultExternalExam . _entityVal . _externalExamTerm) colTermShort
|
||||||
]
|
]
|
||||||
dbtSorting = mconcat
|
dbtSorting = mconcat
|
||||||
[ singletonMap "synced" . SortColumn $ (E./.) <$> view querySynchronised' <*> view queryResults'
|
[ singletonMap "synced" . SortColumn $ (E./.) <$> view querySynchronised' <*> view queryResults'
|
||||||
, singletonMap "is-synced" . SortColumn $ view queryIsSynced'
|
, singletonMap "is-synced" . SortColumn $ view queryIsSynced'
|
||||||
, sortExamName (queryExam . to (E.^. ExamName))
|
, sortExamName (to $ E.unsafeCoalesce . sequence [views queryExam (E.?. ExamName), views queryExternalExam (E.?. ExternalExamExamName)])
|
||||||
, sortExamTime (queryExam . $(multifocusG 2) (to (E.^. ExamStart)) (to (E.^. ExamEnd)))
|
, sortExamTime (queryExam . $(multifocusG 2) (to $ E.joinV . (E.?. ExamStart)) (to $ E.joinV . (E.?. ExamEnd)))
|
||||||
, sortExamFinished (queryExam . to (E.^. ExamFinished))
|
, sortExamFinished (queryExam . to (E.joinV . (E.?. ExamFinished)))
|
||||||
, sortExamClosed (queryExam . to (E.^. ExamClosed))
|
, sortExamClosed (queryExam . to (E.joinV . (E.?. ExamClosed)))
|
||||||
, sortCourseName (queryCourse . to (E.^. CourseName))
|
, sortCourseName (to $ E.unsafeCoalesce . sequence [views queryCourse (E.?. CourseName), views queryExternalExam (E.?. ExternalExamCourseName)])
|
||||||
, sortSchool (queryCourse . to (E.^. CourseSchool))
|
, sortSchool (to $ E.unsafeCoalesce . sequence [views queryCourse (E.?. CourseSchool), views queryExternalExam (E.?. ExternalExamSchool)])
|
||||||
, sortTerm (queryCourse . to (E.^. CourseTerm))
|
, sortTerm (to $ E.unsafeCoalesce . sequence [views queryCourse (E.?. CourseTerm), views queryExternalExam (E.?. ExternalExamTerm)])
|
||||||
]
|
]
|
||||||
|
|
||||||
dbtFilter = mconcat
|
dbtFilter = mconcat
|
||||||
|
|||||||
@ -840,6 +840,31 @@ anchorColonnadeM mkUrl = imapColonnade anchorColonnade'
|
|||||||
anchorColonnade' inp (view dbCell -> (attrs, act)) = review dbCell . (attrs,) $
|
anchorColonnade' inp (view dbCell -> (attrs, act)) = review dbCell . (attrs,) $
|
||||||
view (dbCell . _2) . anchorCellM (mkUrl inp) =<< act
|
view (dbCell . _2) . anchorCellM (mkUrl inp) =<< act
|
||||||
|
|
||||||
|
maybeAnchorColonnade :: forall h r' m a url.
|
||||||
|
( HasRoute UniWorX url
|
||||||
|
, IsDBTable m a
|
||||||
|
, HandlerSite m ~ UniWorX
|
||||||
|
)
|
||||||
|
=> (r' -> Maybe url)
|
||||||
|
-> Colonnade h r' (DBCell m a)
|
||||||
|
-> Colonnade h r' (DBCell m a)
|
||||||
|
maybeAnchorColonnade = maybeAnchorColonnadeM . (hoistMaybe .)
|
||||||
|
|
||||||
|
maybeAnchorColonnadeM :: forall h r' m a url.
|
||||||
|
( HasRoute UniWorX url
|
||||||
|
, IsDBTable m a
|
||||||
|
, HandlerSite m ~ UniWorX
|
||||||
|
)
|
||||||
|
=> (r' -> MaybeT (WidgetFor UniWorX) url)
|
||||||
|
-> Colonnade h r' (DBCell m a)
|
||||||
|
-> Colonnade h r' (DBCell m a)
|
||||||
|
maybeAnchorColonnadeM mkUrl = imapColonnade anchorColonnade'
|
||||||
|
where
|
||||||
|
anchorColonnade' :: r' -> DBCell m a -> DBCell m a
|
||||||
|
anchorColonnade' inp (view dbCell -> (attrs, act)) = review dbCell . (attrs,) $
|
||||||
|
view (dbCell . _2) . maybeAnchorCellM (mkUrl inp) =<< act
|
||||||
|
|
||||||
|
|
||||||
emptyOpticColonnade :: forall h r' focus c.
|
emptyOpticColonnade :: forall h r' focus c.
|
||||||
( Monoid c
|
( Monoid c
|
||||||
)
|
)
|
||||||
|
|||||||
@ -33,6 +33,7 @@ module Handler.Utils.Table.Pagination
|
|||||||
, cell, textCell, stringCell, i18nCell
|
, cell, textCell, stringCell, i18nCell
|
||||||
, anchorCell, anchorCell', anchorCellM, anchorCellM'
|
, anchorCell, anchorCell', anchorCellM, anchorCellM'
|
||||||
, linkEitherCell, linkEitherCellM, linkEitherCellM'
|
, linkEitherCell, linkEitherCellM, linkEitherCellM'
|
||||||
|
, maybeAnchorCellM, maybeAnchorCellM', maybeLinkEitherCellM'
|
||||||
, cellTooltip
|
, cellTooltip
|
||||||
, listCell
|
, listCell
|
||||||
, formCell, DBFormResult(..), getDBFormResult
|
, formCell, DBFormResult(..), getDBFormResult
|
||||||
@ -93,6 +94,8 @@ import Data.Ratio ((%))
|
|||||||
|
|
||||||
import Data.List (elemIndex)
|
import Data.List (elemIndex)
|
||||||
|
|
||||||
|
import Data.Maybe (fromJust)
|
||||||
|
|
||||||
import Data.Aeson (Options(..), SumEncoding(..), defaultOptions)
|
import Data.Aeson (Options(..), SumEncoding(..), defaultOptions)
|
||||||
import Data.Aeson.Text
|
import Data.Aeson.Text
|
||||||
import Data.Aeson.TH (deriveJSON)
|
import Data.Aeson.TH (deriveJSON)
|
||||||
@ -1300,6 +1303,12 @@ anchorCellM routeM widget = anchorCellM' routeM id (const widget)
|
|||||||
anchorCellM' :: (HasRoute UniWorX url, ToWidget UniWorX wgt, IsDBTable m a) => WidgetFor UniWorX x -> (x -> url) -> (x -> wgt) -> DBCell m a
|
anchorCellM' :: (HasRoute UniWorX url, ToWidget UniWorX wgt, IsDBTable m a) => WidgetFor UniWorX x -> (x -> url) -> (x -> wgt) -> DBCell m a
|
||||||
anchorCellM' xM x2route x2widget = linkEitherCellM' xM x2route (x2widget, x2widget)
|
anchorCellM' xM x2route x2widget = linkEitherCellM' xM x2route (x2widget, x2widget)
|
||||||
|
|
||||||
|
maybeAnchorCellM :: (HasRoute UniWorX url, ToWidget UniWorX wgt, IsDBTable m a) => MaybeT (WidgetFor UniWorX) url -> wgt -> DBCell m a
|
||||||
|
maybeAnchorCellM routeM widget = maybeAnchorCellM' routeM id (const widget)
|
||||||
|
|
||||||
|
maybeAnchorCellM' :: (HasRoute UniWorX url, ToWidget UniWorX wgt, IsDBTable m a) => MaybeT (WidgetFor UniWorX) x -> (x -> url) -> (Maybe x -> wgt) -> DBCell m a
|
||||||
|
maybeAnchorCellM' xM x2route x2widget = maybeLinkEitherCellM' xM x2route (x2widget . Just, x2widget)
|
||||||
|
|
||||||
-- | Variant of `anchorCell` that displays different widgets depending whether the route is authorized for current user
|
-- | Variant of `anchorCell` that displays different widgets depending whether the route is authorized for current user
|
||||||
linkEitherCell :: (HasRoute UniWorX url, ToWidget UniWorX wgt, ToWidget UniWorX wgt', IsDBTable m a) => url -> (wgt, wgt') -> DBCell m a
|
linkEitherCell :: (HasRoute UniWorX url, ToWidget UniWorX wgt, ToWidget UniWorX wgt', IsDBTable m a) => url -> (wgt, wgt') -> DBCell m a
|
||||||
linkEitherCell = linkEitherCellM . return
|
linkEitherCell = linkEitherCellM . return
|
||||||
@ -1314,17 +1323,31 @@ linkEitherCellM' :: forall m url wgt wgt' a x.
|
|||||||
, IsDBTable m a
|
, IsDBTable m a
|
||||||
)
|
)
|
||||||
=> WidgetFor UniWorX x -> (x -> url) -> (x -> wgt, x -> wgt') -> DBCell m a
|
=> WidgetFor UniWorX x -> (x -> url) -> (x -> wgt, x -> wgt') -> DBCell m a
|
||||||
linkEitherCellM' xM x2route (x2widgetAuth,x2widgetUnauth) = cell $ do
|
linkEitherCellM' xM x2route (x2widgetAuth,x2widgetUnauth) = maybeLinkEitherCellM' (lift xM) x2route (x2widgetAuth, x2widgetUnauth . fromJust)
|
||||||
x <- xM
|
|
||||||
let route = x2route x
|
maybeLinkEitherCellM' :: forall m url wgt wgt' a x.
|
||||||
widget, widgetUnauth :: Widget
|
( HasRoute UniWorX url
|
||||||
widget = toWidget $ x2widgetAuth x
|
, ToWidget UniWorX wgt
|
||||||
widgetUnauth = toWidget $ x2widgetUnauth x
|
, ToWidget UniWorX wgt'
|
||||||
authResult <- liftHandler $ isAuthorized (urlRoute route) False
|
, IsDBTable m a
|
||||||
linkUrl <- toTextUrl route
|
)
|
||||||
case authResult of
|
=> MaybeT (WidgetFor UniWorX) x -> (x -> url) -> (x -> wgt, Maybe x -> wgt') -> DBCell m a
|
||||||
Authorized -> $(widgetFile "table/cell/link") -- show allowed link
|
maybeLinkEitherCellM' xM x2route (x2widgetAuth,x2widgetUnauth) = cell $ do
|
||||||
_otherwise -> widgetUnauth -- show alternative widget
|
x' <- runMaybeT xM
|
||||||
|
case x' of
|
||||||
|
Just x -> do
|
||||||
|
let route = x2route x
|
||||||
|
widget, widgetUnauth :: Widget
|
||||||
|
widget = toWidget $ x2widgetAuth x
|
||||||
|
widgetUnauth = toWidget . x2widgetUnauth $ Just x
|
||||||
|
authResult <- liftHandler $ isAuthorized (urlRoute route) False
|
||||||
|
linkUrl <- toTextUrl route
|
||||||
|
case authResult of
|
||||||
|
Authorized -> $(widgetFile "table/cell/link") -- show allowed link
|
||||||
|
_otherwise -> widgetUnauth -- show alternative widget
|
||||||
|
_otherwise -> do
|
||||||
|
toWidget $ x2widgetUnauth Nothing
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
12
src/Utils.hs
12
src/Utils.hs
@ -8,7 +8,7 @@ import ClassyPrelude.Yesod hiding (foldlM, Proxy, handle, catch)
|
|||||||
-- import Data.Double.Conversion.Text -- faster implementation for textPercent?
|
-- import Data.Double.Conversion.Text -- faster implementation for textPercent?
|
||||||
import qualified Data.Foldable as Fold
|
import qualified Data.Foldable as Fold
|
||||||
import Data.Foldable as Utils (foldlM, foldrM)
|
import Data.Foldable as Utils (foldlM, foldrM)
|
||||||
import Data.Monoid (Sum(..))
|
import Data.Monoid (First, Sum(..))
|
||||||
import Data.Proxy
|
import Data.Proxy
|
||||||
|
|
||||||
import Data.CaseInsensitive (CI)
|
import Data.CaseInsensitive (CI)
|
||||||
@ -988,3 +988,13 @@ unstableSortOn = unstableSortBy . comparing
|
|||||||
|
|
||||||
unstableSort :: (MonadRandom m, Ord a) => [a] -> m [a]
|
unstableSort :: (MonadRandom m, Ord a) => [a] -> m [a]
|
||||||
unstableSort = unstableSortBy compare
|
unstableSort = unstableSortBy compare
|
||||||
|
|
||||||
|
----------
|
||||||
|
-- Lens --
|
||||||
|
----------
|
||||||
|
|
||||||
|
mpreview :: (MonadPlus m, MonadReader s m) => Getting (First a) s a -> m a
|
||||||
|
mpreview = hoistMaybe <=< preview
|
||||||
|
|
||||||
|
mpreviews :: (MonadPlus m, MonadReader s m) => Getting (First b) s a -> (a -> b) -> m b
|
||||||
|
mpreviews a f = hoistMaybe =<< previews a f
|
||||||
|
|||||||
@ -221,6 +221,7 @@ makeLenses_ ''Tutorial
|
|||||||
|
|
||||||
makeLenses_ ''SessionFile
|
makeLenses_ ''SessionFile
|
||||||
|
|
||||||
|
makeLenses_ ''ExternalExam
|
||||||
makeLenses_ ''ExternalExamOfficeSchool
|
makeLenses_ ''ExternalExamOfficeSchool
|
||||||
makeLenses_ ''ExternalExamStaff
|
makeLenses_ ''ExternalExamStaff
|
||||||
makeLenses_ ''ExternalExamResult
|
makeLenses_ ''ExternalExamResult
|
||||||
|
|||||||
@ -87,7 +87,6 @@ multifocusL = multifocusOptic
|
|||||||
(\s a -> [t|Lens' $(s) $(a)|])
|
(\s a -> [t|Lens' $(s) $(a)|])
|
||||||
(\doGet doSet -> [e|lens $(doGet) $(doSet)|])
|
(\doGet doSet -> [e|lens $(doGet) $(doSet)|])
|
||||||
|
|
||||||
|
|
||||||
multifocusOptic :: _ -> _ -> _ -> _ -> Natural -> ExpQ
|
multifocusOptic :: _ -> _ -> _ -> _ -> Natural -> ExpQ
|
||||||
multifocusOptic _ _ _ _ 0 = [e|united|]
|
multifocusOptic _ _ _ _ 0 = [e|united|]
|
||||||
multifocusOptic doClone _ _ _ 1 = doClone
|
multifocusOptic doClone _ _ _ 1 = doClone
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user