feat(dbtable): extra representations

This commit is contained in:
Gregor Kleen 2021-01-21 13:22:22 +01:00
parent 9a3f401b38
commit 2c0fc63be1
36 changed files with 362 additions and 54 deletions

View File

@ -346,6 +346,7 @@ postAdminFeaturesR = do
& defaultSorting [SortAscBy "key"] & defaultSorting [SortAscBy "key"]
dbtCsvEncode = noCsvEncode dbtCsvEncode = noCsvEncode
dbtCsvDecode = Nothing dbtCsvDecode = Nothing
dbtExtraReps = []
in dbTable psValidator DBTable{..} in dbTable psValidator DBTable{..}
mkStudytermsTable :: Set StudyTermsId -> Set StudyTermsId -> Set (Entity School) -> DB (FormResult (DBFormResult StudyTermsId (Maybe Text, Maybe Text, Set SchoolId, Set StudyTermsId, Maybe StudyDegreeId, Maybe StudyFieldType) (DBRow (Entity StudyTerms, Set (Entity StudyTerms), Set SchoolId))), Widget) mkStudytermsTable :: Set StudyTermsId -> Set StudyTermsId -> Set (Entity School) -> DB (FormResult (DBFormResult StudyTermsId (Maybe Text, Maybe Text, Set SchoolId, Set StudyTermsId, Maybe StudyDegreeId, Maybe StudyFieldType) (DBRow (Entity StudyTerms, Set (Entity StudyTerms), Set SchoolId))), Widget)
@ -401,6 +402,8 @@ postAdminFeaturesR = do
& defaultSorting [SortAscBy "isnew", SortAscBy "isbad", SortAscBy "key"] & defaultSorting [SortAscBy "isnew", SortAscBy "isbad", SortAscBy "key"]
dbtCsvEncode = noCsvEncode dbtCsvEncode = noCsvEncode
dbtCsvDecode = Nothing dbtCsvDecode = Nothing
dbtExtraReps = []
queryField = id queryField = id
_dbrKey' :: Getter (DBRow (Entity StudyTerms, _, _)) StudyTermsId _dbrKey' :: Getter (DBRow (Entity StudyTerms, _, _)) StudyTermsId
@ -438,6 +441,7 @@ postAdminFeaturesR = do
psValidator = def & defaultSorting [SortAscBy "incidence", SortAscBy "key", SortAscBy "name"] psValidator = def & defaultSorting [SortAscBy "incidence", SortAscBy "key", SortAscBy "name"]
dbtCsvEncode = noCsvEncode dbtCsvEncode = noCsvEncode
dbtCsvDecode = Nothing dbtCsvDecode = Nothing
dbtExtraReps = []
in dbTable psValidator DBTable{..} in dbTable psValidator DBTable{..}
mkParentCandidateTable = mkParentCandidateTable =
@ -477,6 +481,8 @@ postAdminFeaturesR = do
& defaultSorting [SortAscBy "child", SortAscBy "incidence", SortAscBy "parent"] & defaultSorting [SortAscBy "child", SortAscBy "incidence", SortAscBy "parent"]
dbtCsvEncode = noCsvEncode dbtCsvEncode = noCsvEncode
dbtCsvDecode = Nothing dbtCsvDecode = Nothing
dbtExtraReps = []
queryCandidate (c `E.LeftOuterJoin` _ `E.LeftOuterJoin` _) = c queryCandidate (c `E.LeftOuterJoin` _ `E.LeftOuterJoin` _) = c
queryParent (_ `E.LeftOuterJoin` p `E.LeftOuterJoin` _) = p queryParent (_ `E.LeftOuterJoin` p `E.LeftOuterJoin` _) = p
@ -517,6 +523,8 @@ postAdminFeaturesR = do
& defaultSorting [SortAscBy "key", SortAscBy "incidence"] & defaultSorting [SortAscBy "key", SortAscBy "incidence"]
dbtCsvEncode = noCsvEncode dbtCsvEncode = noCsvEncode
dbtCsvDecode = Nothing dbtCsvDecode = Nothing
dbtExtraReps = []
queryCandidate (c `E.LeftOuterJoin` _) = c queryCandidate (c `E.LeftOuterJoin` _) = c
queryTerm (_ `E.LeftOuterJoin` t) = t queryTerm (_ `E.LeftOuterJoin` t) = t

View File

@ -128,6 +128,8 @@ getAllocationListR = do
dbtCsvEncode = noCsvEncode dbtCsvEncode = noCsvEncode
dbtCsvDecode = Nothing dbtCsvDecode = Nothing
dbtExtraReps = []
dbtIdent = allocationListIdent dbtIdent = allocationListIdent
psValidator :: PSValidator _ _ psValidator :: PSValidator _ _

View File

@ -317,6 +317,7 @@ postAUsersR tid ssh ash = do
, dbtCsvExampleData = Nothing , dbtCsvExampleData = Nothing
} }
dbtCsvDecode = Nothing dbtCsvDecode = Nothing
dbtExtraReps = []
allocationUsersDBTableValidator = def allocationUsersDBTableValidator = def
& defaultSorting [SortAscBy "priority", SortAscBy "user-matriculation"] & defaultSorting [SortAscBy "priority", SortAscBy "user-matriculation"]
& defaultPagesize (PagesizeLimit 500) & defaultPagesize (PagesizeLimit 500)

View File

@ -491,6 +491,8 @@ postCApplicationsR tid ssh csh = do
where where
Entity _ User{..} = existing ^. singular (ix appId . resultUser) Entity _ User{..} = existing ^. singular (ix appId . resultUser)
dbtExtraReps = []
dbtIdent = courseApplicationsIdent dbtIdent = courseApplicationsIdent
psValidator :: PSValidator _ _ psValidator :: PSValidator _ _

View File

@ -197,6 +197,7 @@ makeCourseTable whereClause colChoices psValidator = do
, dbtIdent = "courses" :: Text , dbtIdent = "courses" :: Text
, dbtCsvEncode = noCsvEncode , dbtCsvEncode = noCsvEncode
, dbtCsvDecode = Nothing , dbtCsvDecode = Nothing
, dbtExtraReps = []
} }
getCourseListR :: Handler Html getCourseListR :: Handler Html

View File

@ -239,6 +239,7 @@ getCShowR tid ssh csh = do
dbtIdent = "tutorials" dbtIdent = "tutorials"
dbtCsvEncode = noCsvEncode dbtCsvEncode = noCsvEncode
dbtCsvDecode = Nothing dbtCsvDecode = Nothing
dbtExtraReps = []
tutorialDBTableValidator = def tutorialDBTableValidator = def
& defaultSorting [SortAscBy "type", SortAscBy "name"] & defaultSorting [SortAscBy "type", SortAscBy "name"]

View File

@ -377,6 +377,7 @@ courseUserExamsSection (Entity cid Course{..}) (Entity uid _) = do
dbtIdent = "course-user-exams" dbtIdent = "course-user-exams"
dbtCsvEncode = noCsvEncode dbtCsvEncode = noCsvEncode
dbtCsvDecode = Nothing dbtCsvDecode = Nothing
dbtExtraReps = []
examDBTableValidator = def & defaultSorting [SortAscBy "registration-time"] examDBTableValidator = def & defaultSorting [SortAscBy "registration-time"]
postprocess :: FormResult (First ExamActionData, DBFormResult ExamId (Bool, _) _) -> FormResult (ExamActionData, Map ExamId _) postprocess :: FormResult (First ExamActionData, DBFormResult ExamId (Bool, _) _) -> FormResult (ExamActionData, Map ExamId _)
postprocess inp = do postprocess inp = do
@ -499,6 +500,7 @@ courseUserTutorialsSection (Entity cid Course{..}) (Entity uid _) = do
dbtIdent = "tutorials" dbtIdent = "tutorials"
dbtCsvEncode = noCsvEncode dbtCsvEncode = noCsvEncode
dbtCsvDecode = Nothing dbtCsvDecode = Nothing
dbtExtraReps = []
tutorialDBTableValidator = def & defaultSorting [SortAscBy "type", SortAscBy "name"] tutorialDBTableValidator = def & defaultSorting [SortAscBy "type", SortAscBy "name"]
postprocess :: FormResult (First TutorialActionData, DBFormResult TutorialParticipantId (Bool, _) _) -> FormResult (TutorialActionData, Map TutorialParticipantId _) postprocess :: FormResult (First TutorialActionData, DBFormResult TutorialParticipantId (Bool, _) _) -> FormResult (TutorialActionData, Map TutorialParticipantId _)
postprocess inp = do postprocess inp = do

