diff --git a/config/settings.yml b/config/settings.yml index d5d64e7c5..07e9ee69f 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -142,7 +142,7 @@ ldap: stripes: "_env:LDAPSTRIPES:1" timeout: "_env:LDAPTIMEOUT:20" limit: "_env:LDAPLIMIT:10" - ++ ldap-re-test-failover: 60 lms-direct: diff --git a/messages/uniworx/categories/qualification/de-de-formal.msg b/messages/uniworx/categories/qualification/de-de-formal.msg index 6374ae394..aa5915d87 100644 --- a/messages/uniworx/categories/qualification/de-de-formal.msg +++ b/messages/uniworx/categories/qualification/de-de-formal.msg @@ -158,5 +158,8 @@ QualFormErrorDuplShort qsh@Text: Es gibt bereits eine Qualifikation mit Kürzel QualFormErrorDuplName qname@Text: Es gibt bereits eine Qualifikation mit Namen #{qname}! QualFormErrorSshMismatch: Qualifikationänderungsformular enthält unglültige Bereichsangabe. Bitte versuchen Sie erneut, nachdem Sie Seite neu geladen haben. -TableLmsOrphanNr: Verwaiste Logins -LmsOrphanNr n@Int: #{n} verwaiste E‑Learning Logins für diese Qualifikation erkannt. \ No newline at end of file +LmsOrphans: Verwaiste Logins +LmsOrphanNr n@Int: #{n} verwaiste E‑Learning Logins für diese Qualifikation erkannt. +LmsOrphanSeenFirst: Zuerst erkannt +LmsOrphanSeenLast: Zuletzt erhalten +LmsOrphanDeletedLast: Zuletzt Löschung beantragt \ No newline at end of file diff --git a/messages/uniworx/categories/qualification/en-eu.msg b/messages/uniworx/categories/qualification/en-eu.msg index 6da4d3df2..ced6ea766 100644 --- a/messages/uniworx/categories/qualification/en-eu.msg +++ b/messages/uniworx/categories/qualification/en-eu.msg @@ -158,5 +158,8 @@ QualFormErrorDuplShort qsh@Text: There already exists a qualification with short QualFormErrorDuplName qname@Text: There already exists a qualification with name #{qname}! QualFormErrorSshMismatch: Qualification edit form department mismatch. Please try again after reloading the page. -TableLmsOrphanNr: Orphaned logins -LmsOrphanNr n@Int: #{n} orphaned e‑learning login detected for this qualification. \ No newline at end of file +LmsOrphans: Orphaned logins +LmsOrphanNr n@Int: #{n} orphaned e‑learning login detected for this qualification. +LmsOrphanSeenFirst: First seen +LmsOrphanSeenLast: Last seen +LmsOrphanDeletedLast: Deletion requested \ No newline at end of file diff --git a/src/Handler/LMS.hs b/src/Handler/LMS.hs index ee9c165a1..a03d9432e 100644 --- a/src/Handler/LMS.hs +++ b/src/Handler/LMS.hs @@ -178,7 +178,7 @@ mkLmsAllTable isAdmin = do $ \(view resultAllQualificationActive -> n) -> wgtCell $ word2widget n , adminable Nothing (i18nCell MsgTableQualificationCountTotal) $ wgtCell . word2widget . view resultAllQualificationTotal -- \(view resultAllQualificationTotal -> n) -> wgtCell $ word2widget n - , adminable Nothing (i18nCell MsgTableLmsOrphanNr) $ wgtCell . word2widget . view resultAllQualificationOrphans + , adminable Nothing (i18nCell MsgLmsOrphans) $ wgtCell . word2widget . view resultAllQualificationOrphans ] dbtSorting = mconcat [ diff --git a/src/Handler/LMS/Learners.hs b/src/Handler/LMS/Learners.hs index 2dbfe33b9..26fb0c1f6 100644 --- a/src/Handler/LMS/Learners.hs +++ b/src/Handler/LMS/Learners.hs @@ -7,6 +7,7 @@ module Handler.LMS.Learners ( getLmsLearnersR , getLmsLearnersDirectR + , getLmsOrphansR ) where @@ -254,3 +255,57 @@ getLmsLearnersDirectR sid qsh = do <* runDB (logInterface "LMS" (ciOriginal qsh) True (Just nr) "") -- direct Download see: -- https://ersocon.net/blog/2017/2/22/creating-csv-files-in-yesod + +-- TODO: show info about orphan handling; + +getLmsOrphansR :: SchoolId -> QualificationShorthand -> Handler Html +getLmsOrphansR sid qsh = do + lmsConf <- getsYesod $ view _appLmsConf + 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 + let + orvDBTable = DBTable{..} + where + -- resultOrphan = _dbrOutput . _entityVal + dbtSQLQuery orv = do + E.where_ $ orv E.^. LmsOrphanQualification E.==. E.val qid + return orv + dbtRowKey = (E.^. LmsOrphanId) + dbtProj = dbtProjId + dbtColonnade = dbColonnade $ mconcat + [ 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-last") (i18nCell MsgLmsOrphanSeenLast) $ \(view $ _dbrOutput . _entityVal . _lmsOrphanSeenLast -> d) -> dateTimeCell d + , sortable (Just "deleted-last") (i18nCell MsgLmsOrphanDeletedLast) $ \(view $ _dbrOutput . _entityVal . _lmsOrphanDeletedLast -> d) -> foldMap dateTimeCell d + ] + dbtSorting = Map.fromList + [ ("ident" , SortColumn (E.^. LmsOrphanIdent)) + , ("seen-first" , SortColumn (E.^. LmsOrphanSeenFirst)) + , ("seen-last" , SortColumn (E.^. LmsOrphanSeenLast)) + , ("deleted-last" , SortColumn (E.^. LmsOrphanDeletedLast)) + ] + dbtFilter = Map.fromList + [ ("ident" , FilterColumn $ E.mkContainsFilterWithCommaPlus LmsIdent (E.^. LmsOrphanIdent)) + ] + dbtFilterUI = \mPrev -> mconcat + [ prismAForm (singletonFilter "ident" . maybePrism _PathPiece) mPrev $ aopt (hoistField lift textField) (fslI MsgTableLmsIdent & setTooltip MsgTableFilterCommaPlus) + ] + dbtStyle = def { dbsFilterLayout = defaultDBSFilterLayout } + dbtParams = def + dbtIdent :: Text + dbtIdent = "lms-orphans" + dbtCsvEncode = noCsvEncode + dbtCsvDecode = Nothing + dbtExtraReps = [] + orvDBTableValidator = def & defaultSorting [SortAscBy "seen-first", SortDescBy "deleted-last"] + dbTable orvDBTableValidator orvDBTable :: DB (Any, Widget) + + siteLayoutMsg MsgLmsOrphans $ do + setTitleI MsgLmsOrphans + $(i18nWidgetFile "lms-orphans") diff --git a/src/Utils/Lens.hs b/src/Utils/Lens.hs index 46f8d5000..bbd38c090 100644 --- a/src/Utils/Lens.hs +++ b/src/Utils/Lens.hs @@ -1,4 +1,4 @@ --- SPDX-FileCopyrightText: 2022-24 Gregor Kleen ,Sarah Vaupel ,Sarah Vaupel ,Steffen Jost ,Steffen Jost +-- SPDX-FileCopyrightText: 2022-25 Gregor Kleen ,Sarah Vaupel ,Sarah Vaupel ,Steffen Jost ,Steffen Jost -- -- SPDX-License-Identifier: AGPL-3.0-or-later @@ -134,6 +134,7 @@ makeClassyFor_ ''QualificationUserBlock makeClassyFor_ ''LmsUser -- makeClassyFor_ ''LmsUserStatus makeClassyFor_ ''LmsReport +makeClassyFor_ ''LmsOrphan makeClassyFor_ ''UserAvs makeLenses_ ''UserDay diff --git a/templates/i18n/lms-orphans/de-de-formal.hamlet b/templates/i18n/lms-orphans/de-de-formal.hamlet new file mode 100644 index 000000000..129c24e60 --- /dev/null +++ b/templates/i18n/lms-orphans/de-de-formal.hamlet @@ -0,0 +1,16 @@ +$newline never + +$# SPDX-FileCopyrightText: 2025 Steffen Jost +$# +$# SPDX-License-Identifier: AGPL-3.0-or-later + +
+ TODO Info about LMS Oprhan handling +
    +
  • #{tshow cutoff_seen_first} +
  • #{tshow cutoff_deleted_last} +
  • #{tshow cutoff_seen_last} +
  • #{tshow orphan_max_batch} +
    +

    + ^{orvTable} diff --git a/templates/i18n/lms-orphans/en-eu.hamlet b/templates/i18n/lms-orphans/en-eu.hamlet new file mode 100644 index 000000000..98e2ff1a2 --- /dev/null +++ b/templates/i18n/lms-orphans/en-eu.hamlet @@ -0,0 +1,18 @@ +$newline never + +$# SPDX-FileCopyrightText: 2025 Steffen Jost +$# +$# SPDX-License-Identifier: AGPL-3.0-or-later + +

    + TODO +
      +
    • #{tshow cutoff_seen_first} +
    • #{tshow cutoff_deleted_last} +
    • #{tshow cutoff_seen_last} +
    • #{tshow orphan_max_batch} + + +
      +

      + ^{orvTable}