merge attempt
This commit is contained in:
commit
801f1f1597
@ -30,7 +30,9 @@ StudyFeatures
|
|||||||
field StudyTermsId
|
field StudyTermsId
|
||||||
type StudyFieldType
|
type StudyFieldType
|
||||||
semester Int
|
semester Int
|
||||||
updated UTCTime default=now -- zuletzt als gültig gesehen
|
updated UTCTime default='NOW()' -- zuletzt als gültig gesehen
|
||||||
|
valid Bool default=true
|
||||||
|
UniqueStudyFeatures user degree field type semester
|
||||||
-- UniqueUserSubject user degree field -- There exists a counterexample
|
-- UniqueUserSubject user degree field -- There exists a counterexample
|
||||||
StudyDegree
|
StudyDegree
|
||||||
key Int
|
key Int
|
||||||
@ -42,3 +44,8 @@ StudyTerms
|
|||||||
shorthand Text Maybe
|
shorthand Text Maybe
|
||||||
name Text Maybe
|
name Text Maybe
|
||||||
Primary key
|
Primary key
|
||||||
|
StudyTermCandidate
|
||||||
|
incidence UUID
|
||||||
|
key Int
|
||||||
|
name Text
|
||||||
|
deriving Show Eq Ord
|
||||||
@ -1,8 +1,16 @@
|
|||||||
module Database.Esqueleto.Utils where
|
{-# OPTIONS_GHC -fno-warn-orphans #-}
|
||||||
|
|
||||||
import ClassyPrelude.Yesod hiding (isInfixOf, (||.))
|
module Database.Esqueleto.Utils
|
||||||
import Data.Foldable as F
|
( true, false
|
||||||
import Database.Esqueleto as E
|
, isInfixOf, hasInfix
|
||||||
|
, any, all
|
||||||
|
, SqlIn(..)
|
||||||
|
) where
|
||||||
|
|
||||||
|
import ClassyPrelude.Yesod hiding (isInfixOf, (||.), any, all)
|
||||||
|
import qualified Data.Foldable as F
|
||||||
|
import qualified Database.Esqueleto as E
|
||||||
|
import Database.Esqueleto.Utils.TH
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
@ -33,13 +41,13 @@ hasInfix = flip isInfixOf
|
|||||||
-- | Given a test and a set of values, check whether anyone succeeds the test
|
-- | Given a test and a set of values, check whether anyone succeeds the test
|
||||||
-- WARNING: SQL leaves it explicitely unspecified whether || is short curcuited (i.e. lazily evaluated)
|
-- WARNING: SQL leaves it explicitely unspecified whether || is short curcuited (i.e. lazily evaluated)
|
||||||
any :: Foldable f =>
|
any :: Foldable f =>
|
||||||
(a -> SqlExpr (E.Value Bool)) -> f a -> SqlExpr (E.Value Bool)
|
(a -> E.SqlExpr (E.Value Bool)) -> f a -> E.SqlExpr (E.Value Bool)
|
||||||
any test = F.foldr (\needle acc -> acc ||. test needle) false
|
any test = F.foldr (\needle acc -> acc E.||. test needle) false
|
||||||
|
|
||||||
-- | Given a test and a set of values, check whether all succeeds the test
|
-- | Given a test and a set of values, check whether all succeeds the test
|
||||||
-- WARNING: SQL leaves it explicitely unspecified whether && is short curcuited (i.e. lazily evaluated)
|
-- WARNING: SQL leaves it explicitely unspecified whether && is short curcuited (i.e. lazily evaluated)
|
||||||
all :: Foldable f =>
|
all :: Foldable f =>
|
||||||
(a -> SqlExpr (E.Value Bool)) -> f a -> SqlExpr (E.Value Bool)
|
(a -> E.SqlExpr (E.Value Bool)) -> f a -> E.SqlExpr (E.Value Bool)
|
||||||
all test = F.foldr (\needle acc -> acc &&. test needle) true
|
all test = F.foldr (\needle acc -> acc E.&&. test needle) true
|
||||||
|
|
||||||
|
|
||||||
|
$(sqlInTuples [2..16])
|
||||||
|
|||||||
48
src/Database/Esqueleto/Utils/TH.hs
Normal file
48
src/Database/Esqueleto/Utils/TH.hs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
module Database.Esqueleto.Utils.TH
|
||||||
|
( SqlIn(..)
|
||||||
|
, sqlInTuple, sqlInTuples
|
||||||
|
) where
|
||||||
|
|
||||||
|
import ClassyPrelude
|
||||||
|
|
||||||
|
import qualified Database.Esqueleto as E
|
||||||
|
import qualified Database.Esqueleto.Internal.Sql as E (SqlSelect)
|
||||||
|
|
||||||
|
import Database.Persist (PersistField)
|
||||||
|
|
||||||
|
import Language.Haskell.TH
|
||||||
|
|
||||||
|
import Data.List (foldr1, foldl)
|
||||||
|
|
||||||
|
class E.SqlSelect a r => SqlIn a r | a -> r, r -> a where
|
||||||
|
sqlIn :: a -> [r] -> E.SqlExpr (E.Value Bool)
|
||||||
|
|
||||||
|
instance PersistField a => SqlIn (E.SqlExpr (E.Value a)) (E.Value a) where
|
||||||
|
x `sqlIn` xs = x `E.in_` E.valList (map E.unValue xs)
|
||||||
|
|
||||||
|
sqlInTuples :: [Int] -> DecsQ
|
||||||
|
sqlInTuples = mapM sqlInTuple
|
||||||
|
|
||||||
|
sqlInTuple :: Int -> DecQ
|
||||||
|
sqlInTuple arity = do
|
||||||
|
tyVars <- replicateM arity $ newName "t"
|
||||||
|
vVs <- replicateM arity $ newName "v"
|
||||||
|
xVs <- replicateM arity $ newName "x"
|
||||||
|
xsV <- newName "xs"
|
||||||
|
|
||||||
|
let
|
||||||
|
matchE = lam1E (tupP $ map (\vV -> conP 'E.Value [varP vV]) vVs) (foldr1 (\e1 e2 -> [e|$(e1) E.&&. $(e2)|]) . map (\(varE -> vE, varE -> xE) -> [e|E.val $(vE) E.==. $(xE)|]) $ zip vVs xVs)
|
||||||
|
tupTy f = foldl (\typ v -> typ `appT` f (varT v)) (tupleT arity) tyVars
|
||||||
|
|
||||||
|
instanceD (cxt $ map (\v -> [t|PersistField $(varT v)|]) tyVars) [t|SqlIn $(tupTy $ \v -> [t|E.SqlExpr (E.Value $(v))|]) $(tupTy $ \v -> [t|E.Value $(v)|])|]
|
||||||
|
[ funD 'sqlIn
|
||||||
|
[ clause [tupP $ map varP xVs, varP xsV]
|
||||||
|
( guardedB
|
||||||
|
[ normalGE [e|null $(varE xsV)|] [e|E.val False|]
|
||||||
|
, normalGE [e|otherwise|] [e|foldr1 (E.||.) $ map $(matchE) $(varE xsV)|]
|
||||||
|
]
|
||||||
|
) []
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
@ -50,6 +50,7 @@ import Data.Conduit (($$))
|
|||||||
import Data.Conduit.List (sourceList)
|
import Data.Conduit.List (sourceList)
|
||||||
|
|
||||||
import qualified Database.Esqueleto as E
|
import qualified Database.Esqueleto as E
|
||||||
|
import qualified Database.Esqueleto.Utils as E
|
||||||
|
|
||||||
import Control.Monad.Except (MonadError(..), runExceptT)
|
import Control.Monad.Except (MonadError(..), runExceptT)
|
||||||
import Control.Monad.Trans.Maybe (MaybeT(..))
|
import Control.Monad.Trans.Maybe (MaybeT(..))
|
||||||
@ -80,6 +81,8 @@ import Data.Bits (Bits(zeroBits))
|
|||||||
|
|
||||||
import Network.Wai.Parse (lbsBackEnd)
|
import Network.Wai.Parse (lbsBackEnd)
|
||||||
|
|
||||||
|
import qualified Data.UUID.V4 as UUID
|
||||||
|
|
||||||
|
|
||||||
instance DisplayAble b => DisplayAble (E.CryptoID a b) where
|
instance DisplayAble b => DisplayAble (E.CryptoID a b) where
|
||||||
display = display . ciphertext
|
display = display . ciphertext
|
||||||
@ -2007,9 +2010,11 @@ instance YesodAuth UniWorX where
|
|||||||
]
|
]
|
||||||
|
|
||||||
userId <- lift $ entityKey <$> upsertBy uAuth newUser userUpdate
|
userId <- lift $ entityKey <$> upsertBy uAuth newUser userUpdate
|
||||||
|
studyTermCandidateIncidence <- liftIO UUID.nextRandom
|
||||||
|
now <- liftIO getCurrentTime
|
||||||
|
|
||||||
let
|
let
|
||||||
userStudyFeatures = concat <$> mapM (parseStudyFeatures userId) userStudyFeatures'
|
userStudyFeatures = fmap concat . forM userStudyFeatures' $ parseStudyFeatures userId now
|
||||||
userStudyFeatures' = do
|
userStudyFeatures' = do
|
||||||
(k, v) <- ldapData
|
(k, v) <- ldapData
|
||||||
guard $ k == Attr "dfnEduPersonFeaturesOfStudy"
|
guard $ k == Attr "dfnEduPersonFeaturesOfStudy"
|
||||||
@ -2017,16 +2022,33 @@ instance YesodAuth UniWorX where
|
|||||||
Right str <- return $ Text.decodeUtf8' v'
|
Right str <- return $ Text.decodeUtf8' v'
|
||||||
return str
|
return str
|
||||||
|
|
||||||
fs <- either (\err -> throwError . ServerError $ "Could not parse features of study: " <> err) return userStudyFeatures
|
termNames = do
|
||||||
-- TODO: just update StudyFeaturesUpdate in case of no-change
|
(k, v) <- ldapData
|
||||||
-- TODO: keep old is referenced in CourseParticipant
|
guard $ k == Attr "dfnEduPersonFieldOfStudyString"
|
||||||
lift $ deleteWhere [StudyFeaturesUser ==. userId]
|
v' <- v
|
||||||
|
Right str <- return $ Text.decodeUtf8' v'
|
||||||
|
return str
|
||||||
|
|
||||||
forM_ fs $ \StudyFeatures{..} -> do
|
fs <- either (\err -> throwError . ServerError $ "Could not parse features of study: " <> err) return userStudyFeatures
|
||||||
|
|
||||||
|
let
|
||||||
|
studyTermCandidates = Set.fromList $ do
|
||||||
|
studyTermCandidateName <- termNames
|
||||||
|
StudyFeatures{ studyFeaturesField = StudyTermsKey' studyTermCandidateKey } <- fs
|
||||||
|
return StudyTermCandidate{..}
|
||||||
|
|
||||||
|
lift . E.update $ \f -> do
|
||||||
|
E.set f [ StudyFeaturesValid E.=. E.false ]
|
||||||
|
E.where_ . E.not_ $ (f E.^. StudyFeaturesUser, f E.^. StudyFeaturesDegree, f E.^. StudyFeaturesField, f E.^. StudyFeaturesType, f E.^. StudyFeaturesSemester)
|
||||||
|
`E.sqlIn` map (\StudyFeatures{..} -> (E.Value studyFeaturesUser, E.Value studyFeaturesDegree, E.Value studyFeaturesField, E.Value studyFeaturesType, E.Value studyFeaturesSemester) ) fs
|
||||||
|
|
||||||
|
lift . insertMany_ $ Set.toList studyTermCandidates
|
||||||
|
forM_ fs $ \f@StudyFeatures{..} -> do
|
||||||
lift . insertMaybe studyFeaturesDegree $ StudyDegree (unStudyDegreeKey studyFeaturesDegree) Nothing Nothing
|
lift . insertMaybe studyFeaturesDegree $ StudyDegree (unStudyDegreeKey studyFeaturesDegree) Nothing Nothing
|
||||||
lift . insertMaybe studyFeaturesField $ StudyTerms (unStudyTermsKey studyFeaturesField) Nothing Nothing
|
lift . insertMaybe studyFeaturesField $ StudyTerms (unStudyTermsKey studyFeaturesField) Nothing Nothing
|
||||||
|
|
||||||
lift $ insertMany_ fs
|
void . lift $ insertUnique f
|
||||||
|
|
||||||
return $ Authenticated userId
|
return $ Authenticated userId
|
||||||
Nothing -> acceptExisting
|
Nothing -> acceptExisting
|
||||||
|
|
||||||
|
|||||||
@ -327,7 +327,7 @@ postCRegisterR tid ssh csh = do
|
|||||||
addMessageI Info MsgCourseDeregisterOk
|
addMessageI Info MsgCourseDeregisterOk
|
||||||
| codeOk -> do
|
| codeOk -> do
|
||||||
actTime <- liftIO getCurrentTime
|
actTime <- liftIO getCurrentTime
|
||||||
regOk <- runDB $ insertUnique $ CourseParticipant cid aid actTime
|
regOk <- runDB $ insertUnique $ CourseParticipant cid aid actTime Nothing -- TODO
|
||||||
when (isJust regOk) $ addMessageI Success MsgCourseRegisterOk
|
when (isJust regOk) $ addMessageI Success MsgCourseRegisterOk
|
||||||
| otherwise -> addMessageI Warning MsgCourseSecretWrong
|
| otherwise -> addMessageI Warning MsgCourseSecretWrong
|
||||||
_other -> return () -- TODO check this!
|
_other -> return () -- TODO check this!
|
||||||
|
|||||||
@ -8,12 +8,12 @@ import Text.Parsec
|
|||||||
import Text.Parsec.Text
|
import Text.Parsec.Text
|
||||||
|
|
||||||
|
|
||||||
parseStudyFeatures :: UserId -> Text -> Either Text [StudyFeatures]
|
parseStudyFeatures :: UserId -> UTCTime -> Text -> Either Text [StudyFeatures]
|
||||||
parseStudyFeatures uId = first tshow . parse (pStudyFeatures uId <* eof) ""
|
parseStudyFeatures uId now = first tshow . parse (pStudyFeatures uId now <* eof) ""
|
||||||
|
|
||||||
|
|
||||||
pStudyFeatures :: UserId -> Parser [StudyFeatures]
|
pStudyFeatures :: UserId -> UTCTime -> Parser [StudyFeatures]
|
||||||
pStudyFeatures studyFeaturesUser = do
|
pStudyFeatures studyFeaturesUser studyFeaturesUpdated = do
|
||||||
studyFeaturesDegree <- StudyDegreeKey' <$> pKey
|
studyFeaturesDegree <- StudyDegreeKey' <$> pKey
|
||||||
void $ string "$$"
|
void $ string "$$"
|
||||||
|
|
||||||
@ -29,6 +29,7 @@ pStudyFeatures studyFeaturesUser = do
|
|||||||
studyFeaturesType <- pType
|
studyFeaturesType <- pType
|
||||||
void $ char '!'
|
void $ char '!'
|
||||||
studyFeaturesSemester <- decimal
|
studyFeaturesSemester <- decimal
|
||||||
|
let studyFeaturesValid = True
|
||||||
return StudyFeatures{..}
|
return StudyFeatures{..}
|
||||||
|
|
||||||
pStudyFeature `sepBy1` char '#'
|
pStudyFeature `sepBy1` char '#'
|
||||||
|
|||||||
@ -39,6 +39,7 @@ import Utils.Lens.TH
|
|||||||
|
|
||||||
import Import hiding (pi)
|
import Import hiding (pi)
|
||||||
import qualified Database.Esqueleto as E
|
import qualified Database.Esqueleto as E
|
||||||
|
import qualified Database.Esqueleto.Utils as E
|
||||||
import qualified Database.Esqueleto.Internal.Sql as E (SqlSelect,unsafeSqlValue)
|
import qualified Database.Esqueleto.Internal.Sql as E (SqlSelect,unsafeSqlValue)
|
||||||
import qualified Database.Esqueleto.Internal.Language as E (From)
|
import qualified Database.Esqueleto.Internal.Language as E (From)
|
||||||
|
|
||||||
@ -89,9 +90,6 @@ import qualified Data.ByteString.Base64.URL as Base64 (encode)
|
|||||||
import qualified Data.ByteString.Lazy as LBS
|
import qualified Data.ByteString.Lazy as LBS
|
||||||
|
|
||||||
|
|
||||||
$(sqlInTuples [2..16])
|
|
||||||
|
|
||||||
|
|
||||||
data SortColumn t = forall a. PersistField a => SortColumn { getSortColumn :: t -> E.SqlExpr (E.Value a) }
|
data SortColumn t = forall a. PersistField a => SortColumn { getSortColumn :: t -> E.SqlExpr (E.Value a) }
|
||||||
|
|
||||||
data SortDirection = SortAsc | SortDesc
|
data SortDirection = SortAsc | SortDesc
|
||||||
@ -370,7 +368,7 @@ singletonFilter key = prism' fromInner (fmap Just . fromOuter)
|
|||||||
|
|
||||||
data DBTable m x = forall a r r' h i t k k'.
|
data DBTable m x = forall a r r' h i t k k'.
|
||||||
( ToSortable h, Functor h
|
( ToSortable h, Functor h
|
||||||
, E.SqlSelect a r, SqlIn k k', DBTableKey k'
|
, E.SqlSelect a r, E.SqlIn k k', DBTableKey k'
|
||||||
, PathPiece i, Eq i
|
, PathPiece i, Eq i
|
||||||
, E.From E.SqlQuery E.SqlExpr E.SqlBackend t
|
, E.From E.SqlQuery E.SqlExpr E.SqlBackend t
|
||||||
) => DBTable
|
) => DBTable
|
||||||
@ -642,7 +640,7 @@ dbTable PSValidator{..} dbtable@DBTable{ dbtIdent = dbtIdent'@(toPathPiece -> db
|
|||||||
-> do
|
-> do
|
||||||
E.limit l
|
E.limit l
|
||||||
E.offset (psPage * l)
|
E.offset (psPage * l)
|
||||||
Just ps -> E.where_ $ dbtRowKey t `sqlIn` ps
|
Just ps -> E.where_ $ dbtRowKey t `E.sqlIn` ps
|
||||||
_other -> return ()
|
_other -> return ()
|
||||||
Map.foldrWithKey (\key args expr -> E.where_ (filterColumn (dbtFilter ! key) args t) >> expr) (return ()) psFilter
|
Map.foldrWithKey (\key args expr -> E.where_ (filterColumn (dbtFilter ! key) args t) >> expr) (return ()) psFilter
|
||||||
return (E.unsafeSqlValue "count(*) OVER ()" :: E.SqlExpr (E.Value Int64), dbtRowKey t, res)
|
return (E.unsafeSqlValue "count(*) OVER ()" :: E.SqlExpr (E.Value Int64), dbtRowKey t, res)
|
||||||
|
|||||||
@ -6,8 +6,6 @@ module Handler.Utils.Table.Pagination.Types
|
|||||||
, sortable
|
, sortable
|
||||||
, ToSortable(..)
|
, ToSortable(..)
|
||||||
, SortableP(..)
|
, SortableP(..)
|
||||||
, SqlIn(..)
|
|
||||||
, sqlInTuples
|
|
||||||
, DBTableInvalid(..)
|
, DBTableInvalid(..)
|
||||||
) where
|
) where
|
||||||
|
|
||||||
@ -20,13 +18,6 @@ import Data.CaseInsensitive (CI)
|
|||||||
|
|
||||||
import Data.Aeson (FromJSON, ToJSON, FromJSONKey, ToJSONKey)
|
import Data.Aeson (FromJSON, ToJSON, FromJSONKey, ToJSONKey)
|
||||||
|
|
||||||
import qualified Database.Esqueleto as E
|
|
||||||
import qualified Database.Esqueleto.Internal.Sql as E (SqlSelect)
|
|
||||||
|
|
||||||
import Language.Haskell.TH
|
|
||||||
|
|
||||||
import Data.List (foldr1, foldl)
|
|
||||||
|
|
||||||
|
|
||||||
newtype FilterKey = FilterKey { _unFilterKey :: CI Text }
|
newtype FilterKey = FilterKey { _unFilterKey :: CI Text }
|
||||||
deriving (Show, Read, Generic)
|
deriving (Show, Read, Generic)
|
||||||
@ -67,38 +58,6 @@ instance ToSortable Headless where
|
|||||||
pSortable = Nothing
|
pSortable = Nothing
|
||||||
|
|
||||||
|
|
||||||
class E.SqlSelect a r => SqlIn a r | a -> r, r -> a where
|
|
||||||
sqlIn :: a -> [r] -> E.SqlExpr (E.Value Bool)
|
|
||||||
|
|
||||||
instance PersistField a => SqlIn (E.SqlExpr (E.Value a)) (E.Value a) where
|
|
||||||
x `sqlIn` xs = x `E.in_` E.valList (map E.unValue xs)
|
|
||||||
|
|
||||||
sqlInTuples :: [Int] -> DecsQ
|
|
||||||
sqlInTuples = mapM sqlInTuple
|
|
||||||
|
|
||||||
sqlInTuple :: Int -> DecQ
|
|
||||||
sqlInTuple arity = do
|
|
||||||
tyVars <- replicateM arity $ newName "t"
|
|
||||||
vVs <- replicateM arity $ newName "v"
|
|
||||||
xVs <- replicateM arity $ newName "x"
|
|
||||||
xsV <- newName "xs"
|
|
||||||
|
|
||||||
let
|
|
||||||
matchE = lam1E (tupP $ map (\vV -> conP 'E.Value [varP vV]) vVs) (foldr1 (\e1 e2 -> [e|$(e1) E.&&. $(e2)|]) . map (\(varE -> vE, varE -> xE) -> [e|E.val $(vE) E.==. $(xE)|]) $ zip vVs xVs)
|
|
||||||
tupTy f = foldl (\typ v -> typ `appT` f (varT v)) (tupleT arity) tyVars
|
|
||||||
|
|
||||||
instanceD (cxt $ map (\v -> [t|PersistField $(varT v)|]) tyVars) [t|SqlIn $(tupTy $ \v -> [t|E.SqlExpr (E.Value $(v))|]) $(tupTy $ \v -> [t|E.Value $(v)|])|]
|
|
||||||
[ funD 'sqlIn
|
|
||||||
[ clause [tupP $ map varP xVs, varP xsV]
|
|
||||||
( guardedB
|
|
||||||
[ normalGE [e|null $(varE xsV)|] [e|E.val False|]
|
|
||||||
, normalGE [e|otherwise|] [e|foldr1 (E.||.) $ map $(matchE) $(varE xsV)|]
|
|
||||||
]
|
|
||||||
) []
|
|
||||||
]
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
data DBTableInvalid = DBTIRowsMissing Int
|
data DBTableInvalid = DBTIRowsMissing Int
|
||||||
deriving (Eq, Ord, Read, Show, Generic, Typeable)
|
deriving (Eq, Ord, Read, Show, Generic, Typeable)
|
||||||
|
|
||||||
|
|||||||
@ -8,6 +8,7 @@ module Model.Types
|
|||||||
, module Numeric.Natural
|
, module Numeric.Natural
|
||||||
, module Mail
|
, module Mail
|
||||||
, module Utils.DateTime
|
, module Utils.DateTime
|
||||||
|
, module Data.UUID.Types
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import ClassyPrelude
|
import ClassyPrelude
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user