356 lines
35 KiB
Haskell
356 lines
35 KiB
Haskell
{-# OPTIONS_GHC -Wno-redundant-constraints #-}
|
|
|
|
module Handler.Utils.ExamSpec (spec) where
|
|
|
|
import TestImport
|
|
import Data.Universe (Universe, Finite, universeF)
|
|
|
|
import ModelSpec () -- instance Arbitrary User
|
|
|
|
import Test.Hspec.QuickCheck (prop)
|
|
import Test.HUnit.Lang (HUnitFailure(..), FailureReason(..))
|
|
|
|
import qualified Data.Map as Map
|
|
import qualified Data.Set as Set
|
|
import qualified Data.Text as Text
|
|
import qualified Data.CaseInsensitive as CI
|
|
|
|
import qualified Data.RFC5051 as RFC5051
|
|
|
|
import Handler.Utils.Exam
|
|
|
|
|
|
-- direct copy&paste from an (currently) unmerged pull request for hspec-expectations
|
|
-- https://github.com/hspec/hspec-expectations/blob/6b4a475e42b0d44008c150727dea25dd79f568f2/src/Test/Hspec/Expectations.hs
|
|
-- |
|
|
-- If you have a test case that has multiple assertions, you can use the
|
|
-- 'annotate' function to provide a string message that will be attached to
|
|
-- the 'Expectation'.
|
|
--
|
|
-- @
|
|
-- describe "annotate" $ do
|
|
-- it "adds the message" $ do
|
|
-- annotate "obvious falsehood" $ do
|
|
-- True `shouldBe` False
|
|
--
|
|
-- ========>
|
|
--
|
|
-- 1) annotate, adds the message
|
|
-- obvious falsehood
|
|
-- expected: False
|
|
-- but got: True
|
|
-- @
|
|
myAnnotate :: (HasCallStack) => String -> Expectation -> Expectation
|
|
myAnnotate msg = handle $ \(HUnitFailure loc exn) ->
|
|
throwIO $ HUnitFailure loc $ case exn of
|
|
Reason str ->
|
|
Reason $ msg ++
|
|
if null str then str else ": " <> str
|
|
ExpectedButGot mmsg expected got ->
|
|
let
|
|
mmsg' =
|
|
Just $ msg <> maybe "" (": " <>) mmsg
|
|
in
|
|
ExpectedButGot mmsg' expected got
|
|
|
|
|
|
instance Arbitrary ExamOccurrence where
|
|
arbitrary = ExamOccurrence
|
|
<$> arbitrary -- examOccurrenceExam
|
|
<*> arbitrary -- examOccurrenceName
|
|
<*> arbitrary -- examOccurrenceRoom
|
|
<*> arbitrary -- examOccurrenceRoomHidden
|
|
<*> frequency [(let d = fromIntegral i in ceiling $ 100 * exp(- d*d / 50), pure i) | i <- [10 ..1000]] -- examOccurrenceCapacity
|
|
<*> arbitrary -- examOccurrenceStart
|
|
<*> arbitrary -- examOccurrenceEnd
|
|
<*> arbitrary -- examOccurrenceDescription
|
|
|
|
|
|
data Preselection = NoPreselection | SomePreselection
|
|
deriving stock (Show, Bounded, Enum)
|
|
deriving anyclass (Universe, Finite)
|
|
|
|
data Nudges = NoNudges | SmallNudges | LargeNudges
|
|
deriving stock (Show, Bounded, Enum)
|
|
deriving anyclass (Universe, Finite)
|
|
|
|
uncurry3 :: (a -> b -> c -> d) -> (a, b, c) -> d
|
|
uncurry3 f (a, b, c) = f a b c
|
|
|
|
uncurry4 :: (a -> b -> c -> d -> e) -> (a, b, c, d) -> e
|
|
uncurry4 f (a, b, c, d) = f a b c d
|
|
|
|
-- | Kopie der User-Datenstruktur beschränkt auf interessante Felder (besser verständliche Show-Instanz)
|
|
newtype UserProperties = UserProperties {user :: User}
|
|
|
|
instance Show UserProperties where
|
|
--show :: UserProperties -> String
|
|
show UserProperties {user=User {userSurname, userMatrikelnummer}}
|
|
= "User {userSurname=" ++ show userSurname
|
|
++ ", userMatrikelnummer=" ++ show userMatrikelnummer ++ "}"
|
|
|
|
-- function Handler.Utils.examAutoOccurrence
|
|
-- examAutoOccurrence :: forall seed.
|
|
-- Hashable seed
|
|
-- => seed
|
|
-- -> ExamOccurrenceRule
|
|
-- -> ExamAutoOccurrenceConfig
|
|
-- -> Map ExamOccurrenceId Natural
|
|
-- -> Map UserId (User, Maybe ExamOccurrenceId)
|
|
-- -> (Maybe (ExamOccurrenceMapping ExamOccurrenceId), Map UserId (Maybe ExamOccurrenceId))
|
|
-- examAutoOccurrence (hash -> seed) rule ExamAutoOccurrenceConfig{..} occurrences users
|
|
spec :: Spec
|
|
spec = do
|
|
describe "examAutoOccurrence" $ do
|
|
describe "Surname" $ testWithRule ExamRoomSurname
|
|
describe "Matriculation" $ testWithRule ExamRoomMatriculation
|
|
describe "Random" $ testWithRule ExamRoomRandom
|
|
where
|
|
testWithRule :: ExamOccurrenceRule -> Spec
|
|
testWithRule rule =
|
|
forM_ universeF $ \nudges -> describe (show nudges) $
|
|
forM_ universeF $ \preselection ->
|
|
prop (show preselection) $ propertyTest rule nudges preselection
|
|
seed :: ()
|
|
seed = ()
|
|
propertyTest :: ExamOccurrenceRule -> Nudges -> Preselection -> Gen Property
|
|
propertyTest rule nudges preselection = do
|
|
(users, occurrences) <- genUsersWithOccurrences preselection
|
|
eaocNudge <- case nudges of
|
|
NoNudges -> pure Map.empty
|
|
SmallNudges -> let nudgeFrequency = [(10, 0), (5, 1), (5, -1), (3, 2), (3, -2), (1, 3), (1, -3)]
|
|
in foldM (genNudge nudgeFrequency) Map.empty $ Map.keys occurrences
|
|
LargeNudges -> let nudgeFrequency = [(7, 0), (5, 3), (5, -3), (3, 6), (3, -6), (2, 9), (2, -9),
|
|
(2, 11), (2, -11), (1, 15), (1,-15), (1, 17), (1, -17)]
|
|
in foldM (genNudge nudgeFrequency) Map.empty $ Map.keys occurrences
|
|
let config :: ExamAutoOccurrenceConfig
|
|
config = def {eaocNudge}
|
|
(maybeMapping, userMap) = examAutoOccurrence seed rule config occurrences users
|
|
pure $ ioProperty $ do
|
|
-- user count stays constant
|
|
myAnnotate "number of users changed" $ shouldBe (length userMap) (length users)
|
|
-- no room is overfull
|
|
let userProperties :: Map UserId (UserProperties, Maybe ExamOccurrenceId)
|
|
userProperties = Map.map (first UserProperties) users
|
|
myAnnotate "room capacity exceeded" $ shouldSatisfy (userProperties, occurrences, userMap) $ uncurry3 fitsInRooms
|
|
case maybeMapping of
|
|
(Just occurrenceMapping) -> do
|
|
-- mapping is a valid description
|
|
myAnnotate "invalid mapping description" $ shouldSatisfy occurrenceMapping validRangeDescription
|
|
-- every (relevant) user got assigned a room
|
|
let foldFn :: (UserId, Maybe ExamOccurrenceId) -> Bool -> Bool
|
|
foldFn _userMapping False = False
|
|
foldFn (_userId, Just _occurrenceId) True = True
|
|
foldFn (userId, Nothing) True
|
|
= (rule == ExamRoomMatriculation)
|
|
-- every user with a userMatrikelnummer got a room
|
|
-- fail on unknown user
|
|
|| (fromMaybe False $ isNothing . userMatrikelnummer . fst <$> Map.lookup userId users)
|
|
myAnnotate "user didn't get a room" $ shouldSatisfy userMap $ foldr foldFn True . Map.toList
|
|
-- all users match the shown ranges
|
|
myAnnotate "shown ranges don't match userMap"
|
|
$ shouldSatisfy (rule, userProperties, occurrenceMapping, userMap) $ uncurry4 showsCorrectRanges
|
|
-- is a nullResult justified?
|
|
Nothing ->
|
|
-- disabled for now, probably not correct with the current implementation
|
|
myAnnotate "unjustified nullResult"
|
|
$ shouldSatisfy (rule, userProperties, occurrences) $ uncurry3 isNullResultJustified
|
|
-- | generate users without any pre-assigned rooms
|
|
genUsersWithOccurrences :: Preselection -> Gen (Map UserId (User, Maybe ExamOccurrenceId), Map ExamOccurrenceId Natural)
|
|
genUsersWithOccurrences preselection = do
|
|
rawUsers <- scale (50 *) $ listOf $ Entity <$> arbitrary <*> arbitrary
|
|
occurrences <- genOccurrences $ length rawUsers
|
|
-- user surnames anpassen, sodass interessante instanz
|
|
users <- fmap Map.fromList $ forM rawUsers $ \Entity {entityKey, entityVal} -> do
|
|
userSurname <- elements surnames
|
|
assignedRoom <- case preselection of
|
|
NoPreselection -> pure Nothing
|
|
SomePreselection -> frequency [(97, pure Nothing), (3, elements $ map Just $ Map.keys occurrences)]
|
|
pure (entityKey, (entityVal {userSurname}, assignedRoom))
|
|
pure (users, occurrences)
|
|
genOccurrences :: Int -> Gen (Map ExamOccurrenceId Natural)
|
|
genOccurrences numUsers = do
|
|
-- extra space to allow nice borders
|
|
extraSpace <- elements [numUsers `div` 5 .. numUsers `div` 2]
|
|
let totalSpaceRequirement = fromIntegral $ numUsers + extraSpace
|
|
createOccurrences acc
|
|
| sum (map snd acc) < totalSpaceRequirement = do
|
|
Entity {entityKey, entityVal} <- Entity <$> arbitrary <*> arbitrary
|
|
createOccurrences $ (entityKey, examOccurrenceCapacity entityVal) : acc
|
|
| otherwise = pure acc
|
|
Map.fromList <$> createOccurrences []
|
|
genNudge :: [(Int, Integer)] -> Map ExamOccurrenceId Integer -> ExamOccurrenceId -> Gen (Map ExamOccurrenceId Integer)
|
|
genNudge nudgesList acc occurrenceId
|
|
= fmap appendNonZero $ frequency $ map (second pure) nudgesList
|
|
where
|
|
appendNonZero :: Integer -> Map ExamOccurrenceId Integer
|
|
appendNonZero 0 = acc
|
|
appendNonZero nudge = Map.insert occurrenceId nudge acc
|
|
-- name list copied from test/Database/Fill.hs
|
|
surnames :: [Text]
|
|
surnames = [ "Smith", "Johnson", "Williams", "Brown"
|
|
, "Jones", "Miller", "Davis", "Garcia"
|
|
, "Rodriguez", "Wilson", "Martinez", "Anderson"
|
|
, "Taylor", "Thomas", "Hernandez", "Moore"
|
|
, "Martin", "Jackson", "Thompson", "White"
|
|
, "Lopez", "Lee", "Gonzalez", "Harris"
|
|
, "Clark", "Lewis", "Robinson", "Walker"
|
|
, "Perez", "Hall", "Young", "zu Allen", "Fu"
|
|
, "Únîcòdé", "Ähm-Ümlaüte", "von Leerzeichen"
|
|
]
|
|
occurrenceMap :: Map UserId (Maybe ExamOccurrenceId) -> Map ExamOccurrenceId [UserId]
|
|
occurrenceMap userMap = foldl' (\acc (userId, maybeOccurrenceId) -> appendJust maybeOccurrenceId userId acc)
|
|
Map.empty $ Map.toAscList userMap
|
|
where
|
|
appendJust :: Maybe ExamOccurrenceId -> UserId -> Map ExamOccurrenceId [UserId] -> Map ExamOccurrenceId [UserId]
|
|
appendJust Nothing _userId = id
|
|
appendJust (Just occurrenceId) userId = Map.insertWith (++) occurrenceId [userId]
|
|
-- | Are all rooms large enough to hold all assigned Users?
|
|
fitsInRooms :: Map UserId (UserProperties, Maybe ExamOccurrenceId)
|
|
-> Map ExamOccurrenceId Natural
|
|
-> Map UserId (Maybe ExamOccurrenceId)
|
|
-> Bool
|
|
fitsInRooms userProperties occurrences userMap
|
|
= all roomIsBigEnough $ Map.toAscList $ occurrenceMap userMap
|
|
where
|
|
roomIsBigEnough :: (ExamOccurrenceId, [UserId]) -> Bool
|
|
roomIsBigEnough (roomId, userIds) = case lookup roomId occurrences of
|
|
Nothing -> False
|
|
(Just capacity) -> length userIds <= fromIntegral capacity
|
|
|| all (isJust . snd) (Map.restrictKeys userProperties $ Set.fromList userIds)
|
|
-- | No range overlap for different rooms + end is always the greater value
|
|
validRangeDescription :: ExamOccurrenceMapping ExamOccurrenceId -> Bool
|
|
validRangeDescription ExamOccurrenceMapping {examOccurrenceMappingMapping}
|
|
= all (\(roomId, ranges) -> all (descriptionValid roomId) ranges) $ Map.toAscList examOccurrenceMappingMapping
|
|
where
|
|
descriptionValid:: ExamOccurrenceId -> ExamOccurrenceMappingDescription -> Bool
|
|
descriptionValid roomId description
|
|
= endAfterStart description && all (all $ noDirectOverlap description) (Map.delete roomId examOccurrenceMappingMapping)
|
|
endAfterStart :: ExamOccurrenceMappingDescription -> Bool
|
|
endAfterStart
|
|
ExamOccurrenceMappingRange {eaomrStart=(pack . map CI.foldedCase -> start), eaomrEnd=(pack . map CI.foldedCase -> end)}
|
|
= RFC5051.compareUnicode start end /= GT
|
|
endAfterStart ExamOccurrenceMappingSpecial {} = True
|
|
noDirectOverlap :: ExamOccurrenceMappingDescription -> ExamOccurrenceMappingDescription -> Bool
|
|
noDirectOverlap
|
|
ExamOccurrenceMappingRange {eaomrStart=(pack . map CI.foldedCase -> s0), eaomrEnd=(pack . map CI.foldedCase -> e0)}
|
|
ExamOccurrenceMappingRange {eaomrStart=(pack . map CI.foldedCase -> s1), eaomrEnd=(pack . map CI.foldedCase -> e1)}
|
|
= (RFC5051.compareUnicode s0 s1 == LT && RFC5051.compareUnicode e0 s1 == LT)
|
|
|| (RFC5051.compareUnicode s0 e1 == GT && RFC5051.compareUnicode e0 s1 == GT)
|
|
noDirectOverlap
|
|
ExamOccurrenceMappingRange {eaomrStart=(pack . map CI.foldedCase -> start), eaomrEnd=(pack . map CI.foldedCase -> end)}
|
|
ExamOccurrenceMappingSpecial {eaomrSpecial=(pack . map CI.foldedCase -> special)}
|
|
= RFC5051.compareUnicode special start == LT || RFC5051.compareUnicode special end == GT
|
|
noDirectOverlap
|
|
ExamOccurrenceMappingSpecial {eaomrSpecial=(pack . map CI.foldedCase -> special)}
|
|
ExamOccurrenceMappingRange {eaomrStart=(pack . map CI.foldedCase -> start), eaomrEnd=(pack . map CI.foldedCase -> end)}
|
|
= RFC5051.compareUnicode special start == LT || RFC5051.compareUnicode special end == GT
|
|
noDirectOverlap ExamOccurrenceMappingSpecial {eaomrSpecial=s1} ExamOccurrenceMappingSpecial {eaomrSpecial=s2}
|
|
= s1 /= s2
|
|
-- RFC5051.compareUnicode :: Text -> Text -> Ordering
|
|
-- | Does the (currently surname) User fit to the displayed ranges?
|
|
-- Users with a previously assigned room are checked if the assignment stays the same, regardless of the ranges.
|
|
showsCorrectRanges :: ExamOccurrenceRule
|
|
-> Map UserId (UserProperties, Maybe ExamOccurrenceId)
|
|
-> ExamOccurrenceMapping ExamOccurrenceId
|
|
-> Map UserId (Maybe ExamOccurrenceId)
|
|
-> Bool
|
|
showsCorrectRanges rule userProperties ExamOccurrenceMapping {examOccurrenceMappingMapping} userMap
|
|
= all userFitsInRange $ Map.toAscList $ occurrenceMap userMap
|
|
where
|
|
userFitsInRange :: (ExamOccurrenceId, [UserId]) -> Bool
|
|
userFitsInRange (roomId, userIds) = flip all userIds $ \userId ->
|
|
case (Map.lookup roomId examOccurrenceMappingMapping, Map.lookup userId userProperties) of
|
|
(_maybeRanges, Just (_userProperty, Just fixedRoomId))
|
|
-> roomId == fixedRoomId
|
|
(Just ranges, Just (UserProperties User {userSurname, userMatrikelnummer}, Nothing))
|
|
-> any fitsInRange ranges
|
|
where
|
|
ciTag :: Maybe [CI Char]
|
|
ciTag = map CI.mk . Text.unpack <$> case rule of
|
|
ExamRoomSurname
|
|
| Text.null userSurname -> Nothing
|
|
| otherwise-> Just userSurname
|
|
ExamRoomMatriculation
|
|
| maybe True Text.null userMatrikelnummer -> Nothing
|
|
| otherwise -> userMatrikelnummer
|
|
_rule -> Nothing
|
|
fitsInRange :: ExamOccurrenceMappingDescription -> Bool
|
|
fitsInRange mappingDescription = case (ciTag, mappingDescription) of
|
|
(Nothing, _mappingDescription) -> True
|
|
(Just tag, ExamOccurrenceMappingRange {eaomrStart=(pack . map CI.foldedCase -> start), eaomrEnd=(pack . map CI.foldedCase-> end)})
|
|
-> (RFC5051.compareUnicode start (pack $ map CI.foldedCase $ transformTag start tag) /= GT)
|
|
&& (RFC5051.compareUnicode end (pack $ map CI.foldedCase $ transformTag end tag) /= LT)
|
|
(Just tag, ExamOccurrenceMappingSpecial {eaomrSpecial})
|
|
-> checkSpecial eaomrSpecial tag
|
|
transformTag :: (MonoFoldable f) => f -> [CI Char] -> [CI Char]
|
|
transformTag (length -> rangeLength) = case rule of
|
|
ExamRoomMatriculation -> reverse . take rangeLength . reverse
|
|
_rule -> take rangeLength
|
|
checkSpecial :: [CI Char] -> [CI Char] -> Bool
|
|
checkSpecial = case rule of
|
|
ExamRoomMatriculation -> isSuffixOf
|
|
_rule -> isPrefixOf
|
|
_otherwise -> (rule /= ExamRoomSurname) && (rule /= ExamRoomMatriculation)
|
|
-- | Is mapping impossible?
|
|
isNullResultJustified :: ExamOccurrenceRule
|
|
-> Map UserId (UserProperties, Maybe ExamOccurrenceId)
|
|
-> Map ExamOccurrenceId Natural -> Bool
|
|
isNullResultJustified rule userProperties occurrences
|
|
= noRelevantUsers rule userProperties || mappingImpossible rule userProperties occurrences || True
|
|
noRelevantUsers :: ExamOccurrenceRule -> Map UserId (UserProperties, Maybe ExamOccurrenceId) -> Bool
|
|
noRelevantUsers rule = null . Map.filter (isRelevantUser rule)
|
|
isRelevantUser :: ExamOccurrenceRule -> (UserProperties, Maybe ExamOccurrenceId) -> Bool
|
|
isRelevantUser _rule (_user, Just _assignedRoom) = False
|
|
isRelevantUser rule (UserProperties User {userSurname, userMatrikelnummer}, Nothing) = case rule of
|
|
ExamRoomSurname -> not $ null userSurname
|
|
ExamRoomMatriculation -> maybe False (not . null) userMatrikelnummer
|
|
_rule -> False
|
|
mappingImpossible :: ExamOccurrenceRule -> Map UserId (UserProperties, Maybe ExamOccurrenceId) -> Map ExamOccurrenceId Natural -> Bool
|
|
mappingImpossible
|
|
rule
|
|
userProperties@(sort . map (ruleProperty rule . fst) . Map.elems . Map.filter (isRelevantUser rule) -> relevantUsers)
|
|
(map snd . Map.toList . adjustOccurrences userProperties -> occurrences') = go relevantUsers occurrences'
|
|
where
|
|
go :: [Maybe Text] -> [Natural] -> Bool
|
|
go [] _occurrences = False
|
|
go _remainingUsers [] = True
|
|
go remainingUsers (0:t) = go remainingUsers t
|
|
go remainingUsers@(h:_t) (firstOccurrence:laterOccurrences)
|
|
| nextUsers <= firstOccurrence = go remainingUsers' $ firstOccurrence - nextUsers : laterOccurrences
|
|
| otherwise = go remainingUsers laterOccurrences
|
|
where
|
|
(fromIntegral . length -> nextUsers, remainingUsers') = span (== h) remainingUsers
|
|
ruleProperty :: ExamOccurrenceRule -> UserProperties -> Maybe Text
|
|
ruleProperty rule = case rule of
|
|
ExamRoomSurname -> Just . userSurname . user
|
|
ExamRoomMatriculation -> userMatrikelnummer . user
|
|
_rule -> const Nothing
|
|
-- copied and adjusted from Hander.Utils.Exam
|
|
adjustOccurrences :: Map UserId (UserProperties, Maybe ExamOccurrenceId) -> Map ExamOccurrenceId Natural -> Map ExamOccurrenceId Natural
|
|
-- ^ reduce room capacity for every pre-assigned user by 1
|
|
adjustOccurrences userProperties occurrences
|
|
= foldl' (flip $ Map.update predToPositive) (Map.filter (> 0) occurrences) $ Map.mapMaybe snd userProperties
|
|
predToPositive :: Natural -> Maybe Natural
|
|
predToPositive 0 = Nothing
|
|
predToPositive 1 = Nothing
|
|
predToPositive n = Just $ pred n
|
|
|
|
|
|
{-
|
|
-- myAnnotate "room capacity exceeded" $ shouldSatisfy (userProperties, occurrences, userMap) $ uncurry3 fitsInRooms
|
|
|
|
|
|
test/Handler/Utils/ExamSpec.hs:135:55:
|
|
9) Handler.Utils.Exam.examAutoOccurrence.Random.NoNudges NoPreselection
|
|
Falsifiable (after 60 tests):
|
|
|
|
room capacity exceeded: predicate failed on:
|
|
(fromList [(SqlBackendKey {unSqlBackendKey = -125488664963424},(User {userSurname="Robinson", userMatrikelnummer=Just "7959961923374081932782214765091329305474015231525"},Nothing)),(SqlBackendKey {unSqlBackendKey = -123483339090133},(User {userSurname="Perez", userMatrikelnummer=Just "5482528910"},Nothing)),(SqlBackendKey {unSqlBackendKey = -118945904886272},(User {userSurname="Martin", userMatrikelnummer=Just "4784178434461032616814108700264975720374752709612135"},Nothing)),(SqlBackendKey {unSqlBackendKey = -117181862361768},(User {userSurname="Perez", userMatrikelnummer=Just "27558455292870832910016828815"},Nothing)),(SqlBackendKey {unSqlBackendKey = -114302016569843},(User {userSurname="Davis", userMatrikelnummer=Just "13763490282534291475261828187089653743850"},Nothing)),(SqlBackendKey {unSqlBackendKey = -110905706672434},(User {userSurname="Martin", userMatrikelnummer=Just "87771"},Nothing)),(SqlBackendKey {unSqlBackendKey = -110479309905059},(User {userSurname="Miller", userMatrikelnummer=Just "837319545717484402528719189423320042503"},Nothing)),(SqlBackendKey {unSqlBackendKey = -109870640673816},(User {userSurname="Lee", userMatrikelnummer=Just "683673990062514732641480572486537"},Nothing)),(SqlBackendKey {unSqlBackendKey = -107296620544089},(User {userSurname="Jones", userMatrikelnummer=Just "7"},Nothing)),(SqlBackendKey {unSqlBackendKey = -99513965188106},(User {userSurname="Fu", userMatrikelnummer=Just "2264126627908013626998446021883828"},Nothing)),(SqlBackendKey {unSqlBackendKey = -97272139724835},(User {userSurname="Garcia", userMatrikelnummer=Just "5805485123536183163399445024923068597940980999091514924"},Nothing)),(SqlBackendKey {unSqlBackendKey = -89689121706070},(User {userSurname="Moore", userMatrikelnummer=Just "25820678"},Nothing)),(SqlBackendKey {unSqlBackendKey = -82934672292134},(User {userSurname="Clark", userMatrikelnummer=Just "83230945777788677133587861253994"},Nothing)),(SqlBackendKey {unSqlBackendKey = -81484932509371},(User {userSurname="\218n\238c\242d\233", userMatrikelnummer=Just "796271116604649198108082157856143047513009465132"},Nothing)),(SqlBackendKey {unSqlBackendKey = -79707309005258},(User {userSurname="Harris", userMatrikelnummer=Just "5998333311682137188470568100"},Nothing)),(SqlBackendKey {unSqlBackendKey = -69397949201715},(User {userSurname="Martin", userMatrikelnummer=Just "1849501885698871440179319823942093451"},Nothing)),(SqlBackendKey {unSqlBackendKey = -65312057887791},(User {userSurname="Martin", userMatrikelnummer=Just "05371902463238399726808238970049391194390035"},Nothing)),(SqlBackendKey {unSqlBackendKey = -56774863263466},(User {userSurname="Martin", userMatrikelnummer=Just "92010521895170905"},Nothing)),(SqlBackendKey {unSqlBackendKey = -56507095173774},(User {userSurname="Walker", userMatrikelnummer=Just "9765482896810377276569097"},Nothing)),(SqlBackendKey {unSqlBackendKey = -56496232689807},(User {userSurname="Robinson", userMatrikelnummer=Just "10294507776310671607386609437514615"},Nothing)),(SqlBackendKey {unSqlBackendKey = -55463761962077},(User {userSurname="Clark", userMatrikelnummer=Just "96171302"},Nothing)),(SqlBackendKey {unSqlBackendKey = -47160256239906},(User {userSurname="Anderson", userMatrikelnummer=Just "629397997487829607735185241530689914126"},Nothing)),(SqlBackendKey {unSqlBackendKey = -47057392168715},(User {userSurname="Hernandez", userMatrikelnummer=Just "8596763052100458239111713860319080177090372"},Nothing)),(SqlBackendKey {unSqlBackendKey = -36475495367102},(User {userSurname="Thomas", userMatrikelnummer=Just "51974104532662646819818509235177796726237664473842280955"},Nothing)),(SqlBackendKey {unSqlBackendKey = -34853393045082},(User {userSurname="Williams", userMatrikelnummer=Just "8320889107863608561918076120272479388366042278927978933983"},Nothing)),(SqlBackendKey {unSqlBackendKey = -27809999196249},(User {userSurname="Hall", userMatrikelnummer=Just "18153649967432926989"},Nothing)),(SqlBackendKey {unSqlBackendKey = -24390731126883},(User {userSurname="Martin", userMatrikelnummer=Just "88605476038197997"},Nothing)),(SqlBackendKey {unSqlBackendKey = -23884949928568},(User {userSurname="Clark", userMatrikelnummer=Just "6014974616"},Nothing)),(SqlBackendKey {unSqlBackendKey = -13776289327290},(User {userSurname="Robinson", userMatrikelnummer=Just "90803593065964817526260"},Nothing)),(SqlBackendKey {unSqlBackendKey = -11748248612893},(User {userSurname="Hall", userMatrikelnummer=Nothing},Nothing)),(SqlBackendKey {unSqlBackendKey = -4509312461256},(User {userSurname="Garcia", userMatrikelnummer=Just "694356510727040"},Nothing)),(SqlBackendKey {unSqlBackendKey = -1743187887307},(User {userSurname="Davis", userMatrikelnummer=Just "1496965101193"},Nothing)),(SqlBackendKey {unSqlBackendKey = 2874744048737},(User {userSurname="Garcia", userMatrikelnummer=Just "6466567401474884506843768"},Nothing)),(SqlBackendKey {unSqlBackendKey = 12410189320441},(User {userSurname="\218n\238c\242d\233", userMatrikelnummer=Just "249355007798"},Nothing)),(SqlBackendKey {unSqlBackendKey = 13945499340929},(User {userSurname="Wilson", userMatrikelnummer=Just "478802399"},Nothing)),(SqlBackendKey {unSqlBackendKey = 15332482394253},(User {userSurname="Rodriguez", userMatrikelnummer=Just "49478483220134722266262819168998907436"},Nothing)),(SqlBackendKey {unSqlBackendKey = 20786997881191},(User {userSurname="zu Allen", userMatrikelnummer=Just "13454502298971605839584788590546110586249572167114748337"},Nothing)),(SqlBackendKey {unSqlBackendKey = 26440758724805},(User {userSurname="Lee", userMatrikelnummer=Just "65416960634076549440649"},Nothing)),(SqlBackendKey {unSqlBackendKey = 29004383225589},(User {userSurname="Harris", userMatrikelnummer=Just "96722250361346570517250196667002"},Nothing)),(SqlBackendKey {unSqlBackendKey = 33216070681630},(User {userSurname="Smith", userMatrikelnummer=Just "59208656078713048715115675467876458"},Nothing)),(SqlBackendKey {unSqlBackendKey = 39503876519131},(User {userSurname="Brown", userMatrikelnummer=Just "82692663039937699"},Nothing)),(SqlBackendKey {unSqlBackendKey = 48015035621295},(User {userSurname="Taylor", userMatrikelnummer=Just "43879521570872912108895666"},Nothing)),(SqlBackendKey {unSqlBackendKey = 48999734396033},(User {userSurname="Williams", userMatrikelnummer=Just "24057276275826"},Nothing)),(SqlBackendKey {unSqlBackendKey = 56867237245920},(User {userSurname="Taylor", userMatrikelnummer=Just "67027340148075094772624371190836209997485228788200"},Nothing)),(SqlBackendKey {unSqlBackendKey = 61258554389826},(User {userSurname="Brown", userMatrikelnummer=Just "6261759607074867643"},Nothing)),(SqlBackendKey {unSqlBackendKey = 69621863574605},(User {userSurname="Thomas", userMatrikelnummer=Just "7445292342334597558583006"},Nothing)),(SqlBackendKey {unSqlBackendKey = 70256775739937},(User {userSurname="Miller", userMatrikelnummer=Just "9073398641808433754346"},Nothing)),(SqlBackendKey {unSqlBackendKey = 78691366351881},(User {userSurname="Fu", userMatrikelnummer=Just "17364996010931508678470359"},Nothing)),(SqlBackendKey {unSqlBackendKey = 79725690720564},(User {userSurname="Lewis", userMatrikelnummer=Just "8530555313977746655083488750"},Nothing)),(SqlBackendKey {unSqlBackendKey = 81513533696125},(User {userSurname="Jones", userMatrikelnummer=Just "920937317885665192292993250312"},Nothing)),(SqlBackendKey {unSqlBackendKey = 81981029385368},(User {userSurname="Moore", userMatrikelnummer=Just "55414192514542311627214689525944119319963"},Nothing)),(SqlBackendKey {unSqlBackendKey = 85888535534493},(User {userSurname="Rodriguez", userMatrikelnummer=Just "76292280288944780625905"},Nothing)),(SqlBackendKey {unSqlBackendKey = 85996206274915},(User {userSurname="Moore", userMatrikelnummer=Just "32605623608816708701331766199244"},Nothing)),(SqlBackendKey {unSqlBackendKey = 101362991390633},(User {userSurname="White", userMatrikelnummer=Just "9727244257940392263436145522115750"},Nothing)),(SqlBackendKey {unSqlBackendKey = 121131121250399},(User {userSurname="Davis", userMatrikelnummer=Just "5149830893919046016400068583244951"},Nothing)),(SqlBackendKey {unSqlBackendKey = 126412353851801},(User {userSurname="Hall", userMatrikelnummer=Just "28496292322582"},Nothing)),(SqlBackendKey {unSqlBackendKey = 132619389067506},(User {userSurname="Fu", userMatrikelnummer=Just "375800051"},Nothing)),(SqlBackendKey {unSqlBackendKey = 135230960203442},(User {userSurname="Lewis", userMatrikelnummer=Just "2707463072751303"},Nothing))],
|
|
|
|
fromList [(SqlBackendKey {unSqlBackendKey = -129100413068233},14),(SqlBackendKey {unSqlBackendKey = -75701987503352},58),(SqlBackendKey {unSqlBackendKey = -3193586858776},25)],
|
|
|
|
fromList [(SqlBackendKey {unSqlBackendKey = -125488664963424},Just (SqlBackendKey {unSqlBackendKey = -129100413068233})),(SqlBackendKey {unSqlBackendKey = -123483339090133},Just (SqlBackendKey {unSqlBackendKey = -75701987503352})),(SqlBackendKey {unSqlBackendKey = -118945904886272},Just (SqlBackendKey {unSqlBackendKey = -3193586858776})),(SqlBackendKey {unSqlBackendKey = -117181862361768},Just (SqlBackendKey {unSqlBackendKey = -75701987503352})),(SqlBackendKey {unSqlBackendKey = -114302016569843},Just (SqlBackendKey {unSqlBackendKey = -75701987503352})),(SqlBackendKey {unSqlBackendKey = -110905706672434},Just (SqlBackendKey {unSqlBackendKey = -75701987503352})),(SqlBackendKey {unSqlBackendKey = -110479309905059},Just (SqlBackendKey {unSqlBackendKey = -129100413068233})),(SqlBackendKey {unSqlBackendKey = -109870640673816},Just (SqlBackendKey {unSqlBackendKey = -3193586858776})),(SqlBackendKey {unSqlBackendKey = -107296620544089},Just (SqlBackendKey {unSqlBackendKey = -75701987503352})),(SqlBackendKey {unSqlBackendKey = -99513965188106},Just (SqlBackendKey {unSqlBackendKey = -3193586858776})),(SqlBackendKey {unSqlBackendKey = -97272139724835},Just (SqlBackendKey {unSqlBackendKey = -75701987503352})),(SqlBackendKey {unSqlBackendKey = -89689121706070},Just (SqlBackendKey {unSqlBackendKey = -129100413068233})),(SqlBackendKey {unSqlBackendKey = -82934672292134},Just (SqlBackendKey {unSqlBackendKey = -3193586858776})),(SqlBackendKey {unSqlBackendKey = -81484932509371},Just (SqlBackendKey {unSqlBackendKey = -75701987503352})),(SqlBackendKey {unSqlBackendKey = -79707309005258},Just (SqlBackendKey {unSqlBackendKey = -129100413068233})),(SqlBackendKey {unSqlBackendKey = -69397949201715},Just (SqlBackendKey {unSqlBackendKey = -129100413068233})),(SqlBackendKey {unSqlBackendKey = -65312057887791},Just (SqlBackendKey {unSqlBackendKey = -75701987503352})),(SqlBackendKey {unSqlBackendKey = -56774863263466},Just (SqlBackendKey {unSqlBackendKey = -3193586858776})),(SqlBackendKey {unSqlBackendKey = -56507095173774},Just (SqlBackendKey {unSqlBackendKey = -75701987503352})),(SqlBackendKey {unSqlBackendKey = -56496232689807},Just (SqlBackendKey {unSqlBackendKey = -75701987503352})),(SqlBackendKey {unSqlBackendKey = -55463761962077},Just (SqlBackendKey {unSqlBackendKey = -75701987503352})),(SqlBackendKey {unSqlBackendKey = -47160256239906},Just (SqlBackendKey {unSqlBackendKey = -75701987503352})),(SqlBackendKey {unSqlBackendKey = -47057392168715},Just (SqlBackendKey {unSqlBackendKey = -129100413068233})),(SqlBackendKey {unSqlBackendKey = -36475495367102},Just (SqlBackendKey {unSqlBackendKey = -3193586858776})),(SqlBackendKey {unSqlBackendKey = -34853393045082},Just (SqlBackendKey {unSqlBackendKey = -75701987503352})),(SqlBackendKey {unSqlBackendKey = -27809999196249},Just (SqlBackendKey {unSqlBackendKey = -75701987503352})),(SqlBackendKey {unSqlBackendKey = -24390731126883},Just (SqlBackendKey {unSqlBackendKey = -129100413068233})),(SqlBackendKey {unSqlBackendKey = -23884949928568},Just (SqlBackendKey {unSqlBackendKey = -129100413068233})),(SqlBackendKey {unSqlBackendKey = -13776289327290},Just (SqlBackendKey {unSqlBackendKey = -129100413068233})),(SqlBackendKey {unSqlBackendKey = -11748248612893},Just (SqlBackendKey {unSqlBackendKey = -75701987503352})),(SqlBackendKey {unSqlBackendKey = -4509312461256},Just (SqlBackendKey {unSqlBackendKey = -129100413068233})),(SqlBackendKey {unSqlBackendKey = -1743187887307},Just (SqlBackendKey {unSqlBackendKey = -75701987503352})),(SqlBackendKey {unSqlBackendKey = 2874744048737},Just (SqlBackendKey {unSqlBackendKey = -3193586858776})),(SqlBackendKey {unSqlBackendKey = 12410189320441},Just (SqlBackendKey {unSqlBackendKey = -129100413068233})),(SqlBackendKey {unSqlBackendKey = 13945499340929},Just (SqlBackendKey {unSqlBackendKey = -75701987503352})),(SqlBackendKey {unSqlBackendKey = 15332482394253},Just (SqlBackendKey {unSqlBackendKey = -75701987503352})),(SqlBackendKey {unSqlBackendKey = 20786997881191},Just (SqlBackendKey {unSqlBackendKey = -3193586858776})),(SqlBackendKey {unSqlBackendKey = 26440758724805},Just (SqlBackendKey {unSqlBackendKey = -3193586858776})),(SqlBackendKey {unSqlBackendKey = 29004383225589},Just (SqlBackendKey {unSqlBackendKey = -75701987503352})),(SqlBackendKey {unSqlBackendKey = 33216070681630},Just (SqlBackendKey {unSqlBackendKey = -129100413068233})),(SqlBackendKey {unSqlBackendKey = 39503876519131},Just (SqlBackendKey {unSqlBackendKey = -75701987503352})),(SqlBackendKey {unSqlBackendKey = 48015035621295},Just (SqlBackendKey {unSqlBackendKey = -75701987503352})),(SqlBackendKey {unSqlBackendKey = 48999734396033},Just (SqlBackendKey {unSqlBackendKey = -75701987503352})),(SqlBackendKey {unSqlBackendKey = 56867237245920},Just (SqlBackendKey {unSqlBackendKey = -75701987503352})),(SqlBackendKey {unSqlBackendKey = 61258554389826},Just (SqlBackendKey {unSqlBackendKey = -75701987503352})),(SqlBackendKey {unSqlBackendKey = 69621863574605},Just (SqlBackendKey {unSqlBackendKey = -129100413068233})),(SqlBackendKey {unSqlBackendKey = 70256775739937},Just (SqlBackendKey {unSqlBackendKey = -75701987503352})),(SqlBackendKey {unSqlBackendKey = 78691366351881},Just (SqlBackendKey {unSqlBackendKey = -3193586858776})),(SqlBackendKey {unSqlBackendKey = 79725690720564},Just (SqlBackendKey {unSqlBackendKey = -75701987503352})),(SqlBackendKey {unSqlBackendKey = 81513533696125},Just (SqlBackendKey {unSqlBackendKey = -129100413068233})),(SqlBackendKey {unSqlBackendKey = 81981029385368},Just (SqlBackendKey {unSqlBackendKey = -75701987503352})),(SqlBackendKey {unSqlBackendKey = 85888535534493},Just (SqlBackendKey {unSqlBackendKey = -129100413068233})),(SqlBackendKey {unSqlBackendKey = 85996206274915},Just (SqlBackendKey {unSqlBackendKey = -75701987503352})),(SqlBackendKey {unSqlBackendKey = 101362991390633},Just (SqlBackendKey {unSqlBackendKey = -75701987503352})),(SqlBackendKey {unSqlBackendKey = 121131121250399},Just (SqlBackendKey {unSqlBackendKey = -3193586858776})),(SqlBackendKey {unSqlBackendKey = 126412353851801},Just (SqlBackendKey {unSqlBackendKey = -3193586858776})),(SqlBackendKey {unSqlBackendKey = 132619389067506},Just (SqlBackendKey {unSqlBackendKey = -3193586858776})),(SqlBackendKey {unSqlBackendKey = 135230960203442},Just (SqlBackendKey {unSqlBackendKey = -129100413068233}))])
|
|
|
|
-}
|