chore(lms): add note to orphans and create working filter
This commit is contained in:
parent
b78bd7971a
commit
572ad4afa1
@ -162,4 +162,6 @@ LmsOrphans: Verwaiste Logins
|
|||||||
LmsOrphanNr n@Int: #{n} verwaiste E‑Learning Logins für diese Qualifikation erkannt.
|
LmsOrphanNr n@Int: #{n} verwaiste E‑Learning Logins für diese Qualifikation erkannt.
|
||||||
LmsOrphanSeenFirst: Zuerst erkannt
|
LmsOrphanSeenFirst: Zuerst erkannt
|
||||||
LmsOrphanSeenLast: Zuletzt erhalten
|
LmsOrphanSeenLast: Zuletzt erhalten
|
||||||
LmsOrphanDeletedLast: Zuletzt Löschung beantragt
|
LmsOrphanDeletedLast: Zuletzt Löschung beantragt
|
||||||
|
LmsOrphanReason: Bemerkung
|
||||||
|
LmsOrphanPreviewFltr: Vorschau Löschungen bei nächstem Abruf
|
||||||
@ -162,4 +162,6 @@ LmsOrphans: Orphaned logins
|
|||||||
LmsOrphanNr n@Int: #{n} orphaned e‑learning login detected for this qualification.
|
LmsOrphanNr n@Int: #{n} orphaned e‑learning login detected for this qualification.
|
||||||
LmsOrphanSeenFirst: First seen
|
LmsOrphanSeenFirst: First seen
|
||||||
LmsOrphanSeenLast: Last seen
|
LmsOrphanSeenLast: Last seen
|
||||||
LmsOrphanDeletedLast: Deletion requested
|
LmsOrphanDeletedLast: Deletion requested
|
||||||
|
LmsOrphanReason: Note
|
||||||
|
LmsOrphanPreviewFltr: Preview deletions next synch
|
||||||
|
|||||||
@ -172,5 +172,6 @@ LmsOrphan
|
|||||||
seenFirst UTCTime default=now() -- first time reported by LMS
|
seenFirst UTCTime default=now() -- first time reported by LMS
|
||||||
seenLast UTCTime default=now() -- last acknowledgement by LMS, deletion uses QualificationAuditDuration
|
seenLast UTCTime default=now() -- last acknowledgement by LMS, deletion uses QualificationAuditDuration
|
||||||
deletedLast UTCTime Maybe -- last deletion request sent to LMS
|
deletedLast UTCTime Maybe -- last deletion request sent to LMS
|
||||||
|
reason Text Maybe -- to mark explicit e-learning deletions, etc
|
||||||
UniqueLmsOrphan qualification ident -- unlike other tables, LMS Idents must only be unique within qualification, allowing orphans to be handled independently
|
UniqueLmsOrphan qualification ident -- unlike other tables, LMS Idents must only be unique within qualification, allowing orphans to be handled independently
|
||||||
deriving Generic Show
|
deriving Generic Show
|
||||||
@ -185,15 +185,36 @@ getLmsLearnersR sid qsh = do
|
|||||||
setTitleI MsgMenuLmsLearners
|
setTitleI MsgMenuLmsLearners
|
||||||
lmsTable
|
lmsTable
|
||||||
|
|
||||||
getLmsLearnersDirectR :: SchoolId -> QualificationShorthand -> Handler TypedContent
|
|
||||||
getLmsLearnersDirectR sid qsh = do
|
data OrphanParams = OrphanParams { cutoff_seen_first, cutoff_deleted_last, cutoff_seen_last :: UTCTime, orphan_max_batch :: Int64 }
|
||||||
-- $logInfoS "LMS" $ "Direct Download Users for " <> tshow qsh <> " at " <> tshow sid
|
deriving (Show, Generic, Binary)
|
||||||
|
|
||||||
|
selectOrphans :: QualificationId -> UTCTime -> DB ([Entity LmsOrphan], OrphanParams)
|
||||||
|
selectOrphans qid now = do
|
||||||
lmsConf <- getsYesod $ view _appLmsConf
|
lmsConf <- getsYesod $ view _appLmsConf
|
||||||
now <- liftIO getCurrentTime
|
|
||||||
let cutoff_seen_first = addLocalDays (negate $ lmsConf ^. _lmsOrphanDeletionDays) now
|
let cutoff_seen_first = addLocalDays (negate $ lmsConf ^. _lmsOrphanDeletionDays) now
|
||||||
cutoff_deleted_last = addHours (negate $ lmsConf ^. _lmsOrphanRepeatHours) now
|
cutoff_deleted_last = addHours (negate $ lmsConf ^. _lmsOrphanRepeatHours) now
|
||||||
cutoff_seen_last = cutoff_deleted_last
|
cutoff_seen_last = cutoff_deleted_last
|
||||||
orphan_max_batch = lmsConf ^. _lmsOrphanDeletionBatch
|
orphan_max_batch = lmsConf ^. _lmsOrphanDeletionBatch
|
||||||
|
orphans <- Ex.select $ do
|
||||||
|
orv <- Ex.from $ Ex.table @LmsOrphan
|
||||||
|
Ex.where_ $ Ex.val qid Ex.==. orv Ex.^. LmsOrphanQualification
|
||||||
|
Ex.&&. Ex.val cutoff_seen_first Ex.>=. orv Ex.^. LmsOrphanSeenFirst -- has been seen for while
|
||||||
|
Ex.&&. Ex.val cutoff_seen_last Ex.<=. orv Ex.^. LmsOrphanSeenLast -- was still seen recently
|
||||||
|
Ex.&&. Ex.val cutoff_deleted_last E.<~. orv Ex.^. LmsOrphanDeletedLast -- not already recently deleted
|
||||||
|
Ex.&&. Ex.notExists (do -- not currently used anywhere (LmsIdent share the namespace)
|
||||||
|
lusr <- Ex.from $ Ex.table @LmsUser
|
||||||
|
Ex.where_ $ lusr Ex.^. LmsUserIdent Ex.==. orv Ex.^.LmsOrphanIdent
|
||||||
|
)
|
||||||
|
Ex.orderBy [Ex.desc $ orv Ex.^. LmsOrphanDeletedLast, Ex.asc $ orv Ex.^. LmsOrphanSeenLast] -- Note for PostgreSQL: DESC == DESC NULLS FIRST
|
||||||
|
Ex.limit orphan_max_batch
|
||||||
|
return orv
|
||||||
|
return (orphans, OrphanParams{..})
|
||||||
|
|
||||||
|
|
||||||
|
getLmsLearnersDirectR :: SchoolId -> QualificationShorthand -> Handler TypedContent
|
||||||
|
getLmsLearnersDirectR sid qsh = do
|
||||||
|
-- $logInfoS "LMS" $ "Direct Download Users for " <> tshow qsh <> " at " <> tshow sid
|
||||||
(lms_users, orphans, cutoff, qshs) <- runDB $ do
|
(lms_users, orphans, cutoff, qshs) <- runDB $ do
|
||||||
(qid, cutoff) <- getQidCutoff sid qsh
|
(qid, cutoff) <- getQidCutoff sid qsh
|
||||||
qidsReuse <- selectList [QualificationLmsReuses ==. Just qid] []
|
qidsReuse <- selectList [QualificationLmsReuses ==. Just qid] []
|
||||||
@ -216,19 +237,8 @@ getLmsLearnersDirectR sid qsh = do
|
|||||||
, csvLUTstaff = LmsBool False
|
, csvLUTstaff = LmsBool False
|
||||||
}
|
}
|
||||||
-}
|
-}
|
||||||
orphans <- Ex.select $ do
|
now <- liftIO getCurrentTime
|
||||||
orv <- Ex.from $ Ex.table @LmsOrphan
|
orphans <- fst <$> selectOrphans qid now
|
||||||
Ex.where_ $ Ex.val qid Ex.==. orv Ex.^. LmsOrphanQualification
|
|
||||||
Ex.&&. Ex.val cutoff_seen_first Ex.>=. orv Ex.^. LmsOrphanSeenFirst -- has been seen for while
|
|
||||||
Ex.&&. Ex.val cutoff_seen_last Ex.<=. orv Ex.^. LmsOrphanSeenLast -- was still seen recently
|
|
||||||
Ex.&&. Ex.val cutoff_deleted_last E.<~. orv Ex.^. LmsOrphanDeletedLast -- not already recently deleted
|
|
||||||
Ex.&&. Ex.notExists (do -- not currently used anywhere (LmsIdent share the namespace)
|
|
||||||
lusr <- Ex.from $ Ex.table @LmsUser
|
|
||||||
Ex.where_ $ lusr Ex.^. LmsUserIdent Ex.==. orv Ex.^.LmsOrphanIdent
|
|
||||||
)
|
|
||||||
Ex.orderBy [Ex.desc $ orv Ex.^. LmsOrphanDeletedLast, Ex.asc $ orv Ex.^. LmsOrphanSeenLast] -- Note for PostgreSQL: DESC == DESC NULLS FIRST
|
|
||||||
Ex.limit orphan_max_batch
|
|
||||||
return orv
|
|
||||||
updateWhere [LmsOrphanId <-. fmap entityKey orphans] [LmsOrphanDeletedLast =. Just now]
|
updateWhere [LmsOrphanId <-. fmap entityKey orphans] [LmsOrphanDeletedLast =. Just now]
|
||||||
return (lms_users, orphans, cutoff, qshs)
|
return (lms_users, orphans, cutoff, qshs)
|
||||||
|
|
||||||
@ -260,29 +270,25 @@ getLmsLearnersDirectR sid qsh = do
|
|||||||
|
|
||||||
getLmsOrphansR :: SchoolId -> QualificationShorthand -> Handler Html
|
getLmsOrphansR :: SchoolId -> QualificationShorthand -> Handler Html
|
||||||
getLmsOrphansR sid qsh = do
|
getLmsOrphansR sid qsh = do
|
||||||
lmsConf <- getsYesod $ view _appLmsConf
|
(orvTable,OrphanParams{..}) <-runDB $ do
|
||||||
now <- liftIO getCurrentTime
|
now <- liftIO getCurrentTime
|
||||||
let cutoff_seen_first = addLocalDays (negate $ lmsConf ^. _lmsOrphanDeletionDays) now
|
|
||||||
cutoff_deleted_last = addHours (negate $ lmsConf ^. _lmsOrphanRepeatHours) now
|
|
||||||
cutoff_seen_last = cutoff_deleted_last
|
|
||||||
orphan_max_batch = lmsConf ^. _lmsOrphanDeletionBatch
|
|
||||||
|
|
||||||
orvTable <- fmap snd $ runDB $ do
|
|
||||||
qid <- getKeyBy404 $ SchoolQualificationShort sid qsh
|
qid <- getKeyBy404 $ SchoolQualificationShort sid qsh
|
||||||
|
(next_orphans, ops) <- $(memcachedByHere) (Just . Right $ 1 * diffMinute) [st|next-orphan-preview-#{tshow qid}|] (over _1 (map entityKey) <$> selectOrphans qid now)
|
||||||
let
|
let
|
||||||
orvDBTable = DBTable{..}
|
orvDBTable = DBTable{..}
|
||||||
where
|
where
|
||||||
-- resultOrphan = _dbrOutput . _entityVal
|
-- resultOrphan = _dbrOutput . _entityVal -- would need explicit type to work
|
||||||
dbtSQLQuery orv = do
|
dbtSQLQuery orv = do
|
||||||
E.where_ $ orv E.^. LmsOrphanQualification E.==. E.val qid
|
E.where_ $ orv E.^. LmsOrphanQualification E.==. E.val qid
|
||||||
return orv
|
return orv
|
||||||
dbtRowKey = (E.^. LmsOrphanId)
|
dbtRowKey = (E.^. LmsOrphanId)
|
||||||
dbtProj = dbtProjId
|
dbtProj = dbtProjId
|
||||||
dbtColonnade = dbColonnade $ mconcat
|
dbtColonnade = dbColonnade $ mconcat
|
||||||
[ sortable (Just "ident") (i18nCell MsgTableLmsIdent) $ \(view $ _dbrOutput . _entityVal . _lmsOrphanIdent . _getLmsIdent -> lid) -> textCell lid
|
[ sortable (Just "ident") (i18nCell MsgTableLmsIdent) $ \(view $ _dbrOutput . _entityVal . _lmsOrphanIdent . _getLmsIdent -> lid) -> textCell lid
|
||||||
, sortable (Just "seen-first") (i18nCell MsgLmsOrphanSeenFirst) $ \(view $ _dbrOutput . _entityVal . _lmsOrphanSeenFirst -> d) -> dateTimeCell d
|
, sortable (Just "seen-first") (i18nCell MsgLmsOrphanSeenFirst) $ \(view $ _dbrOutput . _entityVal . _lmsOrphanSeenFirst -> d) -> dateTimeCell d
|
||||||
, sortable (Just "seen-last") (i18nCell MsgLmsOrphanSeenLast) $ \(view $ _dbrOutput . _entityVal . _lmsOrphanSeenLast -> d) -> dateTimeCell d
|
, sortable (Just "seen-last") (i18nCell MsgLmsOrphanSeenLast) $ \(view $ _dbrOutput . _entityVal . _lmsOrphanSeenLast -> d) -> dateTimeCell d
|
||||||
, sortable (Just "deleted-last") (i18nCell MsgLmsOrphanDeletedLast) $ \(view $ _dbrOutput . _entityVal . _lmsOrphanDeletedLast -> d) -> foldMap dateTimeCell d
|
, sortable (Just "deleted-last") (i18nCell MsgLmsOrphanDeletedLast) $ \(view $ _dbrOutput . _entityVal . _lmsOrphanDeletedLast -> d) -> foldMap dateTimeCell d
|
||||||
|
, sortable (Just "note") (i18nCell MsgLmsOrphanReason) $ \(view $ _dbrOutput . _entityVal . _lmsOrphanReason -> t) -> foldMap textCell t
|
||||||
]
|
]
|
||||||
dbtSorting = Map.fromList
|
dbtSorting = Map.fromList
|
||||||
[ ("ident" , SortColumn (E.^. LmsOrphanIdent))
|
[ ("ident" , SortColumn (E.^. LmsOrphanIdent))
|
||||||
@ -291,10 +297,15 @@ getLmsOrphansR sid qsh = do
|
|||||||
, ("deleted-last" , SortColumn (E.^. LmsOrphanDeletedLast))
|
, ("deleted-last" , SortColumn (E.^. LmsOrphanDeletedLast))
|
||||||
]
|
]
|
||||||
dbtFilter = Map.fromList
|
dbtFilter = Map.fromList
|
||||||
[ ("ident" , FilterColumn $ E.mkContainsFilterWithCommaPlus LmsIdent (E.^. LmsOrphanIdent))
|
[ ("preview" , FilterColumn $ \row (getLast -> criterion) -> case criterion of
|
||||||
|
Just True -> (row E.^. LmsOrphanId) `E.in_` E.valList next_orphans
|
||||||
|
_ -> E.true
|
||||||
|
)
|
||||||
|
, ("ident" , FilterColumn $ E.mkContainsFilterWithCommaPlus LmsIdent (E.^. LmsOrphanIdent))
|
||||||
]
|
]
|
||||||
dbtFilterUI = \mPrev -> mconcat
|
dbtFilterUI = \mPrev -> mconcat
|
||||||
[ prismAForm (singletonFilter "ident" . maybePrism _PathPiece) mPrev $ aopt (hoistField lift textField) (fslI MsgTableLmsIdent & setTooltip MsgTableFilterCommaPlus)
|
[ prismAForm (singletonFilter "ident" . maybePrism _PathPiece) mPrev $ aopt (hoistField lift textField) (fslI MsgTableLmsIdent & setTooltip MsgTableFilterCommaPlus)
|
||||||
|
, prismAForm (singletonFilter "preview" . maybePrism _PathPiece) mPrev $ aopt checkBoxField (fslI MsgLmsOrphanPreviewFltr)
|
||||||
]
|
]
|
||||||
dbtStyle = def { dbsFilterLayout = defaultDBSFilterLayout }
|
dbtStyle = def { dbsFilterLayout = defaultDBSFilterLayout }
|
||||||
dbtParams = def
|
dbtParams = def
|
||||||
@ -304,7 +315,8 @@ getLmsOrphansR sid qsh = do
|
|||||||
dbtCsvDecode = Nothing
|
dbtCsvDecode = Nothing
|
||||||
dbtExtraReps = []
|
dbtExtraReps = []
|
||||||
orvDBTableValidator = def & defaultSorting [SortAscBy "seen-first", SortDescBy "deleted-last"]
|
orvDBTableValidator = def & defaultSorting [SortAscBy "seen-first", SortDescBy "deleted-last"]
|
||||||
dbTable orvDBTableValidator orvDBTable :: DB (Any, Widget)
|
tbl <- snd <$> (dbTable orvDBTableValidator orvDBTable :: DB (Any, Widget))
|
||||||
|
return (tbl,ops)
|
||||||
|
|
||||||
siteLayoutMsg MsgLmsOrphans $ do
|
siteLayoutMsg MsgLmsOrphans $ do
|
||||||
setTitleI MsgLmsOrphans
|
setTitleI MsgLmsOrphans
|
||||||
|
|||||||
@ -407,6 +407,7 @@ dispatchJobLmsReports qid = JobHandlerAtomic act
|
|||||||
E.<&> E.val now
|
E.<&> E.val now
|
||||||
E.<&> E.val now
|
E.<&> E.val now
|
||||||
E.<&> E.nothing
|
E.<&> E.nothing
|
||||||
|
E.<&> E.nothing
|
||||||
)
|
)
|
||||||
(\_old _new ->
|
(\_old _new ->
|
||||||
[ LmsOrphanSeenLast E.=. E.val now
|
[ LmsOrphanSeenLast E.=. E.val now
|
||||||
|
|||||||
Reference in New Issue
Block a user