Break out api module for timeline.

[?]
Aug 22, 2020, 4:31 AM
RSF6UAJKG7CEKILSVXI6C4YZXY7PIYZM2EMA2IXKQ7SADKNVSH7QC

Dependencies

  • [2] QMEYU4MW Add display for prior intervals.
  • [3] HO2PFRAB Client login now handles response correctly.
  • [4] B6HWAPDP Modularize & update to recent haskoin.
  • [5] 73NDXDEZ Begin implementation of billing event persistence.
  • [6] RPAJLHMT Change to use UUIDs instead of ints for primary keys.
  • [7] 4U7F3CPI THE GREAT RENAMING OF THINGS!
  • [8] 2XQD6KKK Add invitation logic and clean up DBProg error handling.
  • [9] ZIG57EE6 Fix project selection, end log end on project switch.
  • [10] EFSXYZPO Autoformat everything with brittany.
  • [11] NEDDHXUK Reformat via stylish-haskell
  • [12] NJNMO72S Add zcash.com submodule and update client to modern halogen.
  • [13] AL37SVTC Implement payments service endpoints.
  • [14] QU5FW67R Add project selection to time tracker.
  • [15] O722AOKE Add route to allow crediting of events to users/projects.
  • [16] GMYPBCWE Make docker-compose work.
  • [17] JXG3FCXY Upgrade ps + halogen versions.
  • [18] AWWC6P5Z Add migration to include payment network with addresses.
  • [19] J6S23MDG Use server timestamps for interval start and end.
  • [20] HALRDT2F Added initial auction create route.
  • [21] GCVQD44V Create amends endpoint, switch to UUID primary keys
  • [22] BFZN4SUA Make timeline component work.
  • [23] O5FVTOM6 Undo JSON silliness, enable a couple more routes.
  • [24] IPG33FAW Add billing daemon
  • [25] 4R7XIYK3 Switch from ClassyPrelude to Relude
  • [26] MGOF7IUF Update TASKS list to reflect completed projects.
  • [*] EA5BFM5G Split Login component into its own module.
  • [*] IZEVQF62 Work in progress replacing sqlite with postgres.
  • [*] A6HKMINB Attempting to improve JSON handling.
  • [*] 64C6AWH6 Rename Ananke -> Quixotic, project reboot.
  • [*] HMDM3B55 Implement core of payments/billing infrastructure.
  • [*] BROSTG5K Beginning of modularization of server.
  • [*] ADMKQQGC Initial empty Snap project.

