Merge branch 'fradrive/newletter'
This commit is contained in:
commit
6ea3a30afc
@ -9,6 +9,7 @@ QualificationValidIndicator: Gültigkeit
|
|||||||
QualificationValidDuration: Gültigkeitsdauer
|
QualificationValidDuration: Gültigkeitsdauer
|
||||||
QualificationAuditDuration: Aufbewahrung Audit Log
|
QualificationAuditDuration: Aufbewahrung Audit Log
|
||||||
QualificationAuditDurationTooltip n@Int: Optionaler Zeitraum zur Löschung von E‑Learning Daten. Hinweis: Der E‑Learning Server kann seine anonymisierten Daten schon früher löschen, aber spätestens #{n} Tage nach Abschluss.
|
QualificationAuditDurationTooltip n@Int: Optionaler Zeitraum zur Löschung von E‑Learning Daten. Hinweis: Der E‑Learning Server kann seine anonymisierten Daten schon früher löschen, aber spätestens #{n} Tage nach Abschluss.
|
||||||
|
QualificationAuditDurationReuseError: Diese Qualifikation nutzt das E‑Learning einer anderen Qualifikation, für die derzeit keinen Löschzeitraum konfiguriert wurde.
|
||||||
QualificationRefreshWithin: Erneurerungszeitraum
|
QualificationRefreshWithin: Erneurerungszeitraum
|
||||||
QualificationRefreshWithinTooltip: Optionaler Zeitraum vor Ablauf für automatischen Start des E‑Learnings und Versand einer Benachrichtigung per Brief oder Email.
|
QualificationRefreshWithinTooltip: Optionaler Zeitraum vor Ablauf für automatischen Start des E‑Learnings und Versand einer Benachrichtigung per Brief oder Email.
|
||||||
QualificationRefreshReminder: 2. Erinnerung
|
QualificationRefreshReminder: 2. Erinnerung
|
||||||
@ -20,6 +21,8 @@ QualificationExpiryNotificationTooltip: Nutzer werden benachrichtigt, wenn die Q
|
|||||||
TableQualificationCountActive: Aktive
|
TableQualificationCountActive: Aktive
|
||||||
TableQualificationCountActiveTooltip: Anzahl Personen mit momentan gültiger Qualifikation
|
TableQualificationCountActiveTooltip: Anzahl Personen mit momentan gültiger Qualifikation
|
||||||
TableQualificationCountTotal: Gesamt
|
TableQualificationCountTotal: Gesamt
|
||||||
|
TableQualificationLmsReuses: LMS nutzt
|
||||||
|
TableQualificationLmsReusesTooltip: Diese Qualifikation hat kein eigenes E‑Learning, sondern wird über das E‑Learning der angegebenen Qualifikation abgewickelt.
|
||||||
TableQualificationIsAvsLicence: AVS
|
TableQualificationIsAvsLicence: AVS
|
||||||
TableQualificationIsAvsLicenceTooltip: Unter welchem Namen wird diese Qualifikation mit dem Ausweisverwaltungssystem (AVS) synchronisiert? Betrifft nur Benutzer mit AVS PersonenID.
|
TableQualificationIsAvsLicenceTooltip: Unter welchem Namen wird diese Qualifikation mit dem Ausweisverwaltungssystem (AVS) synchronisiert? Betrifft nur Benutzer mit AVS PersonenID.
|
||||||
TableQualificationSapExport: SAP
|
TableQualificationSapExport: SAP
|
||||||
|
|||||||
@ -9,6 +9,7 @@ QualificationValidIndicator: Validity
|
|||||||
QualificationValidDuration: Validity period
|
QualificationValidDuration: Validity period
|
||||||
QualificationAuditDuration: Audit log retention period
|
QualificationAuditDuration: Audit log retention period
|
||||||
QualificationAuditDurationTooltip n@Int: Optional period for deletion of e‑learning data. Note that the e‑learning server may delete its anonymised data earlier, at most #{n} days after closing.
|
QualificationAuditDurationTooltip n@Int: Optional period for deletion of e‑learning data. Note that the e‑learning server may delete its anonymised data earlier, at most #{n} days after closing.
|
||||||
|
QualificationAuditDurationReuseError: This qualification reuses the e‑learning from another qualification, which has no audit duration configured.
|
||||||
QualificationRefreshWithin: Refresh within
|
QualificationRefreshWithin: Refresh within
|
||||||
QualificationRefreshWithinTooltip: Optional period before expiry to start e‑learning and send a notification by post or email.
|
QualificationRefreshWithinTooltip: Optional period before expiry to start e‑learning and send a notification by post or email.
|
||||||
QualificationRefreshReminder: 2. Reminder
|
QualificationRefreshReminder: 2. Reminder
|
||||||
@ -20,6 +21,8 @@ QualificationExpiryNotificationTooltip: Qualification holder are notfied upon in
|
|||||||
TableQualificationCountActive: Active
|
TableQualificationCountActive: Active
|
||||||
TableQualificationCountActiveTooltip: Number of currently valid qualification holders
|
TableQualificationCountActiveTooltip: Number of currently valid qualification holders
|
||||||
TableQualificationCountTotal: Total
|
TableQualificationCountTotal: Total
|
||||||
|
TableQualificationLmsReuses: Reuse LMS
|
||||||
|
TableQualificationLmsReusesTooltip: This qualification reuses the e‑learning of the given qualification, instead of having a separate e‑learning of its own.
|
||||||
TableQualificationIsAvsLicence: AVS driving license
|
TableQualificationIsAvsLicence: AVS driving license
|
||||||
TableQualificationIsAvsLicenceTooltip: Under which name is this qualification synchronized with AVS, if any? Only applies to qualification holders having an AVS PersonID.
|
TableQualificationIsAvsLicenceTooltip: Under which name is this qualification synchronized with AVS, if any? Only applies to qualification holders having an AVS PersonID.
|
||||||
TableQualificationSapExport: Sent to SAP
|
TableQualificationSapExport: Sent to SAP
|
||||||
|
|||||||
@ -14,13 +14,14 @@ Qualification
|
|||||||
refreshReminder CalendarDiffDays Maybe -- send a second notification about renewal within this number of month/days before expiry
|
refreshReminder CalendarDiffDays Maybe -- send a second notification about renewal within this number of month/days before expiry
|
||||||
elearningStart Bool -- automatically schedule e-refresher
|
elearningStart Bool -- automatically schedule e-refresher
|
||||||
elearningRenews Bool default=true -- successful E-learing automatically increases validity automatically by validDuration
|
elearningRenews Bool default=true -- successful E-learing automatically increases validity automatically by validDuration
|
||||||
|
lmsReuses QualificationId Maybe -- if set, lms is also included within the given qualification's lms, but only for direct routes. AuditDuration is used from this Qualification instead.
|
||||||
expiryNotification Bool default=true -- should expiryNotification be generated for this qualification?
|
expiryNotification Bool default=true -- should expiryNotification be generated for this qualification?
|
||||||
avsLicence AvsLicence Maybe -- if set, valid QualificationUsers are synchronized to AVS as a driving licence
|
avsLicence AvsLicence Maybe -- if set, valid QualificationUsers are synchronized to AVS as a driving licence
|
||||||
sapId Text Maybe -- if set, valid QualificationUsers with userCompanyPersonalNumber are transmitted via SAP interface under this id
|
sapId Text Maybe -- if set, valid QualificationUsers with userCompanyPersonalNumber are transmitted via SAP interface under this id
|
||||||
SchoolQualificationShort school shorthand -- must be unique per school and shorthand
|
SchoolQualificationShort school shorthand -- must be unique per school and shorthand
|
||||||
SchoolQualificationName school name -- must be unique per school and name
|
SchoolQualificationName school name -- must be unique per school and name
|
||||||
-- across all schools, only one qualification may be a driving licence:
|
-- across all schools, only one qualification may be a driving licence -- NO LONGER TRUE
|
||||||
UniqueQualificationAvsLicence avsLicence !force -- either empty or unique
|
-- UniqueQualificationAvsLicence avsLicence !force -- either empty or unique
|
||||||
-- NOTE: two NULL values are not equal for the purpose of Uniqueness constraints!
|
-- NOTE: two NULL values are not equal for the purpose of Uniqueness constraints!
|
||||||
deriving Show Eq Generic
|
deriving Show Eq Generic
|
||||||
|
|
||||||
|
|||||||
@ -311,6 +311,7 @@ resultUser = _dbrOutput . _3 . _Just
|
|||||||
mkProblemLogTable :: DB (FormResult (ProblemTableActionData, Set ProblemLogId), Widget)
|
mkProblemLogTable :: DB (FormResult (ProblemTableActionData, Set ProblemLogId), Widget)
|
||||||
mkProblemLogTable = over _1 postprocess <$> dbTable validator DBTable{..}
|
mkProblemLogTable = over _1 postprocess <$> dbTable validator DBTable{..}
|
||||||
where
|
where
|
||||||
|
-- TODO: query to collect all occurring problem types to use as tooltip for the problem filter, so that these don't run out of synch
|
||||||
dbtIdent = "problem-log" :: Text
|
dbtIdent = "problem-log" :: Text
|
||||||
dbtSQLQuery = \(problem `E.LeftOuterJoin` solver `E.LeftOuterJoin` usr) -> do
|
dbtSQLQuery = \(problem `E.LeftOuterJoin` solver `E.LeftOuterJoin` usr) -> do
|
||||||
-- EL.on (usr E.?. UserId E.==. E.text2num (problem E.^. ProblemLogInfo E.->>. "user")) -- works
|
-- EL.on (usr E.?. UserId E.==. E.text2num (problem E.^. ProblemLogInfo E.->>. "user")) -- works
|
||||||
|
|||||||
@ -450,8 +450,8 @@ getProblemAvsSynchR = do
|
|||||||
|
|
||||||
procRes alic (LicenceTableRevokeFDriveData{..}, apids) = do
|
procRes alic (LicenceTableRevokeFDriveData{..}, apids) = do
|
||||||
oks <- runDB $ do
|
oks <- runDB $ do
|
||||||
qId <- getKeyBy404 $ UniqueQualificationAvsLicence $ Just alic
|
qIds <- selectKeysList [QualificationAvsLicence ==. Just alic] [] -- sanity check
|
||||||
if qId /= licenceTableChangeFDriveQId
|
if licenceTableChangeFDriveQId `notElem` qIds
|
||||||
then return (-1)
|
then return (-1)
|
||||||
else do
|
else do
|
||||||
uids <- view _userAvsUser <<$>> selectList [UserAvsPersonId <-. Set.toList apids] []
|
uids <- view _userAvsUser <<$>> selectList [UserAvsPersonId <-. Set.toList apids] []
|
||||||
|
|||||||
@ -129,11 +129,11 @@ _userSheets = _dbrOutput . _7
|
|||||||
|
|
||||||
-- _userQualifications :: Traversal' UserTableData [Entity Qualification]
|
-- _userQualifications :: Traversal' UserTableData [Entity Qualification]
|
||||||
-- _userQualifications = _dbrOutput . _8 . (traverse _1)
|
-- _userQualifications = _dbrOutput . _8 . (traverse _1)
|
||||||
-- last part: ([Entity Qualification] -> f [Entity Qualification]) -> UserTableQualfications -> f UserTableQualifications
|
-- last part: ([Entity Qualification] -> f [Entity Qualification]) -> UserTableQualifications -> f UserTableQualifications
|
||||||
|
|
||||||
_userQualifications :: Getter UserTableData [Entity Qualification]
|
_userQualifications :: Getter UserTableData [Entity Qualification]
|
||||||
_userQualifications = _dbrOutput . _8 . to (fmap fst3)
|
_userQualifications = _dbrOutput . _8 . to (fmap fst3)
|
||||||
-- _userQualifications = _dbrOutput . _8 . each . _1 -- TODO: how to make this work
|
-- _userQualifications = _dbrOutput . _8 . each . _1 -- TODO: how to make this work
|
||||||
|
|
||||||
|
|
||||||
_userCourseQualifications :: Lens' UserTableData UserTableQualifications
|
_userCourseQualifications :: Lens' UserTableData UserTableQualifications
|
||||||
@ -194,7 +194,7 @@ colUserQualifications cutoff = sortable (Just "qualifications") (i18nCell MsgTab
|
|||||||
colUserQualificationBlocked :: forall m c. IsDBTable m c => Bool -> Day -> Colonnade Sortable UserTableData (DBCell m c)
|
colUserQualificationBlocked :: forall m c. IsDBTable m c => Bool -> Day -> Colonnade Sortable UserTableData (DBCell m c)
|
||||||
colUserQualificationBlocked isAdmin cutoff = sortable (Just "qualification-block") (i18nCell MsgQualificationValidIndicator & cellTooltip MsgTableQualificationBlockedTooltipSimple) $
|
colUserQualificationBlocked isAdmin cutoff = sortable (Just "qualification-block") (i18nCell MsgQualificationValidIndicator & cellTooltip MsgTableQualificationBlockedTooltipSimple) $
|
||||||
let qualNamedReasonCell (q,qu,qb) = textCell ((q ^. hasQualification . _qualificationShorthand . _CI) <> ": ") <> qualificationValidReasonCell' Nothing isAdmin cutoff qb qu
|
let qualNamedReasonCell (q,qu,qb) = textCell ((q ^. hasQualification . _qualificationShorthand . _CI) <> ": ") <> qualificationValidReasonCell' Nothing isAdmin cutoff qb qu
|
||||||
in \(view _userCourseQualifications -> qualis) ->
|
in \(view _userCourseQualifications -> qualis) ->
|
||||||
(cellAttrs <>~ [("class", "list--inline list--comma-separated list--iconless")]) . listCell qualis $ qualNamedReasonCell
|
(cellAttrs <>~ [("class", "list--inline list--comma-separated list--iconless")]) . listCell qualis $ qualNamedReasonCell
|
||||||
|
|
||||||
data UserTableCsv = UserTableCsv
|
data UserTableCsv = UserTableCsv
|
||||||
@ -420,12 +420,12 @@ makeCourseUserTable cid acts restrict colChoices psValidator csvColumns = do
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
qualis <- E.select . E.from $ \(qualification `E.InnerJoin` qualificationUser `E.LeftOuterJoin` qualificationBlock) -> do
|
qualis <- E.select . E.from $ \(qualification `E.InnerJoin` qualificationUser `E.LeftOuterJoin` qualificationBlock) -> do
|
||||||
E.on $ qualificationUser E.^. QualificationUserId E.=?. qualificationBlock E.?. QualificationUserBlockQualificationUser
|
E.on $ qualificationUser E.^. QualificationUserId E.=?. qualificationBlock E.?. QualificationUserBlockQualificationUser
|
||||||
E.&&. qualificationBlock `isLatestBlockBefore` E.now_
|
E.&&. qualificationBlock `isLatestBlockBefore` E.now_
|
||||||
E.on $ qualificationUser E.^. QualificationUserQualification E.==. qualification E.^. QualificationId
|
E.on $ qualificationUser E.^. QualificationUserQualification E.==. qualification E.^. QualificationId
|
||||||
E.where_ $ qualificationUser E.^. QualificationUserUser E.==. E.val (entityKey user)
|
E.where_ $ qualificationUser E.^. QualificationUserUser E.==. E.val (entityKey user)
|
||||||
E.&&. qualification E.^. QualificationId `E.in_` E.valList cqids
|
E.&&. qualification E.^. QualificationId `E.in_` E.valList cqids
|
||||||
E.orderBy [E.asc $ qualification E.^. QualificationShorthand] -- we should sort by CourseQualificationSortOrder instead, but since we have not seen a course with multiple qualifications yet, we take a shortcut here
|
E.orderBy [E.asc $ qualification E.^. QualificationShorthand] -- we should sort by CourseQualificationSortOrder instead, but since we have not seen a course with multiple qualifications yet, we take a shortcut here
|
||||||
return (qualification, qualificationUser, qualificationBlock)
|
return (qualification, qualificationUser, qualificationBlock)
|
||||||
let
|
let
|
||||||
regGroups = setOf (folded . _entityVal . _tutorialRegGroup . _Just) tutorials
|
regGroups = setOf (folded . _entityVal . _tutorialRegGroup . _Just) tutorials
|
||||||
@ -739,7 +739,7 @@ postCUsersR tid ssh csh = do
|
|||||||
redirect $ CourseR tid ssh csh CUsersR
|
redirect $ CourseR tid ssh csh CUsersR
|
||||||
(CourseUserRegisterExamData{..}, selectedUsers) -> do
|
(CourseUserRegisterExamData{..}, selectedUsers) -> do
|
||||||
Sum nrReg <- fmap mconcat . runDB . forM (Set.toList selectedUsers) $ \uid -> maybeT (return mempty) $ do
|
Sum nrReg <- fmap mconcat . runDB . forM (Set.toList selectedUsers) $ \uid -> maybeT (return mempty) $ do
|
||||||
guardM . lift $ exists [ CourseParticipantUser ==. uid, CourseParticipantCourse ==. cid, CourseParticipantState ==. CourseParticipantActive ]
|
guardM . lift $ exists [ CourseParticipantUser ==. uid, CourseParticipantCourse ==. cid, CourseParticipantState ==. CourseParticipantActive ]
|
||||||
let (exam, mOccurrence) = registerExam
|
let (exam, mOccurrence) = registerExam
|
||||||
mExamReg <- lift $ insertUnique ExamRegistration
|
mExamReg <- lift $ insertUnique ExamRegistration
|
||||||
{ examRegistrationExam = exam
|
{ examRegistrationExam = exam
|
||||||
@ -763,7 +763,7 @@ postCUsersR tid ssh csh = do
|
|||||||
Just _ -> addMessageI Success $ MsgCourseUsersSubmissionGroupSetNew nrSet
|
Just _ -> addMessageI Success $ MsgCourseUsersSubmissionGroupSetNew nrSet
|
||||||
|
|
||||||
redirect $ CourseR tid ssh csh CUsersR
|
redirect $ CourseR tid ssh csh CUsersR
|
||||||
(CourseUserReRegisterData, selectedUsers) -> do
|
(CourseUserReRegisterData, selectedUsers) -> do
|
||||||
Sum nrSet <- runDB . flip foldMapM selectedUsers $ \uid -> maybeT (return mempty) $ do
|
Sum nrSet <- runDB . flip foldMapM selectedUsers $ \uid -> maybeT (return mempty) $ do
|
||||||
didUpdate <- lift $ updateWhereCount
|
didUpdate <- lift $ updateWhereCount
|
||||||
[ CourseParticipantUser ==. uid
|
[ CourseParticipantUser ==. uid
|
||||||
|
|||||||
@ -325,9 +325,9 @@ addDefaultSupervisorsAll mutualSupervision cids = do
|
|||||||
------------------------------
|
------------------------------
|
||||||
-- repeatedly useful queries
|
-- repeatedly useful queries
|
||||||
|
|
||||||
usrSuperiorCompanies :: E.SqlExpr (Entity Company) -> E.SqlExpr (Entity UserCompany) -> E.SqlQuery ()
|
usrPrimaryCompanies :: E.SqlExpr (Entity Company) -> E.SqlExpr (Entity UserCompany) -> E.SqlQuery ()
|
||||||
-- usrSuperiorCompanies :: E.SqlExpr (E.Value CompanyId) -> E.SqlExpr (Entity UserCompany) -> E.SqlQuery (Entity UserCompany) -- possible alternative
|
-- usrPrimaryCompanies :: E.SqlExpr (E.Value CompanyId) -> E.SqlExpr (Entity UserCompany) -> E.SqlQuery (Entity UserCompany) -- possible alternative
|
||||||
usrSuperiorCompanies cmp usr = do
|
usrPrimaryCompanies cmp usr = do
|
||||||
othr <- E.from $ E.table @UserCompany
|
othr <- E.from $ E.table @UserCompany
|
||||||
E.where_ $ othr E.^. UserCompanyPriority E.>. usr E.^. UserCompanyPriority
|
E.where_ $ othr E.^. UserCompanyPriority E.>. usr E.^. UserCompanyPriority
|
||||||
E.&&. othr E.^. UserCompanyUser E.==. usr E.^. UserCompanyUser
|
E.&&. othr E.^. UserCompanyUser E.==. usr E.^. UserCompanyUser
|
||||||
@ -346,12 +346,12 @@ firmCountUsers = E.subSelectCount . fromUserCompany Nothing
|
|||||||
firmCountUsersPrimary :: E.SqlExpr (Entity Company) -> E.SqlExpr (E.Value Word64)
|
firmCountUsersPrimary :: E.SqlExpr (Entity Company) -> E.SqlExpr (E.Value Word64)
|
||||||
firmCountUsersPrimary cmp = E.subSelectCount $ fromUserCompany (Just primFltr) cmp
|
firmCountUsersPrimary cmp = E.subSelectCount $ fromUserCompany (Just primFltr) cmp
|
||||||
where
|
where
|
||||||
primFltr = E.notExists . usrSuperiorCompanies cmp
|
primFltr = E.notExists . usrPrimaryCompanies cmp
|
||||||
|
|
||||||
firmCountUsersSecondary :: E.SqlExpr (Entity Company) -> E.SqlExpr (E.Value Word64)
|
firmCountUsersSecondary :: E.SqlExpr (Entity Company) -> E.SqlExpr (E.Value Word64)
|
||||||
firmCountUsersSecondary cmp = E.subSelectCount $ fromUserCompany (Just primFltr) cmp
|
firmCountUsersSecondary cmp = E.subSelectCount $ fromUserCompany (Just primFltr) cmp
|
||||||
where
|
where
|
||||||
primFltr = E.exists . usrSuperiorCompanies cmp
|
primFltr = E.exists . usrPrimaryCompanies cmp
|
||||||
|
|
||||||
firmCountSupervisors :: E.SqlExpr (Entity Company) -> E.SqlExpr (E.Value Word64)
|
firmCountSupervisors :: E.SqlExpr (Entity Company) -> E.SqlExpr (E.Value Word64)
|
||||||
firmCountSupervisors = E.subSelectCount . fromUserCompany (Just (E.^. UserCompanySupervisor))
|
firmCountSupervisors = E.subSelectCount . fromUserCompany (Just (E.^. UserCompanySupervisor))
|
||||||
@ -1164,6 +1164,7 @@ querySuperUserCompany = $(sqlLOJproj 2 2)
|
|||||||
type SuperCompanyTableData = DBRow (Entity User, E.Value Word64, E.Value Word64
|
type SuperCompanyTableData = DBRow (Entity User, E.Value Word64, E.Value Word64
|
||||||
, [(E.Value CompanyName, E.Value CompanyShorthand, E.Value Bool)]
|
, [(E.Value CompanyName, E.Value CompanyShorthand, E.Value Bool)]
|
||||||
, E.Value (Maybe Bool), E.Value (Maybe Bool) -- Maybe (Entity UserCompany)
|
, E.Value (Maybe Bool), E.Value (Maybe Bool) -- Maybe (Entity UserCompany)
|
||||||
|
, E.Value Bool
|
||||||
)
|
)
|
||||||
|
|
||||||
resultSuperUser :: Lens' SuperCompanyTableData (Entity User)
|
resultSuperUser :: Lens' SuperCompanyTableData (Entity User)
|
||||||
@ -1184,6 +1185,9 @@ resultSuperCompanyDefaultSuper = _dbrOutput . _5 . _unValue
|
|||||||
resultSuperCompanyDefaultReroute :: Lens' SuperCompanyTableData (Maybe Bool)
|
resultSuperCompanyDefaultReroute :: Lens' SuperCompanyTableData (Maybe Bool)
|
||||||
resultSuperCompanyDefaultReroute = _dbrOutput . _6 . _unValue
|
resultSuperCompanyDefaultReroute = _dbrOutput . _6 . _unValue
|
||||||
|
|
||||||
|
resultSuperCompanySuperior :: Lens' SuperCompanyTableData Bool
|
||||||
|
resultSuperCompanySuperior = _dbrOutput . _7 . _unValue
|
||||||
|
|
||||||
instance HasEntity SuperCompanyTableData User where
|
instance HasEntity SuperCompanyTableData User where
|
||||||
hasEntity = resultSuperUser
|
hasEntity = resultSuperUser
|
||||||
|
|
||||||
@ -1195,6 +1199,7 @@ mkFirmSuperTable :: Bool -> CompanyId -> DB (FormResult (FirmSuperActionData, Se
|
|||||||
mkFirmSuperTable isAdmin cid = do
|
mkFirmSuperTable isAdmin cid = do
|
||||||
msgSupervisorUnchanged <- messageI Info MsgFirmSuperActSwitchSuperInfo
|
msgSupervisorUnchanged <- messageI Info MsgFirmSuperActSwitchSuperInfo
|
||||||
let
|
let
|
||||||
|
reasonSuperior = Just $ tshow SupervisorReasonAvsSuperior
|
||||||
-- fsh = unCompanyKey cid
|
-- fsh = unCompanyKey cid
|
||||||
resultDBTable = DBTable{..}
|
resultDBTable = DBTable{..}
|
||||||
where
|
where
|
||||||
@ -1207,15 +1212,16 @@ mkFirmSuperTable isAdmin cid = do
|
|||||||
, usr & firmCountForSupervisor cid (Just (E.^. UserSupervisorRerouteNotifications))
|
, usr & firmCountForSupervisor cid (Just (E.^. UserSupervisorRerouteNotifications))
|
||||||
, usrCmp E.?. UserCompanySupervisor
|
, usrCmp E.?. UserCompanySupervisor
|
||||||
, usrCmp E.?. UserCompanySupervisorReroute
|
, usrCmp E.?. UserCompanySupervisorReroute
|
||||||
|
, E.exists (firmQuerySupervisedBy cid (Just (\usrSpr -> usrSpr E.^. UserSupervisorReason E.==. E.val reasonSuperior)) usr)
|
||||||
)
|
)
|
||||||
dbtRowKey = querySuperUser >>> (E.^. UserId)
|
dbtRowKey = querySuperUser >>> (E.^. UserId)
|
||||||
dbtProj = dbtProjSimple $ \(usr, supervised, rerouted, supervisor, reroute) -> do
|
dbtProj = dbtProjSimple $ \(usr, supervised, rerouted, supervisor, reroute, isSuperior) -> do
|
||||||
cmps <- E.select $ do
|
cmps <- E.select $ do
|
||||||
(cmp :& usrCmp) <- E.from $ E.table @Company `E.innerJoin` E.table @UserCompany `E.on` (\(cmp :& usrCmp) -> cmp E.^. CompanyId E.==. usrCmp E.^. UserCompanyCompany)
|
(cmp :& usrCmp) <- E.from $ E.table @Company `E.innerJoin` E.table @UserCompany `E.on` (\(cmp :& usrCmp) -> cmp E.^. CompanyId E.==. usrCmp E.^. UserCompanyCompany)
|
||||||
E.where_ $ usrCmp E.^. UserCompanyUser E.==. E.val (entityKey usr)
|
E.where_ $ usrCmp E.^. UserCompanyUser E.==. E.val (entityKey usr)
|
||||||
E.orderBy [E.asc $ cmp E.^. CompanyName]
|
E.orderBy [E.asc $ cmp E.^. CompanyName]
|
||||||
return (cmp E.^. CompanyName, cmp E.^. CompanyShorthand, usrCmp E.^. UserCompanySupervisor)
|
return (cmp E.^. CompanyName, cmp E.^. CompanyShorthand, usrCmp E.^. UserCompanySupervisor)
|
||||||
return (usr, supervised, rerouted, cmps, supervisor, reroute)
|
return (usr, supervised, rerouted, cmps, supervisor, reroute, isSuperior)
|
||||||
dbtColonnade = formColonnade $ mconcat
|
dbtColonnade = formColonnade $ mconcat
|
||||||
[ dbSelect (applying _2) id (return . view (resultSuperUser . _entityKey))
|
[ dbSelect (applying _2) id (return . view (resultSuperUser . _entityKey))
|
||||||
, colUserNameModalHdr MsgTableSupervisor ForProfileDataR
|
, colUserNameModalHdr MsgTableSupervisor ForProfileDataR
|
||||||
@ -1227,7 +1233,15 @@ mkFirmSuperTable isAdmin cid = do
|
|||||||
, colUserEmail
|
, colUserEmail
|
||||||
, sortable (Just "supervised") (i18nCell MsgTableCompanyNrEmpSupervised) $ \(view resultSuperCompanySupervised -> nr) -> wgtCell $ word2widget nr
|
, sortable (Just "supervised") (i18nCell MsgTableCompanyNrEmpSupervised) $ \(view resultSuperCompanySupervised -> nr) -> wgtCell $ word2widget nr
|
||||||
, sortable (Just "rerouted") (i18nCell MsgTableCompanyNrEmpRerouted ) $ \(view resultSuperCompanyReroutes -> nr) -> wgtCell $ word2widget nr
|
, sortable (Just "rerouted") (i18nCell MsgTableCompanyNrEmpRerouted ) $ \(view resultSuperCompanyReroutes -> nr) -> wgtCell $ word2widget nr
|
||||||
, sortable (Just "def-super") (i18nCell MsgTableIsDefaultSupervisor) $ \(view resultSuperCompanyDefaultSuper -> mb) -> case mb of { Nothing -> iconCell IconSupervisorForeign; Just True -> iconCell IconSupervisor; Just False -> iconSpacerCell }
|
-- , sortable (Just "def-super") (i18nCell MsgTableIsDefaultSupervisor) $ \(view resultSuperCompanyDefaultSuper -> mb) -> case mb of { Nothing -> iconCell IconSupervisorForeign; Just True -> iconCell IconSupervisor; Just False -> iconSpacerCell }
|
||||||
|
, sortable (Just "def-super") (i18nCell MsgTableIsDefaultSupervisor) $ \row ->
|
||||||
|
let mb = row ^. resultSuperCompanyDefaultSuper
|
||||||
|
sp = row ^. resultSuperCompanySuperior
|
||||||
|
in case (mb,sp) of
|
||||||
|
(_ , True) -> iconCell IconSuperior
|
||||||
|
(Nothing ,_) -> iconCell IconSupervisorForeign
|
||||||
|
(Just True ,_) -> iconCell IconSupervisor
|
||||||
|
(Just False,_) -> iconSpacerCell
|
||||||
, sortable (Just "def-reroute") (i18nCell MsgTableIsDefaultReroute) $ \(view resultSuperCompanyDefaultReroute -> mb) -> tickmarkCell (mb == Just True)
|
, sortable (Just "def-reroute") (i18nCell MsgTableIsDefaultReroute) $ \(view resultSuperCompanyDefaultReroute -> mb) -> tickmarkCell (mb == Just True)
|
||||||
, sortable Nothing (i18nCell MsgTableUserEdit) $ \(view resultSuperUser -> entUsr) -> cellEditUserModal entUsr
|
, sortable Nothing (i18nCell MsgTableUserEdit) $ \(view resultSuperUser -> entUsr) -> cellEditUserModal entUsr
|
||||||
]
|
]
|
||||||
|
|||||||
@ -19,6 +19,7 @@ import Handler.Utils.LMS
|
|||||||
|
|
||||||
import qualified Data.Map as Map
|
import qualified Data.Map as Map
|
||||||
import qualified Data.Csv as Csv
|
import qualified Data.Csv as Csv
|
||||||
|
import qualified Data.Text as Text
|
||||||
import qualified Data.Conduit.List as C
|
import qualified Data.Conduit.List as C
|
||||||
-- import qualified Database.Esqueleto.Experimental as Ex -- needs TypeApplications Lang-Pragma
|
-- import qualified Database.Esqueleto.Experimental as Ex -- needs TypeApplications Lang-Pragma
|
||||||
import qualified Database.Esqueleto.Legacy as E
|
import qualified Database.Esqueleto.Legacy as E
|
||||||
@ -38,7 +39,7 @@ lmsUser2csv :: UTCTime -> LmsUser -> LmsUserTableCsv
|
|||||||
lmsUser2csv cutoff lu@LmsUser{..} = LmsUserTableCsv
|
lmsUser2csv cutoff lu@LmsUser{..} = LmsUserTableCsv
|
||||||
{ csvLUTident = lmsUserIdent
|
{ csvLUTident = lmsUserIdent
|
||||||
, csvLUTpin = lmsUserPin
|
, csvLUTpin = lmsUserPin
|
||||||
, csvLUTresetPin = LmsBool lmsUserResetPin
|
, csvLUTresetPin = LmsBool lmsUserResetPin
|
||||||
, csvLUTdelete = LmsBool (lmsUserToDelete cutoff lu)
|
, csvLUTdelete = LmsBool (lmsUserToDelete cutoff lu)
|
||||||
, csvLUTstaff = LmsBool (lmsUserStaff lu)
|
, csvLUTstaff = LmsBool (lmsUserStaff lu)
|
||||||
, csvLUTresetTries= LmsBool (lmsUserToResetTries lu) -- TODO: verify this works as intended!
|
, csvLUTresetTries= LmsBool (lmsUserToResetTries lu) -- TODO: verify this works as intended!
|
||||||
@ -92,7 +93,7 @@ instance CsvColumnsExplained LmsUserTableCsv where
|
|||||||
|
|
||||||
mkUserTable :: SchoolId -> QualificationShorthand -> QualificationId -> UTCTime -> DB (Any, Widget)
|
mkUserTable :: SchoolId -> QualificationShorthand -> QualificationId -> UTCTime -> DB (Any, Widget)
|
||||||
mkUserTable _sid qsh qid cutoff = do
|
mkUserTable _sid qsh qid cutoff = do
|
||||||
dbtCsvName <- csvFilenameLmsUser qsh
|
dbtCsvName <- csvFilenameLmsUser qsh
|
||||||
let dbtCsvSheetName = dbtCsvName
|
let dbtCsvSheetName = dbtCsvName
|
||||||
let
|
let
|
||||||
userDBTable = DBTable{..}
|
userDBTable = DBTable{..}
|
||||||
@ -166,7 +167,7 @@ getQidCutoff sid qsh = do
|
|||||||
getLmsLearnersR :: SchoolId -> QualificationShorthand -> Handler Html
|
getLmsLearnersR :: SchoolId -> QualificationShorthand -> Handler Html
|
||||||
getLmsLearnersR sid qsh = do
|
getLmsLearnersR sid qsh = do
|
||||||
lmsTable <- runDB $ do
|
lmsTable <- runDB $ do
|
||||||
(qid, cutoff) <- getQidCutoff sid qsh
|
(qid, cutoff) <- getQidCutoff sid qsh
|
||||||
view _2 <$> mkUserTable sid qsh qid cutoff
|
view _2 <$> mkUserTable sid qsh qid cutoff
|
||||||
siteLayoutMsg MsgMenuLmsLearners $ do
|
siteLayoutMsg MsgMenuLmsLearners $ do
|
||||||
setTitleI MsgMenuLmsLearners
|
setTitleI MsgMenuLmsLearners
|
||||||
@ -174,14 +175,17 @@ getLmsLearnersR sid qsh = do
|
|||||||
|
|
||||||
getLmsLearnersDirectR :: SchoolId -> QualificationShorthand -> Handler TypedContent
|
getLmsLearnersDirectR :: SchoolId -> QualificationShorthand -> Handler TypedContent
|
||||||
getLmsLearnersDirectR sid qsh = do
|
getLmsLearnersDirectR sid qsh = do
|
||||||
$logInfoS "LMS" $ "Direct Download Users for " <> tshow qsh <> " at " <> tshow sid
|
$logInfoS "LMS" $ "Direct Download Users for " <> tshow qsh <> " at " <> tshow sid
|
||||||
(lms_users,cutoff) <- runDB $ do
|
(lms_users,cutoff,qshs) <- runDB $ do
|
||||||
(qid, cutoff) <- getQidCutoff sid qsh
|
(qid, cutoff) <- getQidCutoff sid qsh
|
||||||
lms_users <- selectList [ LmsUserQualification ==. qid
|
qidsReuse <- selectList [QualificationLmsReuses ==. Just qid] []
|
||||||
|
let qids = qid : (entityKey <$> qidsReuse)
|
||||||
|
qshs = qsh : (qualificationShorthand . entityVal <$> qidsReuse)
|
||||||
|
lms_users <- selectList [ LmsUserQualification <-. qids
|
||||||
, LmsUserEnded ==. Nothing
|
, LmsUserEnded ==. Nothing
|
||||||
-- , LmsUserReceived ==. Nothing ||. LmsUserResetPin ==. True ||. LmsUserStatus !=. Nothing -- send delta only NOTE: know-how no longer expects delta
|
-- , LmsUserReceived ==. Nothing ||. LmsUserResetPin ==. True ||. LmsUserStatus !=. Nothing -- send delta only NOTE: know-how no longer expects delta
|
||||||
] [Asc LmsUserStarted, Asc LmsUserIdent]
|
] [Asc LmsUserStarted, Asc LmsUserIdent]
|
||||||
return (lms_users, cutoff)
|
return (lms_users, cutoff, qshs)
|
||||||
|
|
||||||
{- To avoid exporting unneeded columns, we would need an SqlSelect instance for LmsUserTableCsv; probably not worth it
|
{- To avoid exporting unneeded columns, we would need an SqlSelect instance for LmsUserTableCsv; probably not worth it
|
||||||
Ex.select $ do
|
Ex.select $ do
|
||||||
@ -196,7 +200,7 @@ getLmsLearnersDirectR sid qsh = do
|
|||||||
, csvLUTstaff = LmsBool False
|
, csvLUTstaff = LmsBool False
|
||||||
}
|
}
|
||||||
-}
|
-}
|
||||||
LmsConf{..} <- getsYesod $ view _appLmsConf
|
LmsConf{..} <- getsYesod $ view _appLmsConf
|
||||||
let --csvRenderedData = toNamedRecord . lmsUser2csv . entityVal <$> lms_users
|
let --csvRenderedData = toNamedRecord . lmsUser2csv . entityVal <$> lms_users
|
||||||
--csvRenderedHeader = lmsUserTableCsvHeader
|
--csvRenderedHeader = lmsUserTableCsvHeader
|
||||||
--cvsRendered = CsvRendered {..}
|
--cvsRendered = CsvRendered {..}
|
||||||
@ -209,7 +213,7 @@ getLmsLearnersDirectR sid qsh = do
|
|||||||
csvOpts = def { csvFormat = fmtOpts }
|
csvOpts = def { csvFormat = fmtOpts }
|
||||||
csvSheetName <- csvFilenameLmsUser qsh
|
csvSheetName <- csvFilenameLmsUser qsh
|
||||||
let nr = length lms_users
|
let nr = length lms_users
|
||||||
msg = "Success. LMS user learners download file " <> csvSheetName <> " containing " <> tshow nr <> " rows"
|
msg = "Success. LMS user learners download file " <> csvSheetName <> " containing " <> tshow nr <> " rows for Qualifications " <> Text.intercalate ", " (ciOriginal <$> qshs)
|
||||||
$logInfoS "LMS" msg
|
$logInfoS "LMS" msg
|
||||||
addHeader "Content-Disposition" $ "attachment; filename=\"" <> csvSheetName <> "\""
|
addHeader "Content-Disposition" $ "attachment; filename=\"" <> csvSheetName <> "\""
|
||||||
csvRenderedToTypedContentWith csvOpts csvSheetName csvRendered
|
csvRenderedToTypedContentWith csvOpts csvSheetName csvRendered
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
-- SPDX-License-Identifier: AGPL-3.0-or-later
|
-- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
{-# OPTIONS_GHC -fno-warn-orphans #-} -- needed for HasEntity instances
|
{-# OPTIONS_GHC -fno-warn-orphans #-} -- needed for HasEntity instances
|
||||||
|
{-# LANGUAGE TypeApplications #-}
|
||||||
|
|
||||||
module Handler.LMS.Report
|
module Handler.LMS.Report
|
||||||
( getLmsReportR, postLmsReportR
|
( getLmsReportR, postLmsReportR
|
||||||
@ -17,10 +18,13 @@ import Handler.Utils
|
|||||||
import Handler.Utils.Csv
|
import Handler.Utils.Csv
|
||||||
import Handler.Utils.LMS
|
import Handler.Utils.LMS
|
||||||
|
|
||||||
|
import qualified Data.Text as Text
|
||||||
import qualified Data.Map as Map
|
import qualified Data.Map as Map
|
||||||
import qualified Data.Csv as Csv
|
import qualified Data.Csv as Csv
|
||||||
import qualified Data.Conduit.List as C
|
import qualified Data.Conduit.List as C
|
||||||
import qualified Database.Esqueleto.Legacy as E
|
-- import Database.Esqueleto.Experimental ((:&)(..))
|
||||||
|
import qualified Database.Esqueleto.Experimental as E -- needs TypeApplications Lang-Pragma
|
||||||
|
import qualified Database.Esqueleto.PostgreSQL as E
|
||||||
import qualified Database.Esqueleto.Utils as E
|
import qualified Database.Esqueleto.Utils as E
|
||||||
|
|
||||||
import Jobs.Queue
|
import Jobs.Queue
|
||||||
@ -121,7 +125,7 @@ mkReportTable sid qsh qid = do
|
|||||||
]
|
]
|
||||||
dbtFilter = Map.fromList
|
dbtFilter = Map.fromList
|
||||||
[ (csvLmsIdent , FilterColumn $ E.mkContainsFilterWithCommaPlus LmsIdent (E.^. LmsReportIdent))
|
[ (csvLmsIdent , FilterColumn $ E.mkContainsFilterWithCommaPlus LmsIdent (E.^. LmsReportIdent))
|
||||||
, (csvLmsDate , FilterColumn $ E.mkExactFilter (E.^. LmsReportDate))
|
, (csvLmsDate , FilterColumn $ E.mkExactFilter (E.^. LmsReportDate))
|
||||||
]
|
]
|
||||||
dbtFilterUI = \mPrev -> mconcat
|
dbtFilterUI = \mPrev -> mconcat
|
||||||
[ prismAForm (singletonFilter csvLmsIdent . maybePrism _PathPiece) mPrev $ aopt (hoistField lift textField) (fslI MsgTableLmsIdent & setTooltip MsgTableFilterCommaPlus)
|
[ prismAForm (singletonFilter csvLmsIdent . maybePrism _PathPiece) mPrev $ aopt (hoistField lift textField) (fslI MsgTableLmsIdent & setTooltip MsgTableFilterCommaPlus)
|
||||||
@ -199,7 +203,7 @@ mkReportTable sid qsh qid = do
|
|||||||
, LmsReportResult =. lmsReportCsvResult actionData
|
, LmsReportResult =. lmsReportCsvResult actionData
|
||||||
, LmsReportLock =. lmsReportCsvLock actionData
|
, LmsReportLock =. lmsReportCsvLock actionData
|
||||||
, LmsReportTimestamp =. eanow
|
, LmsReportTimestamp =. eanow
|
||||||
]
|
]
|
||||||
lift . queueDBJob $ JobLmsReports qid
|
lift . queueDBJob $ JobLmsReports qid
|
||||||
return $ LmsReportR sid qsh
|
return $ LmsReportR sid qsh
|
||||||
, dbtCsvRenderKey = const $ \case
|
, dbtCsvRenderKey = const $ \case
|
||||||
@ -246,8 +250,8 @@ postLmsReportR sid qsh = do
|
|||||||
|
|
||||||
-- Direct File Upload/Download
|
-- Direct File Upload/Download
|
||||||
|
|
||||||
saveReportCsv :: UTCTime -> QualificationId -> Int -> LmsReportTableCsv -> JobDB Int
|
saveReportCsv :: UTCTime -> NonEmpty QualificationId -> Int -> LmsReportTableCsv -> JobDB Int
|
||||||
saveReportCsv now qid i LmsReportTableCsv{..} = do
|
saveReportCsv now (qid :| []) i LmsReportTableCsv{..} = do
|
||||||
void $ upsert
|
void $ upsert
|
||||||
LmsReport
|
LmsReport
|
||||||
{ lmsReportQualification = qid
|
{ lmsReportQualification = qid
|
||||||
@ -263,6 +267,30 @@ saveReportCsv now qid i LmsReportTableCsv{..} = do
|
|||||||
, LmsReportTimestamp =. now
|
, LmsReportTimestamp =. now
|
||||||
]
|
]
|
||||||
return $ succ i
|
return $ succ i
|
||||||
|
saveReportCsv now qids@(qid :| _) i lrtc@LmsReportTableCsv{..} = do
|
||||||
|
ok <- E.insertSelectWithConflictCount UniqueLmsReport
|
||||||
|
(do
|
||||||
|
lusr <- E.from $ E.table @LmsUser
|
||||||
|
E.where_ $ lusr E.^. LmsUserIdent E.==. E.val csvLRident
|
||||||
|
E.&&. lusr E.^. LmsUserQualification `E.in_` E.vals qids
|
||||||
|
return $ LmsReport
|
||||||
|
E.<# (lusr E.^. LmsUserQualification)
|
||||||
|
E.<&> E.val csvLRident
|
||||||
|
E.<&> E.val (csvLRdate <&> lms2timestamp)
|
||||||
|
E.<&> E.val csvLRresult
|
||||||
|
E.<&> E.val (csvLRlock & lms2bool)
|
||||||
|
E.<&> E.val now
|
||||||
|
)
|
||||||
|
(\_old _new ->
|
||||||
|
[ LmsReportDate E.=. E.val (csvLRdate <&> lms2timestamp)
|
||||||
|
, LmsReportResult E.=. E.val csvLRresult
|
||||||
|
, LmsReportLock E.=. E.val (csvLRlock & lms2bool)
|
||||||
|
, LmsReportTimestamp E.=. E.val now
|
||||||
|
]
|
||||||
|
)
|
||||||
|
if ok > 0
|
||||||
|
then return $ succ i
|
||||||
|
else saveReportCsv now (qid :| []) i lrtc -- save unknown LmsIdent to primary qid regardless, so that the error can be tracked
|
||||||
|
|
||||||
makeReportUploadForm :: Form FileInfo
|
makeReportUploadForm :: Form FileInfo
|
||||||
makeReportUploadForm = renderAForm FormStandard $ fileAFormReq "Report CSV"
|
makeReportUploadForm = renderAForm FormStandard $ fileAFormReq "Report CSV"
|
||||||
@ -276,15 +304,18 @@ postLmsReportUploadR sid qsh = do
|
|||||||
FormSuccess file -> do
|
FormSuccess file -> do
|
||||||
-- content <- fileSourceByteString file
|
-- content <- fileSourceByteString file
|
||||||
-- return $ Just (fileName file, content)
|
-- return $ Just (fileName file, content)
|
||||||
(nr, qid) <- runDBJobs $ do
|
(nr, qids, qshs) <- runDBJobs $ do
|
||||||
qid <- getKeyBy404 $ SchoolQualificationShort sid qsh
|
qid <- getKeyBy404 $ SchoolQualificationShort sid qsh
|
||||||
|
qidsReuse <- selectList [QualificationLmsReuses ==. Just qid] []
|
||||||
|
let qids = qid :| (entityKey <$> qidsReuse)
|
||||||
|
qshs = qsh : (qualificationShorthand . entityVal <$> qidsReuse)
|
||||||
nr <- runConduit $ fileSource file
|
nr <- runConduit $ fileSource file
|
||||||
.| decodeCsv
|
.| decodeCsv
|
||||||
.| foldMC (saveReportCsv now qid) 0
|
.| foldMC (saveReportCsv now qids) 0
|
||||||
return (nr, qid)
|
return (nr, qids, qshs)
|
||||||
addMessage Success $ toHtml $ pack "Erfolgreicher Upload der Datei " <> fileName file <> pack (" mit " <> show nr <> " Zeilen")
|
addMessage Success $ toHtml $ pack "Erfolgreicher Upload der Datei " <> fileName file <> pack (" mit " <> show nr <> " Zeilen") <> " für Qualifikationen: " <> Text.intercalate ", " (ciOriginal <$> qshs)
|
||||||
-- redirect $ LmsReportR sid qsh
|
-- redirect $ LmsReportR sid qsh
|
||||||
getLmsReportR sid qsh <* queueJob' (JobLmsReports qid) -- show uploaded data before processing
|
getLmsReportR sid qsh <* forM_ qids (queueJob' . JobLmsReports) -- show uploaded data before processing
|
||||||
|
|
||||||
FormFailure errs -> do
|
FormFailure errs -> do
|
||||||
forM_ errs $ addMessage Error . toHtml
|
forM_ errs $ addMessage Error . toHtml
|
||||||
@ -294,7 +325,7 @@ postLmsReportUploadR sid qsh = do
|
|||||||
setTitleI MsgMenuLmsUpload
|
setTitleI MsgMenuLmsUpload
|
||||||
[whamlet|$newline never
|
[whamlet|$newline never
|
||||||
<form method=post enctype=#{enctype}>
|
<form method=post enctype=#{enctype}>
|
||||||
^{widget}
|
^{widget}
|
||||||
<input type=submit>
|
<input type=submit>
|
||||||
|]
|
|]
|
||||||
|
|
||||||
@ -308,18 +339,21 @@ postLmsReportDirectR sid qsh = do
|
|||||||
lmsDecoder <- getLmsCsvDecoder
|
lmsDecoder <- getLmsCsvDecoder
|
||||||
runDBJobs $ do
|
runDBJobs $ do
|
||||||
qid <- getKeyBy404 $ SchoolQualificationShort sid qsh
|
qid <- getKeyBy404 $ SchoolQualificationShort sid qsh
|
||||||
|
qidsReuse <- selectList [QualificationLmsReuses ==. Just qid] []
|
||||||
|
let qids = qid :| (entityKey <$> qidsReuse)
|
||||||
|
qshs = qsh : (qualificationShorthand . entityVal <$> qidsReuse)
|
||||||
enr <- try $ runConduit $ fileSource file
|
enr <- try $ runConduit $ fileSource file
|
||||||
.| lmsDecoder
|
.| lmsDecoder
|
||||||
.| foldMC (saveReportCsv now qid) 0
|
.| foldMC (saveReportCsv now qids) 0
|
||||||
case enr of
|
case enr of
|
||||||
Left (e :: SomeException) -> do -- catch all to avoid ok220 in case of any error
|
Left (e :: SomeException) -> do -- catch all to avoid ok220 in case of any error
|
||||||
$logWarnS "LMS" $ "Report upload failed parsing: " <> tshow e
|
$logWarnS "LMS" $ "Report upload failed parsing: " <> tshow e <> " for Qualification: " <> Text.intercalate ", " (ciOriginal <$> qshs)
|
||||||
logInterface "LMS" (ciOriginal qsh) False Nothing ""
|
logInterface "LMS" (ciOriginal qsh) False Nothing ""
|
||||||
return (badRequest400, "Exception: " <> tshow e)
|
return (badRequest400, "Exception: " <> tshow e)
|
||||||
Right nr -> do
|
Right nr -> do
|
||||||
let msg = "Success. LMS Report upload file " <> fileName file <> " containing " <> tshow nr <> " rows for " <> fhead <> ". "
|
let msg = "Success. LMS Report upload file " <> fileName file <> " containing " <> tshow nr <> " rows for " <> fhead <> " and Qualifications: " <> Text.intercalate ", " (ciOriginal <$> qshs)
|
||||||
$logInfoS "LMS" msg
|
$logInfoS "LMS" msg
|
||||||
when (nr > 0) $ queueDBJob $ JobLmsReports qid
|
when (nr > 0) $ forM_ qids (queueDBJob . JobLmsReports)
|
||||||
logInterface "LMS" (ciOriginal qsh) True (Just nr) ""
|
logInterface "LMS" (ciOriginal qsh) True (Just nr) ""
|
||||||
return (ok200, msg)
|
return (ok200, msg)
|
||||||
[] -> do
|
[] -> do
|
||||||
|
|||||||
@ -106,6 +106,8 @@ mkQualificationAllTable isAdmin = do
|
|||||||
$ tickmarkCell . view (resultAllQualification . _qualificationElearningRenews)
|
$ tickmarkCell . view (resultAllQualification . _qualificationElearningRenews)
|
||||||
, sortable (Just "noteexpiry") (i18nCell MsgQualificationExpiryNotification & cellTooltip MsgQualificationExpiryNotificationTooltip)
|
, sortable (Just "noteexpiry") (i18nCell MsgQualificationExpiryNotification & cellTooltip MsgQualificationExpiryNotificationTooltip)
|
||||||
$ tickmarkCell . view (resultAllQualification . _qualificationExpiryNotification)
|
$ tickmarkCell . view (resultAllQualification . _qualificationExpiryNotification)
|
||||||
|
, sortable Nothing (i18nCell MsgTableQualificationLmsReuses & cellTooltip MsgTableQualificationLmsReusesTooltip)
|
||||||
|
$ \(view (resultAllQualification . _qualificationLmsReuses) -> reuseQid) -> maybeCell reuseQid qualificationIdShortCell
|
||||||
, sortable Nothing (i18nCell MsgTableQualificationIsAvsLicence & cellTooltip MsgTableQualificationIsAvsLicenceTooltip)
|
, sortable Nothing (i18nCell MsgTableQualificationIsAvsLicence & cellTooltip MsgTableQualificationIsAvsLicenceTooltip)
|
||||||
$ \(view (resultAllQualification . _qualificationAvsLicence) -> licence) -> maybeCell licence $ textCell . T.singleton . licence2char
|
$ \(view (resultAllQualification . _qualificationAvsLicence) -> licence) -> maybeCell licence $ textCell . T.singleton . licence2char
|
||||||
, sortable Nothing (i18nCell MsgTableQualificationSapExport & cellTooltip MsgTableQualificationSapExportTooltip)
|
, sortable Nothing (i18nCell MsgTableQualificationSapExport & cellTooltip MsgTableQualificationSapExportTooltip)
|
||||||
@ -528,14 +530,15 @@ postQualificationR sid qsh = do
|
|||||||
msgUnexpire <- messageIconI Info IconWarning MsgQualificationActUnexpireWarning
|
msgUnexpire <- messageIconI Info IconWarning MsgQualificationActUnexpireWarning
|
||||||
now <- liftIO getCurrentTime
|
now <- liftIO getCurrentTime
|
||||||
let nowaday = utctDay now
|
let nowaday = utctDay now
|
||||||
((lmsRes, qualificationTable), Entity qid quali) <- runDB $ do
|
((lmsRes, qualificationTable), Entity qid quali, lmsQualiReused) <- runDB $ do
|
||||||
qent@Entity{
|
qent@Entity{
|
||||||
entityKey=qid
|
entityKey=qid
|
||||||
, entityVal=Qualification{
|
, entityVal=Qualification{
|
||||||
qualificationAuditDuration=auditMonths
|
qualificationAuditDuration=auditMonths
|
||||||
, qualificationValidDuration=validMonths
|
, qualificationValidDuration=validMonths
|
||||||
|
, qualificationLmsReuses =reuseQuali
|
||||||
}} <- getBy404 $ SchoolQualificationShort sid qsh
|
}} <- getBy404 $ SchoolQualificationShort sid qsh
|
||||||
|
lmsQualiReused <- traverseJoin get reuseQuali
|
||||||
-- Block copied to Handler/Qualifications TODO: refactor
|
-- Block copied to Handler/Qualifications TODO: refactor
|
||||||
let getBlockReasons unblk = Ex.select $ do
|
let getBlockReasons unblk = Ex.select $ do
|
||||||
(quser :& qblock) <- Ex.from $ Ex.table @QualificationUser
|
(quser :& qblock) <- Ex.from $ Ex.table @QualificationUser
|
||||||
@ -608,7 +611,7 @@ postQualificationR sid qsh = do
|
|||||||
]
|
]
|
||||||
psValidator = def & defaultSorting [SortDescBy "last-refresh"]
|
psValidator = def & defaultSorting [SortDescBy "last-refresh"]
|
||||||
tbl <- mkQualificationTable isAdmin qent acts colChoices psValidator
|
tbl <- mkQualificationTable isAdmin qent acts colChoices psValidator
|
||||||
return (tbl, qent)
|
return (tbl, qent, lmsQualiReused)
|
||||||
|
|
||||||
formResult lmsRes $ \case
|
formResult lmsRes $ \case
|
||||||
(QualificationActRenewData renewReason, selectedUsers) | isAdmin -> do
|
(QualificationActRenewData renewReason, selectedUsers) | isAdmin -> do
|
||||||
|
|||||||
@ -57,7 +57,7 @@ instance ToNamedRecord SapUserTableCsv where
|
|||||||
, "Ausprägung" Csv..= csvSUTausprägung
|
, "Ausprägung" Csv..= csvSUTausprägung
|
||||||
]
|
]
|
||||||
|
|
||||||
-- | Removes all personalNummer which are not numbers between 10000 and 99999 (also excludes E-Accounts), which should not be returned by the query anyway (only qualfications with sap id and users with internal personnel number must be transmitted)
|
-- | Removes all personalNummer which are not numbers between 10000 and 99999 (also excludes E-Accounts), which should not be returned by the query anyway (only qualifications with sap id and users with internal personnel number must be transmitted)
|
||||||
-- temporary suspensions are transmitted to SAP in multiple rows: firstheld->suspension1, reinstate1->suspension2, reinstate2->validTo
|
-- temporary suspensions are transmitted to SAP in multiple rows: firstheld->suspension1, reinstate1->suspension2, reinstate2->validTo
|
||||||
sapRes2csv :: [(E.Value (Maybe Text), E.Value (Maybe Text), E.Value Day, E.Value Day, E.Value (Maybe [Maybe Day]), E.Value (Maybe [Maybe Bool]))] -> [SapUserTableCsv]
|
sapRes2csv :: [(E.Value (Maybe Text), E.Value (Maybe Text), E.Value Day, E.Value Day, E.Value (Maybe [Maybe Day]), E.Value (Maybe [Maybe Bool]))] -> [SapUserTableCsv]
|
||||||
sapRes2csv = concatMap procRes
|
sapRes2csv = concatMap procRes
|
||||||
|
|||||||
@ -378,6 +378,25 @@ companyIdCell cid = companyCell csh csh False
|
|||||||
where
|
where
|
||||||
csh = unCompanyKey cid
|
csh = unCompanyKey cid
|
||||||
|
|
||||||
|
-- | Uses DB Lookup to link to a qualification by id only, use sparingly!
|
||||||
|
qualificationIdCell :: (IsDBTable m c) => QualificationId -> DBCell m c
|
||||||
|
qualificationIdCell qid = anchorCellM' qual link name
|
||||||
|
where
|
||||||
|
qual = liftHandler $ runDBRead $ get qid
|
||||||
|
link (Just Qualification{..}) = QualificationR qualificationSchool qualificationShorthand
|
||||||
|
link Nothing = HelpR
|
||||||
|
name Nothing = text2widget "Error: unknown QID"
|
||||||
|
name (Just Qualification{..}) = citext2widget qualificationName
|
||||||
|
|
||||||
|
qualificationIdShortCell :: (IsDBTable m c) => QualificationId -> DBCell m c
|
||||||
|
qualificationIdShortCell qid = anchorCellM' qual link name
|
||||||
|
where
|
||||||
|
qual = liftHandler $ runDBRead $ get qid
|
||||||
|
link (Just Qualification{..}) = QualificationR qualificationSchool qualificationShorthand
|
||||||
|
link Nothing = HelpR
|
||||||
|
name Nothing = text2widget "Error: unknown QID"
|
||||||
|
name (Just Qualification{..}) = citext2widget qualificationShorthand
|
||||||
|
|
||||||
qualificationCell :: (IsDBTable m c, HasQualification a) => a -> DBCell m c
|
qualificationCell :: (IsDBTable m c, HasQualification a) => a -> DBCell m c
|
||||||
qualificationCell (view hasQualification -> Qualification{..}) = anchorCell link name
|
qualificationCell (view hasQualification -> Qualification{..}) = anchorCell link name
|
||||||
where
|
where
|
||||||
|
|||||||
@ -89,7 +89,7 @@ getUserPrimaryCompanyAddress uid prj = runMaybeT $ do
|
|||||||
company <- MaybeT $ get cid
|
company <- MaybeT $ get cid
|
||||||
-- hoistMaybe $ prj company
|
-- hoistMaybe $ prj company
|
||||||
MaybeT $ pure $ prj company
|
MaybeT $ pure $ prj company
|
||||||
|
|
||||||
|
|
||||||
-- | result (True, Nothing, Nothing) indicates that neither userEmail nor userPostAddress is known
|
-- | result (True, Nothing, Nothing) indicates that neither userEmail nor userPostAddress is known
|
||||||
getPostalPreferenceAndAddress :: Entity User -> DB (Bool, Maybe [Text], Maybe UserEmail)
|
getPostalPreferenceAndAddress :: Entity User -> DB (Bool, Maybe [Text], Maybe UserEmail)
|
||||||
@ -111,18 +111,18 @@ getPostalPreferenceAndAddress' usr = do
|
|||||||
finalPref = (usrPrefPost && isJust (fst pa)) || isNothing (fst em)
|
finalPref = (usrPrefPost && isJust (fst pa)) || isNothing (fst em)
|
||||||
-- finalPref = isJust (fst pa) && (usrPrefPost || isNothing (fst em))
|
-- finalPref = isJust (fst pa) && (usrPrefPost || isNothing (fst em))
|
||||||
return (finalPref, pa, em)
|
return (finalPref, pa, em)
|
||||||
|
|
||||||
getEmailAddressFor :: UserId -> DB (Maybe Address)
|
getEmailAddressFor :: UserId -> DB (Maybe Address)
|
||||||
getEmailAddressFor = maybeM (return Nothing) getEmailAddress . getEntity
|
getEmailAddressFor = maybeM (return Nothing) getEmailAddress . getEntity
|
||||||
|
|
||||||
getJustEmailAddressFor :: UserId -> DB Address
|
getJustEmailAddressFor :: UserId -> DB Address
|
||||||
getJustEmailAddressFor = maybeThrowM ExceptionUserHasNoEmail . getEmailAddressFor
|
getJustEmailAddressFor = maybeThrowM ExceptionUserHasNoEmail . getEmailAddressFor
|
||||||
|
|
||||||
getJustEmailAddress :: Entity User -> DB Address
|
getJustEmailAddress :: Entity User -> DB Address
|
||||||
getJustEmailAddress = maybeThrowM ExceptionUserHasNoEmail . getEmailAddress
|
getJustEmailAddress = maybeThrowM ExceptionUserHasNoEmail . getEmailAddress
|
||||||
|
|
||||||
getEmailAddress :: Entity User -> DB (Maybe Address)
|
getEmailAddress :: Entity User -> DB (Maybe Address)
|
||||||
getEmailAddress usr@Entity{entityVal=User{userDisplayName}} = toAddress <<$>> getUserEmail usr
|
getEmailAddress usr@Entity{entityVal=User{userDisplayName}} = toAddress <<$>> getUserEmail usr
|
||||||
where toAddress = Address (Just userDisplayName) . CI.original
|
where toAddress = Address (Just userDisplayName) . CI.original
|
||||||
|
|
||||||
getUserEmail :: Entity User -> DB (Maybe UserEmail)
|
getUserEmail :: Entity User -> DB (Maybe UserEmail)
|
||||||
@ -159,12 +159,12 @@ getPostalAddress Entity{entityKey=uid, entityVal=User{..}}
|
|||||||
getUserPrimaryCompanyAddress uid companyPostAddress >>= \case
|
getUserPrimaryCompanyAddress uid companyPostAddress >>= \case
|
||||||
(Just pa)
|
(Just pa)
|
||||||
-> prefixMarkupName pa
|
-> prefixMarkupName pa
|
||||||
Nothing
|
Nothing
|
||||||
| Just abt <- userCompanyDepartment
|
| Just abt <- userCompanyDepartment
|
||||||
-> return $ Just $ if | "BVD" `isPrefixOf` abt -> [userDisplayName, abt, "Bodenverkehrsdienste"]
|
-> return $ Just $ if | "BVD" `isPrefixOf` abt -> [userDisplayName, abt, "Bodenverkehrsdienste"]
|
||||||
| otherwise -> [userDisplayName, abt, "Hausbriefkasten" ]
|
| otherwise -> [userDisplayName, abt, "Hausbriefkasten" ]
|
||||||
| otherwise -> return Nothing
|
| otherwise -> return Nothing
|
||||||
where
|
where
|
||||||
prefixMarkupName = return . Just . (userDisplayName :) . html2textlines
|
prefixMarkupName = return . Just . (userDisplayName :) . html2textlines
|
||||||
|
|
||||||
-- primed variant returns storedMarkup without prefixed userDisplayName and whether updates are automatic
|
-- primed variant returns storedMarkup without prefixed userDisplayName and whether updates are automatic
|
||||||
@ -174,15 +174,15 @@ getPostalAddress' Entity{entityKey=uid, entityVal=User{..}}
|
|||||||
= do
|
= do
|
||||||
muavs <- getBy $ UniqueUserAvsUser uid
|
muavs <- getBy $ UniqueUserAvsUser uid
|
||||||
let auto = userPostAddress == muavs ^? _Just . _userAvsLastFirmInfo . _Just . _avsFirmPostAddress . _Just -- Recall: using _Just with ^. on Nothing yields mempty
|
let auto = userPostAddress == muavs ^? _Just . _userAvsLastFirmInfo . _Just . _avsFirmPostAddress . _Just -- Recall: using _Just with ^. on Nothing yields mempty
|
||||||
return (userPostAddress, auto)
|
return (userPostAddress, auto)
|
||||||
| otherwise
|
| otherwise
|
||||||
= do
|
= do
|
||||||
getUserPrimaryCompanyAddress uid companyPostAddress >>= \case
|
getUserPrimaryCompanyAddress uid companyPostAddress >>= \case
|
||||||
res@(Just _)
|
res@(Just _)
|
||||||
-> return (res, True)
|
-> return (res, True)
|
||||||
Nothing
|
Nothing
|
||||||
| Just abt <- userCompanyDepartment
|
| Just abt <- userCompanyDepartment
|
||||||
-> return $ (,True) $ Just $ plaintextToStoredMarkup $ textUnlines $
|
-> return $ (,True) $ Just $ plaintextToStoredMarkup $ textUnlines $
|
||||||
if | "BVD" `isPrefixOf` abt -> [userDisplayName, abt, "Bodenverkehrsdienste"]
|
if | "BVD" `isPrefixOf` abt -> [userDisplayName, abt, "Bodenverkehrsdienste"]
|
||||||
| otherwise -> [userDisplayName, abt, "Hausbriefkasten" ]
|
| otherwise -> [userDisplayName, abt, "Hausbriefkasten" ]
|
||||||
| otherwise -> return (Nothing, True)
|
| otherwise -> return (Nothing, True)
|
||||||
@ -214,10 +214,10 @@ getReceiversFor uids = (E.unValue <<$>>) $ E.select $ E.distinct $ do
|
|||||||
|
|
||||||
-- | return underlings for currently logged in user
|
-- | return underlings for currently logged in user
|
||||||
getSupervisees :: DB (Set UserId)
|
getSupervisees :: DB (Set UserId)
|
||||||
getSupervisees = do
|
getSupervisees = do
|
||||||
uid <- requireAuthId
|
uid <- requireAuthId
|
||||||
svs <- userSupervisorUser . entityVal <<$>> selectList [UserSupervisorSupervisor ==. uid] [Asc UserSupervisorUser]
|
svs <- userSupervisorUser . entityVal <<$>> selectList [UserSupervisorSupervisor ==. uid] [Asc UserSupervisorUser]
|
||||||
return $ Set.insert uid $ Set.fromAscList svs
|
return $ Set.insert uid $ Set.fromAscList svs
|
||||||
|
|
||||||
|
|
||||||
computeUserAuthenticationDigest :: AuthenticationMode -> Digest SHA3_256
|
computeUserAuthenticationDigest :: AuthenticationMode -> Digest SHA3_256
|
||||||
@ -408,10 +408,10 @@ assimilateUser :: UserId -- ^ @newUserId@
|
|||||||
-- Fatal errors are thrown, non-fatal warnings are returned
|
-- Fatal errors are thrown, non-fatal warnings are returned
|
||||||
assimilateUser newUserId oldUserId = mapReaderT execWriterT $ do
|
assimilateUser newUserId oldUserId = mapReaderT execWriterT $ do
|
||||||
-- retrieve user entities first, to ensure they both exist
|
-- retrieve user entities first, to ensure they both exist
|
||||||
(oldUserEnt, newUserEnt) <- do
|
(oldUserEnt, newUserEnt) <- do
|
||||||
oldUser <- getEntity oldUserId
|
oldUser <- getEntity oldUserId
|
||||||
newUser <- getEntity newUserId
|
newUser <- getEntity newUserId
|
||||||
case (oldUser, newUser) of
|
case (oldUser, newUser) of
|
||||||
(Just old, Just new) -> return (old,new)
|
(Just old, Just new) -> return (old,new)
|
||||||
_ -> tellError UserAssimilateCouldNotDetermineUserIdents
|
_ -> tellError UserAssimilateCouldNotDetermineUserIdents
|
||||||
let oldUser = oldUserEnt ^. _entityVal
|
let oldUser = oldUserEnt ^. _entityVal
|
||||||
@ -914,7 +914,7 @@ assimilateUser newUserId oldUserId = mapReaderT execWriterT $ do
|
|||||||
|
|
||||||
-- Qualifications and ongoing LMS
|
-- Qualifications and ongoing LMS
|
||||||
-- LmsUser: insertSelectWithConflict impossible due to 2 simultaneous uniqueness constraints; UniqueLmsIdent requires proper update, prohibits insert and then delete
|
-- LmsUser: insertSelectWithConflict impossible due to 2 simultaneous uniqueness constraints; UniqueLmsIdent requires proper update, prohibits insert and then delete
|
||||||
-- updateWhere [ LmsUserUser ==. oldUserId ] [ LmsUserUser =. newUserId ] -- might fail due to UniqueLmsQualficationUuser
|
-- updateWhere [ LmsUserUser ==. oldUserId ] [ LmsUserUser =. newUserId ] -- might fail due to UniqueLmsQualificationUuser
|
||||||
oldLms <- selectList [ LmsUserUser ==. oldUserId ] [ Asc LmsUserQualification ]
|
oldLms <- selectList [ LmsUserUser ==. oldUserId ] [ Asc LmsUserQualification ]
|
||||||
newLms <- selectList [ LmsUserUser ==. newUserId ] [ Asc LmsUserQualification ]
|
newLms <- selectList [ LmsUserUser ==. newUserId ] [ Asc LmsUserQualification ]
|
||||||
let projQ = lmsUserQualification . entityVal
|
let projQ = lmsUserQualification . entityVal
|
||||||
@ -931,13 +931,13 @@ assimilateUser newUserId oldUserId = mapReaderT execWriterT $ do
|
|||||||
EL.on ( newQual E.?. QualificationUserQualification E.?=. oldQual E.^. QualificationUserQualification
|
EL.on ( newQual E.?. QualificationUserQualification E.?=. oldQual E.^. QualificationUserQualification
|
||||||
E.&&. newQual E.?. QualificationUserUser E.?=. E.val newUserId
|
E.&&. newQual E.?. QualificationUserUser E.?=. E.val newUserId
|
||||||
)
|
)
|
||||||
E.where_ $ oldQual E.^. QualificationUserUser E.==. E.val oldUserId
|
E.where_ $ oldQual E.^. QualificationUserUser E.==. E.val oldUserId
|
||||||
return (oldQual, newQual)
|
return (oldQual, newQual)
|
||||||
forM_ usrQualis $ \case
|
forM_ usrQualis $ \case
|
||||||
(Entity oldQKey _, Nothing) -> update oldQKey [ QualificationUserUser =. newUserId ] -- update must succeed if there is not RHS in the join
|
(Entity oldQKey _, Nothing) -> update oldQKey [ QualificationUserUser =. newUserId ] -- update must succeed if there is not RHS in the join
|
||||||
(Entity oldQKey oldQUsr, Just (Entity newQKey newQUsr)) -> do
|
(Entity oldQKey oldQUsr, Just (Entity newQKey newQUsr)) -> do
|
||||||
updateWhere [ QualificationUserBlockQualificationUser ==. oldQKey ] [ QualificationUserBlockQualificationUser =. newQKey ]
|
updateWhere [ QualificationUserBlockQualificationUser ==. oldQKey ] [ QualificationUserBlockQualificationUser =. newQKey ]
|
||||||
update newQKey
|
update newQKey
|
||||||
[ QualificationUserValidUntil =. (max `on` view _qualificationUserValidUntil ) oldQUsr newQUsr
|
[ QualificationUserValidUntil =. (max `on` view _qualificationUserValidUntil ) oldQUsr newQUsr
|
||||||
, QualificationUserLastRefresh =. (max `on` view _qualificationUserLastRefresh ) oldQUsr newQUsr
|
, QualificationUserLastRefresh =. (max `on` view _qualificationUserLastRefresh ) oldQUsr newQUsr
|
||||||
, QualificationUserFirstHeld =. (min `on` view _qualificationUserFirstHeld ) oldQUsr newQUsr
|
, QualificationUserFirstHeld =. (min `on` view _qualificationUserFirstHeld ) oldQUsr newQUsr
|
||||||
@ -945,7 +945,7 @@ assimilateUser newUserId oldUserId = mapReaderT execWriterT $ do
|
|||||||
, QualificationUserLastNotified =. (max `on` view _qualificationUserLastNotified ) oldQUsr newQUsr
|
, QualificationUserLastNotified =. (max `on` view _qualificationUserLastNotified ) oldQUsr newQUsr
|
||||||
]
|
]
|
||||||
delete oldQKey
|
delete oldQKey
|
||||||
-- deleteWhere [ QualificationUserUser ==. oldUserId ] -- no longer needed
|
-- deleteWhere [ QualificationUserUser ==. oldUserId ] -- no longer needed
|
||||||
|
|
||||||
-- PrintJobs
|
-- PrintJobs
|
||||||
updateWhere [ PrintJobRecipient ==. Just oldUserId ] [ PrintJobRecipient =. Just newUserId ]
|
updateWhere [ PrintJobRecipient ==. Just oldUserId ] [ PrintJobRecipient =. Just newUserId ]
|
||||||
@ -963,10 +963,10 @@ assimilateUser newUserId oldUserId = mapReaderT execWriterT $ do
|
|||||||
E.<&> (userSupervisor E.^. UserSupervisorCompany)
|
E.<&> (userSupervisor E.^. UserSupervisorCompany)
|
||||||
E.<&> (userSupervisor E.^. UserSupervisorReason)
|
E.<&> (userSupervisor E.^. UserSupervisorReason)
|
||||||
)
|
)
|
||||||
(\current excluded ->
|
(\current excluded ->
|
||||||
[ UserSupervisorRerouteNotifications E.=. (current E.^. UserSupervisorRerouteNotifications E.||. excluded E.^. UserSupervisorRerouteNotifications)
|
[ UserSupervisorRerouteNotifications E.=. (current E.^. UserSupervisorRerouteNotifications E.||. excluded E.^. UserSupervisorRerouteNotifications)
|
||||||
, UserSupervisorCompany E.=. E.coalesce [current E.^. UserSupervisorCompany, excluded E.^. UserSupervisorCompany]
|
, UserSupervisorCompany E.=. E.coalesce [current E.^. UserSupervisorCompany, excluded E.^. UserSupervisorCompany]
|
||||||
, UserSupervisorReason E.=. E.coalesce [current E.^. UserSupervisorReason , excluded E.^. UserSupervisorReason]
|
, UserSupervisorReason E.=. E.coalesce [current E.^. UserSupervisorReason , excluded E.^. UserSupervisorReason]
|
||||||
] )
|
] )
|
||||||
deleteWhere [ UserSupervisorSupervisor ==. oldUserId]
|
deleteWhere [ UserSupervisorSupervisor ==. oldUserId]
|
||||||
|
|
||||||
@ -981,10 +981,10 @@ assimilateUser newUserId oldUserId = mapReaderT execWriterT $ do
|
|||||||
E.<&> (userSupervisor E.^. UserSupervisorCompany)
|
E.<&> (userSupervisor E.^. UserSupervisorCompany)
|
||||||
E.<&> (userSupervisor E.^. UserSupervisorReason)
|
E.<&> (userSupervisor E.^. UserSupervisorReason)
|
||||||
)
|
)
|
||||||
(\current excluded ->
|
(\current excluded ->
|
||||||
[ UserSupervisorRerouteNotifications E.=. (current E.^. UserSupervisorRerouteNotifications E.||. excluded E.^. UserSupervisorRerouteNotifications)
|
[ UserSupervisorRerouteNotifications E.=. (current E.^. UserSupervisorRerouteNotifications E.||. excluded E.^. UserSupervisorRerouteNotifications)
|
||||||
, UserSupervisorCompany E.=. E.coalesce [current E.^. UserSupervisorCompany, excluded E.^. UserSupervisorCompany]
|
, UserSupervisorCompany E.=. E.coalesce [current E.^. UserSupervisorCompany, excluded E.^. UserSupervisorCompany]
|
||||||
, UserSupervisorReason E.=. E.coalesce [current E.^. UserSupervisorReason , excluded E.^. UserSupervisorReason]
|
, UserSupervisorReason E.=. E.coalesce [current E.^. UserSupervisorReason , excluded E.^. UserSupervisorReason]
|
||||||
] )
|
] )
|
||||||
deleteWhere [ UserSupervisorUser ==. oldUserId]
|
deleteWhere [ UserSupervisorUser ==. oldUserId]
|
||||||
|
|
||||||
@ -1001,7 +1001,7 @@ assimilateUser newUserId oldUserId = mapReaderT execWriterT $ do
|
|||||||
E.<&> (userCompany E.^. UserCompanyPriority)
|
E.<&> (userCompany E.^. UserCompanyPriority)
|
||||||
E.<&> (userCompany E.^. UserCompanyUseCompanyAddress)
|
E.<&> (userCompany E.^. UserCompanyUseCompanyAddress)
|
||||||
)
|
)
|
||||||
(\current excluded ->
|
(\current excluded ->
|
||||||
[ UserCompanySupervisor E.=. E.greatest (current E.^. UserCompanySupervisor) (excluded E.^. UserCompanySupervisor) -- t > f
|
[ UserCompanySupervisor E.=. E.greatest (current E.^. UserCompanySupervisor) (excluded E.^. UserCompanySupervisor) -- t > f
|
||||||
, UserCompanyPriority E.=. E.greatest (current E.^. UserCompanyPriority) (excluded E.^. UserCompanyPriority)
|
, UserCompanyPriority E.=. E.greatest (current E.^. UserCompanyPriority) (excluded E.^. UserCompanyPriority)
|
||||||
, UserCompanyUseCompanyAddress E.=. E.greatest (current E.^. UserCompanyUseCompanyAddress) (excluded E.^. UserCompanyUseCompanyAddress)
|
, UserCompanyUseCompanyAddress E.=. E.greatest (current E.^. UserCompanyUseCompanyAddress) (excluded E.^. UserCompanyUseCompanyAddress)
|
||||||
@ -1010,13 +1010,13 @@ assimilateUser newUserId oldUserId = mapReaderT execWriterT $ do
|
|||||||
deleteWhere [ UserCompanyUser ==. oldUserId]
|
deleteWhere [ UserCompanyUser ==. oldUserId]
|
||||||
|
|
||||||
mbOldAvsId <- getBy $ UniqueUserAvsUser oldUserId
|
mbOldAvsId <- getBy $ UniqueUserAvsUser oldUserId
|
||||||
mbNewAvsId <- getBy $ UniqueUserAvsUser newUserId
|
mbNewAvsId <- getBy $ UniqueUserAvsUser newUserId
|
||||||
case (mbOldAvsId,mbNewAvsId) of
|
case (mbOldAvsId,mbNewAvsId) of
|
||||||
(Nothing, _)
|
(Nothing, _)
|
||||||
-> return ()
|
-> return ()
|
||||||
(Just Entity{entityVal=UserAvs{userAvsPersonId=oldAvsId}}, Just _)
|
(Just Entity{entityVal=UserAvs{userAvsPersonId=oldAvsId}}, Just _)
|
||||||
-> deleteBy (UniqueUserAvsId oldAvsId)
|
-> deleteBy (UniqueUserAvsId oldAvsId)
|
||||||
(Just Entity{entityVal=oldUserAvs}, Nothing)
|
(Just Entity{entityVal=oldUserAvs}, Nothing)
|
||||||
-> void $ upsertBySafe (UniqueUserAvsId (oldUserAvs ^. _userAvsPersonId)) oldUserAvs{userAvsUser=newUserId} (_userAvsUser .~ newUserId)
|
-> void $ upsertBySafe (UniqueUserAvsId (oldUserAvs ^. _userAvsPersonId)) oldUserAvs{userAvsUser=newUserId} (_userAvsUser .~ newUserId)
|
||||||
|
|
||||||
-- merge some optional / incomplete user fields
|
-- merge some optional / incomplete user fields
|
||||||
@ -1025,7 +1025,7 @@ assimilateUser newUserId oldUserId = mapReaderT execWriterT $ do
|
|||||||
oldV = oldUserEnt ^. ufl
|
oldV = oldUserEnt ^. ufl
|
||||||
newV = newUserEnt ^. ufl
|
newV = newUserEnt ^. ufl
|
||||||
in toMaybe (cmp oldV newV) (uf =. oldV)
|
in toMaybe (cmp oldV newV) (uf =. oldV)
|
||||||
|
|
||||||
mergeMaybe :: forall b . PersistField b => EntityField User (Maybe b) -> Maybe (Update User)
|
mergeMaybe :: forall b . PersistField b => EntityField User (Maybe b) -> Maybe (Update User)
|
||||||
mergeMaybe = mergeBy (\oldV newV -> isNothing newV && isJust oldV)
|
mergeMaybe = mergeBy (\oldV newV -> isNothing newV && isJust oldV)
|
||||||
|
|
||||||
@ -1045,14 +1045,14 @@ assimilateUser newUserId oldUserId = mapReaderT execWriterT $ do
|
|||||||
(UserPostLastUpdate =. oldUser ^. _userPostLastUpdate)
|
(UserPostLastUpdate =. oldUser ^. _userPostLastUpdate)
|
||||||
, toMaybe ((isJust (newUser ^. _userPostAddress) || isJust (oldUser ^. _userPostAddress))
|
, toMaybe ((isJust (newUser ^. _userPostAddress) || isJust (oldUser ^. _userPostAddress))
|
||||||
&& (newUser ^. _userPrefersPostal || oldUser ^. _userPrefersPostal))
|
&& (newUser ^. _userPrefersPostal || oldUser ^. _userPrefersPostal))
|
||||||
(UserPrefersPostal =. True)
|
(UserPrefersPostal =. True)
|
||||||
, mergeMaybe UserPinPassword
|
, mergeMaybe UserPinPassword
|
||||||
, mergeMaybe UserLanguages
|
, mergeMaybe UserLanguages
|
||||||
, mergeMaybe UserSex
|
, mergeMaybe UserSex
|
||||||
, mergeMaybe UserBirthday
|
, mergeMaybe UserBirthday
|
||||||
, mergeMaybe UserTelephone
|
, mergeMaybe UserTelephone
|
||||||
, mergeMaybe UserMobile
|
, mergeMaybe UserMobile
|
||||||
]
|
]
|
||||||
|
|
||||||
delete oldUserId
|
delete oldUserId
|
||||||
let oldUsrIdent = oldUser ^. _userIdent
|
let oldUsrIdent = oldUser ^. _userIdent
|
||||||
|
|||||||
@ -265,7 +265,7 @@ dispatchJobLmsReports :: QualificationId -> JobHandler UniWorX
|
|||||||
dispatchJobLmsReports qid = JobHandlerAtomic act
|
dispatchJobLmsReports qid = JobHandlerAtomic act
|
||||||
where
|
where
|
||||||
-- act :: YesodJobDB UniWorX ()
|
-- act :: YesodJobDB UniWorX ()
|
||||||
act = whenM (exists [LmsReportQualification ==. qid]) $ do -- executing twice must be prohibited due to assertion that ALL learners are always sent (D fails otherwise)
|
act = whenM (exists [LmsReportQualification ==. qid]) $ do -- executing twice must be prohibited due to assertion that ALL learners are always sent (E fails otherwise)
|
||||||
now <- liftIO getCurrentTime
|
now <- liftIO getCurrentTime
|
||||||
-- DEBUG 2rows; remove later
|
-- DEBUG 2rows; remove later
|
||||||
totalrows <- count [LmsReportQualification ==. qid]
|
totalrows <- count [LmsReportQualification ==. qid]
|
||||||
|
|||||||
@ -12,4 +12,4 @@ $# SPDX-License-Identifier: AGPL-3.0-or-later
|
|||||||
<p>
|
<p>
|
||||||
FRADrive supports training courses by handling #
|
FRADrive supports training courses by handling #
|
||||||
registration, correspondence, course homepages, examinations and #
|
registration, correspondence, course homepages, examinations and #
|
||||||
managing the gained qualfications.
|
managing the gained qualifications.
|
||||||
|
|||||||
@ -17,7 +17,19 @@ $# SPDX-License-Identifier: AGPL-3.0-or-later
|
|||||||
|
|
||||||
$maybe daudit <- qualificationAuditDuration quali
|
$maybe daudit <- qualificationAuditDuration quali
|
||||||
<dt .deflist__dt>_{MsgQualificationAuditDuration}
|
<dt .deflist__dt>_{MsgQualificationAuditDuration}
|
||||||
<dd .deflist__dd>_{MsgMonths (fromIntegral daudit)}
|
<dd .deflist__dd>
|
||||||
|
$maybe lqre <- lmsQualiReused
|
||||||
|
$maybe daudit <- qualificationAuditDuration lqre
|
||||||
|
_{MsgMonths (fromIntegral daudit)}
|
||||||
|
$nothing
|
||||||
|
_{MsgQualificationAuditDurationReuseError}
|
||||||
|
$nothing
|
||||||
|
_{MsgMonths (fromIntegral daudit)}
|
||||||
|
$nothing
|
||||||
|
$maybe lqre <- lmsQualiReused
|
||||||
|
$maybe daudit <- qualificationAuditDuration lqre
|
||||||
|
<dt .deflist__dt>_{MsgQualificationAuditDuration}
|
||||||
|
<dd .deflist__dd>_{MsgMonths (fromIntegral daudit)}
|
||||||
|
|
||||||
$maybe drefresh <- qualificationRefreshWithin quali
|
$maybe drefresh <- qualificationRefreshWithin quali
|
||||||
<dt .deflist__dt>_{MsgQualificationRefreshWithin} ^{iconTooltip (msg2widget MsgQualificationRefreshWithinTooltip) Nothing True}
|
<dt .deflist__dt>_{MsgQualificationRefreshWithin} ^{iconTooltip (msg2widget MsgQualificationRefreshWithinTooltip) Nothing True}
|
||||||
@ -42,6 +54,10 @@ $# SPDX-License-Identifier: AGPL-3.0-or-later
|
|||||||
, #
|
, #
|
||||||
$if drd > 0
|
$if drd > 0
|
||||||
_{MsgDays (fromIntegral drd)}
|
_{MsgDays (fromIntegral drd)}
|
||||||
|
|
||||||
|
$maybe lqre <- lmsQualiReused
|
||||||
|
<dt .deflist__dt>_{MsgTableQualificationLmsReusesTooltip}
|
||||||
|
<dd .deflist__dd>^{simpleLink (citext2widget (qualificationName lqre)) (QualificationR (qualificationSchool lqre) (qualificationShorthand lqre))}
|
||||||
|
|
||||||
<dt .deflist__dt>_{MsgQualificationElearningStart}
|
<dt .deflist__dt>_{MsgQualificationElearningStart}
|
||||||
<dd .deflist__dd>#{boolSymbol (qualificationElearningStart quali)}
|
<dd .deflist__dd>#{boolSymbol (qualificationElearningStart quali)}
|
||||||
|
|||||||
@ -701,7 +701,7 @@ fillDb = do
|
|||||||
]
|
]
|
||||||
++ take 444 [ UserSupervisor fhamann uid True Nothing (Just $ tshow SupervisorReasonCompanyDefault) | Entity uid _ <- matUsers, uid /= jost]
|
++ take 444 [ UserSupervisor fhamann uid True Nothing (Just $ tshow SupervisorReasonCompanyDefault) | Entity uid _ <- matUsers, uid /= jost]
|
||||||
++ take 123 [ UserSupervisor gkleen uid True Nothing Nothing | Entity uid _ <- drop 369 matUsers ]
|
++ take 123 [ UserSupervisor gkleen uid True Nothing Nothing | Entity uid _ <- drop 369 matUsers ]
|
||||||
++ take 11 [ UserSupervisor jost uid False Nothing (Just $ tshow SupervisorReasonCompanyDefault) | Entity uid _ <- drop 501 matUsers ]
|
++ take 11 [ UserSupervisor jost uid False Nothing (Just $ tshow SupervisorReasonAvsSuperior) | Entity uid _ <- drop 501 matUsers ]
|
||||||
upsertManyWhere supvs [] [] []
|
upsertManyWhere supvs [] [] []
|
||||||
-- insertMany_ supvs -- NOTE: multiple calls like this throw a runtime error!
|
-- insertMany_ supvs -- NOTE: multiple calls like this throw a runtime error!
|
||||||
-- upsertManyWhere supvs [] [] [] -- NOTE: multiple calls like this are ok
|
-- upsertManyWhere supvs [] [] [] -- NOTE: multiple calls like this are ok
|
||||||
@ -753,18 +753,20 @@ fillDb = do
|
|||||||
let r_descr = Just $ htmlToStoredMarkup [shamlet|<p>Berechtigung zum Führen eines Fahrzeuges auf dem gesamten Rollfeld.|]
|
let r_descr = Just $ htmlToStoredMarkup [shamlet|<p>Berechtigung zum Führen eines Fahrzeuges auf dem gesamten Rollfeld.|]
|
||||||
let l_descr = Just $ htmlToStoredMarkup [shamlet|<p>für unhabilitierte|]
|
let l_descr = Just $ htmlToStoredMarkup [shamlet|<p>für unhabilitierte|]
|
||||||
|
|
||||||
qid_f <- insert' $ Qualification avn "F" "Vorfeldführerschein" f_descr (Just 24) (Just 6) (Just $ CalendarDiffDays 0 60) (Just $ CalendarDiffDays 0 14) True True True (Just AvsLicenceVorfeld) $ Just "F4466"
|
qid_f <- insert' $ Qualification avn "F" "Vorfeldführerschein" f_descr (Just 24) (Just 8) (Just $ CalendarDiffDays 0 60) (Just $ CalendarDiffDays 0 14) True True Nothing True (Just AvsLicenceVorfeld) $ Just "F4466"
|
||||||
qid_r <- insert' $ Qualification avn "R" "Rollfeldführerschein" r_descr (Just 12) (Just 6) (Just $ CalendarDiffDays 2 3) Nothing False False False (Just AvsLicenceRollfeld) $ Just "R2801"
|
qid_r <- insert' $ Qualification avn "R" "Rollfeldführerschein" r_descr (Just 12) (Just 6) (Just $ CalendarDiffDays 2 3) Nothing False False Nothing False (Just AvsLicenceRollfeld) $ Just "R2801"
|
||||||
qid_l <- insert' $ Qualification ifi "L" "Lehrbefähigung" l_descr Nothing (Just 6) Nothing Nothing True False True Nothing Nothing
|
qid_rp <- insert' $ Qualification avn "R+" "Rollfeldführerschein-Plus" r_descr (Just 12) (Just 4) (Just $ CalendarDiffDays 2 3) Nothing False False (Just qid_r) False (Just AvsLicenceRollfeld) $ Just "R2802"
|
||||||
|
qid_l <- insert' $ Qualification ifi "L" "Lehrbefähigung" l_descr Nothing (Just 6) Nothing Nothing True False Nothing True Nothing Nothing
|
||||||
qfjost <- insert' $ QualificationUser jost qid_f (n_day 11) (n_day $ -1) (n_day $ -22) True (n_day' $ -9) -- TODO: better dates!
|
qfjost <- insert' $ QualificationUser jost qid_f (n_day 11) (n_day $ -1) (n_day $ -22) True (n_day' $ -9) -- TODO: better dates!
|
||||||
void . insert $ QualificationUserBlock qfjost False (n_day' $ -6) "First block" (Just svaupel)
|
void . insert $ QualificationUserBlock qfjost False (n_day' $ -6) "First block" (Just svaupel)
|
||||||
void . insert $ QualificationUserBlock qfjost True (n_day' $ -5) "Second unblock" (Just gkleen)
|
void . insert $ QualificationUserBlock qfjost True (n_day' $ -5) "Second unblock" (Just gkleen)
|
||||||
void . insert $ QualificationUserBlock qfjost False (n_day' $ -4) "Third block" Nothing
|
void . insert $ QualificationUserBlock qfjost False (n_day' $ -4) "Third block" Nothing
|
||||||
void . insert $ QualificationUserBlock qfjost True (n_day' $ -3) "Fourth unblock" (Just sbarth)
|
void . insert $ QualificationUserBlock qfjost True (n_day' $ -3) "Fourth unblock" (Just sbarth)
|
||||||
void . insert $ QualificationUserBlock qfjost False (n_day' $ -1) "Fifth block" (Just svaupel)
|
void . insert $ QualificationUserBlock qfjost False (n_day' $ -1) "Fifth block" (Just svaupel)
|
||||||
void . insert' $ QualificationUser jost qid_r (n_day 99) (n_day $ -11) (n_day $ -222) True (n_day' $ -9) -- TODO: better dates!
|
void . insert' $ QualificationUser jost qid_r (n_day 99) (n_day $ -11) (n_day $ -222) True (n_day' $ -9) -- TODO: better dates!
|
||||||
void . insert' $ QualificationUser jost qid_l (n_day 999) (n_day $ -111) (n_day $ -2222) True (n_day' $ -9) -- TODO: better dates!
|
void . insert' $ QualificationUser jost qid_l (n_day 999) (n_day $ -111) (n_day $ -2222) True (n_day' $ -9) -- TODO: better dates!
|
||||||
qfkleen <- insert' $ QualificationUser gkleen qid_f (n_day 33) (n_day $ -4) (n_day $ -20) True (n_day' $ -9)
|
void . insert' $ QualificationUser jost qid_rp (n_day 999) (n_day $ -111) (n_day $ -2222) True (n_day' $ -9) -- TODO: better dates!
|
||||||
|
qfkleen <- insert' $ QualificationUser gkleen qid_f (n_day 33) (n_day $ -4) (n_day $ -20) True (n_day' $ -9)
|
||||||
void . insert $ QualificationUserBlock qfkleen False (n_day' 1) "Future block" (Just svaupel)
|
void . insert $ QualificationUserBlock qfkleen False (n_day' 1) "Future block" (Just svaupel)
|
||||||
void . insert' $ QualificationUser maxMuster qid_f (n_day 0) (n_day $ -2) (n_day $ -8) False (n_day' $ -1)
|
void . insert' $ QualificationUser maxMuster qid_f (n_day 0) (n_day $ -2) (n_day $ -8) False (n_day' $ -1)
|
||||||
qfvaupel <- insert' $ QualificationUser svaupel qid_f (n_day 2) (n_day $ -1) (n_day $ -2) True (n_day' $ -9)
|
qfvaupel <- insert' $ QualificationUser svaupel qid_f (n_day 2) (n_day $ -1) (n_day $ -2) True (n_day' $ -9)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user