View File

@ -167,10 +167,10 @@ colUserSheets shns = cap (Sortable Nothing caption) $ foldMap userSheetCol shns
data UserTableCsv = UserTableCsv data UserTableCsv = UserTableCsv
{ csvUserName :: Text { csvUserName :: UserDisplayName
, csvUserSex :: Maybe Sex , csvUserSex :: Maybe Sex
, csvUserMatriculation :: Maybe Text , csvUserMatriculation :: Maybe UserMatriculation
, csvUserEmail :: CI Email , csvUserEmail :: UserEmail
, csvUserStudyFeatures :: UserTableStudyFeatures , csvUserStudyFeatures :: UserTableStudyFeatures
, csvUserSubmissionGroup :: Maybe SubmissionGroupName , csvUserSubmissionGroup :: Maybe SubmissionGroupName
, csvUserRegistration :: UTCTime , csvUserRegistration :: UTCTime
@ -482,6 +482,7 @@ makeCourseUserTable cid acts restrict colChoices psValidator csvColumns = do
CourseUserNote{..} <- lift . lift $ getJust noteId CourseUserNote{..} <- lift . lift $ getJust noteId
return courseUserNoteNote return courseUserNoteNote
dbtCsvDecode = Nothing dbtCsvDecode = Nothing
dbtExtraReps = withCsvExtraRep (UserCsvExportData True) dbtCsvEncode []
over _1 postprocess <$> dbTable psValidator DBTable{..} over _1 postprocess <$> dbTable psValidator DBTable{..}
where where
postprocess :: FormResult (First act', DBFormResult UserId Bool UserTableData) -> FormResult (act', Set UserId) postprocess :: FormResult (First act', DBFormResult UserId Bool UserTableData) -> FormResult (act', Set UserId)

View File

@ -71,6 +71,7 @@ mkExamTable (Entity cid Course{..}) = do
dbtIdent = "exams" dbtIdent = "exams"
dbtCsvEncode = noCsvEncode dbtCsvEncode = noCsvEncode
dbtCsvDecode = Nothing dbtCsvDecode = Nothing
dbtExtraReps = []
examDBTableValidator = def examDBTableValidator = def
& defaultSorting [SortAscBy "time"] & defaultSorting [SortAscBy "time"]

View File

@ -944,6 +944,8 @@ postEUsersR tid ssh csh examn = do
[occId] -> return occId [occId] -> return occId
_other -> throwM ExamUserCsvExceptionNoMatchingOccurrence _other -> throwM ExamUserCsvExceptionNoMatchingOccurrence
dbtExtraReps = []
examUsersDBTableValidator = def & defaultSorting [SortAscBy "user-name"] examUsersDBTableValidator = def & defaultSorting [SortAscBy "user-name"]
& defaultPagesize PagesizeAll & defaultPagesize PagesizeAll

View File

@ -406,6 +406,8 @@ postEGradesR tid ssh csh examn = do
} }
dbtCsvDecode = Nothing dbtCsvDecode = Nothing
dbtExtraReps = []
examUsersDBTableValidator = def & defaultSorting [SortAscBy "is-synced", SortAscBy "user-name"] examUsersDBTableValidator = def & defaultSorting [SortAscBy "is-synced", SortAscBy "user-name"]
& defaultPagesize PagesizeAll & defaultPagesize PagesizeAll

View File

@ -249,6 +249,8 @@ getEOExamsR = do
dbtCsvEncode = noCsvEncode dbtCsvEncode = noCsvEncode
dbtCsvDecode = Nothing dbtCsvDecode = Nothing
dbtExtraReps = []
examsDBTableValidator = def examsDBTableValidator = def
& defaultSorting [SortAscBy "is-synced", SortAscBy "exam-time"] & defaultSorting [SortAscBy "is-synced", SortAscBy "exam-time"]

View File

@ -70,6 +70,7 @@ getEExamListR = do
dbtIdent = "external-exams" dbtIdent = "external-exams"
dbtCsvEncode = noCsvEncode dbtCsvEncode = noCsvEncode
dbtCsvDecode = Nothing dbtCsvDecode = Nothing
dbtExtraReps = []
examDBTableValidator = def examDBTableValidator = def
& defaultSorting [SortDescBy "term", SortAscBy "school", SortAscBy "course", SortAscBy "name"] & defaultSorting [SortDescBy "term", SortAscBy "school", SortAscBy "course", SortAscBy "name"]
& forceFilter "may-access" (Any True) & forceFilter "may-access" (Any True)

View File

@ -144,6 +144,7 @@ getMaterialListR tid ssh csh = do
, dbtFilterUI = mempty , dbtFilterUI = mempty
, dbtCsvEncode = noCsvEncode , dbtCsvEncode = noCsvEncode
, dbtCsvDecode = Nothing , dbtCsvDecode = Nothing
, dbtExtraReps = []
} }
let headingLong = prependCourseTitle tid ssh csh MsgMaterialListHeading let headingLong = prependCourseTitle tid ssh csh MsgMaterialListHeading
@ -248,6 +249,7 @@ getMShowR tid ssh csh mnm = do
] ]
, dbtCsvEncode = noCsvEncode , dbtCsvEncode = noCsvEncode
, dbtCsvDecode = Nothing , dbtCsvDecode = Nothing
, dbtExtraReps = []
} }
return (matEnt,fileTable',zipLink) return (matEnt,fileTable',zipLink)
-- File table has no filtering by access, because we assume that -- File table has no filtering by access, because we assume that

View File

@ -199,6 +199,7 @@ newsUpcomingSheets uid = do
, dbtIdent = "upcoming-sheets" :: Text , dbtIdent = "upcoming-sheets" :: Text
, dbtCsvEncode = noCsvEncode , dbtCsvEncode = noCsvEncode
, dbtCsvDecode = Nothing , dbtCsvDecode = Nothing
, dbtExtraReps = []
} }
$(widgetFile "news/upcomingSheets") $(widgetFile "news/upcomingSheets")
@ -334,6 +335,7 @@ newsUpcomingExams uid = do
dbtIdent = "exams" dbtIdent = "exams"
dbtCsvEncode = noCsvEncode dbtCsvEncode = noCsvEncode
dbtCsvDecode = Nothing dbtCsvDecode = Nothing
dbtExtraReps = []
examDBTableValidator = def examDBTableValidator = def
& defaultSorting [SortAscBy "time"] & defaultSorting [SortAscBy "time"]

View File

