Merge branch 'fradrive/newletter'

This commit is contained in:
Steffen Jost 2024-07-02 18:16:48 +02:00
commit 9e2a964ef7
25 changed files with 585 additions and 517 deletions

View File

@ -4,7 +4,9 @@
AvsPersonInfo: AVS Personendaten AvsPersonInfo: AVS Personendaten
AvsPersonId: AVS Personen Id AvsPersonId: AVS Personen Id
AvsPersonNo: AVS Personennummer AvsPersonNo: AVS Personennummer
AvsPersonNoNotId: AVS Personennummer dient zur menschlichen Kommunikation mit der Ausweisstelle und darf nicht verwechselt werden mit der maschinell verwendeten AVS Personen Id
AvsPersonNoMismatch: AVS Personennummer hat sich geändert und wurde in FRADrive noch nicht aktualisiert AvsPersonNoMismatch: AVS Personennummer hat sich geändert und wurde in FRADrive noch nicht aktualisiert
AvsPersonNoDiffers: Es sind derzeit zwei verschiedene AVS Personennummern zugeordnet. Bitte einen Administrator kontaktieren.
AvsCardNo: Ausweiskartennummer AvsCardNo: Ausweiskartennummer
AvsFirstName: Vorname AvsFirstName: Vorname
AvsLastName: Nachname AvsLastName: Nachname
@ -15,7 +17,6 @@ AvsQueryNeeded: Benötigt Verbindung zum AVS.
AvsQueryEmpty: Bitte mindestens ein Anfragefeld ausfüllen! AvsQueryEmpty: Bitte mindestens ein Anfragefeld ausfüllen!
AvsQueryStatusInvalid t@Text: Nur numerische IDs eingeben, durch Komma getrennt! Erhalten: #{show t} AvsQueryStatusInvalid t@Text: Nur numerische IDs eingeben, durch Komma getrennt! Erhalten: #{show t}
AvsLicence: Fahrberechtigung AvsLicence: Fahrberechtigung
AvsPersonNoNotId: AVS Personennummer dient zur menschlichen Kommunikation mit der Ausweisstelle und darf nicht verwechselt werden mit der maschinell verwendeten AVS Personen Id
AvsTitleLicenceSynch: Abgleich Fahrberechtigungen zwischen AVS und FRADrive AvsTitleLicenceSynch: Abgleich Fahrberechtigungen zwischen AVS und FRADrive
BtnAvsRevokeUnknown: Fahrberechtigungen im AVS sofort entziehen BtnAvsRevokeUnknown: Fahrberechtigungen im AVS sofort entziehen
BtnAvsImportUnknown: AVS Daten unbekannter Personen importieren BtnAvsImportUnknown: AVS Daten unbekannter Personen importieren
@ -45,6 +46,7 @@ AvsCardColorBlue: Blau
AvsCardColorRed: Rot AvsCardColorRed: Rot
AvsCardColorYellow: Gelb AvsCardColorYellow: Gelb
LastAvsSynchronisation: Letzte AVS-Synchronisation LastAvsSynchronisation: Letzte AVS-Synchronisation
LastAvsSyncedBefore: Letzte AVS-Synchronisation vor
LastAvsSynchError: Letzte AVS-Fehlermeldung LastAvsSynchError: Letzte AVS-Fehlermeldung
AvsInterfaceUnavailable: AVS Schnittstelle nicht richtig konfiguriert oder antwortet nicht AvsInterfaceUnavailable: AVS Schnittstelle nicht richtig konfiguriert oder antwortet nicht

View File

@ -4,7 +4,9 @@
AvsPersonInfo: AVS person info AvsPersonInfo: AVS person info
AvsPersonId: AVS person id AvsPersonId: AVS person id
AvsPersonNo: AVS person number AvsPersonNo: AVS person number
AvsPersonNoNotId: AVS person number is used in human communication only and must not be mistaken for the AVS personen id used in machine communications
AvsPersonNoMismatch: AVS person number has changed and was not yet updated in FRADrive AvsPersonNoMismatch: AVS person number has changed and was not yet updated in FRADrive
AvsPersonNoDiffers: There are currently two differing AVS person numbers associated with this user. Please contact an administrator to resolve this.
AvsCardNo: Card number AvsCardNo: Card number
AvsFirstName: First name AvsFirstName: First name
AvsLastName: Last name AvsLastName: Last name
@ -15,7 +17,7 @@ AvsQueryNeeded: AVS connection required.
AvsQueryEmpty: At least one query field must be filled! AvsQueryEmpty: At least one query field must be filled!
AvsQueryStatusInvalid t: Numeric IDs only, comma seperated! #{show t} AvsQueryStatusInvalid t: Numeric IDs only, comma seperated! #{show t}
AvsLicence: Driving Licence AvsLicence: Driving Licence
AvsPersonNoNotId: AVS person number is used in human communication only and must not be mistaken for the AVS personen id used in machine communications
AvsTitleLicenceSynch: Synchronisation driving licences between AVS and FRADrive AvsTitleLicenceSynch: Synchronisation driving licences between AVS and FRADrive
BtnAvsRevokeUnknown: Revoke AVS driving licences for unknown persons immediately BtnAvsRevokeUnknown: Revoke AVS driving licences for unknown persons immediately
BtnAvsImportUnknown: Import AVS data for unknown persons BtnAvsImportUnknown: Import AVS data for unknown persons
@ -45,6 +47,7 @@ AvsCardColorBlue: Blue
AvsCardColorRed: Red AvsCardColorRed: Red
AvsCardColorYellow: Yellow AvsCardColorYellow: Yellow
LastAvsSynchronisation: Last AVS synchronisation LastAvsSynchronisation: Last AVS synchronisation
LastAvsSyncedBefore: Last AVS synchronisation before
LastAvsSynchError: Last AVS Error LastAvsSynchError: Last AVS Error
AvsInterfaceUnavailable: AVS interface was not configured correctly or does not respond AvsInterfaceUnavailable: AVS interface was not configured correctly or does not respond

View File

@ -25,10 +25,14 @@ PersonalInfoTutorialsWip: Die Anzeige von Kurse, zu denen Sie angemeldet sind wi
ProfileGroupSubmissionDates: Bei Gruppenabgaben wird kein Datum angezeigt, wenn Sie die Gruppenabgabe nie selbst hochgeladen haben. ProfileGroupSubmissionDates: Bei Gruppenabgaben wird kein Datum angezeigt, wenn Sie die Gruppenabgabe nie selbst hochgeladen haben.
ProfileCorrectorRemark: Die oberhalb angezeigte Tabelle zeigt nur prinzipielle Einteilungen als Korrektor zu einem Übungsblatt. Auch ohne Einteilung können Korrekturen einzeln zugewiesen werden, welche hier dann nicht aufgeführt werden. ProfileCorrectorRemark: Die oberhalb angezeigte Tabelle zeigt nur prinzipielle Einteilungen als Korrektor zu einem Übungsblatt. Auch ohne Einteilung können Korrekturen einzeln zugewiesen werden, welche hier dann nicht aufgeführt werden.
ProfileCorrections: Auflistung aller zugewiesenen Korrekturen ProfileCorrections: Auflistung aller zugewiesenen Korrekturen
Remarks: Hinweise Remarks: Hinweis:
ProfileSupervisor: Übergeordnete Ansprechpartner ProfileNoSupervisor: Keine übergeordneten Ansprechpartner vorhanden
ProfileSupervisee: Ist Ansprechpartner für ProfileSupervisor n@Int m@Int: #{n} #{pluralDE n "übergeordneter" "übergeordnete"} Ansprechpartner#{noneMoreDE m "" (", davon " <> tshow m <> " mit Benachrichtigungsumleitung")}
ProfileSupervisorRemark n@Int m@Int l@Int: #{m}/#{n} #{pluralDE m "übergeordneter" "übergeordnete"} Ansprechpartner mit Benachrichtigungsumleitung#{noneMoreDE l "" (", davon " <> tshow l <> " mit postalischer Benachrichtigung")}
ProfileNoSupervisee: Ist kein Ansprechpartner für irgendjemand
ProfileSupervisee n@Int m@Int: Ist Ansprechpartner für #{n} #{pluralDE n "Person" "Personen"}#{noneMoreDE m "" (", davon " <> tshow m <> " mit Benachrichtigungsumleitung")}
ProfileSuperviseeRemark n@Int m@Int: Dieser Nutzer ist Ansprechpartner für #{n} #{pluralDE n "Person" "Personen"}#{noneMoreDE m "" (", davon " <> tshow m <> " mit Benachrichtigungsumleitung")}
UserTelephone: Telefon UserTelephone: Telefon
UserMobile: Mobiltelefon UserMobile: Mobiltelefon

View File

@ -25,10 +25,14 @@ PersonalInfoTutorialsWip: The feature to display courses you have registered for
ProfileGroupSubmissionDates: No date is shown for group submissions if you have never uploaded the submission yourself. ProfileGroupSubmissionDates: No date is shown for group submissions if you have never uploaded the submission yourself.
ProfileCorrectorRemark: The table above only shows registration as a corrector in principle. Even without registration corrections can be assigned individually and are not listed. ProfileCorrectorRemark: The table above only shows registration as a corrector in principle. Even without registration corrections can be assigned individually and are not listed.
ProfileCorrections: List of all assigned corrections ProfileCorrections: List of all assigned corrections
Remarks: Remarks Remarks: Remark:
ProfileSupervisor: Supervised by ProfileNoSupervisor: Is not supervised by anynone
ProfileSupervisee: Supervises ProfileSupervisor n m: #{pluralENsN n "supervisor"} #{noneMoreEN m "" ("with " <> tshow m <> " active notification rerouting")}
ProfileSupervisorRemark n@Int m@Int l@Int: #{m}/#{n} #{pluralENs m "supervisor"} with active notification rerouting#{noneMoreEN l "" (", and " <> tshow l <> "of these prefer postal notifications")}
ProfileNoSupervisee: Does not supervise anynone
ProfileSupervisee n m: Supervises #{pluralENsN n "person"} #{noneMoreEN m "" ("with " <> tshow m <> " active notification rerouting")}
ProfileSuperviseeRemark n m: This person supervises #{pluralENsN n "person"}#{noneMoreEN m "" (" with " <> tshow m <> " having active notifications rerouting to this user")}
UserTelephone: Phone UserTelephone: Phone
UserMobile: Mobile UserMobile: Mobile

View File

@ -88,15 +88,14 @@ pluralDE num singularForm pluralForm
| otherwise = pluralForm | otherwise = pluralForm
pluralDEx :: (Eq a, Num a) => Char -> a -> Text -> Text pluralDEx :: (Eq a, Num a) => Char -> a -> Text -> Text
-- ^ @pluralENs n "Monat" = pluralEN n "Monat" "Monate"@
pluralDEx c n t = pluralDE n t $ t `snoc` c pluralDEx c n t = pluralDE n t $ t `snoc` c
-- | like `pluralDEe` but also prefixes with the number -- | like `pluralDEx` but also prefixes with the number
pluralDExN :: (Eq a, Num a, Show a) => Char -> a -> Text -> Text pluralDExN :: (Eq a, Num a, Show a) => Char -> a -> Text -> Text
pluralDExN c n t = tshow n <> cons ' ' (pluralDEx c n t) pluralDExN c n t = tshow n <> cons ' ' (pluralDEx c n t)
pluralDEe :: (Eq a, Num a) => a -> Text -> Text pluralDEe :: (Eq a, Num a) => a -> Text -> Text
-- ^ @pluralENs n "Monat" = pluralEN n "Monat" "Monate"@ -- ^ @pluralDEe n "Monat" = pluralDEe n "Monat" "Monate"@
pluralDEe = pluralDEx 'e' pluralDEe = pluralDEx 'e'
-- | like `pluralDEe` but also prefixes with the number -- | like `pluralDEe` but also prefixes with the number
@ -124,14 +123,14 @@ noneOneMoreDE num noneText singularForm pluralForm
| num == 1 = singularForm | num == 1 = singularForm
| otherwise = pluralForm | otherwise = pluralForm
-- noneMoreDE :: (Eq a, Num a) noneMoreDE :: (Eq a, Num a)
-- => a -- ^ Count => a -- ^ Count
-- -> Text -- ^ None -> Text -- ^ None
-- -> Text -- ^ Some -> Text -- ^ Some
-- -> Text -> Text
-- noneMoreDE num noneText someText noneMoreDE num noneText someText
-- | num == 0 = noneText | num == 0 = noneText
-- | otherwise = someText | otherwise = someText
pluralEN :: (Eq a, Num a) pluralEN :: (Eq a, Num a)
=> a -- ^ Count => a -- ^ Count
@ -164,14 +163,14 @@ noneOneMoreEN num noneText singularForm pluralForm
| num == 1 = singularForm | num == 1 = singularForm
| otherwise = pluralForm | otherwise = pluralForm
-- noneMoreEN :: (Eq a, Num a) noneMoreEN :: (Eq a, Num a)
-- => a -- ^ Count => a -- ^ Count
-- -> Text -- ^ None -> Text -- ^ None
-- -> Text -- ^ Some -> Text -- ^ Some
-- -> Text -> Text
-- noneMoreEN num noneText someText noneMoreEN num noneText someText
-- | num == 0 = noneText | num == 0 = noneText
-- | otherwise = someText | otherwise = someText
_ordinalEN :: ToMessage a _ordinalEN :: ToMessage a
=> a => a

