From d1d308725f504f221b37eae511580221a0344df0 Mon Sep 17 00:00:00 2001 From: David Mosbach Date: Fri, 10 Nov 2023 13:34:55 +0000 Subject: [PATCH 1/7] chore(settings): uncommented qualification-check-hour --- config/settings.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/settings.yml b/config/settings.yml index ecc94093d..ebbc296ef 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -91,7 +91,7 @@ study-features-recache-relevance-within: 172800 study-features-recache-relevance-interval: 293 # Enqueue at specified hour, dequeue 30min later -# qualification-check-hour: 3 +qualification-check-hour: 3 log-settings: detailed: "_env:DETAILED_LOGGING:false" -- 2.39.2 From 8e1f94bb7c20b2c303ddbcb974a65bdeaa3595ab Mon Sep 17 00:00:00 2001 From: David Mosbach Date: Fri, 10 Nov 2023 13:41:36 +0000 Subject: [PATCH 2/7] fix(crontab): specified day of week for lms jobs --- src/Jobs/Crontab.hs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Jobs/Crontab.hs b/src/Jobs/Crontab.hs index e352758ef..8d782e0d7 100644 --- a/src/Jobs/Crontab.hs +++ b/src/Jobs/Crontab.hs @@ -396,7 +396,8 @@ determineCrontab = execWriterT $ do (JobCtlQueue JobLmsQualificationsEnqueue) Cron { cronInitial = CronAsap -- time after scheduling - , cronRepeat = CronRepeatScheduled $ cronCalendarAny { cronHour = cronMatchOne hour -- cronHour = CronMatchSome (impureNonNull $ Set.fromList [3,15] ) + , cronRepeat = CronRepeatScheduled $ cronCalendarAny { cronDayOfWeek = CronMatchSome . impureNonNull . Set.fromList $ [1..7] + , cronHour = cronMatchOne hour -- cronHour = CronMatchSome (impureNonNull $ Set.fromList [3,15] ) , cronMinute = cronMatchOne 3 , cronSecond = cronMatchOne 27 } @@ -408,7 +409,8 @@ determineCrontab = execWriterT $ do (JobCtlQueue JobLmsQualificationsDequeue) Cron { cronInitial = CronAsap -- time after scheduling - , cronRepeat = CronRepeatScheduled $ cronCalendarAny { cronHour = cronMatchOne hour -- cronHour = CronMatchSome (impureNonNull $ Set.fromList [3,15] ) + , cronRepeat = CronRepeatScheduled $ cronCalendarAny { cronDayOfWeek = CronMatchSome . impureNonNull . Set.fromList $ [1..7] + , cronHour = cronMatchOne hour -- cronHour = CronMatchSome (impureNonNull $ Set.fromList [3,15] ) , cronMinute = cronMatchOne 33 , cronSecond = cronMatchOne 27 } -- 2.39.2 From 0331010ad761ddf24c6589245f4641694d1ad0bb Mon Sep 17 00:00:00 2001 From: David Mosbach Date: Sat, 11 Nov 2023 03:41:31 +0000 Subject: [PATCH 3/7] fix(crontab):increased cronNotAfter for lms jobs --- src/Jobs/Crontab.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Jobs/Crontab.hs b/src/Jobs/Crontab.hs index 8d782e0d7..a115c8c88 100644 --- a/src/Jobs/Crontab.hs +++ b/src/Jobs/Crontab.hs @@ -402,7 +402,7 @@ determineCrontab = execWriterT $ do , cronSecond = cronMatchOne 27 } , cronRateLimit = nominalDay / 2 -- minimal time between two executions, before the second job is skipped - , cronNotAfter = Left nominalDay -- maximal delay of an execution, before it is skipped entirely + , cronNotAfter = Left $ 1.5 * nominalDay -- maximal delay of an execution, before it is skipped entirely } whenIsJust appQualificationCheckHour $ \hour -> tell $ HashMap.singleton @@ -415,7 +415,7 @@ determineCrontab = execWriterT $ do , cronSecond = cronMatchOne 27 } , cronRateLimit = nominalDay / 2 -- minimal time between two executions, before the second job is skipped - , cronNotAfter = Left nominalDay -- maximal delay of an execution, before it is skipped entirely + , cronNotAfter = Left $ 1.5 * nominalDay -- maximal delay of an execution, before it is skipped entirely } let -- 2.39.2 From c393992ffee95f0ea8a91916d0cd7256c2fa8610 Mon Sep 17 00:00:00 2001 From: David Mosbach Date: Wed, 22 Nov 2023 03:03:24 +0000 Subject: [PATCH 4/7] fix(crontab): execute lms jobs once a day mo - fri --- src/Jobs/Crontab.hs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/Jobs/Crontab.hs b/src/Jobs/Crontab.hs index a115c8c88..006a1ae25 100644 --- a/src/Jobs/Crontab.hs +++ b/src/Jobs/Crontab.hs @@ -392,30 +392,31 @@ determineCrontab = execWriterT $ do -- , cronNotAfter = Right . CronTimestamp . utcToLocalTimeTZ appTZ $ addUTCTime appStudyFeaturesRecacheRelevanceInterval nextIntervalTime -- } + whenIsJust appQualificationCheckHour $ \hour -> tell $ HashMap.singleton (JobCtlQueue JobLmsQualificationsEnqueue) Cron { cronInitial = CronAsap -- time after scheduling - , cronRepeat = CronRepeatScheduled $ cronCalendarAny { cronDayOfWeek = CronMatchSome . impureNonNull . Set.fromList $ [1..7] + , cronRepeat = CronRepeatScheduled $ cronCalendarAny { cronDayOfWeek = CronMatchSome . impureNonNull . Set.fromList $ [1..5] , cronHour = cronMatchOne hour -- cronHour = CronMatchSome (impureNonNull $ Set.fromList [3,15] ) , cronMinute = cronMatchOne 3 , cronSecond = cronMatchOne 27 } - , cronRateLimit = nominalDay / 2 -- minimal time between two executions, before the second job is skipped - , cronNotAfter = Left $ 1.5 * nominalDay -- maximal delay of an execution, before it is skipped entirely + , cronRateLimit = 0 -- minimal time between two executions, before the second job is skipped + , cronNotAfter = Right CronNotScheduled -- maximal delay of an execution, before it is skipped entirely } whenIsJust appQualificationCheckHour $ \hour -> tell $ HashMap.singleton (JobCtlQueue JobLmsQualificationsDequeue) Cron { cronInitial = CronAsap -- time after scheduling - , cronRepeat = CronRepeatScheduled $ cronCalendarAny { cronDayOfWeek = CronMatchSome . impureNonNull . Set.fromList $ [1..7] + , cronRepeat = CronRepeatScheduled $ cronCalendarAny { cronDayOfWeek = CronMatchSome . impureNonNull . Set.fromList $ [1..5] , cronHour = cronMatchOne hour -- cronHour = CronMatchSome (impureNonNull $ Set.fromList [3,15] ) , cronMinute = cronMatchOne 33 , cronSecond = cronMatchOne 27 } - , cronRateLimit = nominalDay / 2 -- minimal time between two executions, before the second job is skipped - , cronNotAfter = Left $ 1.5 * nominalDay -- maximal delay of an execution, before it is skipped entirely + , cronRateLimit = 0 -- minimal time between two executions, before the second job is skipped + , cronNotAfter = Right CronNotScheduled -- maximal delay of an execution, before it is skipped entirely } let -- 2.39.2 From 9c047ae749197250fc040777fea4930bad79b902 Mon Sep 17 00:00:00 2001 From: Sarah Vaupel Date: Wed, 22 Nov 2023 19:53:58 +0000 Subject: [PATCH 5/7] Set cronRateLimit to 10 minutes for QualificationCheckHour jobs --- src/Jobs/Crontab.hs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Jobs/Crontab.hs b/src/Jobs/Crontab.hs index 006a1ae25..4e517ad52 100644 --- a/src/Jobs/Crontab.hs +++ b/src/Jobs/Crontab.hs @@ -1,4 +1,4 @@ --- SPDX-FileCopyrightText: 2022 Gregor Kleen ,Sarah Vaupel ,Sarah Vaupel ,Steffen Jost +-- SPDX-FileCopyrightText: 2022-2023 Sarah Vaupel , David Mosbach , Gregor Kleen ,Sarah Vaupel ,Sarah Vaupel ,Steffen Jost -- -- SPDX-License-Identifier: AGPL-3.0-or-later @@ -402,7 +402,7 @@ determineCrontab = execWriterT $ do , cronMinute = cronMatchOne 3 , cronSecond = cronMatchOne 27 } - , cronRateLimit = 0 -- minimal time between two executions, before the second job is skipped + , cronRateLimit = 600 -- minimal time between two executions, before the second job is skipped , cronNotAfter = Right CronNotScheduled -- maximal delay of an execution, before it is skipped entirely } @@ -415,7 +415,7 @@ determineCrontab = execWriterT $ do , cronMinute = cronMatchOne 33 , cronSecond = cronMatchOne 27 } - , cronRateLimit = 0 -- minimal time between two executions, before the second job is skipped + , cronRateLimit = 600 -- minimal time between two executions, before the second job is skipped , cronNotAfter = Right CronNotScheduled -- maximal delay of an execution, before it is skipped entirely } -- 2.39.2 From e723dd9b171708a1f1033f494f60bea6e6bef3e6 Mon Sep 17 00:00:00 2001 From: Steffen Jost Date: Thu, 23 Nov 2023 11:53:14 +0100 Subject: [PATCH 6/7] chore(lms): split qualification-check-hour into two --- config/settings.yml | 5 +- .../categories/qualification/de-de-formal.msg | 4 +- .../categories/qualification/en-eu.msg | 10 ++-- src/Handler/LMS.hs | 7 ++- src/Jobs/Crontab.hs | 8 +-- src/Settings.hs | 6 +- templates/i18n/lms-all/de-de-formal.hamlet | 57 +++++++++++++++++++ templates/i18n/lms-all/en-eu.hamlet | 57 +++++++++++++++++++ templates/lms-all.hamlet | 18 ------ 9 files changed, 134 insertions(+), 38 deletions(-) create mode 100644 templates/i18n/lms-all/de-de-formal.hamlet create mode 100644 templates/i18n/lms-all/en-eu.hamlet delete mode 100644 templates/lms-all.hamlet diff --git a/config/settings.yml b/config/settings.yml index ebbc296ef..b3c228991 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -90,8 +90,9 @@ synchronise-avs-users-interval: "_env:SYNCHRONISE_AVS_INTERVAL:21600" # alle 6 study-features-recache-relevance-within: 172800 study-features-recache-relevance-interval: 293 -# Enqueue at specified hour, dequeue 30min later -qualification-check-hour: 3 +# Enqueue at specified hour, a few minutes later +job-lms-qualifications-enqueue-hour: 15 +job-lms-qualifications-dequeue-hour: 3 log-settings: detailed: "_env:DETAILED_LOGGING:false" diff --git a/messages/uniworx/categories/qualification/de-de-formal.msg b/messages/uniworx/categories/qualification/de-de-formal.msg index 113121211..1571d7ac1 100644 --- a/messages/uniworx/categories/qualification/de-de-formal.msg +++ b/messages/uniworx/categories/qualification/de-de-formal.msg @@ -138,7 +138,5 @@ LmsNotificationSend n@Int: E‑Learning Benachrichtigungen an #{n} #{pluralDE n LmsPinRenewal n@Int: E‑Learning Passwort ausgetauscht für #{n} #{pluralDE n "Prüfling" "Prüflinge"}. LmsActionFailed n@Int: Aktion nicht durchgeführt für #{n} #{pluralDE n "Person" "Personen"}, da diese derzeit nicht an einer Prüfung teilnehmen. LmsStarted: E‑Learning eröffnet -LmsAutomaticQueuing n@Natural: Die folgenden Funktionen werden normalerweise einmal pro Tag um #{show n} Uhr ausgeführt. -LmsManualQueuing: Die folgenden Funktionen sollten einmal pro Tag ausgeführt werden. BtnLmsEnqueue: Nutzer mit ablaufenden Qualifikationen zum E‑Learning anmelden und benachrichtigen -BtnLmsDequeue: Nutzer mit beendetem E‑Learning ggf. benachrichtigen und aufräumen +BtnLmsDequeue: Nutzer mit beendetem E‑Learning aufräumen und ggf. benachrichtigen diff --git a/messages/uniworx/categories/qualification/en-eu.msg b/messages/uniworx/categories/qualification/en-eu.msg index 1cab2c3dd..5d466355b 100644 --- a/messages/uniworx/categories/qualification/en-eu.msg +++ b/messages/uniworx/categories/qualification/en-eu.msg @@ -7,7 +7,7 @@ QualificationName: Qualification QualificationDescription: Description QualificationValidIndicator: Validity QualificationValidDuration: Validity period -QualificationAuditDuration: Audit log keept +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. QualificationRefreshWithin: Refresh within QualificationRefreshWithinTooltip: Optional period before expiry to start e‑learning and send a notification by post or email. @@ -19,7 +19,7 @@ QualificationExpiryNotificationTooltip: Qualification holder are notfied upon in TableQualificationCountActive: Active TableQualificationCountActiveTooltip: Number of currently valid qualification holders TableQualificationCountTotal: Total -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. TableQualificationSapExport: Sent to SAP TableQualificationSapExportTooltip: Is this qualification transmitted to SAP? Only applies to qualification holder having a Fraport AG personnel number. @@ -138,7 +138,5 @@ LmsNotificationSend n: E‑learning notifications will be sent to #{n} #{pluralE LmsPinRenewal n: E‑learning password replaced randomly for #{n} #{pluralENs n "examinee"}. LmsActionFailed n: No action for #{n} #{pluralENs n "person"}, since there was no ongoing examination. LmsStarted: E‑learning open since -LmsAutomaticQueuing n@Natural: The following functions are executed daily at #{show n} o'clock. -LmsManualQueuing: The following functions should be executed daily. -BtnLmsEnqueue: Enqueue users with expiring qualifications for e‑learning and notify them. -BtnLmsDequeue: Dequeue users with finished e‑learning and notify, if appropriate. +BtnLmsEnqueue: Enqueue users with expiring qualifications for e‑learning and notify them +BtnLmsDequeue: Dequeue users with finished e‑learning and notify failed users diff --git a/src/Handler/LMS.hs b/src/Handler/LMS.hs index ae49a06c5..9e820dd21 100644 --- a/src/Handler/LMS.hs +++ b/src/Handler/LMS.hs @@ -76,7 +76,7 @@ embedRenderMessage ''UniWorX ''ButtonManualLms id instance Button UniWorX ButtonManualLms where btnClasses BtnLmsEnqueue = [BCIsButton, BCPrimary] - btnClasses BtnLmsDequeue = [BCIsButton, BCDefault] + btnClasses BtnLmsDequeue = [BCIsButton, BCPrimary] getLmsSchoolR :: SchoolId -> Handler Html @@ -86,7 +86,8 @@ getLmsAllR, postLmsAllR :: Handler Html getLmsAllR = postLmsAllR postLmsAllR = do isAdmin <- hasReadAccessTo AdminR - mbQcheck <- getsYesod $ view _appQualificationCheckHour + mbJLQenqueue <- getsYesod $ view _appJobLmsQualificationsEnqueueHour + mbJLQdequeue <- getsYesod $ view _appJobLmsQualificationsDequeueHour -- TODO: Move this functionality elsewhere without the need for `isAdmin` mbBtnForm <- if not isAdmin then return Nothing else do ((btnResult, btnWdgt), btnEnctype) <- runFormPost $ identifyForm ("buttons" :: Text) (buttonForm :: Form ButtonManualLms) @@ -110,7 +111,7 @@ postLmsAllR = do view _2 <$> mkLmsAllTable isAdmin lmsDeletionDays siteLayoutMsg MsgMenuLms $ do setTitleI MsgMenuLms - $(widgetFile "lms-all") + $(i18nWidgetFile "lms-all") type AllQualificationTableData = DBRow (Entity Qualification, Ex.Value Word64, Ex.Value Word64) resultAllQualification :: Lens' AllQualificationTableData Qualification diff --git a/src/Jobs/Crontab.hs b/src/Jobs/Crontab.hs index 4e517ad52..18802b95a 100644 --- a/src/Jobs/Crontab.hs +++ b/src/Jobs/Crontab.hs @@ -393,26 +393,26 @@ determineCrontab = execWriterT $ do -- } - whenIsJust appQualificationCheckHour $ \hour -> tell $ HashMap.singleton + whenIsJust appJobLmsQualificationsEnqeueHour $ \hour -> tell $ HashMap.singleton (JobCtlQueue JobLmsQualificationsEnqueue) Cron { cronInitial = CronAsap -- time after scheduling , cronRepeat = CronRepeatScheduled $ cronCalendarAny { cronDayOfWeek = CronMatchSome . impureNonNull . Set.fromList $ [1..5] , cronHour = cronMatchOne hour -- cronHour = CronMatchSome (impureNonNull $ Set.fromList [3,15] ) - , cronMinute = cronMatchOne 3 + , cronMinute = cronMatchOne 2 , cronSecond = cronMatchOne 27 } , cronRateLimit = 600 -- minimal time between two executions, before the second job is skipped , cronNotAfter = Right CronNotScheduled -- maximal delay of an execution, before it is skipped entirely } - whenIsJust appQualificationCheckHour $ \hour -> tell $ HashMap.singleton + whenIsJust appJobLmsQualificationsDequeueHour $ \hour -> tell $ HashMap.singleton (JobCtlQueue JobLmsQualificationsDequeue) Cron { cronInitial = CronAsap -- time after scheduling , cronRepeat = CronRepeatScheduled $ cronCalendarAny { cronDayOfWeek = CronMatchSome . impureNonNull . Set.fromList $ [1..5] , cronHour = cronMatchOne hour -- cronHour = CronMatchSome (impureNonNull $ Set.fromList [3,15] ) - , cronMinute = cronMatchOne 33 + , cronMinute = cronMatchOne 7 , cronSecond = cronMatchOne 27 } , cronRateLimit = 600 -- minimal time between two executions, before the second job is skipped diff --git a/src/Settings.hs b/src/Settings.hs index 5b6c139cb..a6c008a1e 100644 --- a/src/Settings.hs +++ b/src/Settings.hs @@ -233,7 +233,8 @@ data AppSettings = AppSettings , appStudyFeaturesRecacheRelevanceWithin :: Maybe NominalDiffTime , appStudyFeaturesRecacheRelevanceInterval :: NominalDiffTime - , appQualificationCheckHour :: Maybe Natural + , appJobLmsQualificationsEnqueueHour :: Maybe Natural + , appJobLmsQualificationsDequeueHour :: Maybe Natural , appFileSourceARCConf :: Maybe (ARCConf Int) , appFileSourcePrewarmConf :: Maybe PrewarmCacheConf @@ -784,7 +785,8 @@ instance FromJSON AppSettings where appStudyFeaturesRecacheRelevanceWithin <- o .:? "study-features-recache-relevance-within" appStudyFeaturesRecacheRelevanceInterval <- o .: "study-features-recache-relevance-interval" - appQualificationCheckHour <- o .:? "qualification-check-hour" + appJobLmsQualificationsEnqueueHour <- o .:? "job-lms-qualifications-enqueue-hour" + appJobLmsQualificationsDequeueHour <- o .:? "job-lms-qualifications-dequeue-hour" appFileSourceARCConf <- assertM isValidARCConf <$> o .:? "file-source-arc" diff --git a/templates/i18n/lms-all/de-de-formal.hamlet b/templates/i18n/lms-all/de-de-formal.hamlet new file mode 100644 index 000000000..c93ddfb58 --- /dev/null +++ b/templates/i18n/lms-all/de-de-formal.hamlet @@ -0,0 +1,57 @@ +$newline never + +$# SPDX-FileCopyrightText: 2022 Steffen Jost +$# +$# SPDX-License-Identifier: AGPL-3.0-or-later + +
+ ^{lmsTable} + +$maybe btnForm <- mbBtnForm +
+