@ -533,6 +533,7 @@ mkOwnedCoursesTable =
dbtParams = def dbtParams = def
dbtCsvEncode = noCsvEncode dbtCsvEncode = noCsvEncode
dbtCsvDecode = Nothing dbtCsvDecode = Nothing
dbtExtraReps = []
in \uid -> let dbtSQLQuery = dbtSQLQuery' uid in (_1 %~ getAny) <$> dbTableWidget validator DBTable{..} in \uid -> let dbtSQLQuery = dbtSQLQuery' uid in (_1 %~ getAny) <$> dbTableWidget validator DBTable{..}
@ -585,6 +586,7 @@ mkEnrolledCoursesTable =
, dbtParams = def , dbtParams = def
, dbtCsvEncode = noCsvEncode , dbtCsvEncode = noCsvEncode
, dbtCsvDecode = Nothing , dbtCsvDecode = Nothing
, dbtExtraReps = []
} }
@ -665,6 +667,7 @@ mkSubmissionTable =
dbtParams = def dbtParams = def
dbtCsvEncode = noCsvEncode dbtCsvEncode = noCsvEncode
dbtCsvDecode = Nothing dbtCsvDecode = Nothing
dbtExtraReps = []
in \uid -> let dbtSQLQuery = dbtSQLQuery' uid in \uid -> let dbtSQLQuery = dbtSQLQuery' uid
dbtSorting = dbtSorting' uid dbtSorting = dbtSorting' uid
in dbTableWidget' validator DBTable{..} in dbTableWidget' validator DBTable{..}
@ -725,6 +728,7 @@ mkSubmissionGroupTable =
dbtParams = def dbtParams = def
dbtCsvEncode = noCsvEncode dbtCsvEncode = noCsvEncode
dbtCsvDecode = Nothing dbtCsvDecode = Nothing
dbtExtraReps = []
in \uid -> let dbtSQLQuery = dbtSQLQuery' uid in \uid -> let dbtSQLQuery = dbtSQLQuery' uid
in dbTableWidget' validator DBTable{..} in dbTableWidget' validator DBTable{..}
@ -800,6 +804,7 @@ mkCorrectionsTable =
dbtParams = def dbtParams = def
dbtCsvEncode = noCsvEncode dbtCsvEncode = noCsvEncode
dbtCsvDecode = Nothing dbtCsvDecode = Nothing
dbtExtraReps = []
in \uid -> let dbtSQLQuery = dbtSQLQuery' uid in \uid -> let dbtSQLQuery = dbtSQLQuery' uid
in dbTableWidget' validator DBTable{..} in dbTableWidget' validator DBTable{..}

View File

@ -43,6 +43,8 @@ getSchoolListR = do
dbtCsvEncode = noCsvEncode dbtCsvEncode = noCsvEncode
dbtCsvDecode = Nothing dbtCsvDecode = Nothing
dbtExtraReps = []
dbtIdent :: Text dbtIdent :: Text
dbtIdent = "schools" dbtIdent = "schools"

View File

@ -174,6 +174,7 @@ getSheetListR tid ssh csh = do
, dbtIdent = "sheets" :: Text , dbtIdent = "sheets" :: Text
, dbtCsvEncode = noCsvEncode , dbtCsvEncode = noCsvEncode
, dbtCsvDecode = Nothing , dbtCsvDecode = Nothing
, dbtExtraReps = []
} }
-- ) ( -- !!!DEPRECTAED!!! Summary only over shown rows !!! -- ) ( -- !!!DEPRECTAED!!! Summary only over shown rows !!!
-- -- Collect summary over all Sheets, not just the ones shown due to pagination: -- -- Collect summary over all Sheets, not just the ones shown due to pagination:

View File

@ -97,6 +97,7 @@ getSShowR tid ssh csh shn = do
, dbtParams = def , dbtParams = def
, dbtCsvEncode = noCsvEncode , dbtCsvEncode = noCsvEncode
, dbtCsvDecode = Nothing , dbtCsvDecode = Nothing
, dbtExtraReps = []
} }
(hasHints, hasSolution) <- runDB $ do (hasHints, hasSolution) <- runDB $ do
hasHints <- E.selectExists . E.from $ \sheet' -> hasHints <- E.selectExists . E.from $ \sheet' ->

View File

@ -536,6 +536,7 @@ submissionHelper tid ssh csh shn mcid = do
, dbtParams = def , dbtParams = def
, dbtCsvEncode = noCsvEncode , dbtCsvEncode = noCsvEncode
, dbtCsvDecode = Nothing , dbtCsvDecode = Nothing
, dbtExtraReps = []
} }
mFileTable <- traverse (runDB . dbTableWidget' def) . fmap smid2ArchiveTable $ (,) <$> msmid <*> mcid mFileTable <- traverse (runDB . dbTableWidget' def) . fmap smid2ArchiveTable $ (,) <$> msmid <*> mcid

View File

@ -409,6 +409,7 @@ makeCorrectionsTable whereClause dbtColonnade dbtFilterUI psValidator dbtParams
, dbtIdent = "corrections" :: Text , dbtIdent = "corrections" :: Text
, dbtCsvEncode = noCsvEncode , dbtCsvEncode = noCsvEncode
, dbtCsvDecode = Nothing , dbtCsvDecode = Nothing
, dbtExtraReps = []
} }
data ActionCorrections = CorrDownload data ActionCorrections = CorrDownload

View File

@ -249,6 +249,7 @@ postMessageListR = do
, dbtIdent = "messages" :: Text , dbtIdent = "messages" :: Text
, dbtCsvEncode = noCsvEncode , dbtCsvEncode = noCsvEncode
, dbtCsvDecode = Nothing , dbtCsvDecode = Nothing
, dbtExtraReps = []
} }
let tableRes = tableRes' <&> _2 %~ Map.keysSet . Map.filter id . getDBFormResult (const False) let tableRes = tableRes' <&> _2 %~ Map.keysSet . Map.filter id . getDBFormResult (const False)

View File

@ -154,6 +154,7 @@ getTermShowR = do
dbtIdent = "terms" :: Text dbtIdent = "terms" :: Text
dbtCsvEncode = noCsvEncode dbtCsvEncode = noCsvEncode
dbtCsvDecode = Nothing dbtCsvDecode = Nothing
dbtExtraReps = []
termDBTableValidator = def & defaultSorting [SortDescBy "term-id"] termDBTableValidator = def & defaultSorting [SortDescBy "term-id"]
in dbTableWidget' termDBTableValidator termDBTable in dbTableWidget' termDBTableValidator termDBTable
defaultLayout $ do defaultLayout $ do

View File

@ -91,6 +91,7 @@ getCTutorialListR tid ssh csh = do
dbtIdent = "tutorials" dbtIdent = "tutorials"
dbtCsvEncode = noCsvEncode dbtCsvEncode = noCsvEncode
dbtCsvDecode = Nothing dbtCsvDecode = Nothing
dbtExtraReps = []
tutorialDBTableValidator = def tutorialDBTableValidator = def
& defaultSorting [SortAscBy "type", SortAscBy "name"] & defaultSorting [SortAscBy "type", SortAscBy "name"]

View File

@ -215,6 +215,7 @@ postUsersR = do
, dbtIdent = "users" :: Text , dbtIdent = "users" :: Text
, dbtCsvEncode = noCsvEncode , dbtCsvEncode = noCsvEncode
, dbtCsvDecode = Nothing , dbtCsvDecode = Nothing
, dbtExtraReps = []
} }
formResult usersRes $ \case formResult usersRes $ \case

View File

@ -529,6 +529,7 @@ makeExternalExamUsersTable mode (Entity eeId ExternalExam{..}) = do
, GuessUserFirstName <$> csvEUserFirstName , GuessUserFirstName <$> csvEUserFirstName
] ]
maybe (throwM ExamUserCsvExceptionNoMatchingUser) return =<< guessUser criteria (Just 1) -- we're only interested in at most one match maybe (throwM ExamUserCsvExceptionNoMatchingUser) return =<< guessUser criteria (Just 1) -- we're only interested in at most one match
dbtExtraReps = []
externalExamUsersDBTableValidator = def externalExamUsersDBTableValidator = def
& defaultSorting (bool id (SortAscBy "is-synced" :) (mode == EEUMGrades) [SortAscBy "user-name"]) & defaultSorting (bool id (SortAscBy "is-synced" :) (mode == EEUMGrades) [SortAscBy "user-name"])
& defaultPagesize PagesizeAll & defaultPagesize PagesizeAll

View File

