QMRKFEPGFBCEWAIXPEIG5ILKAJ2JH5L3TOITHR4HNJXK5FN3KXBQC
4U7F3CPIDTK6JSEDMNMHVKSR7HOQDLZQD2PPVMDLHO5SFSIMUXZAC
64C6AWH66FDKU6UE6Z6JPX2J2GBM2JOPTH2GL6LHKAIUBGNGDZ5AC
RPAJLHMTUJU4AYNBOHVGHGGB4NY2NLY3BVPYN5FMWB3ZIMAUQHCQC
IZEVQF627FA7VV25KJAWYWGGC35LZUUBBQRPN5ZAINTQLPEDRTEAC
GCVQD44VRPQVKPZEPIC4AOIXLJIG2ZMV3QI2Y7KALUT6NVUBSGSAC
A6HKMINBNGQLLX4QJMYWKQ4JAEHVJ4HIRVDKPPDI3FJUO2AAB7OQC
KNSI575VAW6HRCZYXOEPQ4DTSML4EORML5MV4DJBRKE7TXCPS4EAC
FD7SV5I6VCW27HZ3T3K4MMGB2OYGJTPKFFA263TNTAMRJGQJWVNAC
TNR3TEHKVADAEZSTOD2XLSUTSW5AWST2YUW4CWK5KE7DSC6XHZNAC
O5FVTOM6YFBLEPF3S576K6IMT6ZZ5VQCSB3YVXNS4CKBITKCRZ7AC
Z7KS5XHHC6PAMTVBHXY7KUSS3BWAOU6FSYIITUCFOOJZU4OUJHBAC
EZQG2APB36DDMIAYDPPDGOIXOD7K2RZZSGC2NKGZIHB2HZBTW7EQC
7KZP4RHZ3QSYTPPQ257A65Z5UPX44TF2LAI2U5EMULQCLDCEUK2AC
PBD7LZYQHXAA3KLH2ZUX5GW4UFML6BQ32KXZF4KZ6OYFASUYFJ5QC
LD4GLVSF6YTA7OZWIGJ45H6TUXGM4WKUIYXKWQFNUP36WDMYSMXAC
2G3GNDDUOVPF45PELJ65ZB2IXEHJJXJILFRVHZXGPXUL4BVNZJFQC
BXGLKYRXO2O4NRM3BLNWQ7AWVPQXAMFS57MFYHJNOZZEZZW5BH6AC
EMVTF2IWNQGRL44FC4JNG5FYYQTZSFPNM6SOM7IAEH6T7PPK2NVAC
TZQJVHBAMDNWDBYCDE3SDVGBG2T5FOE3J5JAD6NENRW36XBHUUFQC
5XFJNUAZUCQ3WCGW4QRIAWR764QYDOPHOIVO2TRMGSSG7UDX2M2AC
64VI73NPSFNWTL6UXM6YHRFLNJZ3NUJ2R3CL53MO2HSZWFGBIRTQC
NTPC7KJEAPA34SBIA74FVQSJXYNW32RIUQTHUSUTKMEUCPLUIBJAC
LAROLAYUGJ4Q5AEFV5EJMIA2ZKBNCBWHHHPCJ3CKCNIUIYUKRFVQC
W35DDBFYF6Z4ZPCFEO5RPAONZLYCSNTXUSTS6FIUUVZHCI6Q7GHAC
SLL7262CJUE7TZDDZZXFROHCVVDA527WA4PHXCKEGZUJF2EN5MQAC
NVOCQVASZWTKQJG7GPH7KHKZZR7NUG4WLV5YY4KAIRPCJRWCZPIAC
TLQ72DSJD7GGPWN6HGBHAVPBRQFKEQ6KSK43U7JWWID4ZWAF47JAC
P6NR2CGXCWAW6GXXSIXCGOBIRAS2BM4LEM6D5ADPN4IL7TMW6UVAC
4QX5E5ACVN57KJLCWOM4JEI6JSV4XZNCWVYPOTKSOMUW3SOMCNJAC
A2J7B4SCCJYKQV3G2LDHEFNE2GUICO3N3Y5FKF4EUZW5AG7PTDWAC
2OIPAQCBDIUJBXB4K2QVP3IEBIUOCQHSWSWFVMVSVZC7GHX2VK7AC
N4NDAZYTLSI2W22KT3SYXL257DBMSH3UT2BXOYM7LH7FSZAY4RLAC
75N3UJ4JK56KXF56GASGPAWLFYGJDETVJNYTF4KXFCQM767JUU5AC
I2KHGVD44KT4MQJXGCTVSQKMBO6TVCY72F26TLXGWRL6PHGF6RNQC
XTBSG4C7SCZUFOU2BTNFR6B6TCGYI35BWUV4PVTS3N7KNH5VEARQC
BROSTG5KP3NUNLSYPVQID254TE47E5RKQAKLPIY7PGWETE6JNMTAC
ADMKQQGCGVSHHIMVQ4XFRDCG544SBJCYALSKZV45CQQBZ4ACUH2AC
VJPT6HDRMJAJD5PT3VOYJYW43ISKLICEHLSDWSROX2XZWO2OFZPQC
2Y2QZFVFSKXEFEGYJB5A7GI735ONWPCF7DVTIY5T73AUEVTZTBBQC
createEvent' :: ProjectId -> UserId -> LogEntry -> QDBM EventId
createEvent' (ProjectId pid) (UserId uid) (LogEntry a e m) =
pinsert EventId
"INSERT INTO work_events (project_id, user_id, btc_addr, event_type, event_time, event_metadata) \
\VALUES (?, ?, ?, ?, ?, ?) \
\RETURNING id"
( pid, uid
, a ^. _BtcAddr
, eventName e
, fromThyme $ e ^. eventTime
, m
)
instance DBEval QDBM where
dbEval (CreateEvent (ProjectId pid) (UserId uid) (LogEntry a e m)) =
pinsert EventId
"INSERT INTO work_events (project_id, user_id, btc_addr, event_type, event_time, event_metadata) \
\VALUES (?, ?, ?, ?, ?, ?) \
\RETURNING id"
( pid, uid
, a ^. _BtcAddr
, eventName e
, fromThyme $ e ^. eventTime
, m
)
findEvent' :: EventId -> QDBM (Maybe QDBLogEntry)
findEvent' (EventId eid) = do
logEntries <- pquery qdbLogEntryParser
"SELECT id, project_id, user_id, btc_addr, event_type, event_time, event_metadata FROM work_events \
\WHERE id = ?"
(Only eid)
pure $ headMay logEntries
dbEval (FindEvent (EventId eid)) = do
logEntries <- pquery qdbLogEntryParser
"SELECT project_id, user_id, btc_addr, event_type, event_time, event_metadata FROM work_events \
\WHERE id = ?"
(Only eid)
pure $ headMay logEntries
findEvents' :: ProjectId -> UserId -> Interval' -> QDBM [LogEntry]
findEvents' (ProjectId pid) (UserId uid) ival =
let q p (Before e) = pquery p
"SELECT btc_addr, event_type, event_time, event_metadata FROM work_events \
\WHERE project_id = ? AND user_id = ? AND event_time <= ?"
(pid, uid, PUTCTime e)
q p (During s e) = pquery p
"SELECT btc_addr, event_type, event_time, event_metadata FROM work_events \
\WHERE project_id = ? AND user_id = ? \
\AND event_time >= ? AND event_time <= ?"
(pid, uid, PUTCTime s, PUTCTime e)
q p (After s) = pquery p
"SELECT btc_addr, event_type, event_time, event_metadata FROM work_events \
\WHERE project_id = ? AND user_id = ? AND event_time >= ?"
(pid, uid, PUTCTime s)
in q logEntryParser ival
dbEval (FindEvents (ProjectId pid) (UserId uid) ival) =
let q p (Before e) = pquery p
"SELECT btc_addr, event_type, event_time, event_metadata FROM work_events \
\WHERE project_id = ? AND user_id = ? AND event_time <= ?"
(pid, uid, PUTCTime e)
q p (During s e) = pquery p
"SELECT btc_addr, event_type, event_time, event_metadata FROM work_events \
\WHERE project_id = ? AND user_id = ? \
\AND event_time >= ? AND event_time <= ?"
(pid, uid, PUTCTime s, PUTCTime e)
q p (After s) = pquery p
"SELECT btc_addr, event_type, event_time, event_metadata FROM work_events \
\WHERE project_id = ? AND user_id = ? AND event_time >= ?"
(pid, uid, PUTCTime s)
in q logEntryParser ival
amendEvent' :: EventId -> EventAmendment -> QDBM AmendmentId
amendEvent' (EventId eid) (TimeChange mt t) =
pinsert AmendmentId
"INSERT INTO event_time_amendments (event_id, mod_time, event_time) VALUES (?, ?, ?) RETURNING id"
( eid, fromThyme $ mt ^. _ModTime, fromThyme t )
dbEval (AmendEvent (EventId eid) (TimeChange mt t)) =
pinsert AmendmentId
"INSERT INTO event_time_amendments (event_id, mod_time, event_time) VALUES (?, ?, ?) RETURNING id"
( eid, fromThyme $ mt ^. _ModTime, fromThyme t )
amendEvent' (EventId eid) (AddressChange mt addr) =
pinsert AmendmentId
"INSERT INTO event_addr_amendments (event_id, mod_time, btc_addr) VALUES (?, ?, ?) RETURNING id"
( eid, fromThyme $ mt ^. _ModTime, addr ^. _BtcAddr )
dbEval (AmendEvent (EventId eid) (AddressChange mt addr)) =
pinsert AmendmentId
"INSERT INTO event_addr_amendments (event_id, mod_time, btc_addr) VALUES (?, ?, ?) RETURNING id"
( eid, fromThyme $ mt ^. _ModTime, addr ^. _BtcAddr )
amendEvent' (EventId eid) (MetadataChange mt v) =
pinsert AmendmentId
"INSERT INTO event_metadata_amendments (event_id, mod_time, btc_addr) VALUES (?, ?, ?) RETURNING id"
( eid, fromThyme $ mt ^. _ModTime, v)
readWorkIndex' :: ProjectId -> QDBM WorkIndex
readWorkIndex' pid = do
logEntries <- pquery logEntryParser
"SELECT btc_addr, event_type, event_time, event_metadata FROM work_events WHERE project_id = ?"
(Only $ PPid pid)
pure $ workIndex logEntries
createAuction' :: ProjectId -> Auction -> QDBM AuctionId
createAuction' pid auc =
pinsert AuctionId
"INSERT INTO auctions (project_id, raise_amount, end_time) \
\VALUES (?, ?, ?) RETURNING id"
(pid ^. (_ProjectId), auc ^. (raiseAmount.to PBTC), auc ^. auctionEnd)
findAuction' :: AuctionId -> QDBM (Maybe Auction)
findAuction' aucId = do
auctions <- pquery auctionParser
"SELECT raise_amount, end_time FROM auctions WHERE id = ?"
(Only (aucId ^. _AuctionId))
pure $ headMay auctions
createBid' :: AuctionId -> Bid -> QDBM BidId
createBid' (AuctionId aucId) bid = do
pinsert BidId
"INSERT INTO bids (auction_id, bidder_id, bid_seconds, bid_amount, bid_time) \
\VALUES (?, ?, ?, ?, ?) RETURNING id"
( aucId
, bid ^. (bidUser._UserId)
, case bid ^. bidSeconds of (Seconds i) -> i
, bid ^. (bidAmount.to PBTC)
, bid ^. bidTime
)
dbEval (AmendEvent (EventId eid) (MetadataChange mt v)) =
pinsert AmendmentId
"INSERT INTO event_metadata_amendments (event_id, mod_time, btc_addr) VALUES (?, ?, ?) RETURNING id"
( eid, fromThyme $ mt ^. _ModTime, v)
readBids' :: AuctionId -> QDBM [Bid]
readBids' aucId =
pquery bidParser
"SELECT user_id, bid_seconds, bid_amount, bid_time FROM bids WHERE auction_id = ?"
(Only $ (aucId ^. _AuctionId))
dbEval (ReadWorkIndex pid) = do
logEntries <- pquery logEntryParser
"SELECT btc_addr, event_type, event_time, event_metadata FROM work_events WHERE project_id = ?"
(Only $ PPid pid)
pure $ workIndex logEntries
createUser' :: User -> QDBM UserId
createUser' user' =
pinsert UserId
"INSERT INTO users (handle, btc_addr, email) VALUES (?, ?, ?) RETURNING id"
(user' ^. (username._UserName), user' ^. (userAddress._BtcAddr), user' ^. userEmail)
dbEval (CreateAuction pid auc) =
pinsert AuctionId
"INSERT INTO auctions (project_id, raise_amount, end_time) \
\VALUES (?, ?, ?) RETURNING id"
(pid ^. (_ProjectId), auc ^. (raiseAmount.to PBTC), auc ^. auctionEnd)
findUser' :: UserId -> QDBM (Maybe User)
findUser' (UserId uid) = do
users <- pquery userParser
"SELECT handle, btc_addr, email FROM users WHERE id = ?"
(Only uid)
pure $ headMay users
dbEval (FindAuction aucId) = do
auctions <- pquery auctionParser
"SELECT raise_amount, end_time FROM auctions WHERE id = ?"
(Only (aucId ^. _AuctionId))
pure $ headMay auctions
findUserByUserName' :: UserName -> QDBM (Maybe QDBUser)
findUserByUserName' (UserName h) = do
users <- pquery qdbUserParser
"SELECT id, handle, btc_addr, email FROM users WHERE handle = ?"
(Only h)
pure $ headMay users
dbEval (CreateBid (AuctionId aucId) bid) =
pinsert BidId
"INSERT INTO bids (auction_id, bidder_id, bid_seconds, bid_amount, bid_time) \
\VALUES (?, ?, ?, ?, ?) RETURNING id"
( aucId
, bid ^. (bidUser._UserId)
, case bid ^. bidSeconds of (Seconds i) -> i
, bid ^. (bidAmount.to PBTC)
, bid ^. bidTime
)
createProject' :: Project -> QDBM ProjectId
createProject' p = do
let uid = p ^. (initiator._UserId)
pid <- pinsert ProjectId
"INSERT INTO projects (project_name, inception_date, initiator_id) VALUES (?, ?, ?) RETURNING id"
(p ^. projectName, p ^. inceptionDate, uid)
void $ pexec
"INSERT INTO project_companions (project_id, user_id) VALUES (?, ?)"
(pid ^. _ProjectId, uid)
pure pid
dbEval (ReadBids aucId) =
pquery bidParser
"SELECT user_id, bid_seconds, bid_amount, bid_time FROM bids WHERE auction_id = ?"
(Only $ (aucId ^. _AuctionId))
findProject' :: ProjectId -> QDBM (Maybe Project)
findProject' (ProjectId pid) = do
projects <- pquery projectParser
"SELECT project_name, inception_date, initiator_id FROM projects WHERE id = ?"
(Only pid)
pure $ headMay projects
dbEval (CreateUser user') =
pinsert UserId
"INSERT INTO users (handle, btc_addr, email) VALUES (?, ?, ?) RETURNING id"
(user' ^. (username._UserName), user' ^. (userAddress._BtcAddr), user' ^. userEmail)
findUserProjects' :: UserId -> QDBM [QDBProject]
findUserProjects' (UserId uid) =
pquery qdbProjectParser
"SELECT p.id, p.project_name, p.inception_date, p.initiator_id \
\FROM projects p JOIN project_companions pc ON pc.project_id = p.id \
\WHERE pc.user_id = ?"
(Only uid)
dbEval (FindUser (UserId uid)) = do
users <- pquery userParser
"SELECT handle, btc_addr, email FROM users WHERE id = ?"
(Only uid)
pure $ headMay users
postgresQDB :: QDB QDBM
postgresQDB = QDB
{ createEvent = createEvent'
, amendEvent = amendEvent'
, findEvent = findEvent'
, findEvents = findEvents'
, readWorkIndex = readWorkIndex'
dbEval (CreateProject p) =
pinsert ProjectId
"INSERT INTO projects (project_name, inception_date, initiator_id, depreciation_fn) \
\VALUES (?, ?, ?, ?) RETURNING id"
(p ^. projectName, p ^. inceptionDate, p ^. (initiator._UserId), toJSON $ p ^. depf)
, createBid = createBid'
, readBids = readBids'
dbEval (FindUserProjects (UserId uid)) =
pquery qdbProjectParser
"SELECT p.id, p.project_name, p.inception_date, p.initiator_id \
\FROM projects p JOIN project_companions pc ON pc.project_id = p.id \
\WHERE pc.user_id = ? \
\UNION \
\SELECT p.id, p.project_name, p.inception_date, p.initiator_id \
\FROM projects p \
\WHERE p.initiator_id = ?"
(uid, uid)
, createUser = createUser'
, findUser = findUser'
, findUserByUserName = findUserByUserName'
dbEval (AddUserToProject pid current new) = do
void $ pexec
"INSERT INTO project_companions (project_id, user_id, invited_by) VALUES (?, ?, ?)"
(pid ^. _ProjectId, new ^. _UserId, current ^. _UserId)
, createProject = createProject'
, findProject = findProject'
, findUserProjects = findUserProjects'
}
-- FIXME, these are just placeholders
dbEval (OpForbidden _ reason _) = fail $ show reason
dbEval (SubjectNotFound _) = fail "Subject of operation was not found."
import Aftok.Util
type KeyedUser = (UserId, User)
type KeyedLogEntry = (ProjectId, UserId, LogEntry)
type KeyedProject = (ProjectId, Project)
type InvitingUID = UserId
type InvitedUID = UserId
type DBProg a = Program DBOp a
data DBOp a where
CreateUser :: User -> DBOp UserId
FindUser :: UserId -> DBOp (Maybe User)
FindUserByName :: UserName -> DBOp (Maybe KeyedUser)
CreateProject :: Project -> DBOp ProjectId
FindProject :: ProjectId -> DBOp (Maybe Project)
FindUserProjects :: UserId -> DBOp [KeyedProject]
AddUserToProject :: ProjectId -> InvitingUID -> InvitedUID -> DBOp ()
CreateEvent :: ProjectId -> UserId -> LogEntry -> DBOp EventId
AmendEvent :: EventId -> EventAmendment -> DBOp AmendmentId
FindEvent :: EventId -> DBOp (Maybe KeyedLogEntry)
FindEvents :: ProjectId -> UserId -> Interval' -> DBOp [LogEntry]
ReadWorkIndex :: ProjectId -> DBOp WorkIndex
CreateAuction :: ProjectId -> Auction -> DBOp AuctionId
FindAuction :: AuctionId -> DBOp (Maybe Auction)
CreateBid :: AuctionId -> Bid -> DBOp BidId
ReadBids :: AuctionId -> DBOp [Bid]
OpForbidden :: forall x. UserId -> OpForbiddenReason -> DBOp x -> DBOp x
SubjectNotFound :: forall x. DBOp x -> DBOp x
data OpForbiddenReason = UserNotProjectMember
| UserNotEventLogger
deriving (Eq, Show)
class DBEval m where
dbEval :: DBOp a -> m a
-- User ops
createUser :: User -> DBProg UserId
createUser = fc . CreateUser
findUser :: UserId -> DBProg (Maybe User)
findUser = fc . FindUser
type QDBUser = (UserId, User)
type QDBLogEntry = (EventId, ProjectId, UserId, LogEntry)
type QDBProject = (ProjectId, Project)
findUserByName :: UserName -> DBProg (Maybe KeyedUser)
findUserByName = fc . FindUserByName
-- Project ops
createProject :: Project -> DBProg ProjectId
createProject p = do
pid <- fc $ CreateProject p
addUserToProject pid (p ^. initiator) (p ^. initiator)
return pid
findProject :: ProjectId -> UserId -> DBProg (Maybe Project)
findProject pid uid = do
kps <- findUserProjects uid
pure $ fmap snd (find (\(pid', _) -> pid' == pid) kps)
findUserProjects :: UserId -> DBProg [KeyedProject]
findUserProjects = fc . FindUserProjects
addUserToProject :: ProjectId -> InvitingUID -> InvitedUID -> DBProg ()
addUserToProject pid current new =
withProjectAuth pid current $ AddUserToProject pid current new
withProjectAuth :: ProjectId -> UserId -> DBOp a -> DBProg a
withProjectAuth pid uid act = do
px <- findUserProjects uid
fc $ if any (\(pid', _) -> pid' == pid) px
then act
else OpForbidden uid UserNotProjectMember act
data QDB m = QDB
{ createEvent :: ProjectId -> UserId -> LogEntry -> m EventId
, amendEvent :: EventId -> EventAmendment -> m AmendmentId
, findEvent :: EventId -> m (Maybe QDBLogEntry)
, findEvents :: ProjectId -> UserId -> Interval' -> m [LogEntry]
, readWorkIndex :: ProjectId -> m WorkIndex
-- Log ops
-- TODO: ignore "duplicate" events within some small time limit?
createEvent :: ProjectId -> UserId -> LogEntry -> DBProg EventId
createEvent p u l = withProjectAuth p u $ CreateEvent p u l
amendEvent :: UserId -> EventId -> EventAmendment -> DBProg AmendmentId
amendEvent uid eid a = do
ev <- findEvent eid
let act = AmendEvent eid a
forbidden = OpForbidden uid UserNotEventLogger act
missing = SubjectNotFound act
fc $ maybe missing (\(_, uid', _) -> if uid' == uid then act else forbidden) ev
, createBid :: AuctionId -> Bid -> m BidId
, readBids :: AuctionId -> m [Bid]
findEvents :: ProjectId -> UserId -> Interval' -> DBProg [LogEntry]
findEvents p u i = fc $ FindEvents p u i
, createUser :: User -> m UserId
, findUser :: UserId -> m (Maybe User)
, findUserByUserName :: UserName -> m (Maybe QDBUser)
readWorkIndex :: ProjectId -> UserId -> DBProg WorkIndex
readWorkIndex pid uid = withProjectAuth pid uid $ ReadWorkIndex pid
, createProject :: Project -> m ProjectId
, findProject :: ProjectId -> m (Maybe Project)
, findUserProjects :: UserId -> m [QDBProject]
}
linearDepreciation :: Months -> -- ^ The number of initial months during which no depreciation occurs
Months -> -- ^ The number of months over which each logged interval will be depreciated
DepF
linearDepreciation undepPeriod depPeriod =
linearDepreciation :: Months -- ^ The number of initial months during which no depreciation occurs
-> Months -- ^ The number of months over which each logged interval will be depreciated
-> DepF -- ^ The resulting configured depreciation function.
linearDepreciation undepLength depLength =
{-# LANGUAGE RankNTypes #-}
module Aftok.Util where
import ClassyPrelude
import Control.Monad.Free.Church
import Data.Functor.Coyoneda
type Program f a = F (Coyoneda f) a
-- Shouldn't this exist already in a library somewhere?
interpret :: Monad m => (forall x. f x -> m x) -> Program f a -> m a
interpret nt p =
let eval (Coyoneda cf cm) = nt cm >>= cf
in iterM eval p
fc :: f a -> Program f a
fc = liftF . liftCoyoneda
-- | others tbd
instance ToJSON DepreciationFunction where
toJSON (LinearDepreciation (Months up) (Months dp)) =
object [ "type" .= ("LinearDepreciation" :: Text)
, "arguments" .= (
object [ "undep" .= up
, "dep" .= dp
]
)]
instance FromJSON DepreciationFunction where
parseJSON (Object v) = do
t <- v .: "text" :: Parser Text
args <- v .: "arguments"
case unpack t of
"LinearDepreciation" ->
let undep = Months <$> (args .: "undep")
dep = Months <$> (args .: "dep")
in LinearDepreciation <$> undep <*> dep
x -> fail $ "No depreciation function recognized for type " <> x
parseJSON _ = mzero
Nothing -> snapError 403 "Value of parameter projectId could not be parsed to a valid value."
Just pid -> do
uid <- requireUserId
projects <- liftPG . runReaderT $ findUserProjects uid
if any (\p -> p ^. _1 == pid) projects
then pure (uid, pid)
else snapError 403 $ "User " ++ (tshow uid) ++ " does not have access to project " ++ (tshow pid)
Nothing -> snapError 400 "Value of parameter projectId could not be parsed to a valid value."
Just pid -> pure pid
QDB{..} <- view qdb <$> with qm get
pid <- fmap snd requireProjectAccess
mp <- liftPG . runReaderT $ findProject pid
uid <- requireUserId
pid <- requireProjectId
mp <- snapEval $ findProject pid uid
QDB{..} <- view qdb <$> with qm get
pid <- fmap snd requireProjectAccess
liftPG . runReaderT $ readWorkIndex pid
uid <- requireUserId
pid <- requireProjectId
snapEval $ readWorkIndex pid uid
(QModules QDB{..} df) <- with qm get
pid <- fmap snd requireProjectAccess
widx <- liftPG . runReaderT $ readWorkIndex pid
uid <- requireUserId
pid <- requireProjectId
projectMay <- snapEval $ findProject pid uid
project <- maybe (snapError 400 $ "Project not found for id " <> tshow pid) pure projectMay
widx <- snapEval $ readWorkIndex pid uid
if uid' == uid
then either
(snapError 400 . pack)
(liftPG . runReaderT . amendEvent eventId)
(parseEither (parseEventAmendment modTime) requestJSON)
else
(snapError 403 "You do not have permission to view this event.")
either
(snapError 400 . pack)
(snapEval . amendEvent uid eventId)
(parseEither (parseEventAmendment modTime) requestJSON)
-- | FIXME, make configurable
qdbpgSnapletInit :: SnapletInit a QModules
qdbpgSnapletInit = makeSnaplet "qdbpg" "QDB on Postgresql" Nothing $ do
pure $ QModules postgresQDB $ linearDepreciation (Months 6) (Months 60)
snapEval :: DBProg a -> Handler App App a
snapEval p = liftPG . runReaderT . runQDBM $ interpret dbEval p