UWMGUJOW5X5HQTS76T2FD7MNAJF7SESPQVU5FDIZO52V75TT2X6AC
ZKFETYRKPM2BYO47I4B7ZTZZNIGTUKKYX2KK27KUETVJXUV5O65AC
7OTVLW6G7IIAJE2Q4PX53DEXQYY6CPNZJVUJO2ELGGAJKQLXQ7FQC
DJATFGIC75CQDWMFHIWOKFXF26GKPINREMP6FNNTLF75JZZ3EQEQC
YWNTVA7PN7MC3HNTER3OCFHQAVKNJUK7KRQDZYFK24S5JLWHNU4AC
EFSXYZPOGA5M4DN65IEIDO7JK6U34DMQELAPHOIL2UAT6HRC66BAC
LTSVBVA235BQAIU3SQURKSRHIAL33K47G4J6TSEP2K353OCHNJEAC
IPG33FAWXGEQ2PO6OXRT2PWWXHRNMPVUKKADL6UKKN5GD2CNZ25AC
LAROLAYUGJ4Q5AEFV5EJMIA2ZKBNCBWHHHPCJ3CKCNIUIYUKRFVQC
SEWTRB6S5PO5MQBLCPVBD7XT2BDYNZUE2RO6Z2XENZRIOCN6OZJAC
JFOEOFGA4CQR2LW43IVQGDZSPVJAD4KDN2DZMZXGM2QDIUD7AVCAC
DFOBMSAODB3NKW37B272ZXA2ML5HMIH3N3C4GT2DPEQS7ZFK4SNAC
AL37SVTCKRSG4HG2PCYK5Z7QSIZZH5JHH4Q2VLMXFAXSAQRFFG4QC
B6HWAPDPXIWH7CHK5VLMWLL6EQN6NOFZEFYO47BPUY2ZO4SL7VDAC
NEDDHXUK3GNFMOFO3KLU7NRIHCTYNWBT3D6HTKZAOXFDE6HMDZ6AC
IZEVQF627FA7VV25KJAWYWGGC35LZUUBBQRPN5ZAINTQLPEDRTEAC
A6HKMINBNGQLLX4QJMYWKQ4JAEHVJ4HIRVDKPPDI3FJUO2AAB7OQC
AWWC6P5ZVFDQHX3EAYDG4DKTUZ6A5LHQAV3NIUO3VP6FM7JKPK5AC
HMDM3B557TO5RYP2IGFFC2C2VN6HYZTDQ47CJY2O37BW55DSMFZAC
J6S23MDGHVSCVVIRB6XRNSY3EGTDNWFJHV7RYLIEHBUK5KU63CFQC
RN7EI6INGUUHGMNY5RU3NH56WPLRZY5ZMYDNFMNE3TGV4ESFLQIAC
QMRKFEPGFBCEWAIXPEIG5ILKAJ2JH5L3TOITHR4HNJXK5FN3KXBQC
64C6AWH66FDKU6UE6Z6JPX2J2GBM2JOPTH2GL6LHKAIUBGNGDZ5AC
2J37EVJMX255K3XEJHTZGRPEIRMAQ62JQWOA7JU3YTZUB6PUPWVQC
RSF6UAJKG7CEKILSVXI6C4YZXY7PIYZM2EMA2IXKQ7SADKNVSH7QC
73NDXDEZRMK672GHSTC3CI6YHXFZ2GGJI5IKQGHKFDZKTNSQXLLQC
FXJQACESPGTLPG5ELXBU3M3OQXUZQQIR7HPIEHQ3FNUTMWVH4WBAC
W35DDBFYF6Z4ZPCFEO5RPAONZLYCSNTXUSTS6FIUUVZHCI6Q7GHAC
ZIG57EE6RB22JGB3CT33EN2HVYCHCXBT5GROBTBMBLEMDTGQOOBQC
ASF3UPJLCX7KIUCNJD5KAXSPDXCUEJHLL4HBRTRUBPCW73IXOCWQC
O5FVTOM6YFBLEPF3S576K6IMT6ZZ5VQCSB3YVXNS4CKBITKCRZ7AC
O227CEAV7BTKSE3SSC7XHC5IWEBXZL2AOOKJMBMOOFNTLINBLQMAC
4R7XIYK3BP664CO3YJ2VM64ES2JYN27UTQG5KS34OTEPAIODSZLQC
BSIUHCGFDFDFGWYMHZB7OVU3Z3IHPEUXRISIOPGZI2RUXZFDS2EQC
Q5X5RYQLP5K7REYD6VLHOKC4W36ZELJYA45V6YFKTD5S6MPN3NDQC
WZFQDWW4XK6M4A4PQ7WQJUTZUPRGQR7V7ZVZY5ZTL5AMGIFMHB2QC
POX3UAMTGCNS3SU5I6IKDNCCSHEAUSFBZ3WCMQ3EXKPNXETOI6BAC
NVOCQVASZWTKQJG7GPH7KHKZZR7NUG4WLV5YY4KAIRPCJRWCZPIAC
RPAJLHMTUJU4AYNBOHVGHGGB4NY2NLY3BVPYN5FMWB3ZIMAUQHCQC
TLQ72DSJD7GGPWN6HGBHAVPBRQFKEQ6KSK43U7JWWID4ZWAF47JAC
SLL7262CJUE7TZDDZZXFROHCVVDA527WA4PHXCKEGZUJF2EN5MQAC
GCVQD44VRPQVKPZEPIC4AOIXLJIG2ZMV3QI2Y7KALUT6NVUBSGSAC
P6NR2CGXCWAW6GXXSIXCGOBIRAS2BM4LEM6D5ADPN4IL7TMW6UVAC
EMVTF2IWNQGRL44FC4JNG5FYYQTZSFPNM6SOM7IAEH6T7PPK2NVAC
Y35QCWYW2OTZ27ZVTH2BA3XJTUCJ2WMLKU32ZCOCDY3AW7TIZXRAC
4QX5E5ACVN57KJLCWOM4JEI6JSV4XZNCWVYPOTKSOMUW3SOMCNJAC
7HPY3QPFPN35PSPUBVNW2GTFB3CBQZBST4J2BAVJ7QMXLIUN52JAC
7KZP4RHZ3QSYTPPQ257A65Z5UPX44TF2LAI2U5EMULQCLDCEUK2AC
5DRIWGLUKMQZU2ZPBXSTLAWJKAMOD5YXAHM5LEDQHDFGYYLHWCDQC
KNSI575VAW6HRCZYXOEPQ4DTSML4EORML5MV4DJBRKE7TXCPS4EAC
SCXG6TJWYIPRUMT27KGKIIF6FYKTUTY74UNZ2FQTT63XZ6HIF3AAC
2KZPOGRBY6KBMO76F55ZKIVOLSG3O63VP3RHRZVANXYT3OLZ3OWQC
7DBNV3GV773FH5ZLQWFX4RBOS4Q3CIK2RYZNNABY3ZOETYZCXRNQC
2OIPAQCBDIUJBXB4K2QVP3IEBIUOCQHSWSWFVMVSVZC7GHX2VK7AC
HALRDT2F22DAQ54M334YSKD2XJCAXDXEZPDKGMOWKH7XELBAS6MAC
NJNMO72S7VIUV22JXB4IFPZMHWTJAOTP6EC6Z4QSKYIROSXT52MQC
I2KHGVD44KT4MQJXGCTVSQKMBO6TVCY72F26TLXGWRL6PHGF6RNQC
PT4276XCOP5NJ3GRFJLIBZKVNVAOATAY5PLWV7FWK6RZW5FTEP5AC
NLZ3JXLOOIL37O3RRQWXHNPNSNEOOLPD6MCB754BEBECQB3KGR2AC
PBD7LZYQHXAA3KLH2ZUX5GW4UFML6BQ32KXZF4KZ6OYFASUYFJ5QC
BROSTG5KP3NUNLSYPVQID254TE47E5RKQAKLPIY7PGWETE6JNMTAC
2XQD6KKKD6QVHFHAEMVE3XXY7X2T7BLTLL7XIILZAXNJJH2YONUQC
Z24SZOGZJLDTDTGWH7M25RYQ7MYSU52ZLFWJ2PSQFTMK4J35PIWAC
QMEYU4MWLTSWPWEEOFRLK2IKE64BY3V5X73323WPLCGPP3TPDYGAC
ADMKQQGCGVSHHIMVQ4XFRDCG544SBJCYALSKZV45CQQBZ4ACUH2AC
GMYPBCWEB6NKURRILAHR3TJUKDOGR2ZMK5I6MS6P5G2LAGH36P3QC
MGOF7IUFGXYQKZOKMM2GGULFFVAULEHLZDSHMUW6B5DBKVXXR74AC
RFYEVKZQLOOQP536GRZOROSQW2O7TEHJ2HZDRVVUSBKLY5FBEO3QC
UUR6SMCAJMA7O3ZFUCQMPZFDDIPUVQ5IHUAC5F252YVD6H3JIKPQC
LHJ2HFXVUQ4VG25I7DADWU73G5K5WNZBDQ3SVNKFYLZ5BEYM4XCQC
EQXRXRZDYCM7BDAVBOXQYPG6C7IJT3OFGNIXCDAHJJBRKAXNGL7AC
import Control.Monad.Trans.Except ( withExceptT
)
import Control.Monad.Trans.Reader ( mapReaderT
)
import Control.Monad.Trans.Except ( withExceptT )
import Control.Monad.Trans.Reader ( mapReaderT )
{-
- A stream of dates upon which the specified subscription
- should be billed, beginning with the first day of the
- subscription.
-}
-- | A stream of dates upon which the specified subscription
-- should be billed, beginning with the first day of the
-- subscription.
next :: Maybe T.Day -> Maybe (T.Day, Maybe T.Day)
next d = do
d' <- d
if (all (d' <) subEndDay) then Just (d', nextRecurrence rec d') else Nothing
next :: Maybe T.Day -> Maybe (T.Day, Maybe T.Day)
next d = do
d' <- d
if (all (d' <) subEndDay) then Just (d', nextRecurrence rec d') else Nothing
let err = returnError ConversionFailed
f
("could not deserialize value " <> T.unpack fieldValue <>
" to a valid BTC address for network " <> show n
)
let err = returnError
ConversionFailed
f
( "could not deserialize value "
<> T.unpack fieldValue
<> " to a valid BTC address for network "
<> show n
)
where
parser :: Text -> RowParser (CreditTo (NetworkId, Address))
parser = \case
"credit_to_address" -> CreditToCurrency <$> (addressParser mode <* nullField <* nullField)
"credit_to_user" -> CreditToUser <$> (nullField *> nullField *> idParser UserId <* nullField)
"credit_to_project" -> CreditToProject <$> (nullField *> nullField *> nullField *> idParser ProjectId)
_ -> empty
where
parser :: Text -> RowParser (CreditTo (NetworkId, Address))
parser = \case
"credit_to_address" ->
CreditToCurrency <$> (addressParser mode <* nullField <* nullField)
"credit_to_user" ->
CreditToUser <$> (nullField *> nullField *> idParser UserId <* nullField)
"credit_to_project" ->
CreditToProject
<$> (nullField *> nullField *> nullField *> idParser ProjectId)
_ -> empty
CreateUser ::BTCUser -> DBOp UserId
FindUser ::UserId -> DBOp (Maybe BTCUser)
FindUserByName ::UserName -> DBOp (Maybe (UserId, BTCUser))
CreateUser :: BTCUser -> DBOp UserId
FindUser :: UserId -> DBOp (Maybe BTCUser)
FindUserByName :: UserName -> DBOp (Maybe (UserId, BTCUser))
CreateProject ::Project -> DBOp ProjectId
FindProject ::ProjectId -> DBOp (Maybe Project)
ListProjects ::DBOp [ProjectId]
FindSubscribers ::ProjectId -> DBOp [UserId]
FindUserProjects ::UserId -> DBOp [(ProjectId, Project)]
AddUserToProject ::ProjectId -> InvitingUID -> InvitedUID -> DBOp ()
CreateInvitation ::ProjectId -> InvitingUID -> Email -> C.UTCTime -> DBOp InvitationCode
FindInvitation ::InvitationCode -> DBOp (Maybe Invitation)
AcceptInvitation ::UserId -> InvitationCode -> C.UTCTime -> DBOp ()
CreateProject :: Project -> DBOp ProjectId
FindProject :: ProjectId -> DBOp (Maybe Project)
ListProjects :: DBOp [ProjectId]
FindSubscribers :: ProjectId -> DBOp [UserId]
FindUserProjects :: UserId -> DBOp [(ProjectId, Project)]
AddUserToProject :: ProjectId -> InvitingUID -> InvitedUID -> DBOp ()
CreateInvitation :: ProjectId -> InvitingUID -> Email -> C.UTCTime -> DBOp InvitationCode
FindInvitation :: InvitationCode -> DBOp (Maybe Invitation)
AcceptInvitation :: UserId -> InvitationCode -> C.UTCTime -> DBOp ()
CreateEvent ::ProjectId -> UserId -> LogEntry BTCNet -> DBOp EventId
AmendEvent ::EventId -> EventAmendment BTCNet -> DBOp AmendmentId
FindEvent ::EventId -> DBOp (Maybe (KeyedLogEntry BTCNet))
FindEvents ::ProjectId -> UserId -> RangeQuery -> Word32 -> DBOp [LogEntry BTCNet]
ReadWorkIndex ::ProjectId -> DBOp (WorkIndex BTCNet)
CreateEvent :: ProjectId -> UserId -> LogEntry BTCNet -> DBOp EventId
AmendEvent :: EventId -> EventAmendment BTCNet -> DBOp AmendmentId
FindEvent :: EventId -> DBOp (Maybe (KeyedLogEntry BTCNet))
FindEvents :: ProjectId -> UserId -> RangeQuery -> Word32 -> DBOp [LogEntry BTCNet]
ReadWorkIndex :: ProjectId -> DBOp (WorkIndex BTCNet)
CreateAuction ::Auction -> DBOp AuctionId
FindAuction ::AuctionId -> DBOp (Maybe Auction)
CreateBid ::AuctionId -> Bid -> DBOp BidId
FindBids ::AuctionId -> DBOp [(BidId, Bid)]
CreateAuction :: Auction -> DBOp AuctionId
FindAuction :: AuctionId -> DBOp (Maybe Auction)
CreateBid :: AuctionId -> Bid -> DBOp BidId
FindBids :: AuctionId -> DBOp [(BidId, Bid)]
CreateBillable ::UserId -> Billable -> DBOp BillableId
FindBillable ::BillableId -> DBOp (Maybe Billable)
FindBillables ::ProjectId -> DBOp [(BillableId, Billable)]
CreateBillable :: UserId -> Billable -> DBOp BillableId
FindBillable :: BillableId -> DBOp (Maybe Billable)
FindBillables :: ProjectId -> DBOp [(BillableId, Billable)]
CreateSubscription ::UserId -> BillableId -> T.Day -> DBOp SubscriptionId
FindSubscription ::SubscriptionId -> DBOp (Maybe Subscription)
FindSubscriptions ::UserId -> ProjectId -> DBOp [(SubscriptionId, Subscription)]
CreateSubscription :: UserId -> BillableId -> T.Day -> DBOp SubscriptionId
FindSubscription :: SubscriptionId -> DBOp (Maybe Subscription)
FindSubscriptions :: UserId -> ProjectId -> DBOp [(SubscriptionId, Subscription)]
CreatePaymentRequest ::PaymentRequest -> DBOp PaymentRequestId
FindPaymentRequests ::SubscriptionId -> DBOp [(PaymentRequestId, PaymentRequest)]
FindUnpaidRequests ::SubscriptionId -> DBOp [BillDetail]
FindPaymentRequest ::PaymentKey -> DBOp (Maybe (PaymentRequestId, PaymentRequest))
FindPaymentRequestId ::PaymentRequestId -> DBOp (Maybe PaymentRequest)
CreatePaymentRequest :: PaymentRequest -> DBOp PaymentRequestId
FindPaymentRequests :: SubscriptionId -> DBOp [(PaymentRequestId, PaymentRequest)]
FindUnpaidRequests :: SubscriptionId -> DBOp [BillDetail]
FindPaymentRequest :: PaymentKey -> DBOp (Maybe (PaymentRequestId, PaymentRequest))
FindPaymentRequestId :: PaymentRequestId -> DBOp (Maybe PaymentRequest)
workIndexJSON nmode (WorkIndex widx) =
v2 $ obj ["workIndex" .= fmap widxRec (MS.assocs widx)]
where
widxRec :: (CreditTo (NetworkId, Address), NonEmpty Interval) -> Value
widxRec (c, l) = object
[ "creditTo" .= creditToJSON nmode c
, "intervals" .= (intervalJSON <$> L.toList l)
]
workIndexJSON nmode (WorkIndex widx) = v2
$ obj ["workIndex" .= fmap widxRec (MS.assocs widx)]
where
widxRec :: (CreditTo (NetworkId, Address), NonEmpty Interval) -> Value
widxRec (c, l) = object
[ "creditTo" .= creditToJSON nmode c
, "intervals" .= (intervalJSON <$> L.toList l)
]
import Control.Error.Util (maybeT)
import Control.Lens (makeClassy, makeClassyPrisms, review,
view, (%~), (^.), traverseOf)
import Control.Error.Util ( maybeT )
import Control.Lens ( makeClassy
, makeClassyPrisms
, review
, view
, (%~)
, (^.)
, traverseOf
)
import Control.Monad.Except (MonadError, throwError)
import qualified Crypto.PubKey.RSA.Types as RSA (Error (..), PrivateKey)
import Crypto.Random.Types (MonadRandom, getRandomBytes)
import Control.Monad.Except ( MonadError
, throwError
)
import qualified Crypto.PubKey.RSA.Types as RSA
( Error(..)
, PrivateKey
)
import Crypto.Random.Types ( MonadRandom
, getRandomBytes
)
import qualified Bippy as B
import qualified Bippy.Proto as P
import qualified Bippy.Types as BT
import Haskoin.Address (Address(..))
import Haskoin.Address.Base58 (encodeBase58Check)
import Haskoin.Script (ScriptOutput (..))
import qualified Bippy as B
import qualified Bippy.Proto as P
import qualified Bippy.Types as BT
import Haskoin.Address ( Address(..) )
import Haskoin.Address.Base58 ( encodeBase58Check )
import Haskoin.Script ( ScriptOutput(..) )
createPaymentRequests :: ( MonadRandom m
, MonadReader r m, HasPaymentsConfig r
, MonadError e m, AsPaymentError e
, MonadDB m
)
=> BillingOps m -- ^ generators for payment request components
-> C.UTCTime -- ^ timestamp for payment request creation
-> UserId -- ^ customer responsible for payment
-> ProjectId -- ^ project whose worklog is to be paid
-> m [PaymentRequestId]
createPaymentRequests
:: ( MonadRandom m
, MonadReader r m
, HasPaymentsConfig r
, MonadError e m
, AsPaymentError e
, MonadDB m
)
=> BillingOps m -- ^ generators for payment request components
-> C.UTCTime -- ^ timestamp for payment request creation
-> UserId -- ^ customer responsible for payment
-> ProjectId -- ^ project whose worklog is to be paid
-> m [PaymentRequestId]
createSubscriptionPaymentRequests ::
( MonadRandom m
, MonadReader r m, HasPaymentsConfig r
, MonadError e m, AsPaymentError e
createSubscriptionPaymentRequests
:: ( MonadRandom m
, MonadReader r m
, HasPaymentsConfig r
, MonadError e m
, AsPaymentError e
billableSub <- maybeT (raiseSubjectNotFound . FindBillable $ sub ^. billable) pure $
traverseOf billable findBillable sub
billableSub <-
maybeT (raiseSubjectNotFound . FindBillable $ sub ^. billable) pure
$ traverseOf billable findBillable sub
billableDates <- findUnbilledDates now (view billable billableSub) paymentRequests $
takeWhile (< view _utctDay now) $ billingSchedule billableSub
billableDates <-
findUnbilledDates now (view billable billableSub) paymentRequests
$ takeWhile (< view _utctDay now)
$ billingSchedule billableSub
createPaymentRequest ::
( MonadRandom m
, MonadReader r m, HasPaymentsConfig r
, MonadError e m, AsPaymentError e
createPaymentRequest
:: ( MonadRandom m
, MonadReader r m
, HasPaymentsConfig r
, MonadError e m
, AsPaymentError e
findUnbilledDates :: (MonadDB m, MonadError e m, AsPaymentError e)
=> C.UTCTime -- ^ the date against which payment request expiration should be checked
-> Billable
-> [(PaymentRequestId, PaymentRequest)] -- ^ the list of existing payment requests
-> [T.Day] -- ^ the list of expected billing days
-> m [T.Day] -- ^ the list of billing days for which no payment request exists
findUnbilledDates now b (px @ (p : ps)) (dx @ (d : ds)) =
findUnbilledDates
:: (MonadDB m, MonadError e m, AsPaymentError e)
=> C.UTCTime -- ^ the date against which payment request expiration should be checked
-> Billable
-> [(PaymentRequestId, PaymentRequest)] -- ^ the list of existing payment requests
-> [T.Day] -- ^ the list of expected billing days
-> m [T.Day] -- ^ the list of billing days for which no payment request exists
findUnbilledDates now b (px@(p : ps)) (dx@(d : ds)) =
Expired r -> if view _utctDay now > addDays (view gracePeriod b) (view billingDate r)
then throwError (review _Overdue (r ^. subscription))
else fmap (d :) $ findUnbilledDates now b px dx -- d will be rebilled
_ -> findUnbilledDates now b ps ds -- if paid or unpaid, nothing to do
Expired r ->
if view _utctDay now > addDays (view gracePeriod b) (view billingDate r)
then throwError (review _Overdue (r ^. subscription))
else fmap (d :) $ findUnbilledDates now b px dx -- d will be rebilled
_ -> findUnbilledDates now b ps ds -- if paid or unpaid, nothing to do
getRequestStatus :: (MonadDB m)
=> C.UTCTime -- ^ the date against which request expiration should be checked
-> (PaymentRequestId, PaymentRequest) -- ^ the request for which to find a payment
-> m PaymentRequestStatus
getRequestStatus
:: (MonadDB m)
=> C.UTCTime -- ^ the date against which request expiration should be checked
-> (PaymentRequestId, PaymentRequest) -- ^ the request for which to find a payment
-> m PaymentRequestStatus
pure $ B.createPaymentDetails
(toNetwork (cfg ^. networkMode) BTC)
outputs
(T.fromThyme billingTime)
expiry memo uri payload
where
payoutTime = T.mkUTCTime payoutDate (fromInteger 0)
pure $ B.createPaymentDetails (toNetwork (cfg ^. networkMode) BTC)
outputs
(T.fromThyme billingTime)
expiry
memo
uri
payload
where payoutTime = T.mkUTCTime payoutDate (fromInteger 0)
join <$> (traverse checkAccess $ filter (not . isExpired now . view _2) requests)
where
findOp = FindUnpaidRequests sid
checkAccess d =
if view (_3 . customer) d == uid
then pure [d]
else raiseOpForbidden uid (UserNotSubscriber sid) findOp
join
<$> (traverse checkAccess $ filter (not . isExpired now . view _2) requests)
where
findOp = FindUnpaidRequests sid
checkAccess d = if view (_3 . customer) d == uid
then pure [d]
else raiseOpForbidden uid (UserNotSubscriber sid) findOp
( LogEntry(..), creditTo, event, eventMeta
, CreditTo(..), _CreditToCurrency, _CreditToUser, _CreditToProject, creditToName
, LogEvent(..), eventName, nameEvent, eventTime
, WorkIndex(WorkIndex), _WorkIndex, workIndex
, DepF, toDepF
, EventId(EventId), _EventId
, ModTime(ModTime), _ModTime
( LogEntry(..)
, creditTo
, event
, eventMeta
, CreditTo(..)
, _CreditToCurrency
, _CreditToUser
, _CreditToProject
, creditToName
, LogEvent(..)
, eventName
, nameEvent
, eventTime
, WorkIndex(WorkIndex)
, _WorkIndex
, workIndex
, DepF
, toDepF
, EventId(EventId)
, _EventId
, ModTime(ModTime)
, _ModTime
import Data.Eq (Eq, (==))
import Data.Either (Either(..), rights)
import Data.Foldable as F
import Data.Function (($), (.), id)
import Data.Functor (fmap)
import Data.Heap as H
import Data.List.NonEmpty as L
import Data.Maybe (Maybe(..))
import Data.Map.Strict as MS
import Data.Ord (Ord(..), Ordering(..))
import Data.Ratio (Rational)
import Data.Text (Text)
import Data.Thyme.Clock as C
import Data.Eq ( Eq
, (==)
)
import Data.Either ( Either(..)
, rights
)
import Data.Foldable as F
import Data.Function ( ($)
, (.)
, id
)
import Data.Functor ( fmap )
import Data.Heap as H
import Data.List.NonEmpty as L
import Data.Maybe ( Maybe(..) )
import Data.Map.Strict as MS
import Data.Ord ( Ord(..)
, Ordering(..)
)
import Data.Ratio ( Rational )
import Data.Text ( Text )
import Data.Thyme.Clock as C
let combine (StartWork t) (StopWork t') | t' > t = Right $ Interval t t'
combine (e1 @ (StartWork _)) (e2 @ (StartWork _)) = Left $ max e1 e2 -- ignore redundant starts
combine (e1 @ (StopWork _)) (e2 @ (StopWork _)) = Left $ min e1 e2 -- ignore redundant ends
combine _ e2 = Left e2
let combine :: LogEvent -> LogEvent -> Either LogEvent Interval
combine (StartWork t) (StopWork t') | t' > t = Right $ Interval t t'
combine (e1@(StartWork _)) (e2@(StartWork _)) = Left $ max e1 e2 -- ignore redundant starts
combine (e1@(StopWork _)) (e2@(StopWork _)) = Left $ min e1 e2 -- ignore redundant ends
combine _ e2 = Left e2
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
:: 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.
depPct dt =
if dt < monthsLength undepLength then 1
else toSeconds (max zeroV (maxDepreciable ^-^ dt)) / toSeconds maxDepreciable
depPct dt = if dt < monthsLength undepLength
then 1
else toSeconds (max zeroV (maxDepreciable ^-^ dt))
/ toSeconds maxDepreciable
req <- getRequest
rawHeader <- maybe (throwMissingAuth ()) pure $ getHeader "Authorization" req
req <- getRequest
rawHeader <- maybe (throwMissingAuth ()) pure $ getHeader "Authorization" req
credentials <- case
A.eitherDecode requestBody >>= A.parseEither parseLoginRequest
of
Left _ -> snapError 400 $ "Unable to parse login credentials object."
credentials <-
case A.eitherDecode requestBody >>= A.parseEither parseLoginRequest of
Left _ -> snapError 400 $ "Unable to parse login credentials object."
authResult <- with auth $ AU.loginByUsername (loginUser credentials) (AU.ClearText (encodeUtf8 $ loginPass credentials)) False
authResult <- with auth $ AU.loginByUsername
(loginUser credentials)
(AU.ClearText (encodeUtf8 $ loginPass credentials))
False
keyedLogEntryJSON :: NetworkMode -> (EventId, KeyedLogEntry (NetworkId, Address)) -> A.Value
keyedLogEntryJSON nmode (eid, (pid, uid, ev)) = v2 . obj $
[ "eventId" .= idValue _EventId eid
, "projectId" .= idValue _ProjectId pid
, "loggedBy" .= idValue _UserId uid
] <> logEntryFields nmode ev
keyedLogEntryJSON
:: NetworkMode -> (EventId, KeyedLogEntry (NetworkId, Address)) -> A.Value
keyedLogEntryJSON nmode (eid, (pid, uid, ev)) =
v2
. obj
$ [ "eventId" .= idValue _EventId eid
, "projectId" .= idValue _ProjectId pid
, "loggedBy" .= idValue _UserId uid
]
<> logEntryFields nmode ev
auctionRoute =
serveJSON auctionJSON $ method GET auctionGetHandler
auctionBidRoute =
serveJSON bidIdJSON $ method POST auctionBidHandler
auctionRoute = serveJSON auctionJSON $ method GET auctionGetHandler
auctionBidRoute = serveJSON bidIdJSON $ method POST auctionBidHandler
, ("login" , loginRoute)
, ("login" , xhrLoginRoute)
, ("logout" , logoutRoute)
, ("login/check" , checkLoginRoute)
, ("register" , registerRoute)
, ("accept_invitation" , acceptInviteRoute)
, ("login" , loginRoute)
, ("login" , xhrLoginRoute)
, ("logout" , logoutRoute)
, ("login/check", checkLoginRoute)
, ("register" , registerRoute)
, ( "accept_invitation"
, acceptInviteRoute
)
, ("user/projects/:projectId/logStart" , logWorkRoute StartWork)
, ("user/projects/:projectId/logEnd" , logWorkRoute StopWork)
, ("user/projects/:projectId/events" , userEventsRoute)
, ("user/projects/:projectId/workIndex" , userWorkIndexRoute)
, ("projects/:projectId/workIndex" , projectWorkIndexRoute)
, ("projects/:projectId/auctions" , auctionCreateRoute) -- <|> auctionListRoute)
, ("projects/:projectId/billables" , billableCreateRoute <|> billableListRoute)
, ("projects/:projectId/payouts" , projectPayoutsRoute)
, ("projects/:projectId/invite" , inviteRoute)
, ("projects/:projectId" , projectRoute)
, ("projects" , projectCreateRoute <|> projectListRoute)
, ("user/projects/:projectId/logStart" , logWorkRoute StartWork)
, ("user/projects/:projectId/logEnd" , logWorkRoute StopWork)
, ("user/projects/:projectId/events" , userEventsRoute)
, ("user/projects/:projectId/workIndex", userWorkIndexRoute)
, ("projects/:projectId/workIndex" , projectWorkIndexRoute)
, ( "projects/:projectId/auctions"
, auctionCreateRoute
) -- <|> auctionListRoute)
, ( "projects/:projectId/billables"
, billableCreateRoute <|> billableListRoute
)
, ("projects/:projectId/payouts", projectPayoutsRoute)
, ("projects/:projectId/invite" , inviteRoute)
, ("projects/:projectId" , projectRoute)
, ("projects" , projectCreateRoute <|> projectListRoute)