@ -13,9 +13,10 @@ module Handler.Utils.Table.Pagination
, module Handler.Utils.Table.Pagination.CsvColumnExplanations , module Handler.Utils.Table.Pagination.CsvColumnExplanations
, DBCsvActionMode(..) , DBCsvActionMode(..)
, DBCsvDiff(..), _DBCsvDiffNew, _DBCsvDiffExisting, _DBCsvDiffMissing, _dbCsvOldKey, _dbCsvOld, _dbCsvNewKey, _dbCsvNew , DBCsvDiff(..), _DBCsvDiffNew, _DBCsvDiffExisting, _DBCsvDiffMissing, _dbCsvOldKey, _dbCsvOld, _dbCsvNewKey, _dbCsvNew
, DBTCsvEncode(..), DBTCsvDecode(..) , DBTCsvEncode(..), DBTCsvDecode(..), DBTExtraRep(..)
, DBTable(..), DBFilterUI, IsDBTable(..), DBCell(..) , DBTable(..), DBFilterUI, IsDBTable(..), DBCell(..)
, noCsvEncode, simpleCsvEncode, simpleCsvEncodeM , noCsvEncode, simpleCsvEncode, simpleCsvEncodeM
, withCsvExtraRep
, singletonFilter , singletonFilter
, DBParams(..) , DBParams(..)
, cellAttrs, cellContents , cellAttrs, cellContents
@ -120,6 +121,8 @@ import qualified Data.Csv as Csv
import Jobs.Queue import Jobs.Queue
import Data.Typeable (eqT)
#if MIN_VERSION_base(4,11,0) #if MIN_VERSION_base(4,11,0)
type Monoid' = Monoid type Monoid' = Monoid
@ -585,11 +588,17 @@ data DBTCsvEncode r' k' csv = forall exportData.
) => DBTCsvEncode ) => DBTCsvEncode
{ dbtCsvExportForm :: AForm DB exportData { dbtCsvExportForm :: AForm DB exportData
, dbtCsvHeader :: Maybe exportData -> DB Csv.Header -- ^ @exportData@ is @Nothing@, if we're reporting an error or exporting example data , dbtCsvHeader :: Maybe exportData -> DB Csv.Header -- ^ @exportData@ is @Nothing@, if we're reporting an error or exporting example data
, dbtCsvExampleData :: Maybe [csv] , dbtCsvExampleData :: Maybe [csv]
, dbtCsvDoEncode :: exportData -> ConduitT (k', r') csv DB () , dbtCsvDoEncode :: exportData -> ConduitT (k', r') csv DB ()
, dbtCsvName :: FilePath , dbtCsvName :: FilePath
, dbtCsvNoExportData :: Maybe (AnIso' exportData ()) , dbtCsvNoExportData :: Maybe (AnIso' exportData ())
} }
data DBTExtraRep r' k' = forall rep.
( HasContentType rep
, DBTableKey k'
) => DBTExtraRep
{ dbtERepDoEncode :: ConduitT (k', r') Void DB rep
}
data DBTCsvDecode r' k' csv = forall route csvAction csvActionClass csvException. data DBTCsvDecode r' k' csv = forall route csvAction csvActionClass csvException.
( FromNamedRecord csv, ToNamedRecord csv ( FromNamedRecord csv, ToNamedRecord csv
, DBTableKey k' , DBTableKey k'
@ -628,6 +637,7 @@ data DBTable m x = forall a r r' h i t k k' csv colonnade (p :: Pillar).
, dbtParams :: DBParams m x , dbtParams :: DBParams m x
, dbtCsvEncode :: Maybe (DBTCsvEncode r' k' csv) , dbtCsvEncode :: Maybe (DBTCsvEncode r' k' csv)
, dbtCsvDecode :: Maybe (DBTCsvDecode r' k' csv) , dbtCsvDecode :: Maybe (DBTCsvDecode r' k' csv)
, dbtExtraReps :: [DBTExtraRep r' k']
, dbtIdent :: i , dbtIdent :: i
} }
@ -666,6 +676,19 @@ simpleCsvEncodeM fName f = Just DBTCsvEncode
, dbtCsvExampleData = Nothing , dbtCsvExampleData = Nothing
} }
withCsvExtraRep :: forall exportData csv r' k'.
Typeable exportData
=> exportData
-> Maybe (DBTCsvEncode r' k' csv)
-> [DBTExtraRep r' k'] -> [DBTExtraRep r' k']
withCsvExtraRep exportData mEncode = maybe id (flip snoc) csvExtraRep
where csvExtraRep = do
DBTCsvEncode{ dbtCsvNoExportData = (_ :: Maybe (AnIso' exportData' ())), .. } <- mEncode
Refl <- eqT @exportData @exportData'
return DBTExtraRep
{ dbtERepDoEncode = toCsvRendered <$> lift (dbtCsvHeader $ Just exportData) <*> (dbtCsvDoEncode exportData .| C.foldMap (pure @[]))
}
class (MonadHandler m, HandlerSite m ~ UniWorX, Monoid' x, Monoid' (DBCell m x), Default (DBParams m x)) => IsDBTable (m :: Type -> Type) (x :: Type) where class (MonadHandler m, HandlerSite m ~ UniWorX, Monoid' x, Monoid' (DBCell m x), Default (DBParams m x)) => IsDBTable (m :: Type -> Type) (x :: Type) where
data DBParams m x :: Type data DBParams m x :: Type
@ -1262,6 +1285,28 @@ dbTable PSValidator{..} dbtable@DBTable{ dbtIdent = dbtIdent'@(toPathPiece -> db
] ]
_other -> return () _other -> return ()
let extraReps = maybe id (flip snoc) csvRep dbtExtraReps
where csvRep = do
DBTCsvEncode{..} <- dbtCsvEncode
noExportData' <- cloneIso <$> dbtCsvNoExportData
let exportData = noExportData' # ()
return DBTExtraRep
{ dbtERepDoEncode = toCsvRendered <$> lift (dbtCsvHeader $ Just exportData) <*> (dbtCsvDoEncode exportData .| C.foldMap (pure @[]))
}
extraReps' = (typeHtml, Nothing) : map ((,) <$> (\DBTExtraRep{..} -> getContentType dbtERepDoEncode) <*> Just) extraReps
doAltRep = maybe True (== dbtIdent) <$> lookupGlobalGetParam GetSelectTable
maybeT (return ()) $ do
guardM doAltRep
cts <- reqAccept <$> getRequest
altRep <- hoistMaybe <=< asum $ do
mRep <- hoistMaybe . selectRep' extraReps' =<< cts
return . return $ mRep <&> \DBTExtraRep{..} -> fmap toTypedContent . runConduit $ C.sourceList (zip currentKeys rows) .| dbtERepDoEncode
lift $ sendResponse =<< altRep
let let
rowCount rowCount
| selectPagesize = fromMaybe 0 $ rows' ^? _head . _1 . _Value | selectPagesize = fromMaybe 0 $ rows' ^? _head . _1 . _Value
@ -1706,4 +1751,4 @@ cap' (view _Cornice -> cornice) = case cornice of
| otherwise = (_Rowspan # (), "2") : filter (hasn't $ _1 . _Rowspan) attrs | otherwise = (_Rowspan # (), "2") : filter (hasn't $ _1 . _Rowspan) attrs
_Rowspan :: Prism' Text () _Rowspan :: Prism' Text ()
_Rowspan = prism' (\() -> "rowspan") $ flip guardOn () . ((==) `on` CI.mk) "rowspan" _Rowspan = nearly <$> id <*> ((==) `on` CI.mk) $ "rowspan"

View File

@ -2,6 +2,7 @@ module Handler.Utils.Workflow.Workflow
( ensureScope ( ensureScope
, followEdge , followEdge
, followAutomaticEdges, WorkflowAutomaticEdgeException(..) , followAutomaticEdges, WorkflowAutomaticEdgeException(..)
, sourceWorkflowActionInfos
, module Handler.Utils.Workflow.Restriction , module Handler.Utils.Workflow.Restriction
) where ) where
@ -14,6 +15,8 @@ import Handler.Utils.Workflow.Restriction
import qualified Data.Set as Set import qualified Data.Set as Set
import qualified Data.Map as Map import qualified Data.Map as Map
import qualified Data.Conduit.Combinators as C
ensureScope :: IdWorkflowScope -> CryptoFileNameWorkflowWorkflow -> MaybeT DB WorkflowWorkflowId ensureScope :: IdWorkflowScope -> CryptoFileNameWorkflowWorkflow -> MaybeT DB WorkflowWorkflowId
ensureScope wiScope cID = do ensureScope wiScope cID = do
@ -75,3 +78,22 @@ followAutomaticEdges WorkflowGraph{..} = go []
return (edgeLbl, nodeLbl) return (edgeLbl, nodeLbl)
filledPayloads = Map.keysSet . Map.filter (not . Set.null) $ workflowStateCurrentPayloads history filledPayloads = Map.keysSet . Map.filter (not . Set.null) $ workflowStateCurrentPayloads history
edgeDecisionInput = (cState, filledPayloads) edgeDecisionInput = (cState, filledPayloads)
sourceWorkflowActionInfos
:: forall backend m.
( MonadHandler m, HandlerSite m ~ UniWorX
, BackendCompatible SqlReadBackend backend
, MonadCrypto m, MonadCryptoKey m ~ CryptoIDKey
, MonadCatch m
)
=> WorkflowWorkflowId
-> WorkflowState FileReference UserId
-> ConduitT () (WorkflowActionInfo FileReference UserId) (ReaderT backend m) ()
-- ^ Does `mayViewWorkflowAction`
sourceWorkflowActionInfos wwId wState = do
mAuthId <- maybeAuthId
let authCheck WorkflowActionInfo{..}
= mayViewWorkflowAction mAuthId wwId waiAction
yieldMany (workflowActionInfos wState) .| C.filterM authCheck

View File

@ -131,6 +131,7 @@ getAdminWorkflowDefinitionListR = do
dbtIdent = "workflow-definitions" dbtIdent = "workflow-definitions"
dbtCsvEncode = noCsvEncode dbtCsvEncode = noCsvEncode
dbtCsvDecode = Nothing dbtCsvDecode = Nothing
dbtExtraReps = []
workflowDefinitionsDBTableValidator = def workflowDefinitionsDBTableValidator = def
& defaultPagesize PagesizeAll & defaultPagesize PagesizeAll
& defaultSorting [SortAscBy "scope", SortAscBy "name"] & defaultSorting [SortAscBy "scope", SortAscBy "name"]

View File

@ -118,6 +118,7 @@ getAdminWorkflowInstanceListR = do
dbtIdent = "workflow-instances" dbtIdent = "workflow-instances"
dbtCsvEncode = noCsvEncode dbtCsvEncode = noCsvEncode
dbtCsvDecode = Nothing dbtCsvDecode = Nothing
dbtExtraReps = []
workflowInstancesDBTableValidator = def workflowInstancesDBTableValidator = def
& defaultSorting [SortAscBy "scope", SortAscBy "name"] & defaultSorting [SortAscBy "scope", SortAscBy "name"]
in dbTableDB' workflowInstancesDBTableValidator workflowInstancesDBTable in dbTableDB' workflowInstancesDBTableValidator workflowInstancesDBTable

View File

@ -11,9 +11,10 @@ module Handler.Workflow.Workflow.List
, getTopWorkflowWorkflowListR , getTopWorkflowWorkflowListR
) where ) where
import Import import Import hiding (Last(..), WriterT)
import Utils.Workflow import Utils.Workflow
import Handler.Utils.Workflow.Workflow
import Handler.Utils.Workflow.CanonicalRoute import Handler.Utils.Workflow.CanonicalRoute
import Handler.Workflow.Workflow.Workflow (WorkflowHistoryItemActor'(..), WorkflowHistoryItemActor) import Handler.Workflow.Workflow.Workflow (WorkflowHistoryItemActor'(..), WorkflowHistoryItemActor)
@ -28,6 +29,13 @@ import qualified Data.CaseInsensitive as CI
import qualified Data.Set as Set import qualified Data.Set as Set
import qualified Data.Map as Map import qualified Data.Map as Map
import qualified Data.Conduit.Combinators as C
import Data.Semigroup (Last(..))
import qualified Data.Monoid as Monoid (Last(..))
import Control.Monad.Trans.Writer.Strict (WriterT)
getGlobalWorkflowWorkflowListR :: Handler Html getGlobalWorkflowWorkflowListR :: Handler Html
getGlobalWorkflowWorkflowListR = workflowWorkflowListR WSGlobal getGlobalWorkflowWorkflowListR = workflowWorkflowListR WSGlobal
@ -115,14 +123,49 @@ type WorkflowWorkflowActionData = ( Maybe Text
, Maybe Icon , Maybe Icon
) )
data JsonWorkflowWorkflow = JsonWorkflowWorkflow
{ jwwScope :: Maybe RouteWorkflowScope
, jwwInstance :: Maybe JsonWorkflowInstance
, jwwLastAction :: Maybe JsonWorkflowAction
, jwwPayload :: Map WorkflowPayloadLabel JsonWorkflowPayload
} deriving (Generic)
data JsonWorkflowAction = JsonWorkflowAction
{ jwaIx :: CryptoUUIDWorkflowStateIndex
, jwaTo :: Maybe WorkflowGraphNodeLabel
, jwaUser :: Maybe JsonWorkflowUser
, jwaTime :: UTCTime
} deriving (Generic)
data JsonWorkflowInstance = JsonWorkflowInstance
{ jwiScope :: RouteWorkflowScope
, jwiName :: WorkflowInstanceName
} deriving (Generic)
data JsonWorkflowPayload = JsonWorkflowPayload
{ jwpPayload :: [WorkflowFieldPayloadW Void JsonWorkflowUser]
, jwpHasFiles :: Bool
} deriving (Generic)
data JsonWorkflowUser
= JsonWorkflowUserUser
{ jwuDisplayName :: UserDisplayName
, jwuMatriculation :: Maybe UserMatriculation
, jwuDisplayEmail :: UserEmail
}
| JsonWorkflowUserAnonymous
| JsonWorkflowUserHidden
| JsonWorkflowUserGone
deriving (Generic)
resultWorkflowWorkflowId :: Lens' WorkflowWorkflowData CryptoFileNameWorkflowWorkflow resultWorkflowWorkflowId :: Lens' WorkflowWorkflowData CryptoFileNameWorkflowWorkflow
resultWorkflowWorkflowId = _dbrOutput . _1 resultWorkflowWorkflowId = _dbrOutput . _1
resultRouteScope :: Lens' WorkflowWorkflowData (Maybe RouteWorkflowScope) resultRouteScope :: Lens' WorkflowWorkflowData (Maybe RouteWorkflowScope)
resultRouteScope = _dbrOutput . _2 resultRouteScope = _dbrOutput . _2
_resultWorkflowWorkflow :: Lens' WorkflowWorkflowData (Entity WorkflowWorkflow) resultWorkflowWorkflow :: Lens' WorkflowWorkflowData (Entity WorkflowWorkflow)
_resultWorkflowWorkflow = _dbrOutput . _3 resultWorkflowWorkflow = _dbrOutput . _3
resultWorkflowInstance :: Lens' WorkflowWorkflowData (Maybe (Entity WorkflowInstance)) resultWorkflowInstance :: Lens' WorkflowWorkflowData (Maybe (Entity WorkflowInstance))
resultWorkflowInstance = _dbrOutput . _4 resultWorkflowInstance = _dbrOutput . _4
@ -288,7 +331,7 @@ workflowWorkflowList (title, heading) WWListColumns{..} sqlPred = do
, singletonMap "current-state" . FilterProjected $ \x (criteria :: Set Text) -> , singletonMap "current-state" . FilterProjected $ \x (criteria :: Set Text) ->
let criteria' = map CI.mk . unpack <$> Set.toList criteria let criteria' = map CI.mk . unpack <$> Set.toList criteria
in maybe False (\cSt -> any (`isInfixOf` cSt) criteria') $ x ^? resultLastAction . _Just . actionTo . _Just . to (map CI.mk . unpack) in maybe False (\cSt -> any (`isInfixOf` cSt) criteria') $ x ^? resultLastAction . _Just . actionTo . _Just . to (map CI.mk . unpack)
, singletonMap "final" . FilterProjected $ \x (criterion :: Last Bool) -> case getLast criterion of , singletonMap "final" . FilterProjected $ \x (criterion :: Monoid.Last Bool) -> case Monoid.getLast criterion of
Nothing -> True Nothing -> True
Just needle -> let val = has (resultLastAction . _Just . actionTo . _Just) x Just needle -> let val = has (resultLastAction . _Just . actionTo . _Just) x
&& has (resultLastAction . _Just . actionFinal . _Just) x && has (resultLastAction . _Just . actionFinal . _Just) x
@ -306,6 +349,78 @@ workflowWorkflowList (title, heading) WWListColumns{..} sqlPred = do
dbtIdent = "workflow-workflows" dbtIdent = "workflow-workflows"
dbtCsvEncode = noCsvEncode dbtCsvEncode = noCsvEncode
dbtCsvDecode = Nothing dbtCsvDecode = Nothing
dbtExtraReps = [ DBTExtraRep $ toPrettyJSON <$> repWorkflowWorkflowJson, DBTExtraRep $ toYAML <$> repWorkflowWorkflowJson ]
repWorkflowWorkflowJson :: ConduitT (E.Value WorkflowWorkflowId, WorkflowWorkflowData) Void DB (Map CryptoFileNameWorkflowWorkflow JsonWorkflowWorkflow)
repWorkflowWorkflowJson = C.foldMapM $ \(E.Value wwId, res) -> do
cID <- encrypt wwId
Map.singleton cID <$> do
let jwwScope = guardOnM wwListColumnScope $ res ^. resultRouteScope
jwwInstance <- fmap join . for (guardOnM wwListColumnInstance $ res ^. resultWorkflowInstance) $ \(Entity _ WorkflowInstance{..}) -> runMaybeT $ do
jwiScope <- toRouteWorkflowScope $ _DBWorkflowScope # workflowInstanceScope
let jwiName = workflowInstanceName
return JsonWorkflowInstance{..}
(fmap getLast -> wState) <-
let go :: forall m.
( MonadHandler m
, HandlerSite m ~ UniWorX
, MonadCatch m
)
=> WorkflowActionInfo FileReference UserId
-> WriterT (Maybe (Last (CryptoUUIDWorkflowStateIndex, Maybe WorkflowGraphNodeLabel, Maybe JsonWorkflowUser, UTCTime, Map WorkflowPayloadLabel JsonWorkflowPayload))) (SqlPersistT m) ()
go WorkflowActionInfo{ waiIx = stIx, waiHistory = (workflowStateCurrentPayloads -> currentPayload), waiAction = WorkflowAction{..}} = maybeT (return ()) $ do
stCID <- encryptWorkflowStateIndex wwId stIx
rScope <- hoistMaybe $ res ^. resultRouteScope
let toJsonUser (Just (Entity _ User{..})) = JsonWorkflowUserUser
{ jwuDisplayName = userDisplayName
, jwuMatriculation = userMatrikelnummer
, jwuDisplayEmail = userDisplayEmail
}
toJsonUser Nothing = JsonWorkflowUserGone
mVia = Map.lookup wpVia . wgnEdges =<< Map.lookup wpTo wgNodes
hasWorkflowRole' role = $cachedHereBinary (rScope, wwId, role) . lift . lift $ is _Authorized <$> hasWorkflowRole (Just wwId) role canonRoute False
canonRoute = _WorkflowScopeRoute # (rScope, WorkflowWorkflowR cID WWWorkflowR)
aUser <- for wpUser $ \wpUser' -> lift . maybeT (return JsonWorkflowUserHidden) $ do
viewActors <- hoistMaybe $ preview _wgeViewActor =<< mVia
guardM $ anyM (otoList viewActors) hasWorkflowRole'
resUser <- lift . lift $ traverse getEntity wpUser'
return $ case resUser of
Just mEnt -> toJsonUser mEnt
Nothing -> JsonWorkflowUserAnonymous
payload <- do
payload' <- fmap Map.fromList . forMaybeM (Map.toList currentPayload) $ \x@(payloadLbl, _) -> x <$ do
WorkflowPayloadView{..} <- hoistMaybe . Map.lookup payloadLbl $ Map.findWithDefault Map.empty wpTo (wgnPayloadView <$> wgNodes)
guardM . $cachedHereBinary payloadLbl . anyM (otoList wpvViewers) $ lift . hasWorkflowRole'
forM payload' $ \(otoList -> payloads) -> fmap (uncurry JsonWorkflowPayload . over _2 getAny) . execWriterT @_ @(_, Any) . forM_ payloads $ \case
WorkflowFieldPayloadW (WFPText t ) -> tell . (, mempty) . pure $ WorkflowFieldPayloadW (WFPText t)
WorkflowFieldPayloadW (WFPNumber n ) -> tell . (, mempty) . pure $ WorkflowFieldPayloadW (WFPNumber n)
WorkflowFieldPayloadW (WFPBool b ) -> tell . (, mempty) . pure $ WorkflowFieldPayloadW (WFPBool b)
WorkflowFieldPayloadW (WFPDay d ) -> tell . (, mempty) . pure $ WorkflowFieldPayloadW (WFPDay d)
WorkflowFieldPayloadW (WFPFile _ ) -> tell (mempty, Any True)
WorkflowFieldPayloadW (WFPUser uid) -> tell . (, mempty) . pure . review (_WorkflowFieldPayloadW . _WorkflowFieldPayload) . toJsonUser =<< lift (lift . lift $ getEntity uid)
nTo <- runMaybeT $ do
WGN{..} <- hoistMaybe $ Map.lookup wpTo wgNodes
WorkflowNodeView{..} <- hoistMaybe wgnViewers
guardM . lift $ anyM (otoList wnvViewers) hasWorkflowRole'
return wpTo
tell . Just $ Last (stCID, nTo, aUser, wpTime, payload)
Entity _ WorkflowWorkflow{..} = res ^. resultWorkflowWorkflow
wState = review _DBWorkflowState workflowWorkflowState
WorkflowGraph{..} = _DBWorkflowGraph # workflowWorkflowGraph
in runConduit $ sourceWorkflowActionInfos wwId wState .| execWriterC (C.mapM_ go)
let jwwLastAction = wState <&> \(jwaIx, jwaTo, jwaUser, jwaTime, _) -> JsonWorkflowAction{..}
jwwPayload = wState ^. _Just . _5
return JsonWorkflowWorkflow{..}
workflowWorkflowDBTableValidator = def workflowWorkflowDBTableValidator = def
& defaultSorting defSort & defaultSorting defSort
& forceFilter "may-access" (Any True) & forceFilter "may-access" (Any True)
@ -317,3 +432,24 @@ workflowWorkflowList (title, heading) WWListColumns{..} sqlPred = do
siteLayoutMsg heading $ do siteLayoutMsg heading $ do
setTitleI title setTitleI title
$(widgetFile "workflows/workflow-list") $(widgetFile "workflows/workflow-list")
deriveJSON defaultOptions
{ fieldLabelModifier = camelToPathPiece' 1
} ''JsonWorkflowWorkflow
deriveJSON defaultOptions
{ fieldLabelModifier = camelToPathPiece' 1
} ''JsonWorkflowAction
deriveJSON defaultOptions
{ fieldLabelModifier = camelToPathPiece' 1
} ''JsonWorkflowInstance
deriveJSON defaultOptions
{ fieldLabelModifier = camelToPathPiece' 1
} ''JsonWorkflowPayload
deriveJSON defaultOptions
{ constructorTagModifier = camelToPathPiece' 3
, fieldLabelModifier = camelToPathPiece' 1
} ''JsonWorkflowUser