+ E‑Learning Starten und Aufräumen +

+ Die folgenden Funktionen sollten normalerweise mindestens einmal pro Tag ausgeführt werden, # + können aber auch bedenkenlos mehrfach pro Tag ausgeführt werden. # + + Die erste Funktion benachrichtigt Inhaber von ablaufenden Lizenzen und # + lädt diese ggf. zum E‑Learning ein. # + + Die zweite Funktion benachrichtigt Inhaber von bereits abgelaufenen Lizenzen und # + räumte beendete E‑Learning Teilnehmer auf, falls der jeweilige Aufbewahrungszeitraum abgelaufen ist. # + + Ein Abgleich mit dem Ausweisverwaltungssystem findet dadurch jedoch noch nicht statt. # + +

+

+ Automatische Ausführung + +
+
+ Start E‑Learning: # +
+   + $maybe hour <- mbJLQenqueue + jeden Wochentag kurz nach # + + #{hour} Uhr + $nothing + + keine automatische Ausführung +
+ Sperren/Aufräumen: # +
+   + $maybe hour <- mbJLQdequeue + jeden Wochentag kurz nach # + + #{hour} Uhr + $nothing + + keine automatische Ausführung +

+

+ Manuelle Ausführung + + ^{btnForm} \ No newline at end of file diff --git a/templates/i18n/lms-all/en-eu.hamlet b/templates/i18n/lms-all/en-eu.hamlet new file mode 100644 index 000000000..69aa8df82 --- /dev/null +++ b/templates/i18n/lms-all/en-eu.hamlet @@ -0,0 +1,57 @@ +$newline never + +$# SPDX-FileCopyrightText: 2022 Steffen Jost +$# +$# SPDX-License-Identifier: AGPL-3.0-or-later + +
+ ^{lmsTable} + +$maybe btnForm <- mbBtnForm +
+