View File

@ -560,7 +560,7 @@ mkLicenceTable apidStatus dbtIdent aLic apids = do
, sortable (Just "avspersonno") (i18nCell MsgAvsPersonNo) $ \(view resultUserAvs -> a) -> avsPersonNoLinkedCellAdmin a , sortable (Just "avspersonno") (i18nCell MsgAvsPersonNo) $ \(view resultUserAvs -> a) -> avsPersonNoLinkedCellAdmin a
-- , colUserCompany -- , colUserCompany
, sortable (Just "user-company") (i18nCell MsgTableCompanies) $ \(view (resultUser . _entityKey) -> uid) -> flip (set' cellContents) mempty $ do -- why does sqlCell not work here? Mismatch "YesodDB UniWorX" and "RWST (Maybe (Env,FileEnv), UniWorX, [Lang]) Enctype Ints (HandlerFor UniWorX" , sortable (Just "user-company") (i18nCell MsgTableCompanies) $ \(view (resultUser . _entityKey) -> uid) -> flip (set' cellContents) mempty $ do -- why does sqlCell not work here? Mismatch "YesodDB UniWorX" and "RWST (Maybe (Env,FileEnv), UniWorX, [Lang]) Enctype Ints (HandlerFor UniWorX"
companies' <- liftHandler . runDB . E.select $ E.from $ \(usrComp `E.InnerJoin` comp) -> do companies' <- liftHandler . runDBRead . E.select $ E.from $ \(usrComp `E.InnerJoin` comp) -> do
E.on $ usrComp E.^. UserCompanyCompany E.==. comp E.^. CompanyId E.on $ usrComp E.^. UserCompanyCompany E.==. comp E.^. CompanyId
E.where_ $ usrComp E.^. UserCompanyUser E.==. E.val uid E.where_ $ usrComp E.^. UserCompanyUser E.==. E.val uid
E.orderBy [E.asc (comp E.^. CompanyName)] E.orderBy [E.asc (comp E.^. CompanyName)]
@ -639,8 +639,8 @@ mkLicenceTable apidStatus dbtIdent aLic apids = do
mkOption :: E.Value Text -> Option Text mkOption :: E.Value Text -> Option Text
mkOption (E.unValue -> t) = Option{ optionDisplay = t, optionInternalValue = t, optionExternalValue = toPathPiece t } mkOption (E.unValue -> t) = Option{ optionDisplay = t, optionInternalValue = t, optionExternalValue = toPathPiece t }
suggestionsBlock :: HandlerFor UniWorX (OptionList Text) suggestionsBlock :: HandlerFor UniWorX (OptionList Text)
suggestionsBlock = mkOptionList . fmap mkOption <$> runDB (getBlockReasons E.not_) suggestionsBlock = mkOptionList . fmap mkOption <$> runDBRead (getBlockReasons E.not_)
suggestionsUnblock = mkOptionList . fmap mkOption <$> runDB (getBlockReasons id) suggestionsUnblock = mkOptionList . fmap mkOption <$> runDBRead (getBlockReasons id)
acts :: Map LicenceTableAction (AForm Handler LicenceTableActionData) acts :: Map LicenceTableAction (AForm Handler LicenceTableActionData)
acts = mconcat acts = mconcat
@ -949,4 +949,3 @@ getProblemAvsErrorR = do
siteLayoutMsg MsgMenuAvsSynchError $ do siteLayoutMsg MsgMenuAvsSynchError $ do
setTitleI MsgMenuAvsSynchError setTitleI MsgMenuAvsSynchError
[whamlet|^{avsSyncErrTbl}|] [whamlet|^{avsSyncErrTbl}|]

View File

