Merge branch 'master' of https://dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive
This commit is contained in:
commit
a3762ce938
1
.gitignore
vendored
1
.gitignore
vendored
@ -42,6 +42,7 @@ src/Handler/Assist.bak
|
|||||||
src/Handler/Course.SnapCustom.hs
|
src/Handler/Course.SnapCustom.hs
|
||||||
*.orig
|
*.orig
|
||||||
/instance
|
/instance
|
||||||
|
backend/instance
|
||||||
.stack-work-*
|
.stack-work-*
|
||||||
.stack-work.lock
|
.stack-work.lock
|
||||||
.directory
|
.directory
|
||||||
|
|||||||
15
Makefile
15
Makefile
@ -3,6 +3,9 @@ export SHELL=bash
|
|||||||
export CLEAN_DEPENDENCIES ?= false
|
export CLEAN_DEPENDENCIES ?= false
|
||||||
export CLEAN_IMAGES ?= false
|
export CLEAN_IMAGES ?= false
|
||||||
|
|
||||||
|
export ENTRYPOINT ?= bash
|
||||||
|
export SRC
|
||||||
|
|
||||||
.PHONY: help
|
.PHONY: help
|
||||||
# HELP: print out this help message
|
# HELP: print out this help message
|
||||||
help:
|
help:
|
||||||
@ -77,11 +80,13 @@ start-%:
|
|||||||
docker compose up -d --build $*
|
docker compose up -d --build $*
|
||||||
|
|
||||||
.PHONY: shell-%
|
.PHONY: shell-%
|
||||||
|
# HELP(shell-$SERVICE): launch a (bash) shell inside a given service
|
||||||
shell-%:
|
shell-%:
|
||||||
docker compose run --build --no-deps $* bash
|
docker compose run --build --no-deps --entrypoint="$(ENTRYPOINT)" $*
|
||||||
.PHONY: ghci
|
.PHONY: ghci
|
||||||
ghci:
|
# HELP: launch ghci instance. Use in combination with SRC to specify the modules to be loaded by ghci: make ghci SRC=src/SomeModule.hs
|
||||||
docker compose run --build --no-deps backend stack ghci $(SRC)
|
ghci: ENTRYPOINT=stack ghci $(SRC)
|
||||||
|
ghci: shell-backend ;
|
||||||
|
|
||||||
.PHONY: stop
|
.PHONY: stop
|
||||||
# HELP: stop all services
|
# HELP: stop all services
|
||||||
@ -104,6 +109,10 @@ status:
|
|||||||
# HELP: print an overview of the ressource usage of the currently running services
|
# HELP: print an overview of the ressource usage of the currently running services
|
||||||
top:
|
top:
|
||||||
docker compose stats
|
docker compose stats
|
||||||
|
.PHONY: list-projects
|
||||||
|
# HELP: list all currently running projects on this machine
|
||||||
|
list-projects:
|
||||||
|
docker compose ls
|
||||||
|
|
||||||
.PHONY: log-%
|
.PHONY: log-%
|
||||||
# HELP(log-$SERVICE): follow the output of a given service. Service must be running.
|
# HELP(log-$SERVICE): follow the output of a given service. Service must be running.
|
||||||
|
|||||||
@ -1,20 +1,20 @@
|
|||||||
ARG FROM_IMG=docker.io/library/haskell
|
ARG FROM_IMG=docker.io/library/debian
|
||||||
ARG FROM_TAG=8.10.4
|
ARG FROM_TAG=12.5
|
||||||
|
|
||||||
FROM ${FROM_IMG}:${FROM_TAG}
|
FROM ${FROM_IMG}:${FROM_TAG}
|
||||||
|
|
||||||
ENV LANG=de_DE.UTF-8
|
ENV LANG=de_DE.UTF-8
|
||||||
|
|
||||||
|
# basic dependencies
|
||||||
|
RUN apt-get -y update && apt-get -y install git
|
||||||
|
RUN apt-get -y update && apt-get -y install haskell-stack
|
||||||
|
RUN apt-get -y update && apt-get -y install llvm
|
||||||
|
RUN apt-get -y update && apt-get install -y --no-install-recommends locales locales-all
|
||||||
|
|
||||||
# compile-time dependencies
|
# compile-time dependencies
|
||||||
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
|
RUN apt-get -y update && apt-get install -y libpq-dev libsodium-dev
|
||||||
--mount=type=cache,target=/var/lib/apt,sharing=locked \
|
RUN apt-get -y update && apt-get -y install g++ libghc-zlib-dev libpq-dev libsodium-dev pkg-config
|
||||||
apt-get -y update && apt-get install -y libpq-dev libsodium-dev
|
RUN apt-get -y update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends tzdata
|
||||||
# RUN apt-get -y update && apt-get -y install llvm
|
|
||||||
# RUN apt-get -y update && apt-get -y install g++ libghc-zlib-dev libpq-dev libsodium-dev pkg-config
|
|
||||||
# RUN apt-get -y update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends tzdata
|
|
||||||
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
|
|
||||||
--mount=type=cache,target=/var/lib/apt,sharing=locked \
|
|
||||||
apt-get -y update && apt-get install -y --no-install-recommends locales locales-all
|
|
||||||
|
|
||||||
# run-time dependencies for uniworx binary
|
# run-time dependencies for uniworx binary
|
||||||
RUN apt-get -y update && apt-get -y install fonts-roboto
|
RUN apt-get -y update && apt-get -y install fonts-roboto
|
||||||
@ -33,4 +33,8 @@ ENV PROJECT_DIR=${PROJECT_DIR}
|
|||||||
# RUN mkdir -p "${PROJECT_DIR}"; chmod -R 777 "${PROJECT_DIR}"
|
# RUN mkdir -p "${PROJECT_DIR}"; chmod -R 777 "${PROJECT_DIR}"
|
||||||
WORKDIR ${PROJECT_DIR}
|
WORKDIR ${PROJECT_DIR}
|
||||||
ENV HOME=${PROJECT_DIR}
|
ENV HOME=${PROJECT_DIR}
|
||||||
ENV STACK_ROOT="${PROJECT_DIR}/.stack"
|
ENV STACK_ROOT="${PROJECT_DIR}/.stack"
|
||||||
|
|
||||||
|
ENV STACK_SRC=""
|
||||||
|
ENV STACK_ENTRY="ghci ${STACK_SRC}"
|
||||||
|
ENTRYPOINT stack ${STACK_ENTRY}
|
||||||
@ -9,7 +9,6 @@ endif
|
|||||||
|
|
||||||
.PHONY: dependencies
|
.PHONY: dependencies
|
||||||
dependencies:
|
dependencies:
|
||||||
chown -R `id -un`:`id -gn` "$(PROJECT_DIR)"; \
|
|
||||||
stack install hpack; stack install yesod-bin; \
|
stack install hpack; stack install yesod-bin; \
|
||||||
stack build -j2 --only-dependencies
|
stack build -j2 --only-dependencies
|
||||||
|
|
||||||
|
|||||||
@ -47,9 +47,11 @@ TutorialUserDeregister: Vom Kurs abmelden
|
|||||||
TutorialUserSendMail: Mitteilung verschicken
|
TutorialUserSendMail: Mitteilung verschicken
|
||||||
TutorialUserPrintQualification: Zertifikat drucken
|
TutorialUserPrintQualification: Zertifikat drucken
|
||||||
TutorialUserGrantQualification: Qualifikation vergeben
|
TutorialUserGrantQualification: Qualifikation vergeben
|
||||||
|
TutorialUserGrantQualificationDateTooltip: Leer lassen, um das Ablaufdatum auf das heutige Datum plus Standardgültigkeitsdauer zu setzen.
|
||||||
|
TutorialUserGrantQualificationDateError qsh@QualificationShorthand: Qualifikation #{qsh} hat keine Standardgültigkeitsdauer, daher ist ein explizites Ablaufdatum erforderlich!
|
||||||
TutorialUserRenewQualification: Qualifikation regulär verlängern
|
TutorialUserRenewQualification: Qualifikation regulär verlängern
|
||||||
TutorialUserRenewedQualification n@Int: Qualifikation für #{tshow n} Kurs-#{pluralDE n "Teilnehmer:in" "Teilnehmer:innen"} regulär verlängert
|
TutorialUserRenewedQualification qsh@QualificationShorthand n@Int: Qualifikation #{qsh} für #{tshow n} Kurs-#{pluralDE n "Teilnehmer:in" "Teilnehmer:innen"} regulär verlängert.
|
||||||
TutorialUserGrantedQualification n@Int: Qualifikation erfolgreich an #{tshow n} Kurs-#{pluralDE n "Teilnehmer:in" "Teilnehmer:innen"} vergeben
|
TutorialUserGrantedQualification qsh@QualificationShorthand day@Text n@Int: Qualifikation #{qsh} bis #{day} erfolgreich an #{tshow n} Kurs-#{pluralDE n "Teilnehmer:in" "Teilnehmer:innen"} vergeben.
|
||||||
TutorialUserAssignExam: Zur Prüfung einteilen
|
TutorialUserAssignExam: Zur Prüfung einteilen
|
||||||
TutorialUserExamAssignedFor n@Int m@Int p@Text: #{n}/#{m} zur Prüfung #{p} eingeteilt
|
TutorialUserExamAssignedFor n@Int m@Int p@Text: #{n}/#{m} zur Prüfung #{p} eingeteilt
|
||||||
CommTutorial: Kursmitteilung
|
CommTutorial: Kursmitteilung
|
||||||
|
|||||||
@ -43,14 +43,15 @@ TutorInviteHeading tutn: Invitation to be instructor for #{tutn}
|
|||||||
TutorInviteExplanation: You were invited to be a instructor.
|
TutorInviteExplanation: You were invited to be a instructor.
|
||||||
TutorCorrectorInvitationAccepted shn: You are now a corrector for #{shn}
|
TutorCorrectorInvitationAccepted shn: You are now a corrector for #{shn}
|
||||||
TutorialUsersDeregistered count: Successfully deregistered #{show count} participants from course
|
TutorialUsersDeregistered count: Successfully deregistered #{show count} participants from course
|
||||||
|
|
||||||
TutorialUserDeregister: Deregister from course
|
TutorialUserDeregister: Deregister from course
|
||||||
TutorialUserSendMail: Send mail
|
TutorialUserSendMail: Send mail
|
||||||
TutorialUserPrintQualification: Print certificate
|
TutorialUserPrintQualification: Print certificate
|
||||||
TutorialUserGrantQualification: Grant qualification
|
TutorialUserGrantQualification: Grant qualification
|
||||||
|
TutorialUserGrantQualificationDateTooltip: Leave blank for expiry on today's date plus standard qualification valid duration.
|
||||||
|
TutorialUserGrantQualificationDateError qsh@QualificationShorthand: Qualification #{qsh} has no standard valid duration. Please provide an explicit expiry date!
|
||||||
TutorialUserRenewQualification: Renew qualification
|
TutorialUserRenewQualification: Renew qualification
|
||||||
TutorialUserRenewedQualification n@Int: Successfully renewed qualification #{tshow n} course #{pluralEN n "user" "users"}
|
TutorialUserRenewedQualification qsh n: Successfully renewed #{qsh} qualification for #{pluralENsN n "course participant"}
|
||||||
TutorialUserGrantedQualification n: Successfully granted qualification #{tshow n} course #{pluralEN n "user" "users"}
|
TutorialUserGrantedQualification qsh day n: Successfully granted #{qsh} qualification until #{day} to #{pluralENsN n "course participant"}
|
||||||
TutorialUserAssignExam: Register for examination
|
TutorialUserAssignExam: Register for examination
|
||||||
TutorialUserExamAssignedFor n@Int m@Int p@Text: #{n}/#{m} enrolled for exam #{p}
|
TutorialUserExamAssignedFor n@Int m@Int p@Text: #{n}/#{m} enrolled for exam #{p}
|
||||||
CommTutorial: Course message
|
CommTutorial: Course message
|
||||||
|
|||||||
@ -614,14 +614,15 @@ postQualificationR sid qsh = do
|
|||||||
(noks,nterm) <- runDB $ (,)
|
(noks,nterm) <- runDB $ (,)
|
||||||
<$> renewValidQualificationUsers qid (canonical $ Just $ Left renewReason) Nothing selUsrs
|
<$> renewValidQualificationUsers qid (canonical $ Just $ Left renewReason) Nothing selUsrs
|
||||||
<*> terminateLms (LmsOrphanReasonManualRenewal renewReason) qid selUsrs
|
<*> terminateLms (LmsOrphanReasonManualRenewal renewReason) qid selUsrs
|
||||||
addMessageI (if noks > 0 && noks == Set.size selectedUsers then Success else Warning) $ MsgTutorialUserRenewedQualification noks
|
addMessageI (if noks > 0 && noks == Set.size selectedUsers then Success else Warning) $ MsgTutorialUserRenewedQualification qsh noks
|
||||||
when (nterm >0) $ addMessageI Warning $ MsgLmsActTerminated nterm
|
when (nterm >0) $ addMessageI Warning $ MsgLmsActTerminated nterm
|
||||||
reloadKeepGetParams $ QualificationR sid qsh
|
reloadKeepGetParams $ QualificationR sid qsh
|
||||||
(QualificationActGrantData grantValidday, selectedUsers) | isAdmin -> do
|
(QualificationActGrantData grantValidDay, selectedUsers) | isAdmin -> do
|
||||||
|
grantValidDayText <- formatTime SelFormatDate grantValidDay
|
||||||
nterm <- runDB $ do
|
nterm <- runDB $ do
|
||||||
forM_ selectedUsers $ upsertQualificationUser qid now grantValidday Nothing "Admin"
|
forM_ selectedUsers $ upsertQualificationUser qid now grantValidDay Nothing "Admin"
|
||||||
terminateLms (LmsOrphanReasonManualGrant $ "bis " <> tshow grantValidday) qid $ Set.toList selectedUsers
|
terminateLms (LmsOrphanReasonManualGrant $ "bis " <> tshow grantValidDay) qid $ Set.toList selectedUsers
|
||||||
addMessageI (if 0 < Set.size selectedUsers then Success else Warning) . MsgTutorialUserGrantedQualification $ Set.size selectedUsers
|
addMessageI (if 0 < Set.size selectedUsers then Success else Warning) . MsgTutorialUserGrantedQualification qsh grantValidDayText $ Set.size selectedUsers
|
||||||
when (nterm > 0) $ addMessageI Warning $ MsgLmsActTerminated nterm
|
when (nterm > 0) $ addMessageI Warning $ MsgLmsActTerminated nterm
|
||||||
reloadKeepGetParams $ QualificationR sid qsh
|
reloadKeepGetParams $ QualificationR sid qsh
|
||||||
(QualificationActStartELearningData, Set.toList -> selectedUsers) | isAdmin -> do
|
(QualificationActStartELearningData, Set.toList -> selectedUsers) | isAdmin -> do
|
||||||
|
|||||||
@ -57,7 +57,7 @@ data TutorialUserActionData
|
|||||||
{ tuQualification :: QualificationId }
|
{ tuQualification :: QualificationId }
|
||||||
| TutorialUserGrantQualificationData
|
| TutorialUserGrantQualificationData
|
||||||
{ tuQualification :: QualificationId
|
{ tuQualification :: QualificationId
|
||||||
, tuValidUntil :: Day
|
, tuValidUntil :: Maybe Day
|
||||||
}
|
}
|
||||||
| TutorialUserSendMailData
|
| TutorialUserSendMailData
|
||||||
| TutorialUserDeregisterData
|
| TutorialUserDeregisterData
|
||||||
@ -116,6 +116,7 @@ postTUsersR tid ssh csh tutn = do
|
|||||||
let heading = prependCourseTitle tid ssh csh $ CI.original tutn
|
let heading = prependCourseTitle tid ssh csh $ CI.original tutn
|
||||||
croute = CTutorialR tid ssh csh tutn TUsersR
|
croute = CTutorialR tid ssh csh tutn TUsersR
|
||||||
now <- liftIO getCurrentTime
|
now <- liftIO getCurrentTime
|
||||||
|
let nowaday = utctDay now
|
||||||
isAdmin <- hasReadAccessTo AdminR
|
isAdmin <- hasReadAccessTo AdminR
|
||||||
(Entity tutid tut@Tutorial{..}, (participantRes, participantTable), qualifications, dbegin, hasExams, exmFltr, exOccs) <- runDB do
|
(Entity tutid tut@Tutorial{..}, (participantRes, participantTable), qualifications, dbegin, hasExams, exmFltr, exOccs) <- runDB do
|
||||||
trm <- get404 tid
|
trm <- get404 tid
|
||||||
@ -123,9 +124,9 @@ postTUsersR tid ssh csh tutn = do
|
|||||||
-- tutEnt@(Entity tutid _) <- fetchTutorial tid ssh csh tutn
|
-- tutEnt@(Entity tutid _) <- fetchTutorial tid ssh csh tutn
|
||||||
(cid, tutEnt@(Entity tutid _)) <- fetchCourseIdTutorial tid ssh csh tutn
|
(cid, tutEnt@(Entity tutid _)) <- fetchCourseIdTutorial tid ssh csh tutn
|
||||||
qualifications <- getCourseQualifications cid
|
qualifications <- getCourseQualifications cid
|
||||||
let nowaday = utctDay now
|
let dayExpiry = case nubOrd (mapMaybe qualificationValidDuration qualifications) of
|
||||||
minDur :: Maybe Int = minimumMaybe $ mapMaybe (view _qualificationValidDuration) qualifications -- no instance Ord CalendarDiffDays
|
[oneDuration] -> Just $ Just $ computeNewValidDate qvd nowaday -- suggest end day only if it is unique for all course qualifications
|
||||||
dayExpiry = flip computeNewValidDate nowaday <$> minDur
|
_ -> Nothing -- using the minimum here causes confusion, better leave blank!
|
||||||
colChoices = mconcat $
|
colChoices = mconcat $
|
||||||
[ dbSelect (applying _2) id (return . view (hasEntity . _entityKey))
|
[ dbSelect (applying _2) id (return . view (hasEntity . _entityKey))
|
||||||
, colUserNameModalHdr MsgTableCourseMembers ForProfileDataR
|
, colUserNameModalHdr MsgTableCourseMembers ForProfileDataR
|
||||||
@ -173,7 +174,7 @@ postTUsersR tid ssh csh tutn = do
|
|||||||
, ( TutorialUserGrantQualification
|
, ( TutorialUserGrantQualification
|
||||||
, TutorialUserGrantQualificationData
|
, TutorialUserGrantQualificationData
|
||||||
<$> apopt (selectField $ pure qualOptions) (fslI MsgQualificationName) Nothing
|
<$> apopt (selectField $ pure qualOptions) (fslI MsgQualificationName) Nothing
|
||||||
<*> apopt dayField (fslI MsgLmsQualificationValidUntil) dayExpiry
|
<*> aopt dayField (fslI MsgLmsQualificationValidUntil & setTooltip MsgTutorialUserGrantQualificationDateTooltip) dayExpiry
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
) ++
|
) ++
|
||||||
@ -184,7 +185,7 @@ postTUsersR tid ssh csh tutn = do
|
|||||||
table <- makeCourseUserTable cid acts isInTut colChoices psValidator (Just csvColChoices)
|
table <- makeCourseUserTable cid acts isInTut colChoices psValidator (Just csvColChoices)
|
||||||
return (tutEnt, table, qualifications, dbegin, hasExams, exmFltr, exOccs)
|
return (tutEnt, table, qualifications, dbegin, hasExams, exmFltr, exOccs)
|
||||||
|
|
||||||
let courseQids = Set.fromList (entityKey <$> qualifications)
|
let courseQids = entities2map qualifications
|
||||||
tcontent <- formResultMaybe participantRes $ \case
|
tcontent <- formResultMaybe participantRes $ \case
|
||||||
(TutorialUserPrintQualificationData, selectedUsers) -> do
|
(TutorialUserPrintQualificationData, selectedUsers) -> do
|
||||||
rcvr <- requireAuth
|
rcvr <- requireAuth
|
||||||
@ -204,25 +205,30 @@ postTUsersR tid ssh csh tutn = do
|
|||||||
-- typePDF = "application/pdf"
|
-- typePDF = "application/pdf"
|
||||||
-- sendResponse (typePDF, toContent pdf)
|
-- sendResponse (typePDF, toContent pdf)
|
||||||
(TutorialUserGrantQualificationData{..}, selectedUsers)
|
(TutorialUserGrantQualificationData{..}, selectedUsers)
|
||||||
| tuQualification `Set.member` courseQids -> do
|
| Just grantQual <- Map.lookup tuQualification courseQids ->
|
||||||
-- today <- localDay . TZ.utcToLocalTimeTZ appTZ <$> liftIO getCurrentTime
|
case tuValidUntil <|> (flip computeNewValidDate nowaday <$> qualificationValidDuration grantQual) of
|
||||||
today <- liftIO getCurrentTime
|
Nothing -> do -- TODO: change QualificationUser to have an optionnal validUntil for idefinitely valid qualifications
|
||||||
let reason = "Kurs " <> CI.original (unSchoolKey ssh) <> "-" <> CI.original csh <> "-" <> CI.original tutn
|
addMessageI Error $ MsgTutorialUserGrantQualificationDateError $ qualificationShorthand grantQual
|
||||||
selUsrs = Set.toList selectedUsers
|
(Just expiryDay) -> do
|
||||||
nterm <- runDB $ do
|
let qsh = qualificationShorthand grantQual
|
||||||
forM_ selUsrs $ upsertQualificationUser tuQualification today tuValidUntil Nothing reason
|
reason = "Kurs " <> CI.original (unSchoolKey ssh) <> "-" <> CI.original csh <> "-" <> CI.original tutn
|
||||||
terminateLms (LmsOrphanReasonManualGrant [st|bis #{tshow tuValidUntil}, #{reason}|]) tuQualification selUsrs
|
selUsrs = Set.toList selectedUsers
|
||||||
addMessageI (if 0 < Set.size selectedUsers then Success else Warning) . MsgTutorialUserGrantedQualification $ Set.size selectedUsers
|
expiryDayText <- formatTime SelFormatDate expiryDay
|
||||||
when (nterm > 0) $ addMessageI Warning $ MsgLmsActTerminated nterm
|
nterm <- runDB $ do
|
||||||
reloadKeepGetParams croute
|
forM_ selUsrs $ upsertQualificationUser tuQualification now expiryDay Nothing reason
|
||||||
|
terminateLms (LmsOrphanReasonManualGrant [st|bis #{expiryDayText}, #{reason}|]) tuQualification selUsrs
|
||||||
|
addMessageI (if 0 < Set.size selectedUsers then Success else Warning) . MsgTutorialUserGrantedQualification qsh expiryDayText $ Set.size selectedUsers
|
||||||
|
when (nterm > 0) $ addMessageI Warning $ MsgLmsActTerminated nterm
|
||||||
|
reloadKeepGetParams croute
|
||||||
(TutorialUserRenewQualificationData{..}, selectedUsers)
|
(TutorialUserRenewQualificationData{..}, selectedUsers)
|
||||||
| tuQualification `Set.member` courseQids -> do
|
| Just grantQual <- Map.lookup tuQualification courseQids -> do
|
||||||
let selUsrs = Set.toList selectedUsers
|
let qsh = qualificationShorthand grantQual
|
||||||
|
selUsrs = Set.toList selectedUsers
|
||||||
mr <- getMessageRender
|
mr <- getMessageRender
|
||||||
(noks,nterm) <- runDB $ (,)
|
(noks,nterm) <- runDB $ (,)
|
||||||
<$> renewValidQualificationUsers tuQualification Nothing Nothing selUsrs
|
<$> renewValidQualificationUsers tuQualification Nothing Nothing selUsrs
|
||||||
<*> terminateLms (LmsOrphanReasonManualGrant $ mr heading) tuQualification selUsrs
|
<*> terminateLms (LmsOrphanReasonManualGrant $ mr heading) tuQualification selUsrs
|
||||||
addMessageI (if noks > 0 && noks == Set.size selectedUsers then Success else Warning) $ MsgTutorialUserRenewedQualification noks
|
addMessageI (if noks > 0 && noks == Set.size selectedUsers then Success else Warning) $ MsgTutorialUserRenewedQualification qsh noks
|
||||||
when (nterm > 0) $ addMessageI Warning $ MsgLmsActTerminated nterm
|
when (nterm > 0) $ addMessageI Warning $ MsgLmsActTerminated nterm
|
||||||
reloadKeepGetParams croute
|
reloadKeepGetParams croute
|
||||||
(TutorialUserSendMailData, selectedUsers) -> do
|
(TutorialUserSendMailData, selectedUsers) -> do
|
||||||
|
|||||||
@ -932,6 +932,11 @@ mapFromSetM = (sequenceA .) . Map.fromSet
|
|||||||
setToMap :: (Ord k) => (v -> k) -> Set v -> Map k v
|
setToMap :: (Ord k) => (v -> k) -> Set v -> Map k v
|
||||||
setToMap mkKey = Map.fromList . fmap (\x -> (mkKey x, x)) . Set.toList
|
setToMap mkKey = Map.fromList . fmap (\x -> (mkKey x, x)) . Set.toList
|
||||||
|
|
||||||
|
-- Create a Map given a key-computation
|
||||||
|
-- For Entity, use Utils.DB.entities2map instead
|
||||||
|
mapFromFoldable :: (Ord k, Foldable t) => (v -> k) -> t v -> Map k v
|
||||||
|
mapFromFoldable getKey = foldMap (Map.singleton =<< getKey)
|
||||||
|
|
||||||
mapFM :: (Applicative m, Ord k, Finite k) => (k -> m v) -> m (Map k v)
|
mapFM :: (Applicative m, Ord k, Finite k) => (k -> m v) -> m (Map k v)
|
||||||
mapFM = sequenceA . mapF
|
mapFM = sequenceA . mapF
|
||||||
|
|
||||||
|
|||||||
@ -9,6 +9,7 @@ flags:
|
|||||||
rebuild-ghc-options: true
|
rebuild-ghc-options: true
|
||||||
#ghc-options:
|
#ghc-options:
|
||||||
# "$everything": -fno-prof-auto
|
# "$everything": -fno-prof-auto
|
||||||
|
allow-different-user: true
|
||||||
|
|
||||||
local-bin-path: ./bin
|
local-bin-path: ./bin
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user