+ Starting and cleaning e‑learning +

+ The following functions should be executed at least once per day, # + but a repeated execution is harmless. # + + The first function notifies holders of expiring licences and # + enlists them for e‑learning, if appropriate for the respective qualification. # + + The second function notifies holders of already expired licences and # + cleans finished e‑learnings after their respective rentention periods. # + + Note that these functions do not trigger an AVS-synchronisation. # + +

+

+ Automatic execution + +
+
+ Start e‑learning: # +
+   + $maybe hour <- mbJLQenqueue + every weekday shortly after # + + #{hour} o'clock + $nothing + + no automatic execution +
+ Block/Clean: # +
+   + $maybe hour <- mbJLQdequeue + every weekday shortly after # + + #{hour} o'clock + $nothing + + no automatic execution +

+

+ Manual execution + + ^{btnForm} \ No newline at end of file diff --git a/templates/lms-all.hamlet b/templates/lms-all.hamlet deleted file mode 100644 index b4e5077fd..000000000 --- a/templates/lms-all.hamlet +++ /dev/null @@ -1,18 +0,0 @@ -$newline never - -$# SPDX-FileCopyrightText: 2022 Steffen Jost -$# -$# SPDX-License-Identifier: AGPL-3.0-or-later - -

- ^{lmsTable} - -$maybe btnForm <- mbBtnForm -

-

- $maybe qcheck <- mbQcheck - _{MsgLmsAutomaticQueuing qcheck} - $nothing - _{MsgLmsManualQueuing} -

- ^{btnForm} \ No newline at end of file -- 2.39.2 From fe54b18b336cfbe2f635d4796366528a51fd5700 Mon Sep 17 00:00:00 2001 From: Steffen Jost Date: Thu, 23 Nov 2023 12:05:49 +0100 Subject: [PATCH 7/7] fix(build): typo --- src/Jobs/Crontab.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Jobs/Crontab.hs b/src/Jobs/Crontab.hs index 18802b95a..093c5cbde 100644 --- a/src/Jobs/Crontab.hs +++ b/src/Jobs/Crontab.hs @@ -393,7 +393,7 @@ determineCrontab = execWriterT $ do -- } - whenIsJust appJobLmsQualificationsEnqeueHour $ \hour -> tell $ HashMap.singleton + whenIsJust appJobLmsQualificationsEnqueueHour $ \hour -> tell $ HashMap.singleton (JobCtlQueue JobLmsQualificationsEnqueue) Cron { cronInitial = CronAsap -- time after scheduling -- 2.39.2