Change contents

  • file addition: Api (d--r------)
    [28.1]
  • file addition: Timeline.purs (----------)
    [0.1]
    module Aftok.Api.Timeline where
    import Prelude
    import Control.Alt ((<|>))
    import Control.Monad.Error.Class (throwError)
    import Control.Monad.Except.Trans (withExceptT, runExceptT)
    import Data.Argonaut.Decode (class DecodeJson, decodeJson, (.:), (.:?))
    import Data.DateTime (DateTime)
    import Data.DateTime.Instant (Instant)
    import Data.Either (Either, note)
    import Data.Foldable (class Foldable, foldMapDefaultR, intercalate, foldr, foldl)
    import Data.JSDate as JD
    import Data.Maybe (Maybe(..))
    import Data.Newtype (class Newtype)
    import Data.Traversable (class Traversable, traverse)
    import Data.UUID as UUID
    import Type.Proxy (Proxy(..))
    -- import Text.Format as F -- (format, zeroFill, width)
    import Effect.Aff (Aff)
    import Effect.Class (liftEffect)
    import Affjax (get, post)
    import Affjax.RequestBody as RB
    import Affjax.ResponseFormat as RF
    import Data.Argonaut.Encode (encodeJson)
    import Aftok.Project (ProjectId(..), pidStr)
    import Aftok.Types (APIError, JsonCompose, decompose, parseDatedResponse)
    data TimelineError
    = LogFailure (APIError)
    | Unexpected String
    instance showTimelineError :: Show TimelineError where
    show = case _ of
    LogFailure e -> show e
    Unexpected t -> t
    data Event' i
    = StartEvent i
    | StopEvent i
    type Event = Event' Instant
    derive instance eventFunctor :: Functor Event'
    instance eventFoldable :: Foldable Event' where
    foldr f b = case _ of
    StartEvent a -> f a b
    StopEvent a -> f a b
    foldl f b = case _ of
    StartEvent a -> f b a
    StopEvent a -> f b a
    foldMap = foldMapDefaultR
    instance eventTraversable :: Traversable Event' where
    traverse f = case _ of
    StartEvent a -> StartEvent <$> f a
    StopEvent a -> StopEvent <$> f a
    sequence = traverse identity
    instance decodeJsonEvent :: DecodeJson (Event' String) where
    decodeJson json = do
    obj <- decodeJson json
    event <- obj .: "event"
    start' <- traverse (_ .: "eventTime") =<< event .:? "start"
    stop' <- traverse (_ .: "eventTime") =<< event .:? "stop"
    note "Only 'stop' and 'start' events are supported." $ (StartEvent <$> start') <|> (StopEvent <$> stop')
    newtype Interval' i = Interval
    { start :: i
    , end :: i
    }
    derive instance intervalEq :: (Eq i) => Eq (Interval' i)
    derive instance intervalNewtype :: Newtype (Interval' i) _
    type Interval = Interval' Instant
    derive instance intervalFunctor :: Functor Interval'
    instance intervalFoldable :: Foldable Interval' where
    foldr f b (Interval i) = f i.start (f i.end b)
    foldl f b (Interval i) = f (f b i.start) i.end
    foldMap = foldMapDefaultR
    instance intervalTraversable :: Traversable Interval' where
    traverse f (Interval i) = interval <$> f i.start <*> f i.end
    sequence = traverse identity
    instance decodeJsonInterval :: DecodeJson (Interval' String) where
    decodeJson json = do
    obj <- decodeJson json
    interval <$> obj .: "start" <*> obj .: "end"
    interval :: forall i. i -> i -> Interval' i
    interval s e = Interval { start: s, end: e }
    data TimeSpan' t
    = Before t
    | During (Interval' t)
    | After t
    type TimeSpan = TimeSpan' DateTime
    derive instance timeSpanFunctor :: Functor TimeSpan'
    instance timeSpanFoldable :: Foldable TimeSpan' where
    foldr f b = case _ of
    Before a -> f a b
    During x -> foldr f b x
    After a -> f a b
    foldl f b = case _ of
    Before a -> f b a
    During x -> foldl f b x
    After a -> f b a
    foldMap = foldMapDefaultR
    instance timeSpanTraversable :: Traversable TimeSpan' where
    traverse f = case _ of
    Before a -> Before <$> f a
    During x -> During <$> traverse f x
    After a -> After <$> f a
    sequence = traverse identity
    apiLogStart :: ProjectId -> Aff (Either TimelineError Instant)
    apiLogStart (ProjectId pid) = do
    let requestBody = Just <<< RB.Json <<< encodeJson $ { schemaVersion: "2.0" }
    response <- post RF.json ("/api/user/projects/" <> UUID.toString pid <> "/logStart") requestBody
    liftEffect <<< runExceptT $ do
    event <- withExceptT LogFailure $ parseDatedResponse response
    case event of
    StartEvent t -> pure t
    StopEvent _ -> throwError <<< Unexpected $ "Expected start event, got stop."
    apiLogEnd :: ProjectId -> Aff (Either TimelineError Instant)
    apiLogEnd (ProjectId pid) = do
    let requestBody = Just <<< RB.Json <<< encodeJson $ { schemaVersion: "2.0" }
    response <- post RF.json ("/api/user/projects/" <> UUID.toString pid <> "/logEnd") requestBody
    liftEffect <<< runExceptT $ do
    event <- withExceptT LogFailure $ parseDatedResponse response
    case event of
    StartEvent _ -> throwError <<< Unexpected $ "Expected stop event, got start."
    StopEvent t -> pure t
    newtype ListIntervalsResponse a = ListIntervalsResponse
    { workIndex :: Array ({ intervals :: Array a })
    }
    derive instance listIntervalsResponseNewtype :: Newtype (ListIntervalsResponse a) _
    derive instance listIntervalsResponseFunctor :: Functor ListIntervalsResponse
    instance listIntervalsResponseFoldable :: Foldable ListIntervalsResponse where
    foldr f b (ListIntervalsResponse r) = foldr f b (r.workIndex >>= _.intervals)
    foldl f b (ListIntervalsResponse r) = foldl f b (r.workIndex >>= _.intervals)
    foldMap = foldMapDefaultR
    instance listIntervalsResponseTraversable :: Traversable ListIntervalsResponse where
    traverse f (ListIntervalsResponse r) =
    let traverseCreditRow r' = ({ intervals: _ }) <$> traverse f r'.intervals
    in (ListIntervalsResponse <<< ({ workIndex: _ })) <$> traverse traverseCreditRow r.workIndex
    sequence = traverse identity
    instance listIntervalsResponseDecodeJson :: DecodeJson a => DecodeJson (ListIntervalsResponse a) where
    decodeJson = map ListIntervalsResponse <<< decodeJson
    _ListIntervalsResponse :: Proxy (JsonCompose ListIntervalsResponse Interval' String)
    _ListIntervalsResponse = Proxy
    apiListIntervals :: ProjectId -> TimeSpan -> Aff (Either TimelineError (Array Interval))
    apiListIntervals pid ts = do
    ts' <- liftEffect $ traverse (JD.toISOString <<< JD.fromDateTime) ts
    let queryElements = case ts' of
    Before t -> ["before=" <> t]
    During (Interval x) -> ["after=" <> x.start, "before=" <> x.end]
    After t -> ["after=" <> t]
    response <- get RF.json ("/api/user/projects/" <> pidStr pid <> "/workIndex?" <> intercalate "&" queryElements)
    liftEffect
    <<< runExceptT
    <<< map (\(ListIntervalsResponse r) -> r.workIndex >>= (_.intervals))
    <<< map decompose
    <<< withExceptT LogFailure
    $ parseDatedResponse response
  • edit in client/src/Aftok/Timeline.purs at line 6
    [3.2520][3.201:247](),[3.247][2.662:722]()
    import Control.Monad.Error.Class (throwError)
    import Control.Monad.Except.Trans (withExceptT, runExceptT)
  • edit in client/src/Aftok/Timeline.purs at line 10
    [2.759][3.316:388](),[3.2546][3.316:388]()
    import Data.Argonaut.Decode (class DecodeJson, decodeJson, (.:), (.:?))
  • replacement in client/src/Aftok/Timeline.purs at line 13
    [2.928][3.389:427](),[3.2739][3.389:427]()
    import Data.Either (Either(..), note)
    [2.928]
    [2.929]
    import Data.Either (Either(..))
  • replacement in client/src/Aftok/Timeline.purs at line 15
    [2.957][2.957:1077]()
    import Data.Foldable (class Foldable, any, foldMapDefaultR, intercalate, foldr, foldl, length)
    import Data.JSDate as JD
    [2.957]
    [2.1077]
    import Data.Foldable (any, length)
  • replacement in client/src/Aftok/Timeline.purs at line 18
    [3.1054][2.1099:1143]()
    import Data.Newtype (class Newtype, unwrap)
    [3.1054]
    [3.2539]
    import Data.Newtype (unwrap)
  • replacement in client/src/Aftok/Timeline.purs at line 21
    [3.2626][3.488:553]()
    import Data.Traversable (class Traversable, traverse_, traverse)
    [3.2626]
    [2.1144]
    import Data.Traversable (traverse_)
  • edit in client/src/Aftok/Timeline.purs at line 24
    [3.80][3.2663:2688]()
    import Data.UUID as UUID
  • edit in client/src/Aftok/Timeline.purs at line 25
    [3.2922][2.1180:1210]()
    import Type.Proxy (Proxy(..))
  • edit in client/src/Aftok/Timeline.purs at line 32
    [2.1291][3.3035:3036](),[3.301530][3.3035:3036](),[3.3035][3.3035:3036](),[3.3036][2.1292:1318](),[2.1318][3.2764:2873](),[3.2764][3.2764:2873]()
    import Affjax (get, post)
    import Affjax.RequestBody as RB
    import Affjax.ResponseFormat as RF
    import Data.Argonaut.Encode (encodeJson)
  • edit in client/src/Aftok/Timeline.purs at line 48
    [3.301880]
    [3.2875]
    import Aftok.Api.Timeline as TL
    import Aftok.Api.Timeline (TimelineError, Interval'(..), Interval, TimeSpan)
  • replacement in client/src/Aftok/Timeline.purs at line 51
    [3.2907][2.1407:1557](),[3.599][3.3001:3002](),[2.1557][3.3001:3002](),[3.3001][3.3001:3002](),[3.3002][2.1558:1605](),[3.633][3.3220:3221](),[2.1605][3.3220:3221](),[3.3036][3.3220:3221](),[3.301927][3.3220:3221](),[3.3220][3.3220:3221](),[3.3221][2.1606:2534](),[2.2534][3.3278:3282](),[3.3278][3.3278:3282](),[3.3282][2.2535:3080]()
    import Aftok.Project (Project, Project'(..), ProjectId(..), pidStr)
    import Aftok.Types (APIError, System, JsonCompose, decompose, parseDatedResponse)
    data Event' i
    = StartEvent i
    | StopEvent i
    type Event = Event' Instant
    derive instance eventFunctor :: Functor Event'
    instance eventFoldable :: Foldable Event' where
    foldr f b = case _ of
    StartEvent a -> f a b
    StopEvent a -> f a b
    foldl f b = case _ of
    StartEvent a -> f b a
    StopEvent a -> f b a
    foldMap = foldMapDefaultR
    instance eventTraversable :: Traversable Event' where
    traverse f = case _ of
    StartEvent a -> StartEvent <$> f a
    StopEvent a -> StopEvent <$> f a
    sequence = traverse identity
    instance decodeJsonEvent :: DecodeJson (Event' String) where
    decodeJson json = do
    obj <- decodeJson json
    event <- obj .: "event"
    start' <- traverse (_ .: "eventTime") =<< event .:? "start"
    stop' <- traverse (_ .: "eventTime") =<< event .:? "stop"
    note "Only 'stop' and 'start' events are supported." $ (StartEvent <$> start') <|> (StopEvent <$> stop')
    newtype Interval' i = Interval
    { start :: i
    , end :: i
    }
    derive instance intervalEq :: (Eq i) => Eq (Interval' i)
    derive instance intervalNewtype :: Newtype (Interval' i) _
    type Interval = Interval' Instant
    derive instance intervalFunctor :: Functor Interval'
    instance intervalFoldable :: Foldable Interval' where
    foldr f b (Interval i) = f i.start (f i.end b)
    foldl f b (Interval i) = f (f b i.start) i.end
    foldMap = foldMapDefaultR
    instance intervalTraversable :: Traversable Interval' where
    traverse f (Interval i) = interval <$> f i.start <*> f i.end
    sequence = traverse identity
    [3.2907]
    [2.3080]
    import Aftok.Project (Project, Project'(..), ProjectId)
    import Aftok.Types (System)
  • edit in client/src/Aftok/Timeline.purs at line 54
    [2.3081][2.3081:3987](),[2.3987][3.3282:3283](),[3.3282][3.3282:3283]()
    instance decodeJsonInterval :: DecodeJson (Interval' String) where
    decodeJson json = do
    obj <- decodeJson json
    interval <$> obj .: "start" <*> obj .: "end"
    interval :: forall i. i -> i -> Interval' i
    interval s e = Interval { start: s, end: e }
    data TimeSpan' t
    = Before t
    | During (Interval' t)
    | After t
    type TimeSpan = TimeSpan' DateTime
    derive instance timeSpanFunctor :: Functor TimeSpan'
    instance timeSpanFoldable :: Foldable TimeSpan' where
    foldr f b = case _ of
    Before a -> f a b
    During x -> foldr f b x
    After a -> f a b
    foldl f b = case _ of
    Before a -> f b a
    During x -> foldl f b x
    After a -> f b a
    foldMap = foldMapDefaultR
    instance timeSpanTraversable :: Traversable TimeSpan' where
    traverse f = case _ of
    Before a -> Before <$> f a
    During x -> During <$> traverse f x
    After a -> After <$> f a
    sequence = traverse identity
  • edit in client/src/Aftok/Timeline.purs at line 73
    [3.3626][3.3112:3157](),[3.3157][2.4057:4079](),[2.4079][3.634:736](),[3.3157][3.634:736](),[3.736][2.4080:4102](),[3.736][3.3157:3158](),[2.4102][3.3157:3158](),[3.3157][3.3157:3158]()
    data TimelineError
    = LogFailure (APIError)
    | Unexpected String
    instance showTimelineError :: Show TimelineError where
    show = case _ of
    LogFailure e -> show e
    Unexpected t -> t
  • replacement in client/src/Aftok/Timeline.purs at line 105
    [3.1336][2.4492:4560]()
    { limits: { bounds: interval bottom bottom, current: bottom }
    [3.1336]
    [2.4560]
    { limits: { bounds: TL.interval bottom bottom, current: bottom }
  • replacement in client/src/Aftok/Timeline.purs at line 165
    [3.2652][2.5549:5646]()
    timeSpan <- Before <$> lift system.nowDateTime -- FIXME, should come from a form control
    [3.2652]
    [2.5646]
    timeSpan <- TL.Before <$> lift system.nowDateTime -- FIXME, should come from a form control
  • replacement in client/src/Aftok/Timeline.purs at line 203
    [2.6469][2.6469:6540]()
    in interval startInstant (maybe startInstant fromDateTime endOfDay)
    [2.6469]
    [2.6540]
    in TL.interval startInstant (maybe startInstant fromDateTime endOfDay)
  • replacement in client/src/Aftok/Timeline.purs at line 287
    [3.3520][2.7524:7572]()
    s { active = s.active <|> Just (interval t t)
    [3.3520]
    [3.305818]
    s { active = s.active <|> Just (TL.interval t t)
  • replacement in client/src/Aftok/Timeline.purs at line 294
    [2.7615][2.7615:7701]()
    (\i -> M.unionWith (<>) (toHistory [interval (unwrap i).start t]) s.history)
    [2.7615]
    [2.7701]
    (\i -> M.unionWith (<>) (toHistory [TL.interval (unwrap i).start t]) s.history)
  • replacement in client/src/Aftok/Timeline.purs at line 302
    [3.1387][2.7719:7785]()
    , active = map (\(Interval i) -> interval i.start t) s.active
    [3.1387]
    [3.306012]
    , active = map (\(Interval i) -> TL.interval i.start t) s.active
  • edit in client/src/Aftok/Timeline.purs at line 310
    [3.6890][3.3613:3709](),[3.3709][3.6092:6171](),[3.6092][3.6092:6171](),[3.6171][2.7786:8116](),[3.2588][3.6622:6623](),[2.8116][3.6622:6623](),[3.6622][3.6622:6623](),[3.6623][3.3710:3802](),[3.3802][3.6709:6788](),[3.6709][3.6709:6788](),[3.6788][2.8117:10282](),[3.3240][3.7237:7238](),[2.10282][3.7237:7238](),[3.7237][3.7237:7238]()
    apiLogStart :: ProjectId -> Aff (Either TimelineError Instant)
    apiLogStart (ProjectId pid) = do
    let requestBody = Just <<< RB.Json <<< encodeJson $ { schemaVersion: "2.0" }
    response <- post RF.json ("/api/user/projects/" <> UUID.toString pid <> "/logStart") requestBody
    liftEffect <<< runExceptT $ do
    event <- withExceptT LogFailure $ parseDatedResponse response
    case event of
    StartEvent t -> pure t
    StopEvent _ -> throwError <<< Unexpected $ "Expected start event, got stop."
    apiLogEnd :: ProjectId -> Aff (Either TimelineError Instant)
    apiLogEnd (ProjectId pid) = do
    let requestBody = Just <<< RB.Json <<< encodeJson $ { schemaVersion: "2.0" }
    response <- post RF.json ("/api/user/projects/" <> UUID.toString pid <> "/logEnd") requestBody
    liftEffect <<< runExceptT $ do
    event <- withExceptT LogFailure $ parseDatedResponse response
    case event of
    StartEvent _ -> throwError <<< Unexpected $ "Expected stop event, got start."
    StopEvent t -> pure t
    newtype ListIntervalsResponse a = ListIntervalsResponse
    { workIndex :: Array ({ intervals :: Array a })
    }
    derive instance listIntervalsResponseNewtype :: Newtype (ListIntervalsResponse a) _
    derive instance listIntervalsResponseFunctor :: Functor ListIntervalsResponse
    instance listIntervalsResponseFoldable :: Foldable ListIntervalsResponse where
    foldr f b (ListIntervalsResponse r) = foldr f b (r.workIndex >>= _.intervals)
    foldl f b (ListIntervalsResponse r) = foldl f b (r.workIndex >>= _.intervals)
    foldMap = foldMapDefaultR
    instance listIntervalsResponseTraversable :: Traversable ListIntervalsResponse where
    traverse f (ListIntervalsResponse r) =
    let traverseCreditRow r' = ({ intervals: _ }) <$> traverse f r'.intervals
    in (ListIntervalsResponse <<< ({ workIndex: _ })) <$> traverse traverseCreditRow r.workIndex
    sequence = traverse identity
    instance listIntervalsResponseDecodeJson :: DecodeJson a => DecodeJson (ListIntervalsResponse a) where
    decodeJson = map ListIntervalsResponse <<< decodeJson
    _ListIntervalsResponse :: Proxy (JsonCompose ListIntervalsResponse Interval' String)
    _ListIntervalsResponse = Proxy
    apiListIntervals :: ProjectId -> TimeSpan -> Aff (Either TimelineError (Array Interval))
    apiListIntervals pid ts = do
    ts' <- liftEffect $ traverse (JD.toISOString <<< JD.fromDateTime) ts
    let queryElements = case ts' of
    Before t -> ["before=" <> t]
    During (Interval x) -> ["after=" <> x.start, "before=" <> x.end]
    After t -> ["after=" <> t]
    response <- get RF.json ("/api/user/projects/" <> pidStr pid <> "/workIndex?" <> intercalate "&" queryElements)
    liftEffect
    <<< runExceptT
    <<< map (\(ListIntervalsResponse r) -> r.workIndex >>= (_.intervals))
    <<< map decompose
    <<< withExceptT LogFailure
    $ parseDatedResponse response
  • replacement in client/src/Aftok/Timeline.purs at line 313
    [2.10317][2.10317:10402]()
    , logStart: apiLogStart
    , logEnd: apiLogEnd
    , listIntervals: apiListIntervals
    [2.10317]
    [2.10402]
    , logStart: TL.apiLogStart
    , logEnd: TL.apiLogEnd
    , listIntervals: TL.apiListIntervals
  • replacement in lib/Aftok/Database/PostgreSQL.hs at line 400
    [3.1002][3.1002:1102](),[3.1102][3.11913:12004](),[3.20358][3.11913:12004](),[3.11913][3.11913:12004](),[3.12004][3.16287:16361](),[3.16287][3.16287:16361]()
    credit_to_network, credit_to_address, credit_to_user_id, credit_to_project_id,
    event_type, event_time, event_metadata
    FROM work_events
    WHERE project_id = ? AND user_id = ? AND event_time >= ? |]
    [3.1002]
    [3.20359]
    credit_to_network, credit_to_address, credit_to_user_id, credit_to_project_id,
    event_type, event_time, event_metadata
    FROM work_events
    WHERE project_id = ? AND user_id = ? AND event_time >= ? |]
  • edit in lib/Aftok/Database/PostgreSQL.hs at line 406
    [3.12014]
    [30.1951]
    pgEval (FindLatestEvents (ProjectId pid) (UserId uid) i) = do
    mode <- askNetworkMode
    pquery
    (logEntryParser mode)
    [sql| SELECT credit_to_type,
    credit_to_network, credit_to_address, credit_to_user_id, credit_to_project_id,
    event_type, event_time, event_metadata
    FROM work_events
    WHERE project_id = ?
    AND user_id = ?
    ORDER BY event_time DESC
    LIMIT ?|]
    (pid, uid, i)
  • edit in lib/Aftok/Database.hs at line 61
    [3.27304]
    [3.27304]
    FindLatestEvents ::ProjectId -> UserId -> Int -> DBOp [LogEntry BTCNet]
  • edit in lib/Aftok/Database.hs at line 224
    [32.8899]
    [3.1303]
    findLatestEvents :: (MonadDB m) => ProjectId -> UserId -> Int -> m [LogEntry BTCNet]
    findLatestEvents p u i = liftdb $ FindLatestEvents p u i
  • edit in server/Aftok/Snaplet/Util.hs at line 2
    [3.2709][3.4243:4244](),[3.4243][3.4243:4244]()
  • edit in server/Aftok/Snaplet/Util.hs at line 3
    [3.4267]
    [3.5709]
    import Data.Attoparsec.ByteString (parseOnly)
    import Data.Attoparsec.ByteString.Char8 (decimal)
  • edit in server/Aftok/Snaplet/Util.hs at line 19
    [3.4619]
    decimalParam :: (Integral i, MonadSnap m) => ByteString -> m (Maybe i)
    decimalParam k = runMaybeT $ do
    bs <- MaybeT $ getParam k
    MaybeT . pure . either (const Nothing) Just $ parseOnly decimal bs
  • edit in server/Aftok/Snaplet/WorkLog.hs at line 101
    [3.5386]
    [2.15051]
    userLatestEntries :: S.Handler App App [LogEntry (NetworkId, Address)]
    userLatestEntries = do
    uid <- requireUserId
    pid <- requireProjectId
    limit <- fromMaybe 1 <$> decimalParam "limit"
    snapEval $ findLatestEvents pid uid limit
  • edit in server/Main.hs at line 75
    [2.15343]
    [3.2549]
    projectPayoutsRoute =
    serveJSON (payoutsJSON nmode) $ method GET payoutsHandler
  • edit in server/Main.hs at line 78
    [3.2550][2.15344:15428](),[3.2613][3.2569:2570](),[3.2979][3.2569:2570](),[3.3485][3.2569:2570](),[3.7960][3.2569:2570](),[2.15428][3.2569:2570](),[3.26983][3.2569:2570](),[3.39611][3.2569:2570](),[3.63767][3.2569:2570](),[3.2569][3.2569:2570]()
    projectPayoutsRoute = serveJSON (payoutsJSON nmode) $ method GET payoutsHandler
  • replacement in server/Main.hs at line 91
    [3.64102][3.64102:64251]()
    auctionRoute = serveJSON auctionJSON $ method GET auctionGetHandler
    auctionBidRoute = serveJSON bidIdJSON $ method POST auctionBidHandler
    [3.64102]
    [3.321]
    auctionRoute =
    serveJSON auctionJSON $ method GET auctionGetHandler
    auctionBidRoute =
    serveJSON bidIdJSON $ method POST auctionBidHandler