View File

@ -22,7 +22,7 @@ import qualified Data.Set as Set
import qualified Data.Sequence as Seq import qualified Data.Sequence as Seq
import qualified Control.Monad.State.Class as State import qualified Control.Monad.State.Class as State
import Control.Monad.Trans.RWS.Strict (RWST, execRWST) import Control.Monad.Trans.RWS.Strict (RWST)
import qualified Crypto.Saltine.Class as Saltine import qualified Crypto.Saltine.Class as Saltine
import qualified Data.Binary as Binary import qualified Data.Binary as Binary
@ -36,7 +36,7 @@ import qualified Data.Scientific as Scientific
import Text.Blaze (toMarkup) import Text.Blaze (toMarkup)
import Data.Void (absurd) import Data.Void (absurd)
import Data.List (inits) import qualified Data.Conduit.Combinators as C
data WorkflowHistoryItemActor' user = WHIASelf | WHIAOther (Maybe user) | WHIAHidden | WHIAGone data WorkflowHistoryItemActor' user = WHIASelf | WHIAOther (Maybe user) | WHIAHidden | WHIAGone
@ -108,14 +108,10 @@ workflowR rScope cID = do
, HandlerSite m ~ UniWorX , HandlerSite m ~ UniWorX
, MonadCatch m , MonadCatch m
) )
=> WorkflowStateIndex => WorkflowActionInfo FileReference UserId
-> Maybe WorkflowGraphNodeLabel
-> [WorkflowAction FileReference UserId]
-> WorkflowAction FileReference UserId
-> RWST () (Maybe (Last WorkflowCurrentState), [WorkflowHistoryItem]) (Map WorkflowPayloadLabel (Set (WorkflowFieldPayloadW FileReference UserId))) (SqlPersistT m) () -> RWST () (Maybe (Last WorkflowCurrentState), [WorkflowHistoryItem]) (Map WorkflowPayloadLabel (Set (WorkflowFieldPayloadW FileReference UserId))) (SqlPersistT m) ()
go stIx wpFrom history@(workflowStateCurrentPayloads -> currentPayload) act@WorkflowAction{..} = maybeT (return ()) $ do go WorkflowActionInfo{ waiIx = stIx, waiFrom = wpFrom, waiHistory = history@(workflowStateCurrentPayloads -> currentPayload), waiAction = WorkflowAction{..} } = maybeT (return ()) $ do
mAuthId <- maybeAuthId mAuthId <- maybeAuthId
guardM . lift . lift . hoist liftHandler $ mayViewWorkflowAction mAuthId wwId act
stCID <- encryptWorkflowStateIndex wwId stIx stCID <- encryptWorkflowStateIndex wwId stIx
let nodeView nodeLbl = do let nodeView nodeLbl = do
@ -160,32 +156,18 @@ workflowR rScope cID = do
payloadSort :: WorkflowFieldPayloadW Void (Maybe (Entity User)) payloadSort :: WorkflowFieldPayloadW Void (Maybe (Entity User))
-> WorkflowFieldPayloadW Void (Maybe (Entity User)) -> WorkflowFieldPayloadW Void (Maybe (Entity User))
-> Ordering -> Ordering
payloadSort (WorkflowFieldPayloadW a) (WorkflowFieldPayloadW b) = case (a, b) of payloadSort = workflowPayloadSort ordFiles ordUsers
(WFPFile a', _ ) -> absurd a' where
(_, WFPFile a' ) -> absurd a' ordFiles = absurd
(WFPText a', WFPText b' ) -> compareUnicode a' b' ordUsers a' b' = case (a', b') of
(WFPText{}, _ ) -> LT (Nothing, _) -> GT
(WFPNumber a', WFPNumber b') -> compare a' b' (_, Nothing) -> LT
(WFPNumber{}, WFPText{} ) -> GT (Just (Entity _ uA), Just (Entity _ uB))
(WFPNumber{}, _ ) -> LT -> (compareUnicode `on` userSurname) uA uB
(WFPBool a', WFPBool b' ) -> compare a' b' <> (compareUnicode `on` userDisplayName) uA uB
(WFPBool{}, WFPText{} ) -> GT <> comparing userIdent uA uB
(WFPBool{}, WFPNumber{} ) -> GT
(WFPBool{}, _ ) -> LT forM payload' $ \(lblText, (otoList -> payloads, fRoute)) -> fmap ((lblText, ) . over _1 (sortBy payloadSort)) . mapMOf _2 (traverse toTextUrl . bool Nothing (Just fRoute) . getAny) <=< execWriterT @_ @(_, Any) . forM_ payloads $ \case
(WFPDay a', WFPDay b' ) -> compare a' b'
(WFPDay{}, WFPText{} ) -> GT
(WFPDay{}, WFPNumber{} ) -> GT
(WFPDay{}, WFPBool{} ) -> GT
(WFPDay{}, _ ) -> LT
(WFPUser a', WFPUser b' ) -> case (a', b') of
(Nothing, _) -> GT
(_, Nothing) -> LT
(Just (Entity _ uA), Just (Entity _ uB))
-> (compareUnicode `on` userSurname) uA uB
<> (compareUnicode `on` userDisplayName) uA uB
<> comparing userIdent uA uB
(WFPUser{}, _ ) -> GT
forM payload' $ \(lblText, (otoList -> payloads, fRoute)) -> fmap ((lblText, ) . over _1 (sortBy payloadSort)) . mapMOf _2 (traverse toTextUrl . bool Nothing (Just fRoute) . getAny) <=< execWriterT @_ @(_, Any). forM_ payloads $ \case
WorkflowFieldPayloadW (WFPText t ) -> tell . (, mempty) . pure $ WorkflowFieldPayloadW (WFPText t) WorkflowFieldPayloadW (WFPText t ) -> tell . (, mempty) . pure $ WorkflowFieldPayloadW (WFPText t)
WorkflowFieldPayloadW (WFPNumber n ) -> tell . (, mempty) . pure $ WorkflowFieldPayloadW (WFPNumber n) WorkflowFieldPayloadW (WFPNumber n ) -> tell . (, mempty) . pure $ WorkflowFieldPayloadW (WFPNumber n)
WorkflowFieldPayloadW (WFPBool b ) -> tell . (, mempty) . pure $ WorkflowFieldPayloadW (WFPBool b) WorkflowFieldPayloadW (WFPBool b ) -> tell . (, mempty) . pure $ WorkflowFieldPayloadW (WFPBool b)
@ -215,14 +197,8 @@ workflowR rScope cID = do
, pure WorkflowHistoryItem{..} , pure WorkflowHistoryItem{..}
) )
WorkflowGraph{..} = wGraph WorkflowGraph{..} = wGraph
wState = otoList $ review _DBWorkflowState workflowWorkflowState wState = review _DBWorkflowState workflowWorkflowState
in fmap (over _2 (sortOn (Down . whiTime) . reverse) . view _2) . (\act -> execRWST act () Map.empty) $ sequence_ in fmap (over _2 (sortOn (Down . whiTime) . reverse) . view _2) . runConduit $ sourceWorkflowActionInfos wwId wState .| execRWSC () Map.empty (C.mapM_ go)
[ go stIx fromSt payload act
| fromSt <- Nothing : map (Just . wpTo) wState
| act <- wState
| stIx <- [minBound..]
| payload <- tailEx $ inits wState
]
return (mEdge, (workflowState, workflowHistory)) return (mEdge, (workflowState, workflowHistory))
sequenceOf_ (_Just . _1 . _1 . _Just) mEdge sequenceOf_ (_Just . _1 . _1 . _Just) mEdge