@ -68,7 +68,7 @@ courseRegisterForm (Entity cid Course{..}) = liftHandler $ do
| otherwise | otherwise
-> return $ FormSuccess () -> return $ FormSuccess ()
mayViewCourseAfterDeregistration <- liftHandler . runDB $ E.selectExists . E.from $ \course -> do mayViewCourseAfterDeregistration <- liftHandler . runDBRead $ E.selectExists . E.from $ \course -> do
E.where_ $ course E.^. CourseId E.==. E.val cid E.where_ $ course E.^. CourseId E.==. E.val cid
E.&&. ( isSchoolAdminLike muid ata (course E.^. CourseSchool) E.&&. ( isSchoolAdminLike muid ata (course E.^. CourseSchool)
E.||. mayEditCourse muid ata course E.||. mayEditCourse muid ata course

View File

@ -119,7 +119,7 @@ firmActionHandler route isAdmin = flip formResult faHandler
faHandler (_,fids) | null fids = addMessageI Error MsgNoCompanySelected faHandler (_,fids) | null fids = addMessageI Error MsgNoCompanySelected
faHandler (FirmActNotifyData, Set.toList -> fids) = do faHandler (FirmActNotifyData, Set.toList -> fids) = do
usrs <- runDB $ E.select $ E.distinct $ do usrs <- runDBRead $ E.select $ E.distinct $ do
(usr :& uc) <- E.from $ E.table @User `E.innerJoin` E.table @UserCompany `E.on` (\(emp :& uc) -> emp E.^. UserId E.==. uc E.^. UserCompanyUser) (usr :& uc) <- E.from $ E.table @User `E.innerJoin` E.table @UserCompany `E.on` (\(emp :& uc) -> emp E.^. UserId E.==. uc E.^. UserCompanyUser)
E.where_ $ uc E.^. UserCompanyCompany `E.in_` E.valList fids E.where_ $ uc E.^. UserCompanyCompany `E.in_` E.valList fids
return $ usr E.^. UserId return $ usr E.^. UserId
@ -325,34 +325,33 @@ addDefaultSupervisorsAll mutualSupervision cids = do
------------------------------ ------------------------------
-- repeatedly useful queries -- repeatedly useful queries
usrSuperiorCompanies :: 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
usrSuperiorCompanies cmp usr = do
othr <- E.from $ E.table @UserCompany
E.where_ $ othr E.^. UserCompanyPriority E.>. usr E.^. UserCompanyPriority
E.&&. othr E.^. UserCompanyUser E.==. usr E.^. UserCompanyUser
E.&&. othr E.^. UserCompanyCompany E.!=. cmp E.^. CompanyId -- redundant due to > above, but likely performance improving
-- return othr
fromUserCompany :: Maybe (E.SqlExpr (Entity UserCompany) -> E.SqlExpr (E.Value Bool)) -> E.SqlExpr (Entity Company) -> E.SqlQuery () fromUserCompany :: Maybe (E.SqlExpr (Entity UserCompany) -> E.SqlExpr (E.Value Bool)) -> E.SqlExpr (Entity Company) -> E.SqlQuery ()
fromUserCompany mbFltr cmpy = do fromUserCompany mbFltr cmpy = do
usrCmpy <- E.from $ E.table @UserCompany usrCmpy <- E.from $ E.table @UserCompany
let basecond = usrCmpy E.^. UserCompanyCompany E.==. cmpy E.^. CompanyId let basecond = usrCmpy E.^. UserCompanyCompany E.==. cmpy E.^. CompanyId
E.where_ $ maybe basecond ((basecond E.&&.).($ usrCmpy)) mbFltr E.where_ $ maybe basecond ((basecond E.&&.).($ usrCmpy)) mbFltr
firmCountUsers :: E.SqlExpr (Entity Company) -> E.SqlExpr (E.Value Word64) firmCountUsers :: E.SqlExpr (Entity Company) -> E.SqlExpr (E.Value Word64)
firmCountUsers = E.subSelectCount . fromUserCompany Nothing 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 usr = E.notExists (do primFltr = E.notExists . usrSuperiorCompanies cmp
othr <- E.from $ E.table @UserCompany
E.where_ $ othr E.^. UserCompanyPriority E.>. usr E.^. UserCompanyPriority
E.&&. othr E.^. UserCompanyUser E.==. usr E.^. UserCompanyUser
E.&&. othr E.^. UserCompanyCompany E.!=. cmp E.^. CompanyId -- redundant due to > above, but likely performance improving
)
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 usr = E.exists (do primFltr = E.exists . usrSuperiorCompanies cmp
othr <- E.from $ E.table @UserCompany
E.where_ $ othr E.^. UserCompanyPriority E.>. usr E.^. UserCompanyPriority
E.&&. othr E.^. UserCompanyUser E.==. usr E.^. UserCompanyUser
E.&&. othr E.^. UserCompanyCompany E.!=. cmp E.^. CompanyId -- redundant due to > above, but likely performance improving
)
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))
@ -1375,14 +1374,14 @@ handleFirmCommR ultDest cs = do
csKeys = CompanyKey <$> cs csKeys = CompanyKey <$> cs
mbUser <- maybeAuthId mbUser <- maybeAuthId
-- get employees of chosen companies -- get employees of chosen companies
empys <- mkCompanyUsrList <$> runDB (E.select $ do empys <- mkCompanyUsrList <$> runDBRead (E.select $ do
(emp :& cmp) <- E.from $ E.table @User `E.innerJoin` E.table @UserCompany `E.on` (\(emp :& cmp) -> emp E.^. UserId E.==. cmp E.^. UserCompanyUser) (emp :& cmp) <- E.from $ E.table @User `E.innerJoin` E.table @UserCompany `E.on` (\(emp :& cmp) -> emp E.^. UserId E.==. cmp E.^. UserCompanyUser)
E.where_ $ cmp E.^. UserCompanyCompany `E.in_` E.valList csKeys E.where_ $ cmp E.^. UserCompanyCompany `E.in_` E.valList csKeys
E.orderBy [E.ascNullsFirst $ cmp E.^. UserCompanyCompany] E.orderBy [E.ascNullsFirst $ cmp E.^. UserCompanyCompany]
return (E.just $ cmp E.^. UserCompanyCompany, emp E.^. UserId) return (E.just $ cmp E.^. UserCompanyCompany, emp E.^. UserId)
) )
-- get supervisors of employees -- get supervisors of employees
sprs <- mkCompanyUsrList <$> runDB (E.select $ do sprs <- mkCompanyUsrList <$> runDBRead (E.select $ do
(spr :& cmp) <- E.from $ E.table @User `E.leftJoin` E.table @UserCompany `E.on` (\(spr :& cmp) -> spr E.^. UserId E.=?. cmp E.?. UserCompanyUser) (spr :& cmp) <- E.from $ E.table @User `E.leftJoin` E.table @UserCompany `E.on` (\(spr :& cmp) -> spr E.^. UserId E.=?. cmp E.?. UserCompanyUser)
E.where_ $ (E.isTrue (cmp E.?. UserCompanySupervisor) E.&&. cmp E.?. UserCompanyCompany `E.in_` E.justValList csKeys) E.where_ $ (E.isTrue (cmp E.?. UserCompanySupervisor) E.&&. cmp E.?. UserCompanyCompany `E.in_` E.justValList csKeys)
E.||. (spr E.^. UserId E.=?. E.val mbUser) E.||. (spr E.^. UserId E.=?. E.val mbUser)

View File

@ -792,7 +792,7 @@ viewLmsUserR :: Maybe SchoolId -> Maybe QualificationShorthand -> CryptoUUIDUser
viewLmsUserR msid mqsh uuid = do viewLmsUserR msid mqsh uuid = do
uid <- decrypt uuid uid <- decrypt uuid
now <- liftIO getCurrentTime now <- liftIO getCurrentTime
(user@User{userDisplayName}, quals, qblocks) <- runDB $ do (user@User{userDisplayName}, quals, qblocks) <- runDBRead $ do
usr <- get404 uid usr <- get404 uid
qs <- Ex.select $ do qs <- Ex.select $ do
(qual :& qualUsr :& lmsUsr) <- (qual :& qualUsr :& lmsUsr) <-

View File

@ -315,16 +315,16 @@ newsUpcomingExams uid = do
| otherwise -> mempty | otherwise -> mempty
] ]
dbtSorting = Map.fromList dbtSorting = Map.fromList
[ ("demo-both", SortColumn $ queryCourse &&& queryExam >>> (\(_course,exam)-> exam E.^. ExamName)) [ ("demo-both", SortColumns $ queryCourse &&& queryExam >>> (\(course,exam)-> [SomeExprValue $ course E.^. CourseShorthand, SomeExprValue $ exam E.^. ExamName]))
, ("term", SortColumn $ queryCourse >>> (E.^. CourseTerm )) , ("term", SortColumn $ queryCourse >>> (E.^. CourseTerm ))
, ("school", SortColumn $ queryCourse >>> (E.^. CourseSchool )) , ("school", SortColumn $ queryCourse >>> (E.^. CourseSchool ))
, ("course", SortColumn $ queryCourse >>> (E.^. CourseShorthand )) , ("course", SortColumn $ queryCourse >>> (E.^. CourseShorthand ))
, ("name", SortColumn $ queryExam >>> (E.^. ExamName )) , ("name", SortColumn $ queryExam >>> (E.^. ExamName ))
, ("time", SortColumn $ queryExam >>> (E.^. ExamStart )) , ("time", SortColumn $ queryExam >>> (E.^. ExamStart ))
, ("register-from", SortColumn $ queryExam >>> (E.^. ExamRegisterFrom )) , ("register-from", SortColumn $ queryExam >>> (E.^. ExamRegisterFrom ))
, ("register-to", SortColumn $ queryExam >>> (E.^. ExamRegisterTo )) , ("register-to", SortColumn $ queryExam >>> (E.^. ExamRegisterTo ))
, ("visible", SortColumn $ queryExam >>> (E.^. ExamVisibleFrom )) , ("visible", SortColumn $ queryExam >>> (E.^. ExamVisibleFrom ))
, ("registered", SortColumn $ queryExam >>> (\exam -> , ("registered", SortColumn $ queryExam >>> (\exam ->
E.exists $ E.from $ \registration -> do E.exists $ E.from $ \registration -> do
E.where_ $ registration E.^. ExamRegistrationUser E.==. E.val uid E.where_ $ registration E.^. ExamRegistrationUser E.==. E.val uid
E.where_ $ registration E.^. ExamRegistrationExam E.==. exam E.^. ExamId E.where_ $ registration E.^. ExamRegistrationExam E.==. exam E.^. ExamId

View File

@ -156,7 +156,7 @@ schoolsForm template = formToAForm $ schoolsFormView =<< renderWForm FormStandar
where where
schoolsForm' :: WForm Handler (FormResult (Set SchoolId)) schoolsForm' :: WForm Handler (FormResult (Set SchoolId))
schoolsForm' = do schoolsForm' = do
allSchools <- liftHandler . runDB $ selectList [] [Asc SchoolName] allSchools <- liftHandler . runDBRead $ selectList [] [Asc SchoolName]
let let
schoolForm (Entity ssh School{schoolName}) schoolForm (Entity ssh School{schoolName})
@ -585,6 +585,41 @@ getForProfileDataR cID = do
setTitleI $ MsgHeadingForProfileData $ userDisplayName user setTitleI $ MsgHeadingForProfileData $ userDisplayName user
dataWidget dataWidget
-- data TableHasData = TableHasData{tableHasRows :: Bool, tableWidget :: Widget}
-- a poor man's record subsitute
{-
type TableHasData = (Bool, Widget)
tableHasRows :: TableHasData -> Bool
tableHasRows = fst
tableWidget :: TableHasData -> Widget
tableWidget = snd
-}
maybeTable :: (RenderMessage UniWorX a)
=> a -> (Bool, Widget) -> Widget
maybeTable m = maybeTable' m Nothing Nothing
maybeTable' :: (RenderMessage UniWorX a)
=> a -> Maybe a -> Maybe Widget -> (Bool, Widget) -> Widget
maybeTable' _ Nothing _ (False, _ ) = mempty
maybeTable' _ (Just nodata) _ (False, _ ) =
[whamlet|
<div .container>
_{nodata}
|]
maybeTable' hdr _ mbRemark (True ,tbl) =
[whamlet|
<div .container>
<h2> _{hdr}
<div .container>
^{tbl}
$maybe remark <- mbRemark
<em>_{MsgProfileRemark}
\ ^{remark}
|]
makeProfileData :: Entity User -> DB Widget makeProfileData :: Entity User -> DB Widget
makeProfileData usrEnt@(Entity uid usrVal@User{..}) = do makeProfileData usrEnt@(Entity uid usrVal@User{..}) = do
now <- liftIO getCurrentTime now <- liftIO getCurrentTime
@ -605,42 +640,51 @@ makeProfileData usrEnt@(Entity uid usrVal@User{..}) = do
E.where_ $ studyfeat E.^. StudyFeaturesUser E.==. E.val uid E.where_ $ studyfeat E.^. StudyFeaturesUser E.==. E.val uid
return (studyfeat, studydegree, studyterms) return (studyfeat, studydegree, studyterms)
companies <- wgtCompanies uid companies <- wgtCompanies uid
supervisors' <- E.select $ E.from $ \(spvr `E.InnerJoin` usrSpvr) -> do -- supervisors' <- E.select $ E.from $ \(spvr `E.InnerJoin` usrSpvr) -> do
E.on $ spvr E.^. UserSupervisorSupervisor E.==. usrSpvr E.^. UserId -- E.on $ spvr E.^. UserSupervisorSupervisor E.==. usrSpvr E.^. UserId
E.where_ $ spvr E.^. UserSupervisorUser E.==. E.val uid -- E.where_ $ spvr E.^. UserSupervisorUser E.==. E.val uid
E.orderBy [E.asc (usrSpvr E.^. UserDisplayName)] -- E.orderBy [E.asc (usrSpvr E.^. UserDisplayName)]
return (usrSpvr, spvr E.^. UserSupervisorRerouteNotifications) -- return (usrSpvr, spvr E.^. UserSupervisorRerouteNotifications)
let numSupervisors = length supervisors' -- let numSupervisors = length supervisors'
supervisors = intersperse (text2widget ", ") $ -- supervisors = intersperse (text2widget ", ") $
(\(usr, E.Value reroutCom) -> linkUserWidget ForProfileDataR usr <> bool mempty icnReroute reroutCom) <$> supervisors' -- (\(usr, E.Value reroutCom) -> linkUserWidget ForProfileDataR usr <> bool mempty icnReroute reroutCom) <$> supervisors'
icnReroute = text2widget " " <> toWgt (icon IconLetter) -- icnReroute = text2widget " " <> toWgt (icon IconReroute)
supervisees' <- E.select $ E.from $ \(spvr `E.InnerJoin` usrSpvr) -> do -- supervisees' <- E.select $ E.from $ \(spvr `E.InnerJoin` usrSpvr) -> do
E.on $ spvr E.^. UserSupervisorUser E.==. usrSpvr E.^. UserId -- E.on $ spvr E.^. UserSupervisorUser E.==. usrSpvr E.^. UserId
E.where_ $ spvr E.^. UserSupervisorSupervisor E.==. E.val uid -- E.where_ $ spvr E.^. UserSupervisorSupervisor E.==. E.val uid
return (usrSpvr, spvr E.^. UserSupervisorRerouteNotifications) -- return (usrSpvr, spvr E.^. UserSupervisorRerouteNotifications)
let numSupervisees = length supervisees' -- let numSupervisees = length supervisees'
supervisees = intersperse (text2widget ", ") $ -- supervisees = intersperse (text2widget ", ") $
(\(usr, E.Value reroutCom) -> linkUserWidget ForProfileDataR usr <> bool mempty icnReroute reroutCom) <$> supervisees' -- (\(usr, E.Value reroutCom) -> linkUserWidget ForProfileDataR usr <> bool mempty icnReroute reroutCom) <$> supervisees'
-- icnReroute = text2widget " " <> toWgt (icon IconLetter) -- -- icnReroute = text2widget " " <> toWgt (icon IconReroute)
--Tables --Tables
(hasRowsOwnedCourses, ownedCoursesTable) <- mkOwnedCoursesTable uid -- Tabelle mit eigenen Kursen ownedCoursesTable <- mkOwnedCoursesTable uid -- Tabelle mit eigenen Kursen
enrolledCoursesTable <- mkEnrolledCoursesTable uid -- Tabelle mit allen Teilnehmer: Kurs (link), Datum enrolledCoursesTable <- mkEnrolledCoursesTable uid -- Tabelle mit allen Teilnehmer: Kurs (link), Datum
submissionTable <- mkSubmissionTable uid -- Tabelle mit allen Abgaben und Abgabe-Gruppen submissionTable <- mkSubmissionTable uid -- Tabelle mit allen Abgaben und Abgabe-Gruppen
submissionGroupTable <- mkSubmissionGroupTable uid -- Tabelle mit allen Abgabegruppen submissionGroupTable <- mkSubmissionGroupTable uid -- Tabelle mit allen Abgabegruppen
correctionsTable <- mkCorrectionsTable uid -- Tabelle mit allen Korrektor-Aufgaben correctionsTable <- mkCorrectionsTable uid -- Tabelle mit allen Korrektor-Aufgaben
qualificationsTable <- mkQualificationsTable now uid -- Tabelle mit allen Qualifikationen qualificationsTable <- mkQualificationsTable now uid -- Tabelle mit allen Qualifikationen
supervisorsTable <- mkSupervisorsTable uid -- Tabelle mit allen Supervisors supervisorsTable <- mkSupervisorsTable uid -- Tabelle mit allen Supervisors
superviseesTable <- mkSuperviseesTable uid -- Tabelle mit allen Supervisees superviseesTable <- mkSuperviseesTable actualPrefersPostal uid -- Tabelle mit allen Supervisees
let examTable, ownTutorialTable, tutorialTable :: Widget let supervisorsWgt :: Widget =
examTable = i18n MsgPersonalInfoExamAchievementsWip let ((getSum -> nrSupers, getSum -> nrReroute, getSum -> nrLetter), tWgt) = supervisorsTable
ownTutorialTable = i18n MsgPersonalInfoOwnTutorialsWip in maybeTable' (MsgProfileSupervisor nrSupers nrReroute) (Just MsgProfileNoSupervisor)
tutorialTable = i18n MsgPersonalInfoTutorialsWip (toMaybe (nrReroute > 0) $ msg2widget $ MsgProfileSupervisorRemark nrSupers nrReroute nrLetter) (nrSupers > 0, tWgt)
superviseesWgt :: Widget =
let ((getSum -> nrSubs, getSum -> nrReroute), tWgt) = superviseesTable
in maybeTable' (MsgProfileSupervisee nrSubs nrReroute) (Just MsgProfileNoSupervisee)
(toMaybe (nrReroute > 0) $ msg2widget $ MsgProfileSuperviseeRemark nrSubs nrReroute) (nrSubs > 0, tWgt)
-- let examTable, ownTutorialTable, tutorialTable :: Widget
-- examTable = i18n MsgPersonalInfoExamAchievementsWip
-- ownTutorialTable = i18n MsgPersonalInfoOwnTutorialsWip
-- tutorialTable = i18n MsgPersonalInfoTutorialsWip
cID <- encrypt uid cID <- encrypt uid
mCRoute <- getCurrentRoute mCRoute <- getCurrentRoute
showAdminInfo <- pure (mCRoute == Just (AdminUserR cID)) `or2M` hasReadAccessTo (AdminUserR cID) showAdminInfo <- pure (mCRoute == Just (AdminUserR cID)) `or2M` hasReadAccessTo (AdminUserR cID)
tooltipAvsPersNo <- messageI Info MsgAvsPersonNoNotId tooltipAvsPersNo <- messageI Info MsgAvsPersonNoNotId
tooltipInvalidEmail <- messageI Error MsgInvalidEmailAddress tooltipAvsPersNoDiffers <- messageI Error MsgAvsPersonNoDiffers
tooltipInvalidEmail <- messageI Error MsgInvalidEmailAddress
let profileRemarks = $(i18nWidgetFile "profile-remarks") let profileRemarks = $(i18nWidgetFile "profile-remarks")
return $(widgetFile "profileData") return $(widgetFile "profileData")
@ -698,7 +742,7 @@ mkOwnedCoursesTable =
-- | Table listing all courses that the given user is enrolled in -- | Table listing all courses that the given user is enrolled in
mkEnrolledCoursesTable :: UserId -> DB Widget mkEnrolledCoursesTable :: UserId -> DB (Bool, Widget)
mkEnrolledCoursesTable = mkEnrolledCoursesTable =
let withType :: ((E.SqlExpr (Entity Course) `E.InnerJoin` E.SqlExpr (Entity CourseParticipant)) -> a) let withType :: ((E.SqlExpr (Entity Course) `E.InnerJoin` E.SqlExpr (Entity CourseParticipant)) -> a)
-> ((E.SqlExpr (Entity Course) `E.InnerJoin` E.SqlExpr (Entity CourseParticipant)) -> a) -> ((E.SqlExpr (Entity Course) `E.InnerJoin` E.SqlExpr (Entity CourseParticipant)) -> a)
@ -706,7 +750,7 @@ mkEnrolledCoursesTable =
validator = def & defaultSorting [SortDescBy "time"] validator = def & defaultSorting [SortDescBy "time"]
in \uid -> dbTableWidget' validator in \uid -> (_1 %~ getAny) <$> dbTableWidget validator
DBTable DBTable
{ dbtIdent = "courseMembership" :: Text { dbtIdent = "courseMembership" :: Text
, dbtSQLQuery = \(course `E.InnerJoin` participant) -> do , dbtSQLQuery = \(course `E.InnerJoin` participant) -> do
@ -717,7 +761,7 @@ mkEnrolledCoursesTable =
, dbtRowKey = \(course `E.InnerJoin` _) -> course E.^. CourseId , dbtRowKey = \(course `E.InnerJoin` _) -> course E.^. CourseId
, dbtProj = dbtProjId <&> _dbrOutput . _2 %~ E.unValue , dbtProj = dbtProjId <&> _dbrOutput . _2 %~ E.unValue
, dbtColonnade = mconcat , dbtColonnade = mconcat
[ sortable (Just "term") (i18nCell MsgTableTerm) $ [ sortable (Just "term") (i18nCell MsgTableTerm) $ fmap addIndicatorCell
termCell <$> view (_dbrOutput . _1 . _entityVal . _courseTerm) termCell <$> view (_dbrOutput . _1 . _entityVal . _courseTerm)
, sortable (Just "school") (i18nCell MsgTableCourseSchool) . magnify (_dbrOutput . _1 . _entityVal) $ , sortable (Just "school") (i18nCell MsgTableCourseSchool) . magnify (_dbrOutput . _1 . _entityVal) $
schoolCell <$> view _courseTerm schoolCell <$> view _courseTerm
@ -750,7 +794,7 @@ mkEnrolledCoursesTable =
-- | Table listing all submissions for the given user -- | Table listing all submissions for the given user
mkSubmissionTable :: UserId -> DB Widget mkSubmissionTable :: UserId -> DB (Bool, Widget)
mkSubmissionTable = mkSubmissionTable =
let dbtIdent = "submissions" :: Text let dbtIdent = "submissions" :: Text
dbtStyle = def dbtStyle = def
@ -784,7 +828,7 @@ mkSubmissionTable =
<&> _dbrOutput . _4 %~ E.unValue <&> _dbrOutput . _4 %~ E.unValue
dbtColonnade = mconcat dbtColonnade = mconcat
[ sortable (Just "term") (i18nCell MsgTableTerm) $ [ sortable (Just "term") (i18nCell MsgTableTerm) $ fmap addIndicatorCell
termCell <$> view (_dbrOutput . _1 . _1) termCell <$> view (_dbrOutput . _1 . _1)
, sortable (Just "school") (i18nCell MsgTableCourseSchool) . magnify (_dbrOutput . _1 ) $ , sortable (Just "school") (i18nCell MsgTableCourseSchool) . magnify (_dbrOutput . _1 ) $
schoolCell <$> view _1 schoolCell <$> view _1
@ -828,14 +872,10 @@ mkSubmissionTable =
dbtExtraReps = [] dbtExtraReps = []
in \uid -> let dbtSQLQuery = dbtSQLQuery' uid in \uid -> let dbtSQLQuery = dbtSQLQuery' uid
dbtSorting = dbtSorting' uid dbtSorting = dbtSorting' uid
in dbTableWidget' validator DBTable{..} in (_1 %~ getAny) <$> dbTableWidget validator DBTable{..}
-- in do dbtSQLQuery <- dbtSQLQuery'
-- dbtSorting <- dbtSorting'
-- return $ dbTableWidget' validator $ DBTable {..}
-- | Table listing all submissions for the given user -- | Table listing all submissions for the given user
mkSubmissionGroupTable :: UserId -> DB Widget mkSubmissionGroupTable :: UserId -> DB (Bool, Widget)
mkSubmissionGroupTable = mkSubmissionGroupTable =
let dbtIdent = "subGroups" :: Text let dbtIdent = "subGroups" :: Text
dbtStyle = def dbtStyle = def
@ -858,7 +898,7 @@ mkSubmissionGroupTable =
<&> _dbrOutput . _1 %~ $(E.unValueN 3) <&> _dbrOutput . _1 %~ $(E.unValueN 3)
dbtColonnade = mconcat dbtColonnade = mconcat
[ sortable (Just "term") (i18nCell MsgTableTerm) $ [ sortable (Just "term") (i18nCell MsgTableTerm) $ fmap addIndicatorCell
termCell <$> view (_dbrOutput . _1 . _1) termCell <$> view (_dbrOutput . _1 . _1)
, sortable (Just "school") (i18nCell MsgTableCourseSchool) . magnify (_dbrOutput . _1 ) $ , sortable (Just "school") (i18nCell MsgTableCourseSchool) . magnify (_dbrOutput . _1 ) $
schoolCell <$> view _1 schoolCell <$> view _1
@ -887,10 +927,10 @@ mkSubmissionGroupTable =
dbtCsvDecode = Nothing dbtCsvDecode = Nothing
dbtExtraReps = [] dbtExtraReps = []
in \uid -> let dbtSQLQuery = dbtSQLQuery' uid in \uid -> let dbtSQLQuery = dbtSQLQuery' uid
in dbTableWidget' validator DBTable{..} in (_1 %~ getAny) <$> dbTableWidget validator DBTable{..}
mkCorrectionsTable :: UserId -> DB Widget mkCorrectionsTable :: UserId -> DB (Bool, Widget)
mkCorrectionsTable = mkCorrectionsTable =
let dbtIdent = "corrections" :: Text let dbtIdent = "corrections" :: Text
dbtStyle = def dbtStyle = def
@ -923,7 +963,7 @@ mkCorrectionsTable =
<&> _dbrOutput . _2 %~ E.unValue <&> _dbrOutput . _2 %~ E.unValue
dbtColonnade = mconcat dbtColonnade = mconcat
[ sortable (Just "term") (i18nCell MsgTableTerm) $ [ sortable (Just "term") (i18nCell MsgTableTerm) $ fmap addIndicatorCell
termCellCL <$> view (_dbrOutput . _1) termCellCL <$> view (_dbrOutput . _1)
, sortable (Just "school") (i18nCell MsgTableCourseSchool) $ , sortable (Just "school") (i18nCell MsgTableCourseSchool) $
schoolCellCL <$> view (_dbrOutput . _1) schoolCellCL <$> view (_dbrOutput . _1)
@ -960,7 +1000,7 @@ mkCorrectionsTable =
dbtCsvDecode = Nothing dbtCsvDecode = Nothing
dbtExtraReps = [] dbtExtraReps = []
in \uid -> let dbtSQLQuery = dbtSQLQuery' uid in \uid -> let dbtSQLQuery = dbtSQLQuery' uid
in dbTableWidget' validator DBTable{..} in (_1 %~ getAny) <$> dbTableWidget validator DBTable{..}
-- | Table listing all qualifications that the given user is enrolled in -- | Table listing all qualifications that the given user is enrolled in
@ -983,7 +1023,7 @@ mkQualificationsTable =
, dbtProj = dbtProjId , dbtProj = dbtProjId
, dbtColonnade = mconcat , dbtColonnade = mconcat
[ colSchool (_dbrOutput . _1 . _entityVal . _qualificationSchool) [ colSchool (_dbrOutput . _1 . _entityVal . _qualificationSchool)
, sortable (Just "quali") (i18nCell MsgQualificationName) $ qualificationDescrCell <$> view (_dbrOutput . _1 . _entityVal) , sortable (Just "quali") (i18nCell MsgQualificationName) $ qualificationDescrCell <$> view (_dbrOutput . _1 . _entityVal)
, sortable (Just "first-held") (i18nCell MsgTableQualificationFirstHeld) $ dayCell <$> view (_dbrOutput . _2 . _entityVal . _qualificationUserFirstHeld ) , sortable (Just "first-held") (i18nCell MsgTableQualificationFirstHeld) $ dayCell <$> view (_dbrOutput . _2 . _entityVal . _qualificationUserFirstHeld )
, sortable (Just "last-refresh") (i18nCell MsgTableQualificationLastRefresh) $ dayCell <$> view (_dbrOutput . _2 . _entityVal . _qualificationUserLastRefresh) , sortable (Just "last-refresh") (i18nCell MsgTableQualificationLastRefresh) $ dayCell <$> view (_dbrOutput . _2 . _entityVal . _qualificationUserLastRefresh)
, sortable (Just "valid-until") (i18nCell MsgLmsQualificationValidUntil) $ dayCell <$> view (_dbrOutput . _2 . _entityVal . _qualificationUserValidUntil ) , sortable (Just "valid-until") (i18nCell MsgLmsQualificationValidUntil) $ dayCell <$> view (_dbrOutput . _2 . _entityVal . _qualificationUserValidUntil )
@ -1027,8 +1067,8 @@ instance HasUser TblSupervisorData where
hasUser = _dbrOutput . _1 . _entityVal hasUser = _dbrOutput . _1 . _entityVal
-- | Table listing all supervisor of the given user -- | Table listing all supervisor of the given user
mkSupervisorsTable :: UserId -> DB Widget mkSupervisorsTable :: UserId -> DB ((Sum Int, Sum Int, Sum Int), Widget)
mkSupervisorsTable uid = dbTableWidget' validator DBTable{..} mkSupervisorsTable uid = dbTableWidget validator DBTable{..}
where where
dbtIdent = "userSupervisedBy" :: Text dbtIdent = "userSupervisedBy" :: Text
dbtStyle = def dbtStyle = def
@ -1043,8 +1083,15 @@ mkSupervisorsTable uid = dbTableWidget' validator DBTable{..}
dbtColonnade = mconcat dbtColonnade = mconcat
[ colUserNameModalHdr MsgTableSupervisor ForProfileDataR [ colUserNameModalHdr MsgTableSupervisor ForProfileDataR
, colUserEmail , colUserEmail
, sortable (Just "postal-pref") (i18nCell MsgPrefersPostal) $ \(view $ resultUser . _userPrefersPostal -> b) -> iconFixedCell $ iconLetterOrEmail b -- , sortable (Just "rerouted") (i18nCell MsgTableRerouteActive) $ \(view $ resultUserSupervisor . _entityVal . _userSupervisorRerouteNotifications -> b) -> indicatorCell <> ifIconCell b IconReroute
, sortable (Just "rerouted") (i18nCell MsgTableRerouteActive) $ \(view $ resultUserSupervisor . _entityVal . _userSupervisorRerouteNotifications -> b) -> tickmarkCell b -- , sortable (Just "postal-pref") (i18nCell MsgPrefersPostal) $ \(view $ resultUser . _userPrefersPostal -> b) -> iconFixedCell $ iconLetterOrEmail b
, sortable (Just "reroute") (i18nCell MsgTableRerouteActive) $ \row ->
let isReroute = row ^. resultUserSupervisor . _entityVal ._userSupervisorRerouteNotifications
isLetter = row ^. resultUser . _userPrefersPostal
in tellCell (Sum 1, Sum $ fromEnum isReroute, Sum $ fromEnum $ isReroute && isLetter) $
if isReroute
then iconCell IconReroute <> spacerCell <> iconFixedCell (iconLetterOrEmail isLetter)
else mempty
, sortable (Just "cshort") (i18nCell MsgTableCompany) $ \(view $ resultUserSupervisor . _entityVal . _userSupervisorCompany -> mc) -> maybeCell mc (\(unCompanyKey -> c) -> anchorCell (FirmUsersR c) $ citext2widget c) , sortable (Just "cshort") (i18nCell MsgTableCompany) $ \(view $ resultUserSupervisor . _entityVal . _userSupervisorCompany -> mc) -> maybeCell mc (\(unCompanyKey -> c) -> anchorCell (FirmUsersR c) $ citext2widget c)
, sortable (Just "reason") (i18nCell MsgSupervisorReason) $ \(view $ resultUserSupervisor . _entityVal . _userSupervisorReason -> mr) -> maybeCell mr textCell , sortable (Just "reason") (i18nCell MsgSupervisorReason) $ \(view $ resultUserSupervisor . _entityVal . _userSupervisorReason -> mr) -> maybeCell mr textCell
] ]
@ -1054,6 +1101,11 @@ mkSupervisorsTable uid = dbTableWidget' validator DBTable{..}
, singletonMap & uncurry $ sortUserEmail queryUser , singletonMap & uncurry $ sortUserEmail queryUser
, singletonMap "postal-pref" $ SortColumn $ queryUser >>> (E.^. UserPrefersPostal) , singletonMap "postal-pref" $ SortColumn $ queryUser >>> (E.^. UserPrefersPostal)
, singletonMap "rerouted" $ SortColumn $ queryUserSupervisor >>> (E.^. UserSupervisorRerouteNotifications) , singletonMap "rerouted" $ SortColumn $ queryUserSupervisor >>> (E.^. UserSupervisorRerouteNotifications)
-- , singletonMap "reroute" $ SortColumn $ queryUserSupervisor &&& queryUser >>> (\(spr,usr) -> mTuple (spr E.^. UserSupervisorRerouteNotifications) (usr E.^. UserPrefersPostal))
, singletonMap "reroute" $ SortColumns $ \row ->
[ SomeExprValue $ queryUserSupervisor row E.^. UserSupervisorRerouteNotifications
, SomeExprValue $ queryUser row E.^. UserPrefersPostal
]
, singletonMap "cshort" $ SortColumn $ queryUserSupervisor >>> (E.^. UserSupervisorCompany) , singletonMap "cshort" $ SortColumn $ queryUserSupervisor >>> (E.^. UserSupervisorCompany)
, singletonMap "reason" $ SortColumn $ queryUserSupervisor >>> (E.^. UserSupervisorReason) , singletonMap "reason" $ SortColumn $ queryUserSupervisor >>> (E.^. UserSupervisorReason)
] ]
@ -1068,8 +1120,8 @@ mkSupervisorsTable uid = dbTableWidget' validator DBTable{..}
-- | Table listing all persons supervised by the given user -- | Table listing all persons supervised by the given user
mkSuperviseesTable :: UserId -> DB Widget mkSuperviseesTable ::Bool -> UserId -> DB ((Sum Int, Sum Int), Widget)
mkSuperviseesTable uid = dbTableWidget' validator DBTable{..} mkSuperviseesTable userPrefersPostal uid = dbTableWidget validator DBTable{..}
where where
dbtIdent = "userSupervisedBy" :: Text dbtIdent = "userSupervisedBy" :: Text
dbtStyle = def dbtStyle = def
@ -1081,11 +1133,15 @@ mkSuperviseesTable uid = dbTableWidget' validator DBTable{..}
dbtRowKey (_ `E.InnerJoin` spr) = spr E.^. UserSupervisorId dbtRowKey (_ `E.InnerJoin` spr) = spr E.^. UserSupervisorId
dbtProj = dbtProjId dbtProj = dbtProjId
iconCellLetterOrEmail = spacerCell <> iconFixedCell (iconLetterOrEmail userPrefersPostal) -- only notification type of supervisor matters here
dbtColonnade = mconcat dbtColonnade = mconcat
[ colUserNameModalHdr MsgTableSupervisee ForProfileDataR [ colUserNameModalHdr MsgTableSupervisee ForProfileDataR
-- , colUserEmail , colUserEmail
-- , sortable (Just "rerouted") (i18nCell MsgTableRerouteActive) $ \(view $ resultUserSupervisor . _entityVal . _userSupervisorRerouteNotifications -> b) -> ifIconCell b IconReroute
-- , sortable (Just "postal-pref") (i18nCell MsgPrefersPostal) $ \(view $ resultUser . _userPrefersPostal -> b) -> iconFixedCell $ iconLetterOrEmail b -- , sortable (Just "postal-pref") (i18nCell MsgPrefersPostal) $ \(view $ resultUser . _userPrefersPostal -> b) -> iconFixedCell $ iconLetterOrEmail b
, sortable (Just "rerouted") (i18nCell MsgTableRerouteActive) $ \(view $ resultUserSupervisor . _entityVal . _userSupervisorRerouteNotifications -> b) -> tickmarkCell b , sortable (Just "reroute") (i18nCell MsgTableRerouteActive) $ \row ->
let isReroute = row ^. resultUserSupervisor . _entityVal ._userSupervisorRerouteNotifications
in tellCell (Sum 1, Sum $ fromEnum isReroute) $ boolCell isReroute $ iconCell IconReroute <> iconCellLetterOrEmail
, sortable (Just "cshort") (i18nCell MsgTableCompany) $ \(view $ resultUserSupervisor . _entityVal . _userSupervisorCompany -> mc) -> maybeCell mc (\(unCompanyKey -> c) -> anchorCell (FirmUsersR c) $ citext2widget c) , sortable (Just "cshort") (i18nCell MsgTableCompany) $ \(view $ resultUserSupervisor . _entityVal . _userSupervisorCompany -> mc) -> maybeCell mc (\(unCompanyKey -> c) -> anchorCell (FirmUsersR c) $ citext2widget c)
, sortable (Just "reason") (i18nCell MsgSupervisorReason) $ \(view $ resultUserSupervisor . _entityVal . _userSupervisorReason -> mr) -> maybeCell mr textCell , sortable (Just "reason") (i18nCell MsgSupervisorReason) $ \(view $ resultUserSupervisor . _entityVal . _userSupervisorReason -> mr) -> maybeCell mr textCell
] ]
@ -1093,8 +1149,12 @@ mkSuperviseesTable uid = dbTableWidget' validator DBTable{..}
dbtSorting = mconcat dbtSorting = mconcat
[ singletonMap & uncurry $ sortUserNameLink queryUser [ singletonMap & uncurry $ sortUserNameLink queryUser
, singletonMap & uncurry $ sortUserEmail queryUser , singletonMap & uncurry $ sortUserEmail queryUser
, singletonMap "postal-pref" $ SortColumn $ queryUser >>> (E.^. UserPrefersPostal) -- , singletonMap "postal-pref" $ SortColumn $ queryUser >>> (E.^. UserPrefersPostal)
, singletonMap "rerouted" $ SortColumn $ queryUserSupervisor >>> (E.^. UserSupervisorRerouteNotifications) -- , singletonMap "rerouted" $ SortColumn $ queryUserSupervisor >>> (E.^. UserSupervisorRerouteNotifications)
, singletonMap "reroute" $ SortColumns $ \row ->
[ SomeExprValue $ queryUserSupervisor row E.^. UserSupervisorRerouteNotifications
, SomeExprValue $ queryUser row E.^. UserPrefersPostal
]
, singletonMap "cshort" $ SortColumn $ queryUserSupervisor >>> (E.^. UserSupervisorCompany) , singletonMap "cshort" $ SortColumn $ queryUserSupervisor >>> (E.^. UserSupervisorCompany)
, singletonMap "reason" $ SortColumn $ queryUserSupervisor >>> (E.^. UserSupervisorReason) , singletonMap "reason" $ SortColumn $ queryUserSupervisor >>> (E.^. UserSupervisorReason)
] ]

View File

@ -98,7 +98,7 @@ getQualificationSAPDirectR = do
now <- liftIO getCurrentTime now <- liftIO getCurrentTime
fdate <- formatTime' "%Y%m%d_%H-%M" now fdate <- formatTime' "%Y%m%d_%H-%M" now
let ldap_cutoff = addDiffDaysRollOver (fromMonths $ -3) now let ldap_cutoff = addDiffDaysRollOver (fromMonths $ -3) now
qualUsers <- runDB $ E.select $ do qualUsers <- runDBRead $ E.select $ do
(qual :& qualUser :& user :& qualBlock) <- (qual :& qualUser :& user :& qualBlock) <-
E.from $ E.table @Qualification E.from $ E.table @Qualification
`E.innerJoin` E.table @QualificationUser `E.innerJoin` E.table @QualificationUser

View File

@ -121,21 +121,21 @@ postUsersR = do
-- (AdminUserR <$> encrypt uid) -- (AdminUserR <$> encrypt uid)
-- (toWidget . display $ last $ impureNonNull $ words $ userDisplayName) -- (toWidget . display $ last $ impureNonNull $ words $ userDisplayName)
, sortable (Just "user-supervisor") (i18nCell MsgTableSupervisor) $ \DBRow{ dbrOutput = Entity uid _ } -> flip (set' cellContents) mempty $ do , sortable (Just "user-supervisor") (i18nCell MsgTableSupervisor) $ \DBRow{ dbrOutput = Entity uid _ } -> flip (set' cellContents) mempty $ do
supervisors' <- liftHandler . runDB . E.select $ E.from $ \(spvr `E.InnerJoin` usrSpvr) -> do supervisors' <- liftHandler . runDBRead . E.select $ E.from $ \(spvr `E.InnerJoin` usrSpvr) -> do
E.on $ spvr E.^. UserSupervisorSupervisor E.==. usrSpvr E.^. UserId E.on $ spvr E.^. UserSupervisorSupervisor E.==. usrSpvr E.^. UserId
E.where_ $ spvr E.^. UserSupervisorUser E.==. E.val uid E.where_ $ spvr E.^. UserSupervisorUser E.==. E.val uid
E.orderBy [E.asc (usrSpvr E.^. UserDisplayName)] E.orderBy [E.asc (usrSpvr E.^. UserDisplayName)]
return (usrSpvr, spvr E.^. UserSupervisorRerouteNotifications) return (usrSpvr, spvr E.^. UserSupervisorRerouteNotifications)
let supervisors = intersperse (text2widget ", ") $ let supervisors = intersperse (text2widget ", ") $
(\(usr, E.Value reroutCom) -> linkUserWidget ForProfileDataR usr <> bool mempty icnReroute reroutCom) <$> supervisors' (\(usr, E.Value reroutCom) -> linkUserWidget ForProfileDataR usr <> bool mempty icnReroute reroutCom) <$> supervisors'
icnReroute = text2widget " " <> toWgt (icon IconLetter) icnReroute = text2widget " " <> toWgt (icon IconReroute)
pure $ mconcat supervisors pure $ mconcat supervisors
, sortable (Just "last-login") (i18nCell MsgLastLogin) $ \DBRow{ dbrOutput = Entity _ User{..} } -> maybe mempty dateTimeCell userLastAuthentication , sortable (Just "last-login") (i18nCell MsgLastLogin) $ \DBRow{ dbrOutput = Entity _ User{..} } -> maybe mempty dateTimeCell userLastAuthentication
, sortable (Just "auth-ldap") (i18nCell MsgAuthMode) $ \DBRow{ dbrOutput = Entity _ User{..} } -> i18nCell userAuthentication , sortable (Just "auth-ldap") (i18nCell MsgAuthMode) $ \DBRow{ dbrOutput = Entity _ User{..} } -> i18nCell userAuthentication
, sortable (Just "ldap-sync") (i18nCell MsgLdapSynced) $ \DBRow{ dbrOutput = Entity _ User{..} } -> maybe mempty dateTimeCell userLastLdapSynchronisation , sortable (Just "ldap-sync") (i18nCell MsgLdapSynced) $ \DBRow{ dbrOutput = Entity _ User{..} } -> maybe mempty dateTimeCell userLastLdapSynchronisation
, flip foldMap universeF $ \function -> , flip foldMap universeF $ \function ->
sortable (Just $ SortingKey $ CI.mk $ toPathPiece function) (i18nCell function) $ \DBRow{ dbrOutput = Entity uid _ } -> flip (set' cellContents) mempty $ do sortable (Just $ SortingKey $ CI.mk $ toPathPiece function) (i18nCell function) $ \DBRow{ dbrOutput = Entity uid _ } -> flip (set' cellContents) mempty $ do
schools <- liftHandler . runDB . E.select . E.from $ \(school `E.InnerJoin` userFunction) -> do schools <- liftHandler . runDBRead . E.select . E.from $ \(school `E.InnerJoin` userFunction) -> do
E.on $ school E.^. SchoolId E.==. userFunction E.^. UserFunctionSchool E.on $ school E.^. SchoolId E.==. userFunction E.^. UserFunctionSchool
E.where_ $ userFunction E.^. UserFunctionUser E.==. E.val uid E.where_ $ userFunction E.^. UserFunctionUser E.==. E.val uid
E.&&. userFunction E.^. UserFunctionFunction E.==. E.val function E.&&. userFunction E.^. UserFunctionFunction E.==. E.val function
@ -148,7 +148,7 @@ postUsersR = do
<li>#{sh} <li>#{sh}
|] |]
, sortable (Just "system-function") (i18nCell MsgUserSystemFunctions) $ \DBRow{ dbrOutput = Entity uid _ } -> , sortable (Just "system-function") (i18nCell MsgUserSystemFunctions) $ \DBRow{ dbrOutput = Entity uid _ } ->
let getFunctions = fmap (map $ userSystemFunctionFunction . entityVal) . liftHandler . runDB $ selectList [ UserSystemFunctionUser ==. uid, UserSystemFunctionIsOptOut ==. False ] [ Asc UserSystemFunctionFunction ] let getFunctions = fmap (map $ userSystemFunctionFunction . entityVal) . liftHandler . runDBRead $ selectList [ UserSystemFunctionUser ==. uid, UserSystemFunctionIsOptOut ==. False ] [ Asc UserSystemFunctionFunction ]
in listCell' getFunctions i18nCell in listCell' getFunctions i18nCell
, sortable Nothing (mempty & cellAttrs <>~ pure ("hide-columns--hider-label", mr MsgTableActionsHead)) $ \inp@DBRow{ dbrOutput = Entity uid _ } -> FormCell , sortable Nothing (mempty & cellAttrs <>~ pure ("hide-columns--hider-label", mr MsgTableActionsHead)) $ \inp@DBRow{ dbrOutput = Entity uid _ } -> FormCell
{ formCellAttrs = [] { formCellAttrs = []
@ -299,6 +299,12 @@ postUsersR = do
in E.maybe E.true (E.<=. E.val minTime) $ user E.^. UserLastLdapSynchronisation in E.maybe E.true (E.<=. E.val minTime) $ user E.^. UserLastLdapSynchronisation
| otherwise -> E.val True :: E.SqlExpr (E.Value Bool) | otherwise -> E.val True :: E.SqlExpr (E.Value Bool)
) )
, ( "avs-sync", FilterColumn . E.mkExistsFilter $ \user criterion ->
E.from $ \usrAvs -> do
let minTime = (E.val criterion :: E.SqlExpr (E.Value UTCTime))
E.where_ $ usrAvs E.^. UserAvsUser E.==. user E.^. UserId
E.&&. usrAvs E.^. UserAvsLastSynch E.<=. minTime
)
, ( "user-company", FilterColumn . E.mkExistsFilter $ \user criterion -> , ( "user-company", FilterColumn . E.mkExistsFilter $ \user criterion ->
E.from $ \(usrComp `E.InnerJoin` comp) -> do E.from $ \(usrComp `E.InnerJoin` comp) -> do
let testname = (E.val criterion :: E.SqlExpr (E.Value (CI Text))) `E.isInfixOf` let testname = (E.val criterion :: E.SqlExpr (E.Value (CI Text))) `E.isInfixOf`
@ -343,6 +349,7 @@ postUsersR = do
, prismAForm (singletonFilter "is-supervisor" . maybePrism _PathPiece) mPrev $ aopt (boolField . Just $ SomeMessage MsgBoolIrrelevant) (fslI MsgUserIsSupervisor) , prismAForm (singletonFilter "is-supervisor" . maybePrism _PathPiece) mPrev $ aopt (boolField . Just $ SomeMessage MsgBoolIrrelevant) (fslI MsgUserIsSupervisor)
, prismAForm (singletonFilter "auth-ldap" . maybePrism _PathPiece) mPrev $ aopt (lift `hoistField` selectFieldList [(MsgAuthPWHash "", False), (MsgAuthLDAP, True)]) (fslI MsgAuthMode) , prismAForm (singletonFilter "auth-ldap" . maybePrism _PathPiece) mPrev $ aopt (lift `hoistField` selectFieldList [(MsgAuthPWHash "", False), (MsgAuthLDAP, True)]) (fslI MsgAuthMode)
, prismAForm (singletonFilter "ldap-sync" . maybePrism _PathPiece) mPrev $ aopt utcTimeField (fslI MsgLdapSyncedBefore) , prismAForm (singletonFilter "ldap-sync" . maybePrism _PathPiece) mPrev $ aopt utcTimeField (fslI MsgLdapSyncedBefore)
, prismAForm (singletonFilter "avs-sync" . maybePrism _PathPiece) mPrev $ aopt utcTimeField (fslI MsgLastAvsSyncedBefore)
] ]
, dbtStyle = def { dbsFilterLayout = defaultDBSFilterLayout } , dbtStyle = def { dbsFilterLayout = defaultDBSFilterLayout }
, dbtParams = DBParamsForm , dbtParams = DBParamsForm

View File

@ -171,11 +171,11 @@ lookupAvsUsers apis = do
updateReceivers :: UserId -> Handler (Entity User, [Entity User], Bool) updateReceivers :: UserId -> Handler (Entity User, [Entity User], Bool)
updateReceivers uid = do updateReceivers uid = do
-- First perform AVS update for receiver -- First perform AVS update for receiver
runDB (getBy (UniqueUserAvsUser uid)) >>= \case runDBRead (getBy (UniqueUserAvsUser uid)) >>= \case
Just Entity{entityVal=UserAvs{userAvsPersonId = apid}} -> catchAll2log $ upsertAvsUserById apid Just Entity{entityVal=UserAvs{userAvsPersonId = apid}} -> catchAll2log $ upsertAvsUserById apid
Nothing -> return () Nothing -> return ()
-- Retrieve updated user and supervisors now -- Retrieve updated user and supervisors now
(underling :: Entity User, avsSupers :: [(E.Value UserId, E.Value (Maybe AvsPersonId))]) <- runDB $ (,) (underling :: Entity User, avsSupers :: [(E.Value UserId, E.Value (Maybe AvsPersonId))]) <- runDBRead $ (,)
<$> getJustEntity uid <$> getJustEntity uid
<*> (E.select $ do <*> (E.select $ do
(usrSuper :& usrAvs) <- (usrSuper :& usrAvs) <-
@ -194,7 +194,7 @@ updateReceivers uid = do
if null receiverIDs if null receiverIDs
then directResult then directResult
else do else do
receivers <- runDB $ selectList [UserId <-. receiverIDs] [] -- due to possible address updates, we must runDB once more and cannot join above receivers <- runDBRead $ selectList [UserId <-. receiverIDs] [] -- due to possible address updates, we must runDB once more and cannot join above
if null receivers if null receivers
then directResult then directResult
else return (underling, receivers, uid `elem` (entityKey <$> receivers)) else return (underling, receivers, uid `elem` (entityKey <$> receivers))
@ -450,7 +450,7 @@ updateAvsUserByADC newAvsDataContact@(AvsDataContact apid newAvsPersonInfo newAv
linktoAvsUserByUIDs :: Set UserId -> Handler () linktoAvsUserByUIDs :: Set UserId -> Handler ()
linktoAvsUserByUIDs uids = do linktoAvsUserByUIDs uids = do
ips <- runDB $ E.select $ do ips <- runDBRead $ E.select $ do
usr <- E.from $ E.table @User usr <- E.from $ E.table @User
let uid = usr E.^. UserId let uid = usr E.^. UserId
ipn = usr E.^. UserCompanyPersonalNumber ipn = usr E.^. UserCompanyPersonalNumber
@ -484,18 +484,18 @@ createAvsUserById muid api = do
case Set.toList contactRes of case Set.toList contactRes of
[] -> throwM $ AvsUserUnknownByAvs api [] -> throwM $ AvsUserUnknownByAvs api
(_:_:_) -> throwM $ AvsUserAmbiguous api (_:_:_) -> throwM $ AvsUserAmbiguous api
[AvsDataContact{avsContactPersonInfo=cpi, avsContactFirmInfo=firmInfo, avsContactPersonID}] [adc@AvsDataContact{avsContactPersonInfo=cpi, avsContactFirmInfo=firmInfo, avsContactPersonID}]
| avsContactPersonID /= api -> throwM $ AvsIdMismatch api avsContactPersonID | avsContactPersonID /= api -> throwM $ AvsIdMismatch api avsContactPersonID
| otherwise -> do | otherwise -> do
-- check for matching existing user -- check for matching existing user
let internalPersNo :: Maybe Text = cpi ^? _avsInfoInternalPersonalNo . _Just . _avsInternalPersonalNo let internalPersNo :: Maybe Text = cpi ^? _avsInfoInternalPersonalNo . _Just . _avsInternalPersonalNo
persMail :: Maybe UserEmail = cpi ^? _avsInfoPersonEMail . _Just . from _CI -- persMail :: Maybe UserEmail = cpi ^? _avsInfoPersonEMail . _Just . from _CI
oldUsr <- runDB $ do oldUsr <- runDBRead $ do
mbUid <- if isJust muid mbUid <- if isJust muid
then return muid then return muid
else firstJustM $ catMaybes else firstJustM $ catMaybes
[ internalPersNo <&> (\ipn -> getKeyByFilter [UserCompanyPersonalNumber ==. Just ipn]) -- must ensure filter isnt ==. Nothing [ internalPersNo <&> (\ipn -> getKeyByFilter [UserCompanyPersonalNumber ==. Just ipn]) -- must ensure filter isnt ==. Nothing
, persMail <&> guessUserByEmail -- , persMail <&> guessUserByEmail -- this did not work, as unfortunately, superiors are sometimes listed under _avsInfoPersonEMail!
] ]
mbUAvs <- (getBy . UniqueUserAvsUser) `traverseJoin` mbUid mbUAvs <- (getBy . UniqueUserAvsUser) `traverseJoin` mbUid
return (mbUid, mbUAvs) return (mbUid, mbUAvs)
@ -533,11 +533,11 @@ createAvsUserById muid api = do
, audFirstName = cpi ^. _avsInfoFirstName & Text.strip , audFirstName = cpi ^. _avsInfoFirstName & Text.strip
, audSurname = cpi ^. _avsInfoLastName & Text.strip , audSurname = cpi ^. _avsInfoLastName & Text.strip
, audDisplayName = cpi ^. _avsInfoDisplayName , audDisplayName = cpi ^. _avsInfoDisplayName
, audDisplayEmail = persMail & fromMaybe mempty , audDisplayEmail = adc ^. _avsContactPrimaryEmail . to (fromMaybe mempty) . from _CI
, audEmail = persMail & fromMaybe ("AVSNO:" <> cpi ^. _avsInfoPersonNo . from _CI) , audEmail = "AVSNO:" <> cpi ^. _avsInfoPersonNo . from _CI
, audIdent = persMail & fromMaybe ("AVSID:" <> ciShow api ) , audIdent = "AVSID:" <> ciShow api
, audAuth = maybe AuthKindNoLogin (const AuthKindLDAP) internalPersNo , audAuth = maybe AuthKindNoLogin (const AuthKindLDAP) internalPersNo
, audMatriculation = cpi ^. _avsInfoPersonNo & Just . tshow , audMatriculation = cpi ^. _avsInfoPersonNo & Just
, audSex = Nothing , audSex = Nothing
, audBirthday = cpi ^. _avsInfoDateOfBirth , audBirthday = cpi ^. _avsInfoDateOfBirth
, audMobile = cpi ^. _avsInfoPersonMobilePhoneNo , audMobile = cpi ^. _avsInfoPersonMobilePhoneNo
@ -676,9 +676,14 @@ upsertCompanySuperior (mbCid, newAfi) mbOldAfi = runMaybeT $ do
oldSup = snd <$> oldChanges oldSup = snd <$> oldChanges
unless (supChange == Just False) $ do unless (supChange == Just False) $ do
-- upsert new superior company supervisor -- upsert new superior company supervisor
mbMaxPrio <- E.selectOne $ do
usrCmp <- E.from $ E.table @UserCompany
E.where_ $ usrCmp E.^. UserCompanyUser E.==. E.val supid
return . E.max_ $ usrCmp E.^. UserCompanyPriority
let maxPrio = maybe 1 (fromMaybe 1 . E.unValue) mbMaxPrio
suprEnt <- upsertBy (UniqueUserCompany supid cid) suprEnt <- upsertBy (UniqueUserCompany supid cid)
(UserCompany supid cid True False 1 True) (UserCompany supid cid True False maxPrio True)
[UserCompanySupervisor =. True] [UserCompanySupervisor =. True, UserCompanyPriority =. maxPrio]
E.insertSelectWithConflict UniqueUserSupervisor E.insertSelectWithConflict UniqueUserSupervisor
(do (do
usr <- E.from $ E.table @UserCompany usr <- E.from $ E.table @UserCompany
@ -736,15 +741,15 @@ guessAvsUser :: Text -> Handler (Maybe UserId)
guessAvsUser (Text.splitAt 6 -> (Text.toUpper -> prefix, readMay -> Just nr)) guessAvsUser (Text.splitAt 6 -> (Text.toUpper -> prefix, readMay -> Just nr))
| prefix=="AVSID:" = | prefix=="AVSID:" =
let avsid = AvsPersonId nr in let avsid = AvsPersonId nr in
runDB (getBy $ UniqueUserAvsId avsid) >>= \case runDBRead (getBy $ UniqueUserAvsId avsid) >>= \case
(Just Entity{entityVal=UserAvs{userAvsUser=uid}}) -> return $ Just uid (Just Entity{entityVal=UserAvs{userAvsUser=uid}}) -> return $ Just uid
Nothing -> catchAVS2message $ Just <$> upsertAvsUserById avsid Nothing -> catchAVS2message $ Just <$> upsertAvsUserById avsid
| prefix=="AVSNO:" = | prefix=="AVSNO:" =
runDB (view (_entityVal . _userAvsUser) <<$>> getByFilter [UserAvsNoPerson ==. nr]) runDBRead (view (_entityVal . _userAvsUser) <<$>> getByFilter [UserAvsNoPerson ==. nr])
guessAvsUser someid@(discernAvsCardPersonalNo -> Just someavsid) = guessAvsUser someid@(discernAvsCardPersonalNo -> Just someavsid) =
catchAVS2message $ upsertAvsUserByCard someavsid >>= \case catchAVS2message $ upsertAvsUserByCard someavsid >>= \case
Nothing | Left{} <- someavsid -> -- attempt to find PersonalNumber in DB Nothing | Left{} <- someavsid -> -- attempt to find PersonalNumber in DB
runDB (getKeyByFilter [UserCompanyPersonalNumber ==. Just someid]) runDBRead (getKeyByFilter [UserCompanyPersonalNumber ==. Just someid])
other -> return other other -> return other
guessAvsUser someid = do guessAvsUser someid = do
try (runDB $ ldapLookupAndUpsert someid) >>= \case try (runDB $ ldapLookupAndUpsert someid) >>= \case

View File

@ -109,7 +109,7 @@ data CU_UserAvs_User
| CU_UA_UserMatrikelnummer | CU_UA_UserMatrikelnummer
| CU_UA_UserCompanyPersonalNumber | CU_UA_UserCompanyPersonalNumber
| CU_UA_UserLdapPrimaryKey | CU_UA_UserLdapPrimaryKey
-- CU_UA_UserDisplayEmail -- use _avsContactPrimaryEmailAddress instead -- CU_UA_UserDisplayEmail -- use _avsContactPrimaryEmail instead
deriving (Show, Eq) deriving (Show, Eq)
instance MkCheckUpdate CU_UserAvs_User where instance MkCheckUpdate CU_UserAvs_User where

View File

@ -40,16 +40,16 @@ wgtCompanies = \uid -> do
^{c} ^{c}
$forall c <- otherCmp $forall c <- otherCmp
<p> <p>
#{c} ^{c}
|] |]
return $ toMaybe (notNull topCmp) resWgt return $ toMaybe (notNull topCmp) resWgt
where where
procCmp _ [] = (0, [],[]) procCmp _ [] = (0, [], [])
procCmp maxPri ((E.Value cmpSh, E.Value cmpName, E.Value cmpSpr, E.Value cmpPrio) : cs) = procCmp maxPri ((E.Value cmpSh, E.Value cmpName, E.Value cmpSpr, E.Value cmpPrio) : cs) =
let cmpWgt = companyWidget (cmpSh, cmpName, cmpSpr) let isTop = cmpPrio >= maxPri
isTop = cmpPrio >= maxPri cmpWgt = companyWidget isTop (cmpSh, cmpName, cmpSpr)
(accPri,accTop,accRem) = procCmp maxPri cs (accPri,accTop,accRem) = procCmp maxPri cs
in (max cmpPrio accPri, bool accTop (cmpWgt : accTop) isTop, bool (cmpName : accRem) accRem isTop) -- lazy evaluation after repmin example in (max cmpPrio accPri, bool accTop (cmpWgt : accTop) isTop, bool (cmpWgt : accRem) accRem isTop) -- lazy evaluation after repmin example, don't factor out the bool!
-- TODO: use this function in company view Handler.Firm #157 -- TODO: use this function in company view Handler.Firm #157
-- | add all company supervisors for a given users -- | add all company supervisors for a given users

View File

@ -112,12 +112,14 @@ validQualification' cutoff qualUser =
E.&&. quserBlock' False cutoff qualUser E.&&. quserBlock' False cutoff qualUser
-- selectValidQualifications :: QualificationId -> [UserId] -> UTCTime -> DB [Entity QualificationUser] -- selectValidQualifications :: QualificationId -> [UserId] -> UTCTime -> DB [Entity QualificationUser]
selectValidQualifications :: -- selectValidQualifications ::
( MonadIO m -- ( MonadIO m
, BackendCompatible SqlBackend backend -- , BackendCompatible SqlBackend backend
, PersistQueryRead backend -- , PersistQueryRead backend
, PersistUniqueRead backend -- , PersistUniqueRead backend
) => QualificationId -> [UserId] -> UTCTime -> ReaderT backend m [Entity QualificationUser] -- ) => QualificationId -> [UserId] -> UTCTime -> ReaderT backend m [Entity QualificationUser]
selectValidQualifications :: (MonadIO m, E.SqlBackendCanRead backend)
=> QualificationId -> [UserId] -> UTCTime -> ReaderT backend m [Entity QualificationUser]
selectValidQualifications qid uids cutoff = selectValidQualifications qid uids cutoff =
-- cutoff <- utctDay <$> liftIO getCurrentTime -- cutoff <- utctDay <$> liftIO getCurrentTime
E.select $ do E.select $ do

View File

@ -41,6 +41,9 @@ cellTell = flip tellCell
indicatorCell :: IsDBTable m Any => DBCell m Any -- For dbTables that return a Bool to indicate content indicatorCell :: IsDBTable m Any => DBCell m Any -- For dbTables that return a Bool to indicate content
indicatorCell = writerCell . tell $ Any True indicatorCell = writerCell . tell $ Any True
addIndicatorCell :: IsDBTable m Any => DBCell m Any -> DBCell m Any
addIndicatorCell = tellCell $ Any True
writerCell :: IsDBTable m w => WriterT w m () -> DBCell m w writerCell :: IsDBTable m w => WriterT w m () -> DBCell m w
writerCell act = mempty & cellContents %~ (<* act) writerCell act = mempty & cellContents %~ (<* act)
@ -51,6 +54,10 @@ cellMaybe = foldMap
maybeCell :: IsDBTable m b => Maybe a -> (a -> DBCell m b) -> DBCell m b maybeCell :: IsDBTable m b => Maybe a -> (a -> DBCell m b) -> DBCell m b
maybeCell = flip foldMap maybeCell = flip foldMap
boolCell :: IsDBTable m b => Bool -> DBCell m b -> DBCell m b
boolCell True c = c
boolCell False _ = mempty
htmlCell :: (IsDBTable m a, ToMarkup c) => c -> DBCell m a htmlCell :: (IsDBTable m a, ToMarkup c) => c -> DBCell m a
htmlCell = cell . toWidget . toMarkup htmlCell = cell . toWidget . toMarkup

View File

@ -62,7 +62,7 @@ userWidget :: HasUser c => c -> Widget
userWidget x = nameWidget (x ^. _userDisplayName) (x ^._userSurname) userWidget x = nameWidget (x ^. _userDisplayName) (x ^._userSurname)
userIdWidget :: UserId -> Widget userIdWidget :: UserId -> Widget
userIdWidget uid = maybeM (msg2widget MsgUserUnknown) userWidget (liftHandler $ runDB $ get uid) userIdWidget uid = maybeM (msg2widget MsgUserUnknown) userWidget (liftHandler $ runDBRead $ get uid)
linkUserWidget :: HasRoute UniWorX url => (CryptoUUIDUser -> url) -> Entity User -> Widget linkUserWidget :: HasRoute UniWorX url => (CryptoUUIDUser -> url) -> Entity User -> Widget
linkUserWidget lnk (Entity uid usr) = do linkUserWidget lnk (Entity uid usr) = do
@ -71,7 +71,7 @@ linkUserWidget lnk (Entity uid usr) = do
-- | like linkUserWidget, but on Id only. Requires DB access, use with caution -- | like linkUserWidget, but on Id only. Requires DB access, use with caution
linkUserIdWidget :: HasRoute UniWorX url => (CryptoUUIDUser -> url) -> UserId -> Widget linkUserIdWidget :: HasRoute UniWorX url => (CryptoUUIDUser -> url) -> UserId -> Widget
linkUserIdWidget lnk uid = maybeM (msg2widget MsgUserUnknown) (linkUserWidget lnk . Entity uid) (liftHandler $ runDB $ get uid) linkUserIdWidget lnk uid = maybeM (msg2widget MsgUserUnknown) (linkUserWidget lnk . Entity uid) (liftHandler $ runDBRead $ get uid)
userEmailWidget :: HasUser c => c -> Widget userEmailWidget :: HasUser c => c -> Widget
userEmailWidget x = nameEmailWidget (x ^. _userDisplayEmail) (x ^. _userDisplayName) (x ^. _userSurname) userEmailWidget x = nameEmailWidget (x ^. _userDisplayEmail) (x ^. _userDisplayName) (x ^. _userSurname)
@ -141,15 +141,20 @@ modalAccess wdgtNo wdgtYes writeAccess route = do
else wdgtNo else wdgtNo
-- also see Handler.Utils.Table.Cells.companyCell -- also see Handler.Utils.Table.Cells.companyCell
companyWidget :: (CompanyShorthand, CompanyName, Bool) -> Widget companyWidget :: Bool -> (CompanyShorthand, CompanyName, Bool) -> Widget
companyWidget (csh, cname, isSupervisor) = simpleLink (toWgt name) curl companyWidget isPrimary (csh, cname, isSupervisor)
| isPrimary, isSupervisor = simpleLink (toWgt $ name <> iconSupervisor) curl
| isPrimary = simpleLink (toWgt name ) curl
| isSupervisor = toWgt name <> simpleLink (toWgt iconSupervisor) curl
| otherwise = toWgt name
where where
curl = FirmUsersR csh curl = FirmUsersR csh
corg = ciOriginal cname corg = ciOriginal cname
name name
| isSupervisor = text2markup (corg <> " ") <> icon IconSupervisor | isSupervisor = text2markup (corg <> " ")
| otherwise = text2markup corg | otherwise = text2markup corg
---------- ----------
-- HEAT -- -- HEAT --
---------- ----------

View File

@ -101,7 +101,7 @@ dispatchJobSynchroniseAvs numIterations epoch iteration pause
dispatchJobSynchroniseAvsQueue :: JobHandler UniWorX dispatchJobSynchroniseAvsQueue :: JobHandler UniWorX
dispatchJobSynchroniseAvsQueue = JobHandlerException $ do dispatchJobSynchroniseAvsQueue = JobHandlerException $ do
jobs <- runDB $ do jobs <- runDBRead $ do
E.select (do E.select (do
(avsSync :& usrAvs) <- E.from $ E.table @AvsSync (avsSync :& usrAvs) <- E.from $ E.table @AvsSync
`E.leftJoin` E.table @UserAvs `E.leftJoin` E.table @UserAvs

View File

@ -412,6 +412,10 @@ citext2widget t = [whamlet|#{CI.original t}|]
str2widget :: String -> WidgetFor site () str2widget :: String -> WidgetFor site ()
str2widget s = [whamlet|#{s}|] str2widget s = [whamlet|#{s}|]
-- | hamlet does not like quotes
spaceWidget :: WidgetFor site ()
spaceWidget = str2widget " "
int2widget :: Int64 -> WidgetFor site () int2widget :: Int64 -> WidgetFor site ()
int2widget i = [whamlet|#{tshow i}|] int2widget i = [whamlet|#{tshow i}|]

View File

@ -106,19 +106,21 @@ data Icon
| IconBlocked | IconBlocked
| IconCertificate | IconCertificate
| IconPrintCenter | IconPrintCenter
| IconLetter | IconLetter -- only to be used for postal matters
| IconAt | IconAt
| IconSupervisor | IconSupervisor
| IconSupervisorForeign | IconSupervisorForeign
| IconSuperior -- supervisor and head of department
-- IconWaitingForUser -- IconWaitingForUser
| IconExpired | IconExpired
| IconLocked | IconLocked
| IconUnlocked | IconUnlocked
| IconResetTries -- also see IconReset | IconResetTries -- also see IconReset
| IconCompany | IconCompany
| IconEdit | IconEdit
| IconUserEdit | IconUserEdit
-- IconMagic -- indicates automatic updates -- IconMagic -- indicates automatic updates
| IconReroute -- for notification rerouting
deriving (Eq, Ord, Enum, Bounded, Show, Read, Generic) deriving (Eq, Ord, Enum, Bounded, Show, Read, Generic)
deriving anyclass (Universe, Finite, NFData) deriving anyclass (Universe, Finite, NFData)
@ -158,7 +160,7 @@ iconText = \case
IconSFTHint -> "life-ring" -- for SheetFileType only IconSFTHint -> "life-ring" -- for SheetFileType only
IconSFTSolution -> "exclamation-circle" -- for SheetFileType only IconSFTSolution -> "exclamation-circle" -- for SheetFileType only
IconSFTMarking -> "check-circle" -- for SheetFileType only IconSFTMarking -> "check-circle" -- for SheetFileType only
IconEmail -> "envelope" -- envelope is no longer unamibuous, use IconAt or IconLetter if email and postal need to be distinguished IconEmail -> "envelope" -- envelope is no longer unambiguous, use IconAt or IconLetter if email and postal need to be distinguished
IconRegisterTemplate -> "file-alt" IconRegisterTemplate -> "file-alt"
IconNoCorrectors -> "user-slash" IconNoCorrectors -> "user-slash"
IconRemoveUser -> "user-slash" IconRemoveUser -> "user-slash"
@ -207,6 +209,7 @@ iconText = \case
IconAt -> "at" -- alternative for IconEmail to distinguish from IconLetter IconAt -> "at" -- alternative for IconEmail to distinguish from IconLetter
IconSupervisor -> "head-side" -- must be notably different to user IconSupervisor -> "head-side" -- must be notably different to user
IconSupervisorForeign -> "alien" IconSupervisorForeign -> "alien"
IconSuperior -> "user-tie" -- user-crown
-- IconWaitingForUser -> "user-cog" -- Waiting on a user to do something -- IconWaitingForUser -> "user-cog" -- Waiting on a user to do something
IconExpired -> "hourglass-end" IconExpired -> "hourglass-end"
IconLocked -> "lock" IconLocked -> "lock"
@ -216,7 +219,7 @@ iconText = \case
IconEdit -> "edit" IconEdit -> "edit"
IconUserEdit -> "user-edit" IconUserEdit -> "user-edit"
-- IconMagic -> "wand-magic" -- IconMagic -> "wand-magic"
IconReroute -> "directions"
nullaryPathPiece ''Icon $ camelToPathPiece' 1 nullaryPathPiece ''Icon $ camelToPathPiece' 1
deriveLift ''Icon deriveLift ''Icon
@ -316,6 +319,8 @@ iconExamRegister :: Bool -> Markup
iconExamRegister True = icon IconExamRegisterTrue iconExamRegister True = icon IconExamRegisterTrue
iconExamRegister False = icon IconExamRegisterFalse iconExamRegister False = icon IconExamRegisterFalse
-- | indicator whether notifications are sent by letter or email
-- use iconReroute if type of rerouting is unclear
iconLetterOrEmail :: Bool -> Markup iconLetterOrEmail :: Bool -> Markup
iconLetterOrEmail True = icon IconLetter iconLetterOrEmail True = icon IconLetter
iconLetterOrEmail False = icon IconAt iconLetterOrEmail False = icon IconAt

View File

@ -15,11 +15,18 @@ $# SPDX-License-Identifier: AGPL-3.0-or-later
<dd .deflist__dd> <dd .deflist__dd>
_{userAuthentication} _{userAuthentication}
$maybe avs <- avsId $maybe avs <- avsId
<dt .deflist__dt> $with avsNoPers <- tshow (view _userAvsNoPerson avs)
_{MsgAvsPersonNo} <dt .deflist__dt>
^{messageTooltip tooltipAvsPersNo} _{MsgAvsPersonNo}
<dd .deflist__dd .ldap-primary-key> ^{messageTooltip tooltipAvsPersNo}
#{view _userAvsNoPerson avs} $maybe matnr <- userMatrikelnummer
$if matnr /= avsNoPers
^{messageTooltip tooltipAvsPersNoDiffers}
<dd .deflist__dd .ldap-primary-key>
^{modalAccess (text2widget avsNoPers) (text2widget avsNoPers) False (AdminAvsUserR cID)}
$maybe matnr <- userMatrikelnummer
$if matnr /= avsNoPers
/ #{matnr}
$maybe avsError <- view _userAvsLastSynchError avs $maybe avsError <- view _userAvsLastSynchError avs
<dt .deflist__dt> <dt .deflist__dt>
_{MsgLastAvsSynchError} _{MsgLastAvsSynchError}
@ -29,15 +36,18 @@ $# SPDX-License-Identifier: AGPL-3.0-or-later
_{MsgLastAvsSynchronisation} _{MsgLastAvsSynchronisation}
<dd .deflist__dd> <dd .deflist__dd>
^{formatTimeW SelFormatDateTime (view _userAvsLastSynch avs)} ^{formatTimeW SelFormatDateTime (view _userAvsLastSynch avs)}
$nothing
$maybe matnr <- userMatrikelnummer
<dt .deflist__dt>
_{MsgTableMatrikelNr}
^{messageTooltip tooltipAvsPersNo}
^{usrAutomatic CU_UA_UserMatrikelnummer}
<dd .deflist__dd>
^{modalAccess (text2widget matnr) (text2widget matnr) False (AdminAvsUserR cID)}
<dt .deflist__dt> <dt .deflist__dt>
_{MsgNameSet} ^{usrAutomatic CU_UA_UserDisplayName} _{MsgNameSet} ^{usrAutomatic CU_UA_UserDisplayName}
<dd .deflist__dd> <dd .deflist__dd>
^{nameWidget userDisplayName userSurname} ^{nameWidget userDisplayName userSurname}
$maybe matnr <- userMatrikelnummer
<dt .deflist__dt>
_{MsgTableMatrikelNr} ^{usrAutomatic CU_UA_UserMatrikelnummer}
<dd .deflist__dd>
^{modalAccess (text2widget matnr) (text2widget matnr) False (AdminAvsUserR cID)}
$maybe sex <- userSex $maybe sex <- userSex
<dt .deflist__dt> <dt .deflist__dt>
_{MsgTableSex} _{MsgTableSex}
@ -84,6 +94,7 @@ $# SPDX-License-Identifier: AGPL-3.0-or-later
#{userEmail} #{userEmail}
<dt .deflist__dt> <dt .deflist__dt>
_{MsgAdminUserPinPassword} _{MsgAdminUserPinPassword}
^{usrAutomatic CU_UA_UserPinPassword}
<dd .deflist__dd> <dd .deflist__dd>
$maybe pass <- userPinPassword $maybe pass <- userPinPassword
#{pass} #{pass}
@ -114,18 +125,6 @@ $# SPDX-License-Identifier: AGPL-3.0-or-later
_{MsgCompany} _{MsgCompany}
<dd .deflist__dd> <dd .deflist__dd>
^{compWgt} ^{compWgt}
$if numSupervisors > 0
<dt .deflist__dt>_{MsgProfileSupervisor}
$if numSupervisors > 3
\ #{numSupervisors}
<dd .deflist__dd>
^{mconcat supervisors}
$if numSupervisees > 0
<dt .deflist__dt>_{MsgProfileSupervisee}
$if length supervisees > 3
\ #{numSupervisees}
<dd .deflist__dd>
^{mconcat supervisees}
$if showAdminInfo $if showAdminInfo
<dt .deflist__dt> <dt .deflist__dt>
_{MsgUserCreated} _{MsgUserCreated}
@ -197,67 +196,25 @@ $# SPDX-License-Identifier: AGPL-3.0-or-later
$nothing $nothing
^{formatTimeW SelFormatDateTime studyFeaturesLastObserved} ^{formatTimeW SelFormatDateTime studyFeaturesLastObserved}
<section> <section>
<div .container>
$if hasRowsOwnedCourses
<div .container>
<h2>_{MsgProfileCourses}
<div .container>
^{ownedCoursesTable}
<div .container> ^{supervisorsWgt}
<h2>_{MsgProfileCourseParticipations}
<div .container> ^{superviseesWgt}
^{enrolledCoursesTable}
<div .container> <div .container>
<h2>_{MsgProfileQualifications} <h2>_{MsgProfileQualifications}
<div .container> <div .container>
^{qualificationsTable} ^{qualificationsTable}
<div .container> ^{maybeTable MsgProfileCourses ownedCoursesTable}
<h2>_{MsgProfileCourseExamResults}
<div .container>
^{examTable}
<div .container> ^{maybeTable MsgProfileCourseParticipations enrolledCoursesTable}
<h2>_{MsgProfileTutorials}
<div .container>
^{ownTutorialTable}
<div .container> ^{maybeTable MsgProfileSubmissionGroups submissionGroupTable}
<h2>_{MsgProfileTutorialParticipations}
<div .container>
^{tutorialTable}
<div .container> ^{maybeTable' MsgProfileSubmissions Nothing (Just (msg2widget MsgProfileGroupSubmissionDates)) submissionTable}
<h2>_{MsgProfileSubmissionGroups}
<div .container>
^{submissionGroupTable}
<div .container> ^{maybeTable' MsgTableCorrector Nothing (Just (msg2widget MsgProfileCorrectorRemark <> simpleLinkI MsgProfileCorrections CorrectionsR)) correctionsTable}
<h2>_{MsgProfileSubmissions}
<div .container>
^{submissionTable}
<em>_{MsgProfileRemark}
\ _{MsgProfileGroupSubmissionDates}
<div .container>
<h2> _{MsgTableCorrector}
<div .container>
^{correctionsTable}
<em>_{MsgProfileRemark}
\ _{MsgProfileCorrectorRemark}
<a href=@{CorrectionsR}>_{MsgProfileCorrections}
<div .container>
<h2> _{MsgProfileSupervisor}
<div .container>
^{supervisorsTable}
<div .container>
<h2> _{MsgProfileSupervisee}
<div .container>
^{superviseesTable}
^{profileRemarks} ^{profileRemarks}

View File

@ -656,12 +656,18 @@ fillDb = do
, let rcShort = CI.mk $ "RC" <> tshow n , let rcShort = CI.mk $ "RC" <> tshow n
] ]
void . insert' $ UserCompany jost fraportAg True True 0 False void . insert' $ UserCompany jost fraportAg True True 0 False
void . insert' $ UserCompany svaupel nice True False 0 False void . insert' $ UserCompany svaupel nice True False 2 False
void . insert' $ UserCompany svaupel ffacil False False 1 False
void . insert' $ UserCompany svaupel bpol True False 2 False
void . insert' $ UserCompany svaupel fraGround True False 1 False
void . insert' $ UserCompany gkleen nice False False 1 True void . insert' $ UserCompany gkleen nice False False 1 True
void . insert' $ UserCompany gkleen fraGround False True 2 False void . insert' $ UserCompany gkleen fraGround False True 2 False
void . insert' $ UserCompany gkleen bpol False True 1 False
void . insert' $ UserCompany fhamann bpol False False 1 True void . insert' $ UserCompany fhamann bpol False False 1 True
void . insert' $ UserCompany fhamann ffacil True True 2 True void . insert' $ UserCompany fhamann ffacil True True 2 True
void . insert' $ UserCompany fhamann nice False False 3 False void . insert' $ UserCompany fhamann nice False False 3 False
void . insert' $ UserCompany sbarth nice False False 3 False
void . insert' $ UserCompany sbarth bpol True True 1 True
-- need more tests -- need more tests
insertMany_ [UserCompany uid fraGround False False 0 True | Entity uid User{userFirstName = "John"} <- matUsers] insertMany_ [UserCompany uid fraGround False False 0 True | Entity uid User{userFirstName = "John"} <- matUsers]
insertMany_ [UserCompany uid bpol False False 0 False | Entity uid User{userFirstName = "Elizabeth"} <- matUsers] insertMany_ [UserCompany uid bpol False False 0 False | Entity uid User{userFirstName = "Elizabeth"} <- matUsers]