View File

@ -22,8 +22,10 @@ module Model.Types.Workflow
, WorkflowPayloadLabel(..) , WorkflowPayloadLabel(..)
, WorkflowStateIndex(..), workflowStateIndex, workflowStateSection , WorkflowStateIndex(..), workflowStateIndex, workflowStateSection
, WorkflowState , WorkflowState
, WorkflowActionInfo(..), workflowActionInfos
, WorkflowAction(..), _wpTo, _wpVia, _wpPayload, _wpUser, _wpTime , WorkflowAction(..), _wpTo, _wpVia, _wpPayload, _wpUser, _wpTime
, WorkflowFieldPayloadW(..), _WorkflowFieldPayloadW, IsWorkflowFieldPayload', IsWorkflowFieldPayload , WorkflowFieldPayloadW(..), _WorkflowFieldPayloadW, IsWorkflowFieldPayload', IsWorkflowFieldPayload
, workflowPayloadSort
, WorkflowFieldPayload(..), _WorkflowFieldPayload , WorkflowFieldPayload(..), _WorkflowFieldPayload
, workflowStatePayload, workflowStateCurrentPayloads , workflowStatePayload, workflowStateCurrentPayloads
, WorkflowChildren , WorkflowChildren
@ -59,6 +61,10 @@ import Unsafe.Coerce
import Utils.Lens.TH import Utils.Lens.TH
import Data.List (inits)
import Data.RFC5051 (compareUnicode)
----- WORKFLOW GRAPH ----- ----- WORKFLOW GRAPH -----
@ -364,6 +370,23 @@ data WorkflowAction fileid userid = WorkflowAction
} }
deriving (Eq, Ord, Show, Generic, Typeable) deriving (Eq, Ord, Show, Generic, Typeable)
data WorkflowActionInfo fileid userid = WorkflowActionInfo
{ waiIx :: WorkflowStateIndex
, waiFrom :: Maybe WorkflowGraphNodeLabel
, waiHistory :: [WorkflowAction fileid userid]
, waiAction :: WorkflowAction fileid userid
} deriving (Eq, Ord, Show, Generic, Typeable)
workflowActionInfos :: WorkflowState fileid userid -> [WorkflowActionInfo fileid userid]
workflowActionInfos wState
= [ WorkflowActionInfo{..}
| waiFrom <- Nothing : map (Just . wpTo) wState'
| waiAction <- wState'
| waiIx <- [minBound..]
| waiHistory <- tailEx $ inits wState'
]
where wState' = otoList wState
data WorkflowFieldPayloadW fileid userid = forall payload. IsWorkflowFieldPayload' fileid userid payload => WorkflowFieldPayloadW (WorkflowFieldPayload fileid userid payload) data WorkflowFieldPayloadW fileid userid = forall payload. IsWorkflowFieldPayload' fileid userid payload => WorkflowFieldPayloadW (WorkflowFieldPayload fileid userid payload)
deriving (Typeable) deriving (Typeable)
@ -395,6 +418,35 @@ instance (Ord fileid, Ord userid, Typeable fileid, Typeable userid) => Ord (Work
(WFPFile{}, _) -> LT (WFPFile{}, _) -> LT
(WFPUser{}, _) -> GT (WFPUser{}, _) -> GT
workflowPayloadSort
:: forall fileid userid.
(fileid -> fileid -> Ordering)
-> (userid -> userid -> Ordering)
-> (WorkflowFieldPayloadW fileid userid -> WorkflowFieldPayloadW fileid userid -> Ordering)
workflowPayloadSort ordFiles ordUsers (WorkflowFieldPayloadW a) (WorkflowFieldPayloadW b) = case (a, b) of
(WFPText a', WFPText b' ) -> compareUnicode a' b'
(WFPText{}, _ ) -> LT
(WFPNumber a', WFPNumber b') -> compare a' b'
(WFPNumber{}, WFPText{} ) -> GT
(WFPNumber{}, _ ) -> LT
(WFPBool a', WFPBool b' ) -> compare a' b'
(WFPBool{}, WFPText{} ) -> GT
(WFPBool{}, WFPNumber{} ) -> GT
(WFPBool{}, _ ) -> LT
(WFPDay a', WFPDay b' ) -> compare a' b'
(WFPDay{}, WFPText{} ) -> GT
(WFPDay{}, WFPNumber{} ) -> GT
(WFPDay{}, WFPBool{} ) -> GT
(WFPDay{}, _ ) -> LT
(WFPFile a', WFPFile b' ) -> ordFiles a' b'
(WFPFile{}, WFPText{} ) -> GT
(WFPFile{}, WFPNumber{} ) -> GT
(WFPFile{}, WFPBool{} ) -> GT
(WFPFile{}, WFPDay{} ) -> GT
(WFPFile{}, _ ) -> LT
(WFPUser a', WFPUser b' ) -> ordUsers a' b'
(WFPUser{}, _ ) -> GT
instance (Show fileid, Show userid) => Show (WorkflowFieldPayloadW fileid userid) where instance (Show fileid, Show userid) => Show (WorkflowFieldPayloadW fileid userid) where
show (WorkflowFieldPayloadW payload) = show payload show (WorkflowFieldPayloadW payload) = show payload

View File

@ -17,6 +17,7 @@ import Data.CaseInsensitive (CI)
import qualified Data.CaseInsensitive as CI import qualified Data.CaseInsensitive as CI
import qualified Data.ByteString as BS import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as CBS
import qualified Data.Text as Text import qualified Data.Text as Text
import qualified Data.Text.Encoding as Text import qualified Data.Text.Encoding as Text
@ -222,7 +223,22 @@ delimitInternalState act = bracket createInternalState closeInternalState $ \new
= HandlerData { handlerResource = newInternalState = HandlerData { handlerResource = newInternalState
, .. , ..
} }
selectRep' :: [(ContentType, a)] -> ContentType -> Maybe a
selectRep' cMap _ | null cMap = Nothing
selectRep' cMap' needle = asum
[ guardOnM (needleMain == "*" && needleSub == "*") $ preview (folded . _2) cMap'
, guardOnM (needleSub == "*") $ preview (folded . filtered (views _1 $ views _1 (== needleMain) . contentTypeTypes) . _2) cMap'
, Map.lookup needle cMap
, Map.lookup (noSpaces needle) cMap
, Map.lookup (simpleContentType needle) cMap
]
where
cMap = Map.fromListWith const $ over _1 <$> [id, noSpaces, simpleContentType] <*> cMap'
(needleMain, needleSub) = contentTypeTypes needle
noSpaces = CBS.filter (/= ' ')
--------------------- ---------------------
-- Text and String -- -- Text and String --

View File

@ -14,11 +14,12 @@ import Settings.Mime
import Data.Csv hiding (Name) import Data.Csv hiding (Name)
import Data.Csv.Conduit (CsvParseError) import Data.Csv.Conduit (CsvParseError)
import qualified Data.Csv.Incremental as Incremental
import Language.Haskell.TH (Name) import Language.Haskell.TH (Name)
import Language.Haskell.TH.Lib import Language.Haskell.TH.Lib
import Yesod.Core.Content (ContentType, simpleContentType) import Yesod.Core.Content
import qualified Data.Map as Map import qualified Data.Map as Map
@ -54,6 +55,17 @@ data CsvRendered = CsvRendered
, csvRenderedData :: [NamedRecord] , csvRenderedData :: [NamedRecord]
} deriving (Eq, Read, Show, Generic, Typeable) } deriving (Eq, Read, Show, Generic, Typeable)
instance ToContent CsvRendered where
toContent CsvRendered{..} = toContent . Incremental.encodeByName csvRenderedHeader $ foldr ((<>) . Incremental.encodeNamedRecord) mempty csvRenderedData
instance ToTypedContent CsvRendered where
toTypedContent = TypedContent
<$> getContentType . Identity
<*> toContent
instance HasContentType CsvRendered where
getContentType _ = typeCsv'
toCsvRendered :: forall mono. toCsvRendered :: forall mono.
( ToNamedRecord (Element mono) ( ToNamedRecord (Element mono)
, MonoFoldable mono , MonoFoldable mono

View File

@ -31,6 +31,7 @@ data GlobalGetParam = GetLang
| GetDryRun | GetDryRun
| GetDownload | GetDownload
| GetError | GetError
| GetSelectTable
deriving (Eq, Ord, Enum, Bounded, Read, Show, Generic) deriving (Eq, Ord, Enum, Bounded, Read, Show, Generic)
deriving anyclass (Universe, Finite) deriving anyclass (Universe, Finite)