Return actual events for interval ends, not just timestamps.
[?]
Jan 23, 2021, 6:08 AM
IR75ZMX32SFFMDNV2I2L22X5JTWCOC4UUBCSPU7S6VHR6HFV6ADQCDependencies
- [2]
5SBSBFLSBind log directories to local paths for development. - [3]
4354Y4PEAdd endpoint to list project contributors. - [4]
MU6WOCCJUpdate auctions to permit zcash as a funding currency. - [5]
A2J7B4SCInitial impl of depreciation function. - [6]
Y35QCWYWMinor improvement in WorkIndex type to eliminate duplicated information. - [7]
LTSVBVA2Update to a recent haskoin-core. Fix Stack build. - [8]
EZQG2APBUpdate task list. - [9]
2XQD6KKKAdd invitation logic and clean up DBProg error handling. - [10]
ASF3UPJLAdd auction creation and bid handlers - [11]
TLQ72DSJLenses, sqlite-simple - [12]
W35DDBFYFactor common JSON conversions up into client lib module. - [13]
O5FVTOM6Undo JSON silliness, enable a couple more routes. - [14]
4IQVQL4TAdded client for payouts endpoint. - [15]
PBD7LZYQPostgres & auth are beginning to function. - [16]
WRPIYG3EUse project listing functionality to check for whether we have a cookie. - [17]
QMEYU4MWAdd display for prior intervals. - [18]
2J37EVJMCheck for an open interval on project switch. - [19]
ZIG57EE6Fix project selection, end log end on project switch. - [20]
XTBSG4C7Adding serveJSON combinator to eliminate some boilerplate from handlers. - [21]
EQXRXRZDChanged to use tasty instead of test-framework - [22]
7KZP4RHZSwitch from Data.Time to Data.Thyme - [23]
RPAJLHMTChange to use UUIDs instead of ints for primary keys. - [24]
SOIAMXLWBuild versioned docker images. - [25]
QU5FW67RAdd project selection to time tracker. - [26]
UILI6PILThe route-based logStart/logStop is nicer. - [27]
Z24SZOGZReturn richer information from event logging calls. - [28]
64C6AWH6Rename Ananke -> Quixotic, project reboot. - [29]
Z7KS5XHHVery WIP. Wow. - [30]
QMRKFEPGRefactor QDB to use a free monad algebra instead. - [31]
RN7EI6INUpdate database layer to use CreditTo - [32]
B6HWAPDPModularize & update to recent haskoin. - [33]
SQ7UMLN5Get z-addr checks working. - [34]
SCXG6TJWMake log reduction safer in presence of overlapping events. - [35]
BWN72T44Don't accept work timestamp from an external source. - [36]
NAS4BFL4Trivial stylish-haskell reformat. - [37]
N4NDAZYTInitial implementation of payouts. - [38]
Q5X5RYQLstylish-haskell reformatting - [39]
4U7F3CPITHE GREAT RENAMING OF THINGS! - [40]
GMYPBCWEMake docker-compose work. - [41]
7HPY3QPFFix linting errors. (yay hlint!) - [42]
AL37SVTCImplement payments service endpoints. - [43]
EW2XN7KUUpdate docker build, clean up migration for payments tables. - [44]
BFZN4SUAMake timeline component work. - [45]
JXG3FCXYUpgrade ps + halogen versions. - [46]
RSF6UAJKBreak out api module for timeline. - [47]
JUUMYIQEAdd groupBy utility function for use in TimeLog. - [48]
GKGVYBZGAdded JSON serialization to TimeLog - [49]
U256ZALIAdd captcha check to register route. - [50]
7XN3I3QJAdd 'loggedIntervals' endpoint. - [51]
J6S23MDGUse server timestamps for interval start and end. - [52]
I2KHGVD4Require project permissions for access to most data. - [53]
O227CEAVAdds storage of original event JSON for some DBOp constructors. - [54]
BROSTG5KBeginning of modularization of server. - [55]
Z3MK2PJ5Add GET handler for retrieving auction data. - [56]
HALRDT2FAdded initial auction create route. - [57]
POX3UAMTEnabling logging of time to contributor/project accounts - [58]
GCVQD44VCreate amends endpoint, switch to UUID primary keys - [59]
5XFJNUAZStart of addition of project infrastructure. - [60]
O722AOKEAdd route to allow crediting of events to users/projects. - [61]
OUR4PAOTUse local dates for display of intervals. - [62]
4QX5E5ACInitial compilation of payouts function succeeds. - [63]
SEWTRB6SImplement payment request creation functions. - [64]
NLZ3JXLOFix formatting with stylish-haskell. - [65]
NSRSSSTRUpdate nginx.conf, make aftok host configurable for cli scripts. - [66]
NJNMO72SAdd zcash.com submodule and update client to modern halogen. - [67]
SLL7262CMake depreciation functions more flexible. - [68]
MJDIMD5BImprove documentation of local docker-compose setup. - [69]
5DRIWGLUImproving TimeLog specs - [70]
7DBNV3GVInitial, stack-based impl of time log event reduction. - [71]
NVOCQVASInitial failing tests. - [72]
KNSI575VCleanup of EventLog types. - [73]
EFSXYZPOAutoformat everything with brittany. - [74]
HMDM3B55Implement core of payments/billing infrastructure. - [75]
X3ES7NUAFine. I'll use ormolu. At least it doesn't break the code. - [76]
M4PWY5RUPreliminary work to add support for Zcash payments. - [77]
IPG33FAWAdd billing daemon - [78]
NEDDHXUKReformat via stylish-haskell - [79]
JUFBTX45Add project auction queries. - [80]
7VGYLTMUClean up schema version handling. - [81]
DFOBMSAOInitial work on payments API - [82]
WAIX6AGNAdd event serialization for PaymentRequest & Payment - [83]
AWWC6P5ZAdd migration to include payment network with addresses. - [84]
A6HKMINBAttempting to improve JSON handling. - [85]
4R7XIYK3Switch from ClassyPrelude to Relude - [86]
4FDQGIXNMake payment request retrieval key an opaque 32-bit hash. - [87]
GKLIPHR5Fix error in parsing of event metadata - [88]
UWMGUJOWAutoformat sources. - [*]
LEINLS3XUpdate deployment documentation. - [*]
RB2ETNIFAdd skeletal PureScript client project. - [*]
4ZLEDBK7Initial attempts at dockerizing, cabal isn't cooperating. - [*]
IZEVQF62Work in progress replacing sqlite with postgres. - [*]
2WOOGXDHUse dbmigrations to manage database state. - [*]
ADMKQQGCInitial empty Snap project. - [*]
EMVTF2IWWIP moving back to snap.
Change contents
- replacement in Makefile at line 6
build-image:build-server-image: - replacement in Makefile at line 9
deploy-image: build-imagebuild-client-image:docker build -t aftok/aftok-client:latest -f ./client/Dockerfile .build-images: build-server-image build-client-imagedeploy-images: build-server-image build-client-image - edit in Makefile at line 18[5.204]
docker tag aftok/aftok-client:latest aftok/aftok-client:$(VERSION)docker push docker.io/aftok/aftok-client:latestdocker push docker.io/aftok/aftok-client:$(VERSION) - edit in aftok.cabal at line 163
, Aftok.Snaplet.Json - edit in aftok.cabal at line 209
, unordered-containers - file addition: Dockerfile[91.1]
FROM ubuntu:focalMAINTAINER Kris Nuttycombe <kris@aftok.com>ENV LANG C.UTF-8ENV TZ America/DenverRUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone# Install build tools & library dependenciesRUN apt-get update && \apt-get install -y --no-install-recommends \libtinfo5 nodejs npm netbaseRUN apt-get install -y --no-install-recommends ca-certificatesRUN update-ca-certificatesRUN mkdir -p /opt/aftok/clientWORKDIR /opt/aftok/clientADD ./client/package.json /opt/aftok/client/package.jsonRUN npm installENV PATH="./node_modules/.bin:${PATH}"# Add static assetsADD ./aftok.com/src/assets /opt/aftok/aftok.com/src/assetsADD ./client/dev /opt/aftok/client/devRUN mkdir -p /opt/aftok/client/prod && \ln -s /opt/aftok/aftok.com/src/assets /opt/aftok/client/prod/assets# Add purescript build config & sourcesADD ./client/spago.dhall /opt/aftok/client/spago.dhallADD ./client/packages.dhall /opt/aftok/client/packages.dhallADD ./client/src /opt/aftok/client/srcRUN npm run build-prod# Add dist-volume directory for use with docker-compose sharing# of client executables via volumes.ADD ./docker/aftok-client-cp.sh /opt/aftok/RUN chmod 700 /opt/aftok/aftok-client-cp.shRUN mkdir /opt/aftok/client/dist-volume - edit in client/src/Aftok/Api/Timeline.purs at line 10
import Data.Argonaut.Core (Json) - replacement in client/src/Aftok/Api/Timeline.purs at line 21
import Type.Proxy (Proxy(..))import Foreign.Object (Object)-- import Type.Proxy (Proxy(..)) - replacement in client/src/Aftok/Api/Timeline.purs at line 35
import Aftok.Types (APIError, JsonCompose, decompose, parseDatedResponse)-- import Aftok.Types (APIError, JsonCompose, decompose, parseDatedResponse)import Aftok.Types (APIError, decompose, parseDatedResponse) - replacement in client/src/Aftok/Api/Timeline.purs at line 47
data Event' i= StartEvent i| StopEvent idata Event t= StartEvent t| StopEvent t - replacement in client/src/Aftok/Api/Timeline.purs at line 51
type Event = Event' InstanteventTime :: forall i. Event i -> ieventTime = case _ ofStartEvent t -> tStopEvent t -> t - replacement in client/src/Aftok/Api/Timeline.purs at line 56
derive instance eventFunctor :: Functor Event'instance showEvent :: (Show i) => Show (Event i) whereshow = case _ ofStartEvent t -> "Start " <> show tStopEvent t -> "Stop " <> show t - replacement in client/src/Aftok/Api/Timeline.purs at line 61
instance eventFoldable :: Foldable Event' wherederive instance eventFunctor :: Functor Eventinstance eventFoldable :: Foldable Event where - replacement in client/src/Aftok/Api/Timeline.purs at line 72
instance eventTraversable :: Traversable Event' whereinstance eventTraversable :: Traversable Event where - replacement in client/src/Aftok/Api/Timeline.purs at line 74
StartEvent a -> StartEvent <$> f aStopEvent a -> StopEvent <$> f aStartEvent a -> StartEvent <$> f aStopEvent a -> StopEvent <$> f a - replacement in client/src/Aftok/Api/Timeline.purs at line 78
instance decodeJsonEvent :: DecodeJson (Event' String) whereparseEventFields :: Object Json -> Either String (Event String)parseEventFields obj = doev <- obj .: "event"start' <- traverse (_ .: "eventTime") =<< ev .:? "start"stop' <- traverse (_ .: "eventTime") =<< ev .:? "stop"note "Only 'stop' and 'start' events are supported." $(StartEvent <$> start') <|>(StopEvent <$> stop')instance eventDecodeJSON :: DecodeJson (Event String) wheredecodeJson = parseEventFields <=< decodeJsonnewtype KeyedEvent i = KeyedEvent{ eventId :: String, event :: Event i}keyedEvent :: forall i. String -> Event i -> KeyedEvent ikeyedEvent eid ev = KeyedEvent { eventId: eid, event: ev }eventId :: forall i. KeyedEvent i -> StringeventId (KeyedEvent xs) = xs.eventIdevent :: forall i. KeyedEvent i -> Event ievent (KeyedEvent xs) = xs.eventderive instance keyedEventFunctor :: Functor KeyedEventinstance keyedEventFoldable :: Foldable KeyedEvent wherefoldr f b = foldr f b <<< eventfoldl f b = foldl f b <<< eventfoldMap = foldMapDefaultRinstance keyedEventTraversable :: Traversable KeyedEvent wheretraverse f (KeyedEvent xs) = (\ev -> KeyedEvent { eventId: xs.eventId, event: ev }) <$> traverse f xs.eventsequence = traverse identityinstance keyedEventDecodeJson :: DecodeJson (KeyedEvent String) where - replacement in client/src/Aftok/Api/Timeline.purs at line 118
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')keyedEvent <$> obj .: "eventId" <*> parseEventFields obj - replacement in client/src/Aftok/Api/Timeline.purs at line 120
newtype Interval' i = Intervalnewtype Interval i = Interval - replacement in client/src/Aftok/Api/Timeline.purs at line 125
derive instance intervalEq :: (Eq i) => Eq (Interval' i)derive instance intervalNewtype :: Newtype (Interval' i) _derive instance intervalEq :: (Eq i) => Eq (Interval i)derive instance intervalNewtype :: Newtype (Interval i) _ - replacement in client/src/Aftok/Api/Timeline.purs at line 128
instance showInterval :: Show i => Show (Interval' i) whereinstance showInterval :: Show i => Show (Interval i) where - replacement in client/src/Aftok/Api/Timeline.purs at line 131
type Interval = Interval' Instanttype TimeInterval = Interval Instant - replacement in client/src/Aftok/Api/Timeline.purs at line 133
derive instance intervalFunctor :: Functor Interval'derive instance intervalFunctor :: Functor Interval - replacement in client/src/Aftok/Api/Timeline.purs at line 135
instance intervalFoldable :: Foldable Interval' wherefoldr f b (Interval i) = f i.start (f i.end b)foldl f b (Interval i) = f (f b i.start) i.endinstance intervalFoldable :: Foldable Interval wherefoldr f b (Interval i) = f i.start (f i.end b)foldl f b (Interval i) = f (f b i.start) i.end - replacement in client/src/Aftok/Api/Timeline.purs at line 140
instance intervalTraversable :: Traversable Interval' wheretraverse f (Interval i) = interval <$> f i.start <*> f i.endinstance intervalTraversable :: Traversable Interval wheretraverse f (Interval i) = interval <$> f i.start <*> f i.end - replacement in client/src/Aftok/Api/Timeline.purs at line 144
instance decodeJsonInterval :: DecodeJson (Interval' String) whereinstance intervalDecodeJSON :: DecodeJson i => DecodeJson (Interval i) where - replacement in client/src/Aftok/Api/Timeline.purs at line 149
interval :: forall i. i -> i -> Interval' iinterval :: forall i. i -> i -> Interval i - replacement in client/src/Aftok/Api/Timeline.purs at line 152
start :: forall i. Interval' i -> istart :: forall i. Interval i -> i - replacement in client/src/Aftok/Api/Timeline.purs at line 155
end :: forall i. Interval' i -> iend :: forall i. Interval i -> i - replacement in client/src/Aftok/Api/Timeline.purs at line 160
| During (Interval' t)| During (Interval t) - replacement in client/src/Aftok/Api/Timeline.purs at line 184
apiLogStart :: ProjectId -> Aff (Either TimelineError Instant)apiLogStart :: ProjectId -> Aff (Either TimelineError (KeyedEvent Instant)) - replacement in client/src/Aftok/Api/Timeline.purs at line 189
event <- withExceptT LogFailure $ parseDatedResponse responsecase event ofStartEvent t -> pure tStopEvent _ -> throwError <<< Unexpected $ "Expected start event, got stop."kev <- withExceptT LogFailure $ parseDatedResponse responsecase event kev ofStartEvent _ -> pure kevStopEvent _ -> throwError <<< Unexpected $ "Expected start event, got stop." - replacement in client/src/Aftok/Api/Timeline.purs at line 194
apiLogEnd :: ProjectId -> Aff (Either TimelineError Instant)apiLogEnd :: ProjectId -> Aff (Either TimelineError (KeyedEvent Instant)) - replacement in client/src/Aftok/Api/Timeline.purs at line 199
event <- withExceptT LogFailure $ parseDatedResponse responsecase event ofkev <- withExceptT LogFailure $ parseDatedResponse responsecase event kev of - replacement in client/src/Aftok/Api/Timeline.purs at line 202
StopEvent t -> pure tStopEvent _ -> pure kev - replacement in client/src/Aftok/Api/Timeline.purs at line 205
{ workIndex :: Array ({ intervals :: Array a }){ workIndex :: Array ({ intervals :: Array a }) - replacement in client/src/Aftok/Api/Timeline.purs at line 217
traverse f (ListIntervalsResponse r) =traverse f (ListIntervalsResponse r) = - edit in client/src/Aftok/Api/Timeline.purs at line 224
_ListIntervalsResponse :: Proxy (JsonCompose ListIntervalsResponse Interval' String)_ListIntervalsResponse = Proxy - replacement in client/src/Aftok/Api/Timeline.purs at line 225
apiListIntervals :: ProjectId -> TimeSpan -> Aff (Either TimelineError (Array Interval))apiListIntervals :: ProjectId -> TimeSpan -> Aff (Either TimelineError (Array (Interval (KeyedEvent Instant)))) - replacement in client/src/Aftok/Api/Timeline.purs at line 233
liftEffect<<< runExceptTliftEffect<<< runExceptT - replacement in client/src/Aftok/Api/Timeline.purs at line 236
<<< map decompose<<< withExceptT LogFailure<<< map (map decompose <<< decompose)<<< withExceptT LogFailure - replacement in client/src/Aftok/Api/Timeline.purs at line 240
apiLatestEvent :: ProjectId -> Aff (Either TimelineError (Maybe Event))apiLatestEvent :: ProjectId -> Aff (Either TimelineError (Maybe (KeyedEvent Instant))) - replacement in client/src/Aftok/Api/Timeline.purs at line 243
liftEffect<<< runExceptTliftEffect<<< runExceptT - replacement in client/src/Aftok/Api/Timeline.purs at line 247
<<< withExceptT LogFailure<<< withExceptT LogFailure - replacement in client/src/Aftok/Timeline.purs at line 11
import Data.Array (reverse)import Data.Date as Dimport Data.Array (reverse, cons) - replacement in client/src/Aftok/Timeline.purs at line 23
import Data.Time.Duration (Milliseconds(..), Days(..))import Data.Time.Duration (Milliseconds(..), Hours(..), Days(..)) - replacement in client/src/Aftok/Timeline.purs at line 51
import Aftok.Api.Timeline (TimelineError, Interval'(..), Interval, TimeSpan, start, end, interval)import Aftok.Api.Timeline( TimelineError,Event(..),Interval(..),TimeInterval,KeyedEvent,TimeSpan,start, end, interval,event, eventTime, keyedEvent) - replacement in client/src/Aftok/Timeline.purs at line 66
{ bounds :: Interval{ bounds :: TimeInterval - edit in client/src/Aftok/Timeline.purs at line 70
data TimelineEvent= LoggedEvent (KeyedEvent Instant)| PhantomEvent Instantinstance showTimelineEvent :: Show TimelineEvent whereshow = case _ ofLoggedEvent kev -> "Real event at " <> show (event kev)PhantomEvent i -> "Phantom at " <> show itlEventTime :: TimelineEvent -> InstanttlEventTime = case _ ofLoggedEvent kev -> eventTime <<< event $ kevPhantomEvent i -> i - replacement in client/src/Aftok/Timeline.purs at line 85
{ dayBounds :: Interval, loggedIntervals :: Array Interval{ dayBounds :: TimeInterval, loggedIntervals :: Array (Interval TimelineEvent) - replacement in client/src/Aftok/Timeline.purs at line 94
, active :: Maybe Interval, active :: Maybe (Interval TimelineEvent) - replacement in client/src/Aftok/Timeline.purs at line 115[5.4145]→[5.4145:4207](∅→∅),[5.4207]→[5.3336:3396](∅→∅),[5.3336]→[5.3336:3396](∅→∅),[5.3396]→[5.4208:4296](∅→∅),[5.4296]→[5.605:682](∅→∅)
, logStart :: ProjectId -> m (Either TimelineError Instant), logEnd :: ProjectId -> m (Either TimelineError Instant), listIntervals :: ProjectId -> TimeSpan -> m (Either TimelineError (Array Interval)), getLatestEvent :: ProjectId -> m (Either TimelineError (Maybe TL.Event)), logStart :: ProjectId -> m (Either TimelineError (KeyedEvent Instant)), logEnd :: ProjectId -> m (Either TimelineError (KeyedEvent Instant)), listIntervals :: ProjectId -> TimeSpan -> m (Either TimelineError (Array (Interval (KeyedEvent Instant)))), getLatestEvent :: ProjectId -> m (Either TimelineError (Maybe (KeyedEvent Instant))) - replacement in client/src/Aftok/Timeline.purs at line 200
--(system.log $ "Got " <> show (length ivals :: Int) <> " intervals for project " <> pidStr (unwrap p).projectId) *>pure ivalspure $ map (map LoggedEvent) ivals - replacement in client/src/Aftok/Timeline.purs at line 214
let activeInterval :: TL.Event -> m (Maybe Interval)activeInterval ev = case ev oflet activeInterval :: TL.KeyedEvent Instant -> m (Maybe (Interval TimelineEvent))activeInterval ev = case event ev of - replacement in client/src/Aftok/Timeline.purs at line 218
(Just <<< interval i <$> system.now)(Just <<< interval (LoggedEvent ev) <<< PhantomEvent <$> system.now) - replacement in client/src/Aftok/Timeline.purs at line 269
-> Interval-> Array Interval-> TimeInterval-> Array (Interval TimelineEvent) - replacement in client/src/Aftok/Timeline.purs at line 298[5.4066]→[5.7462:7476](∅→∅),[5.304908]→[5.7462:7476](∅→∅),[5.7476]→[5.3373:3387](∅→∅),[5.3373]→[5.3373:3387](∅→∅)
. Interval-> Interval. TimeInterval-> Interval TimelineEvent - replacement in client/src/Aftok/Timeline.purs at line 304
ileft = ilen limits.start i.startiwidth = ilen i.start i.endileft = ilen limits.start (tlEventTime i.start)iwidth = ilen (tlEventTime i.start) (tlEventTime i.end) - replacement in client/src/Aftok/Timeline.purs at line 308
put $ toPct (ilen limits.start i.end)put $ toPct (ilen limits.start (tlEventTime i.end)) - replacement in client/src/Aftok/Timeline.purs at line 328
updateStart :: Instant -> TimelineState -> TimelineStateupdateStart t s =s { active = s.active <|> Just (TL.interval t t) }updateStart :: KeyedEvent Instant -> TimelineState -> TimelineStateupdateStart ev s =s { active = s.active <|> Just (TL.interval (LoggedEvent ev) (PhantomEvent <<< eventTime <<< event $ ev)) } - replacement in client/src/Aftok/Timeline.purs at line 336
-> Instant-> KeyedEvent Instant - replacement in client/src/Aftok/Timeline.purs at line 339
updateStop system t st = donewHistory <- join <$> traverse (\i -> runMaybeT $ toHistory system [TL.interval (start i) t]) st.activeupdateStop system ev st = dolet updateHistory i = runMaybeT $ toHistory system [TL.interval (start i) (LoggedEvent ev)]newHistory <- join <$> traverse updateHistory st.active - replacement in client/src/Aftok/Timeline.purs at line 350
s { active = map (\(Interval i) -> TL.interval i.start t) s.actives { active = map (\i -> TL.interval (start i) (PhantomEvent t)) s.active - replacement in client/src/Aftok/Timeline.purs at line 370
, logStart: \_ -> Right <$> liftEffect now, logEnd: \_ -> Right <$> liftEffect now, logStart: \_ -> Right <<< keyedEvent "" <<< StartEvent <$> liftEffect now, logEnd: \_ -> Right <<< keyedEvent "" <<< StopEvent <$> liftEffect now - replacement in client/src/Aftok/Timeline.purs at line 376
utcDayBounds :: Instant -> IntervalutcDayBounds :: Instant -> TimeInterval - replacement in client/src/Aftok/Timeline.purs at line 388
-> MaybeT m (Tuple Date Interval)-> MaybeT m (Tuple Date TimeInterval) - replacement in client/src/Aftok/Timeline.purs at line 391
end <- MaybeT <<< pure $ fromDateTime <$> DT.adjust (Days 1.0) (toDateTime start)nextNoon <- MaybeT <<< pure $fromDateTime <$> (DT.adjust (Hours 12.0) <=< DT.adjust (Days 1.0) $(toDateTime start))Tuple _ end <- MaybeT $ system.dateFFI.midnightLocal nextNoon - edit in client/src/Aftok/Timeline.purs at line 398
incrementDayBounds :: Tuple Date Interval -> Maybe (Tuple Date Interval)incrementDayBounds (Tuple d i) =let nextEnd = fromDateTime <$> (DT.adjust (Days 1.0) $ toDateTime (end i))in Tuple <$> D.adjust (Days 1.0) d<*> (interval (end i) <$> nextEnd) - replacement in client/src/Aftok/Timeline.purs at line 403
-> Interval-> Interval TimelineEvent - replacement in client/src/Aftok/Timeline.purs at line 406
--lift <<< system.log $ "Splitting interval " <> show idayBounds@(Tuple date bounds) <- localDayBounds system (start i)split <- if end i < (end bounds)then dopure [Tuple date { dayBounds: bounds, loggedIntervals: [i] }]else dolet firstFragment = [ Tuple date { dayBounds: bounds, loggedIntervals: [interval (start i) (end bounds)]} ]append firstFragment <$> splitInterval system (interval (end bounds) (end i))lift <<< system.log $ "Splitting interval " <> show i-- day bounds are based on the start event.Tuple date bounds <- localDayBounds system (tlEventTime $ start i)lift <<< system.log $ "Splitting on day bounds: " <> show (start bounds) <> " to " <> show (end bounds)split <- if tlEventTime (end i) < end boundsthen dolift <<< system.log $ "Split complete"pure [Tuple date { dayBounds: bounds, loggedIntervals: [i] }]else dolet splitEvent = PhantomEvent (end bounds)currInterval = Tuple date { dayBounds: bounds, loggedIntervals: [interval (start i) splitEvent] }nextInterval = interval splitEvent (end i)lift <<< system.log $ "Split required; first fragment: " <> show currInterval <> "; next interval: " <> show nextIntervalcons currInterval <$> splitInterval system nextInterval - replacement in client/src/Aftok/Timeline.purs at line 427
-> Array Interval-> Array (Interval TimelineEvent) - replacement in client/src/Aftok/Timeline.purs at line 430
splitIntervals <- join <$> traverse (splitInterval system) xspure $ M.fromFoldableWith unionDayIntervals splitIntervalssplits <- join <$> traverse (splitInterval system) xspure $ M.fromFoldableWith unionDayIntervals splits - file addition: aftok-client-cp.sh[92.1510]
#!/bin/bashecho "Copying client build artifacts to mounted volume..."cp -r /opt/aftok/client/dist/* /opt/aftok/client/dist-volumeecho "Client copy complete. The container will now shut down." - edit in docker-compose.yml at line 11
- zcashd - edit in docker-compose.yml at line 24
aftok-client:image: aftok/aftok-client:latestcontainer_name: aftok-cliententrypoint: /opt/aftok/aftok-client-cp.shvolumes:- type: volumesource: v_aftok-clienttarget: /opt/aftok/client/dist-volume - replacement in docker-compose.yml at line 58
source: ./client/dist/source: ./client/dist - edit in docker-compose.yml at line 61
# - type: volume# source: v_aftok-client# target: /opt/static/app# read_only: true - replacement in docker-compose.yml at line 90[5.2945]→[5.2945:3028](∅→∅),[5.3028]→[5.312:340](∅→∅),[5.340]→[5.3028:3039](∅→∅),[5.3028]→[5.3028:3039](∅→∅),[5.3039]→[2.474:496](∅→∅),[5.363]→[5.3059:3091](∅→∅),[2.496]→[5.3059:3091](∅→∅),[5.3059]→[5.3059:3091](∅→∅),[5.3091]→[2.497:539](∅→∅),[2.539]→[5.3138:3192](∅→∅),[5.3138]→[5.3138:3192](∅→∅),[5.3192]→[2.540:584](∅→∅),[2.584]→[5.3241:3283](∅→∅),[5.3241]→[5.3241:3283](∅→∅),[5.3283]→[2.585:609](∅→∅),[5.521]→[5.24689:24721](∅→∅),[2.609]→[5.24689:24721](∅→∅),[5.3283]→[5.24689:24721](∅→∅),[5.24689]→[5.24689:24721](∅→∅)
zcashd:image: electriccoinco/zcashd:v4.0.0container_name: aftok-zcashdexpose:- "18232"ports:- "18233:18233"volumes:- type: bindsource: ./local/zcashd/zcash-datatarget: /srv/zcashd/.zcash- type: bindsource: ./local/zcashd/zcash-paramstarget: /srv/zcashd/.zcash-paramsread_only: truenetworks:- back-tier# zcashd:# image: electriccoinco/zcashd:v4.0.0# container_name: aftok-zcashd# expose:# - "18232"# ports:# - "18233:18233"# volumes:# - type: bind# source: ./local/zcashd/zcash-data# target: /srv/zcashd/.zcash# - type: bind# source: ./local/zcashd/zcash-params# target: /srv/zcashd/.zcash-params# read_only: true# networks:# - back-tier - edit in docker-compose.yml at line 110
v_aftok-client: - edit in lib/Aftok/Database/PostgreSQL/Events.hs at line 17
import qualified Aftok.Billing as B - replacement in lib/Aftok/Database/PostgreSQL/Events.hs at line 26
KeyedLogEntry,KeyedLogEntry(KeyedLogEntry), - edit in lib/Aftok/Database/PostgreSQL/Events.hs at line 28
logEntry,workId, - edit in lib/Aftok/Database/PostgreSQL/Events.hs at line 40
pexec, - edit in lib/Aftok/Database/PostgreSQL/Events.hs at line 43
ptransact, - replacement in lib/Aftok/Database/PostgreSQL/Events.hs at line 49
createSubscriptionJSON,idValue,obj,v1, - replacement in lib/Aftok/Database/PostgreSQL/Events.hs at line 54
import Aftok.TimeLogimport Aftok.TimeLog (WorkIndex,LogEntry(LogEntry),LogEvent(..),EventId(..),EventAmendment(..),AmendmentId(..),eventMeta,_ModTime,_EventId,_AmendmentId,creditTo,eventTime,event,workIndex,eventName,nameEvent,) - replacement in lib/Aftok/Database/PostgreSQL/Events.hs at line 73
import Control.Lens ((^.), _Just, preview)import Control.Lens ((^.), _Just, preview, set, view) - replacement in lib/Aftok/Database/PostgreSQL/Events.hs at line 76
( Value,( (.=),Value, - replacement in lib/Aftok/Database/PostgreSQL/Events.hs at line 113
(,,) <$> idParser ProjectId <*> idParser UserId <*> logEntryParserKeyedLogEntry <$> idParser EventId <*> logEntryParser - edit in lib/Aftok/Database/PostgreSQL/Events.hs at line 138
createSubscriptionJSON :: UserId -> B.BillableId -> Day -> ValuecreateSubscriptionJSON uid bid d =v1 $obj[ "user_id" .= idValue _UserId uid,"billable_id" .= idValue B._BillableId bid,"start_date" .= showGregorian d] - replacement in lib/Aftok/Database/PostgreSQL/Events.hs at line 209
findEvent :: EventId -> DBM (Maybe KeyedLogEntry)findEvent :: EventId -> DBM (Maybe (ProjectId, UserId, KeyedLogEntry)) - replacement in lib/Aftok/Database/PostgreSQL/Events.hs at line 213
keyedLogEntryParser[sql| SELECT project_id, user_id,((,,) <$> idParser ProjectId <*> idParser UserId <*> keyedLogEntryParser)[sql| SELECT project_id, user_id, id, - replacement in lib/Aftok/Database/PostgreSQL/Events.hs at line 216
event_type, event_time, event_metadata FROM work_eventsWHERE id = ? |]event_type, event_time, event_metadataFROM work_eventsWHERE id = ?AND replacement_id IS NULL|] - replacement in lib/Aftok/Database/PostgreSQL/Events.hs at line 223
findEvents :: ProjectId -> UserId -> RangeQuery -> Limit -> DBM [LogEntry]findEvents :: ProjectId -> UserId -> RangeQuery -> Limit -> DBM [KeyedLogEntry] - replacement in lib/Aftok/Database/PostgreSQL/Events.hs at line 228
logEntryParser[sql| SELECT credit_to_type,credit_to_account, credit_to_user_id, credit_to_project_id,event_type, event_time,event_metadataFROM work_eventsWHERE project_id = ? AND user_id = ? AND event_time <= ?ORDER BY event_time DESCLIMIT ?|]keyedLogEntryParser[sql| SELECT id, credit_to_type,credit_to_account, credit_to_user_id, credit_to_project_id,event_type, event_time,event_metadataFROM work_eventsWHERE project_id = ? AND user_id = ? AND event_time <= ?AND replacement_id IS NULLORDER BY event_time DESCLIMIT ?|] - replacement in lib/Aftok/Database/PostgreSQL/Events.hs at line 242
logEntryParser[sql| SELECT credit_to_type,credit_to_account, credit_to_user_id, credit_to_project_id,event_type, event_time, event_metadataFROM work_eventsWHERE project_id = ? AND user_id = ?AND event_time >= ? AND event_time <= ?ORDER BY event_time DESCLIMIT ?|]keyedLogEntryParser[sql| SELECT id, credit_to_type,credit_to_account, credit_to_user_id, credit_to_project_id,event_type, event_time, event_metadataFROM work_eventsWHERE project_id = ? AND user_id = ?AND replacement_id IS NULLAND event_time >= ? AND event_time <= ?ORDER BY event_time DESCLIMIT ?|] - replacement in lib/Aftok/Database/PostgreSQL/Events.hs at line 256
logEntryParser[sql| SELECT credit_to_type,credit_to_account, credit_to_user_id, credit_to_project_id,event_type, event_time, event_metadataFROM work_eventsWHERE project_id = ? AND user_id = ? AND event_time >= ?ORDER BY event_time DESCLIMIT ?|]keyedLogEntryParser[sql| SELECT id, credit_to_type,credit_to_account, credit_to_user_id, credit_to_project_id,event_type, event_time, event_metadataFROM work_eventsWHERE project_id = ? AND user_id = ? AND event_time >= ?AND replacement_id IS NULLORDER BY event_time DESCLIMIT ?|] - replacement in lib/Aftok/Database/PostgreSQL/Events.hs at line 269
logEntryParser[sql| SELECT credit_to_type,credit_to_account, credit_to_user_id, credit_to_project_id,event_type, event_time, event_metadataFROM work_eventsWHERE project_id = ? AND user_id = ?ORDER BY event_time DESCLIMIT ?|]keyedLogEntryParser[sql| SELECT id, credit_to_type,credit_to_account, credit_to_user_id, credit_to_project_id,event_type, event_time, event_metadataFROM work_eventsWHERE project_id = ? AND user_id = ?AND replacement_id IS NULLORDER BY event_time DESCLIMIT ?|] - edit in lib/Aftok/Database/PostgreSQL/Events.hs at line 280
amendEvent :: EventId -> EventAmendment -> DBM AmendmentIdamendEvent (EventId eid) = \case(TimeChange mt t) ->pinsertAmendmentId[sql| INSERT INTO event_time_amendments(event_id, amended_at, event_time)VALUES (?, ?, ?) RETURNING id |](eid, fromThyme $ mt ^. _ModTime, fromThyme t)(CreditToChange mt c@(CreditToAccount acctId)) ->pinsertAmendmentId[sql| INSERT INTO event_credit_to_amendments(event_id, amended_at, credit_to_type, credit_to_account)VALUES (?, ?, ?, ?) RETURNING id |](eid, fromThyme $ mt ^. _ModTime, creditToName c, acctId ^. _AccountId)(CreditToChange mt c@(CreditToProject pid)) ->pinsertAmendmentId[sql| INSERT INTO event_credit_to_amendments(event_id, amended_at, credit_to_type, credit_to_project_id)VALUES (?, ?, ?, ?) RETURNING id |](eid, fromThyme $ mt ^. _ModTime, creditToName c, pid ^. _ProjectId)(CreditToChange mt c@(CreditToUser uid)) ->pinsertAmendmentId[sql| INSERT INTO event_credit_to_amendments(event_id, amended_at, credit_to_type, credit_to_user_id)VALUES (?, ?, ?, ?) RETURNING id |](eid, fromThyme $ mt ^. _ModTime, creditToName c, uid ^. _UserId)(MetadataChange mt v) ->pinsertAmendmentId[sql| INSERT INTO event_metadata_amendments(event_id, amended_at, event_metadata)VALUES (?, ?, ?) RETURNING id |](eid, fromThyme $ mt ^. _ModTime, v) - replacement in lib/Aftok/Database/PostgreSQL/Events.hs at line 281
readWorkIndex :: ProjectId -> DBM WorkIndexreadWorkIndex :: ProjectId -> DBM (WorkIndex KeyedLogEntry) - replacement in lib/Aftok/Database/PostgreSQL/Events.hs at line 285
logEntryParser[sql| SELECT credit_to_type,keyedLogEntryParser[sql| SELECT id, credit_to_type, - replacement in lib/Aftok/Database/PostgreSQL/Events.hs at line 292
pure $ workIndex logEntries[5.42823]pure $ workIndex (view logEntry) logEntriesamendEvent :: ProjectId -> UserId -> KeyedLogEntry -> EventAmendment -> DBM (EventId, AmendmentId)amendEvent pid uid kle amendment = ptransact $ do(amendId, replacement, amend_t :: Text) <- amendnewEventId <- createEvent pid uid (replacement ^. logEntry)void $pexec[sql| UPDATE work_eventsSET replacement_id = ?, amended_by_id = ?, amended_by_type = ?WHERE id = ? |](newEventId ^. _EventId, amendId ^. _AmendmentId, amend_t, kle ^. workId . _EventId)pure (newEventId, amendId)whereamend = case amendment of(TimeChange mt t) -> doaid <-pinsertAmendmentId[sql| INSERT INTO event_time_amendments(work_event_id, amended_at, event_time)VALUES (?, ?, ?) RETURNING id |](kle ^. workId . _EventId, fromThyme $ mt ^. _ModTime, fromThyme t)pure (aid, set (logEntry . event . eventTime) t kle, "amend_event_time")(CreditToChange mt c@(CreditToAccount acctId)) -> doaid <-pinsertAmendmentId[sql| INSERT INTO event_credit_to_amendments(work_event_id, amended_at, credit_to_type, credit_to_account)VALUES (?, ?, ?, ?) RETURNING id |](kle ^. workId . _EventId, fromThyme $ mt ^. _ModTime, creditToName c, acctId ^. _AccountId)pure (aid, set (logEntry . creditTo) c kle, "amend_credit_to")(CreditToChange mt c@(CreditToProject cpid)) -> doaid <-pinsertAmendmentId[sql| INSERT INTO event_credit_to_amendments(work_event_id, amended_at, credit_to_type, credit_to_project_id)VALUES (?, ?, ?, ?) RETURNING id |](kle ^. workId . _EventId, fromThyme $ mt ^. _ModTime, creditToName c, cpid ^. _ProjectId)pure (aid, set (logEntry . creditTo) c kle, "amend_credit_to")(CreditToChange mt c@(CreditToUser cuid)) -> doaid <-pinsertAmendmentId[sql| INSERT INTO event_credit_to_amendments(work_event_id, amended_at, credit_to_type, credit_to_user_id)VALUES (?, ?, ?, ?) RETURNING id |](kle ^. workId . _EventId, fromThyme $ mt ^. _ModTime, creditToName c, cuid ^. _UserId)pure (aid, set (logEntry . creditTo) c kle, "amend_credit_to")(MetadataChange mt v) -> doaid <-pinsertAmendmentId[sql| INSERT INTO event_metadata_amendments(work_event_id, amended_at, event_metadata)VALUES (?, ?, ?) RETURNING id |](kle ^. workId . _EventId, fromThyme $ mt ^. _ModTime, v)pure (aid, set (logEntry . eventMeta) (Just v) kle, "amend_metadata") - replacement in lib/Aftok/Database/PostgreSQL.hs at line 48
(AmendEvent eid amendment) -> Q.amendEvent eid amendment(AmendEvent pid uid kle amendment) -> Q.amendEvent pid uid kle amendment - edit in lib/Aftok/Database.hs at line 25
import qualified Aftok.TimeLog as TL - edit in lib/Aftok/Database.hs at line 32
HasLogEntry, - edit in lib/Aftok/Database.hs at line 45
makeClassy, - edit in lib/Aftok/Database.hs at line 56
data KeyedLogEntry = KeyedLogEntry {_workId :: !EventId,_logEntry :: !LogEntry}makeClassy ''KeyedLogEntry - replacement in lib/Aftok/Database.hs at line 64
type KeyedLogEntry = (ProjectId, UserId, LogEntry)instance HasLogEntry KeyedLogEntry wherelogEntry = Aftok.Database.logEntry - replacement in lib/Aftok/Database.hs at line 90[5.64627]→[5.64627:64742](∅→∅),[5.64742]→[5.2079:2157](∅→∅),[5.2157]→[5.64821:64868](∅→∅),[5.64821]→[5.64821:64868](∅→∅)
AmendEvent :: EventId -> EventAmendment -> DBOp AmendmentIdFindEvent :: EventId -> DBOp (Maybe KeyedLogEntry)FindEvents :: ProjectId -> UserId -> RangeQuery -> Limit -> DBOp [LogEntry]ReadWorkIndex :: ProjectId -> DBOp WorkIndexAmendEvent :: ProjectId -> UserId -> KeyedLogEntry -> EventAmendment -> DBOp (EventId, AmendmentId)FindEvent :: EventId -> DBOp (Maybe (ProjectId, UserId, KeyedLogEntry))FindEvents :: ProjectId -> UserId -> RangeQuery -> Limit -> DBOp [KeyedLogEntry]ReadWorkIndex :: ProjectId -> DBOp (WorkIndex KeyedLogEntry) - replacement in lib/Aftok/Database.hs at line 260
(MonadDB m) => UserId -> EventId -> EventAmendment -> m AmendmentId(MonadDB m) => UserId -> EventId -> EventAmendment -> m (EventId, AmendmentId) - replacement in lib/Aftok/Database.hs at line 262[5.8790]→[5.8790:8812](∅→∅),[5.8812]→[5.29734:29763](∅→∅),[5.29181]→[5.6524:6586](∅→∅),[5.29763]→[5.6524:6586](∅→∅),[5.8841]→[5.6524:6586](∅→∅),[5.6586]→[5.29764:29901](∅→∅)
ev <- findEvent eidlet act = AmendEvent eid aforbidden = raiseOpForbidden uid UserNotEventLogger actmissing = raiseSubjectNotFound actmaybemissing(\(_, uid', _) -> if uid' == uid then liftdb act else forbidden)evevMay <- findEvent eidmaybe missing saveAmendment evMaywheremissing = raiseSubjectNotFound (FindEvent eid)saveAmendment (pid, uid', le) =let act = AmendEvent pid uid le ain if uid' == uidthen liftdb actelse raiseOpForbidden uid UserNotEventLogger act - replacement in lib/Aftok/Database.hs at line 272
findEvent :: (MonadDB m) => EventId -> m (Maybe KeyedLogEntry)findEvent :: (MonadDB m) => EventId -> m (Maybe (ProjectId, UserId, KeyedLogEntry)) - replacement in lib/Aftok/Database.hs at line 281
m [LogEntry]m [KeyedLogEntry] - replacement in lib/Aftok/Database.hs at line 284
readWorkIndex :: (MonadDB m) => ProjectId -> UserId -> m WorkIndexreadWorkIndex :: (MonadDB m) => ProjectId -> UserId -> m (WorkIndex KeyedLogEntry) - edit in lib/Aftok/Interval.hs at line 1
{-# LANGUAGE DeriveFoldable #-}{-# LANGUAGE DeriveFunctor #-}{-# LANGUAGE DeriveTraversable #-} - replacement in lib/Aftok/Interval.hs at line 33
data Intervaldata Interval t - replacement in lib/Aftok/Interval.hs at line 35
{ _start :: C.UTCTime,_end :: C.UTCTime{ _start :: t,_end :: t - replacement in lib/Aftok/Interval.hs at line 38
deriving (Show, Eq, Ord)deriving (Show, Eq, Ord, Functor, Foldable, Traversable) - replacement in lib/Aftok/Interval.hs at line 50
interval :: C.UTCTime -> C.UTCTime -> Intervalinterval :: Ord t => t -> t -> Interval t - replacement in lib/Aftok/Interval.hs at line 56
containsInclusive :: C.UTCTime -> Interval -> BoolcontainsInclusive :: Ord t => t -> Interval t -> Bool - replacement in lib/Aftok/Interval.hs at line 59
ilen :: Interval -> C.NominalDiffTimeilen :: Interval C.UTCTime -> C.NominalDiffTime - replacement in lib/Aftok/Interval.hs at line 62
intervalJSON :: Interval -> ValueintervalJSON ival = object ["start" .= (ival ^. start), "end" .= (ival ^. end)]intervalJSON :: (t -> Value) -> Interval t -> ValueintervalJSON f ival = object ["start" .= f (ival ^. start), "end" .= f (ival ^. end)] - replacement in lib/Aftok/Interval.hs at line 65
parseIntervalJSON :: Value -> Parser IntervalparseIntervalJSON :: (Ord t, FromJSON t) => Value -> Parser (Interval t) - edit in lib/Aftok/Json.hs at line 11
import qualified Aftok.Auction as A - edit in lib/Aftok/Json.hs at line 15
import Aftok.Intervalimport Aftok.Payments.Types( PaymentId,_PaymentId,)import qualified Aftok.Project as P - edit in lib/Aftok/Json.hs at line 22
import qualified Control.Lens as L - edit in lib/Aftok/Json.hs at line 28
import Data.List.NonEmpty as Limport Data.Map.Strict as MS - edit in lib/Aftok/Json.hs at line 30
import Data.Thyme.Calendar (showGregorian) - edit in lib/Aftok/Json.hs at line 31
import Data.Thyme.Time (Day) - replacement in lib/Aftok/Json.hs at line 55
version :: MonadFail m => ByteString -> m Versionversion = fromEitherM fail . PC.parseOnly versionParserparseVersion :: MonadFail m => ByteString -> m VersionparseVersion = fromEitherM fail . PC.parseOnly versionParser - replacement in lib/Aftok/Json.hs at line 70
ver <- version $ C.pack sver <- parseVersion $ C.pack s - edit in lib/Aftok/Json.hs at line 119[5.21864]→[5.1699:1700](∅→∅),[5.1699]→[5.1699:1700](∅→∅),[5.1700]→[5.33600:33656](∅→∅),[5.4321]→[5.33340:33383](∅→∅),[5.33656]→[5.33340:33383](∅→∅),[5.33340]→[5.33340:33383](∅→∅),[5.411]→[5.485:486](∅→∅),[5.807]→[5.485:486](∅→∅),[5.1802]→[5.485:486](∅→∅),[5.2523]→[5.485:486](∅→∅),[5.2559]→[5.485:486](∅→∅),[5.5169]→[5.485:486](∅→∅),[5.5265]→[5.485:486](∅→∅),[5.33383]→[5.485:486](∅→∅),[5.485]→[5.485:486](∅→∅),[5.486]→[5.4322:4415](∅→∅),[5.4415]→[5.4232:4300](∅→∅),[5.1296]→[5.249:286](∅→∅),[5.4300]→[5.249:286](∅→∅),[5.4497]→[5.249:286](∅→∅),[5.33470]→[5.249:286](∅→∅),[5.1693]→[5.249:286](∅→∅),[5.286]→[5.1851:1897](∅→∅),[5.365]→[5.1693:1694](∅→∅),[5.1348]→[5.1693:1694](∅→∅),[5.1897]→[5.1693:1694](∅→∅),[5.1693]→[5.1693:1694](∅→∅),[5.1694]→[5.68674:68708](∅→∅),[5.68708]→[5.33657:33688](∅→∅),[5.5298]→[5.33657:33688](∅→∅),[5.33688]→[5.68709:68807](∅→∅),[5.68807]→[5.33782:33842](∅→∅),[5.33782]→[5.33782:33842](∅→∅),[5.33632]→[5.26515:26516](∅→∅),[5.33842]→[5.26515:26516](∅→∅),[5.1516]→[5.26515:26516](∅→∅),[5.26516]→[5.68808:68858](∅→∅),[5.68858]→[5.4498:4575](∅→∅),[5.26564]→[5.4498:4575](∅→∅),[5.1516]→[5.538:539](∅→∅),[5.4575]→[5.538:539](∅→∅),[5.26622]→[5.538:539](∅→∅),[5.538]→[5.538:539](∅→∅),[5.539]→[5.68859:68945](∅→∅),[5.1568]→[5.481:482](∅→∅),[5.1944]→[5.481:482](∅→∅),[5.68945]→[5.481:482](∅→∅),[5.481]→[5.481:482](∅→∅),[5.482]→[4.5819:5860](∅→∅),[4.5860]→[5.33843:33993](∅→∅),[5.68980]→[5.33843:33993](∅→∅),[5.571]→[5.33843:33993](∅→∅),[5.33993]→[4.5861:6099](∅→∅),[4.6099]→[5.34049:34057](∅→∅),[5.69040]→[5.34049:34057](∅→∅),[5.34049]→[5.34049:34057](∅→∅)
idJSON :: forall a. Text -> Getter a UUID -> a -> ValueidJSON t l a = v1 $ obj [t .= idValue l a]qdbJSON :: Text -> Getter a UUID -> Getter a Value -> a -> ValueqdbJSON name _id _value x =v1 $ obj [(name <> "Id") .= idValue _id x, name .= (x ^. _value)]projectIdJSON :: ProjectId -> ValueprojectIdJSON = idJSON "projectId" _ProjectIdprojectJSON :: P.Project -> ValueprojectJSON p =v1 $obj[ "projectName" .= (p ^. P.projectName),"inceptionDate" .= (p ^. P.inceptionDate),"initiator" .= (p ^. P.initiator . _UserId)]qdbProjectJSON :: (ProjectId, P.Project) -> ValueqdbProjectJSON = qdbJSON "project" (_1 . _ProjectId) (_2 . L.to projectJSON)auctionIdJSON :: A.AuctionId -> ValueauctionIdJSON = idJSON "auctionId" A._AuctionIdauctionJSON :: A.Auction Amount -> ValueauctionJSON x =v1 $obj[ "projectId" .= idValue (A.projectId . _ProjectId) x,"initiator" .= idValue (A.initiator . _UserId) x,"name" .= (x ^. A.name),"description" .= (x ^. A.description),"raiseAmount" .= (x ^. (A.raiseAmount . to amountJSON)),"auctionStart" .= (x ^. A.auctionStart),"auctionEnd" .= (x ^. A.auctionEnd)] - replacement in lib/Aftok/Json.hs at line 120
bidIdJSON :: A.BidId -> ValuebidIdJSON pid = v1 $ obj ["bidId" .= (pid ^. A._BidId)]identifiedJSON :: Text -> Getter a UUID -> Getter a Value -> a -> ValueidentifiedJSON name _id _value x =object [(name <> "Id") .= idValue _id x, name .= (x ^. _value)] - replacement in lib/Aftok/Json.hs at line 130
v2 $ obj ["creditToAccount" .= idValue _AccountId accountId]object ["creditToAccount" .= idValue _AccountId accountId] - replacement in lib/Aftok/Json.hs at line 132
v2 $ obj ["creditToUser" .= idValue _UserId uid]object ["creditToUser" .= idValue _UserId uid] - replacement in lib/Aftok/Json.hs at line 134[5.69340]→[5.34111:34163](∅→∅),[5.22283]→[5.34111:34163](∅→∅),[5.34163]→[5.22337:22338](∅→∅),[5.22337]→[5.22337:22338](∅→∅),[5.22338]→[5.69341:69463](∅→∅),[5.69463]→[5.34257:34298](∅→∅),[5.22551]→[5.34257:34298](∅→∅)
v2 $ obj ["creditToProject" .= projectIdJSON pid]parseCreditTo :: Value -> Parser CreditToparseCreditTo = unversion "CreditTo" $ \case(Version 2 0) -> parseCreditToV2ver -> badVersion "EventAmendment" verobject ["creditToProject" .= idValue _ProjectId pid] - edit in lib/Aftok/Json.hs at line 160[5.23920]→[5.23920:23934](∅→∅),[5.1389]→[5.580:581](∅→∅),[5.2079]→[5.580:581](∅→∅),[5.23934]→[5.580:581](∅→∅),[5.2561]→[5.580:581](∅→∅),[5.581]→[5.69883:69951](∅→∅),[5.69951]→[5.35611:35618](∅→∅),[5.35478]→[5.35611:35618](∅→∅),[5.35618]→[5.69952:70004](∅→∅),[5.70004]→[5.35691:35719](∅→∅),[5.35691]→[5.35691:35719](∅→∅),[5.35719]→[5.70005:70127](∅→∅),[5.70127]→[5.35793:35852](∅→∅),[5.35793]→[5.35793:35852](∅→∅),[5.35852]→[5.24497:24498](∅→∅),[5.35923]→[5.24497:24498](∅→∅),[5.24497]→[5.24497:24498](∅→∅),[5.24498]→[5.70128:70225](∅→∅),[5.70225]→[5.35986:35994](∅→∅),[5.35986]→[5.35986:35994](∅→∅),[5.35994]→[5.70226:70281](∅→∅),[5.70281]→[5.36196:36254](∅→∅),[5.36196]→[5.36196:36254](∅→∅),[5.36254]→[5.35954:35970](∅→∅),[5.35954]→[5.35954:35970](∅→∅),[5.35970]→[5.70282:70340](∅→∅),[5.70340]→[5.36034:36073](∅→∅),[5.36034]→[5.36034:36073](∅→∅),[5.36073]→[5.36255:36412](∅→∅),[5.36185]→[5.24755:24756](∅→∅),[5.36412]→[5.24755:24756](∅→∅),[5.24755]→[5.24755:24756](∅→∅),[5.1905]→[5.24800:24803](∅→∅)
-- Payouts--payoutsJSON :: FractionalPayouts -> ValuepayoutsJSON (Payouts m) =v2 $let payoutsRec :: (CreditTo, Rational) -> ValuepayoutsRec (c, r) =object ["creditTo" .= creditToJSON c, "payoutRatio" .= r, "payoutPercentage" .= (fromRational @Double r * 100)]in obj $ ["payouts" .= fmap payoutsRec (MS.assocs m)]parsePayoutsJSON :: Value -> Parser FractionalPayoutsparsePayoutsJSON = unversion "Payouts" $ pwherep :: Version -> Object -> Parser FractionalPayoutsp (Version 2 0) val =let parsePayoutRecord x =(,)<$> (parseCreditToV2 =<< (x .: "creditTo"))<*> (x .: "payoutRatio")in Payouts. MS.fromList<$> (traverse parsePayoutRecord =<< parseJSON (Object val))p ver x = badVersion "Payouts" ver x-- - edit in lib/Aftok/Json.hs at line 163[5.24820]→[5.70341:70410](∅→∅),[5.70410]→[5.36452:36522](∅→∅),[5.36452]→[5.36452:36522](∅→∅),[5.36522]→[5.70411:70465](∅→∅),[5.70465]→[5.36597:36631](∅→∅),[5.36597]→[5.36597:36631](∅→∅),[5.36631]→[5.70466:70506](∅→∅),[5.70506]→[5.36677:36742](∅→∅),[5.36677]→[5.36677:36742](∅→∅),[5.1264]→[5.2061:2094](∅→∅),[5.1625]→[5.2061:2094](∅→∅),[5.2201]→[5.2061:2094](∅→∅),[5.3969]→[5.2061:2094](∅→∅),[5.4602]→[5.2061:2094](∅→∅),[5.4849]→[5.2061:2094](∅→∅),[5.36570]→[5.2061:2094](∅→∅),[5.36742]→[5.2061:2094](∅→∅),[5.2061]→[5.2061:2094](∅→∅),[5.2094]→[5.2158:2198](∅→∅),[5.2198]→[5.130:131](∅→∅),[5.1799]→[5.130:131](∅→∅),[5.131]→[5.1405:1440](∅→∅),[5.1440]→[5.36571:36659](∅→∅),[5.1530]→[5.254:255](∅→∅),[5.36659]→[5.254:255](∅→∅),[5.254]→[5.254:255](∅→∅),[5.255]→[5.70507:70588](∅→∅),[5.70588]→[5.4909:4910](∅→∅),[5.4909]→[5.4909:4910](∅→∅),[5.4910]→[5.70589:70695](∅→∅),[5.70695]→[5.36783:36837](∅→∅),[5.36783]→[5.36783:36837](∅→∅),[5.36837]→[5.36800:36804](∅→∅),[5.36800]→[5.36800:36804](∅→∅),[5.5216]→[5.5216:5217](∅→∅),[5.5217]→[5.5560:5600](∅→∅),[5.5560]→[5.5560:5600](∅→∅),[5.5600]→[5.2199:2251](∅→∅),[5.2251]→[5.26623:26624](∅→∅)
workIndexJSON :: WorkIndex -> ValueworkIndexJSON (WorkIndex widx) =v2 $obj ["workIndex" .= fmap widxRec (MS.assocs widx)]wherewidxRec :: (CreditTo, NonEmpty Interval) -> ValuewidxRec (c, l) =object[ "creditTo" .= creditToJSON c,"intervals" .= (intervalJSON <$> L.toList l)]eventIdJSON :: EventId -> ValueeventIdJSON = idJSON "eventId" _EventIdlogEventJSON' :: LogEvent -> ValuelogEventJSON' ev =object [eventName ev .= object ["eventTime" .= (ev ^. eventTime)]]logEntryJSON :: LogEntry -> ValuelogEntryJSON le = v2 $ obj (logEntryFields le)logEntryFields :: LogEntry -> [Pair]logEntryFields (LogEntry c ev m) =[ "creditTo" .= creditToJSON c,"event" .= logEventJSON' ev,"eventMeta" .= m]amendmentIdJSON :: AmendmentId -> ValueamendmentIdJSON = idJSON "amendmentId" _AmendmentId - edit in lib/Aftok/Json.hs at line 175[4.6483]→[4.6483:6484](∅→∅),[4.6484]→[5.26624:26715](∅→∅),[5.70883]→[5.26624:26715](∅→∅),[5.26624]→[5.26624:26715](∅→∅)
billableIdJSON :: B.BillableId -> ValuebillableIdJSON = idJSON "billableId" B._BillableId - replacement in lib/Aftok/Json.hs at line 192
qdbJSON "billable" (_1 . B._BillableId) (_2 . to billableJSON)identifiedJSON "billable" (_1 . B._BillableId) (_2 . to billableJSON) - edit in lib/Aftok/Json.hs at line 201[5.5682]→[5.2309:2374](∅→∅),[5.2374]→[5.37381:37573](∅→∅),[5.37432]→[5.3189:3279](∅→∅),[5.37573]→[5.3189:3279](∅→∅),[5.629]→[5.3189:3279](∅→∅),[5.3279]→[5.629:630](∅→∅),[5.629]→[5.629:630](∅→∅),[5.630]→[5.3280:3359](∅→∅),[5.3359]→[5.37574:37769](∅→∅),[5.37564]→[5.3557:3562](∅→∅),[5.37769]→[5.3557:3562](∅→∅),[5.3557]→[5.3557:3562](∅→∅),[5.3562]→[5.26837:26949](∅→∅),[5.26949]→[5.71244:71680](∅→∅),[5.37921]→[5.26950:27033](∅→∅),[5.38380]→[5.26950:27033](∅→∅),[5.71680]→[5.26950:27033](∅→∅),[5.4377]→[5.26950:27033](∅→∅),[5.27033]→[5.4377:4378](∅→∅),[5.4377]→[5.4377:4378](∅→∅)
createSubscriptionJSON :: UserId -> B.BillableId -> Day -> ValuecreateSubscriptionJSON uid bid d =v1 $obj[ "user_id" .= idValue _UserId uid,"billable_id" .= idValue B._BillableId bid,"start_date" .= showGregorian d]subscriptionJSON :: B.Subscription -> ValuesubscriptionJSON = v1 . obj . subscriptionKVsubscriptionKV :: (KeyValue kv) => B.Subscription -> [kv]subscriptionKV sub =[ "user_id" .= idValue (B.customer . _UserId) sub,"billable_id" .= idValue (B.billable . B._BillableId) sub,"start_time" .= view B.startTime sub,"end_time" .= view B.endTime sub]subscriptionIdJSON :: B.SubscriptionId -> ValuesubscriptionIdJSON = idJSON "subscriptionId" B._SubscriptionId-- paymentRequestDetailsJSON :: [PaymentRequestDetail Amount] -> Value-- paymentRequestDetailsJSON r = v1 $ obj ["payment_requests" .= fmap paymentRequestDetailJSON r]---- paymentRequestDetailJSON :: PaymentRequestDetail Amount -> Object-- paymentRequestDetailJSON r = obj $ concat-- [ ["payment_request_id" .= view () r]-- , paymentRequestKV $ view _2 r-- , subscriptionKV $ view _3 r-- , billableKV $ view _4 r-- ]paymentIdJSON :: PaymentId -> ValuepaymentIdJSON = idJSON "paymentId" _PaymentId - edit in lib/Aftok/Json.hs at line 212[5.25424]→[5.5639:5640](∅→∅),[5.3752]→[5.5639:5640](∅→∅),[5.5640]→[5.38853:38876](∅→∅),[5.38893]→[5.38893:38917](∅→∅),[5.38917]→[5.71681:71760](∅→∅),[5.71760]→[5.39025:39033](∅→∅),[5.39025]→[5.39025:39033](∅→∅),[5.39033]→[5.71761:71807](∅→∅),[5.71807]→[5.39137:39181](∅→∅),[5.39137]→[5.39137:39181](∅→∅),[5.2211]→[5.1956:1957](∅→∅),[5.3983]→[5.1956:1957](∅→∅),[5.39181]→[5.1956:1957](∅→∅),[5.1956]→[5.1956:1957](∅→∅),[5.2513]→[5.39698:39723](∅→∅),[5.39740]→[5.39740:39765](∅→∅),[5.39765]→[5.71808:71906](∅→∅),[5.71906]→[5.39882:39944](∅→∅),[5.39882]→[5.39882:39944](∅→∅),[5.39944]→[5.71907:71978](∅→∅),[5.71978]→[5.40021:40217](∅→∅),[5.40021]→[5.40021:40217](∅→∅)
parseEventAmendment ::ModTime ->Value ->Parser EventAmendmentparseEventAmendment t = unversion "EventAmendment" $ pwherep (Version 2 0) = parseEventAmendmentV2 tp ver = badVersion "EventAmendment" verparseEventAmendmentV2 ::ModTime ->Object ->Parser EventAmendmentparseEventAmendmentV2 t o =let parseA :: Text -> Parser EventAmendmentparseA "timeChange" = TimeChange t <$> o .: "eventTime"parseA "creditToChange" = CreditToChange t <$> parseCreditToV2 oparseA "metadataChange" = MetadataChange t <$> o .: "eventMeta"parseA tid =fail . T.unpack $ "Amendment type " <> tid <> " not recognized."in o .: "amendment" >>= parseA - replacement in lib/Aftok/Json.hs at line 218
parseLogEntry uid f = unversion "LogEntry" pparseLogEntry uid f = withObject "LogEntry" p - replacement in lib/Aftok/Json.hs at line 220
p (Version 2 0) o = dop o = do - edit in lib/Aftok/Json.hs at line 224[5.40673]→[5.40673:40715](∅→∅),[5.191]→[5.664:665](∅→∅),[5.407]→[5.664:665](∅→∅),[5.40715]→[5.664:665](∅→∅),[5.664]→[5.664:665](∅→∅),[5.3227]→[5.27467:27516](∅→∅),[5.27516]→[5.26796:26816](∅→∅),[5.26816]→[5.40716:41286](∅→∅),[5.28064]→[5.3264:3265](∅→∅),[5.39875]→[5.3264:3265](∅→∅),[5.41286]→[5.3264:3265](∅→∅),[5.988]→[5.3264:3265](∅→∅),[5.3265]→[5.28065:28114](∅→∅),[5.28114]→[4.6485:6609](∅→∅)
p ver o = badVersion "LogEntry" ver oparseRecurrence :: Object -> Parser B.RecurrenceparseRecurrence o =let parseAnnually o' = const (pure B.Annually) <$> O.lookup "annually" o'parseMonthly o' = fmap B.Monthly . parseJSON <$> O.lookup "monthly" o'parseWeekly o' = fmap B.Weekly . parseJSON <$> O.lookup "weekly" o'parseOneTime o' = const (pure B.OneTime) <$> O.lookup "one-time" o'notFound =fail $ "Value " <> show o <> " does not represent a Recurrence value."parseV val =parseAnnually val<|> parseMonthly val<|> parseWeekly val<|> parseOneTime valin fromMaybe notFound $ parseV oparseRecurrence' :: Value -> Parser B.RecurrenceparseRecurrence' = \case(Object o) -> parseRecurrence oval -> fail $ "Value " <> show val <> " is not a JSON object." - edit in lib/Aftok/TimeLog.hs at line 3
{-# LANGUAGE ViewPatterns #-} - replacement in lib/Aftok/TimeLog.hs at line 7
creditTo,event,eventMeta,HasLogEntry (..), - edit in lib/Aftok/TimeLog.hs at line 13
_StartWork,_StopWork, - edit in lib/Aftok/TimeLog.hs at line 40
import Control.Arrow ((&&&)) - edit in lib/Aftok/TimeLog.hs at line 44
import Data.Either( Either (..),rights,)import Data.Eq( (==),Eq,) - replacement in lib/Aftok/TimeLog.hs at line 45
import Data.Function( ($),(.),id,)import Data.Functor (fmap)import Data.Heap as Himport Data.List.NonEmpty as Limport Data.Map.Strict as MSimport Data.Maybe (Maybe (..))import Data.Ord( Ord (..),Ordering (..),)import Data.Ratio (Rational)import Data.Text (Text)import qualified Data.Map.Strict as MS - replacement in lib/Aftok/TimeLog.hs at line 47
import Data.UUIDimport Data.VectorSpaceimport Text.Show (Show)import Prelude( (*),(/),)import Data.UUID (UUID)import Data.VectorSpace ((*^), Sum (..), (^+^), (^-^), getSum, zeroV)import Prelude hiding (Sum, getSum) - replacement in lib/Aftok/TimeLog.hs at line 57
type DepF = C.UTCTime -> Interval -> NDTtype DepF = C.UTCTime -> Interval C.UTCTime -> NDT - edit in lib/Aftok/TimeLog.hs at line 63
makePrisms ''LogEvent - replacement in lib/Aftok/TimeLog.hs at line 93
makeLenses ''LogEntrymakeClassy ''LogEntry - replacement in lib/Aftok/TimeLog.hs at line 100
newtype EventId = EventId UUID deriving (Show, Eq)newtype EventId = EventId UUID deriving (Show, Eq, Ord) - replacement in lib/Aftok/TimeLog.hs at line 113
newtype AmendmentId = AmendmentId UUID deriving (Show, Eq)newtype AmendmentId = AmendmentId UUID deriving (Show, Eq, Ord) - replacement in lib/Aftok/TimeLog.hs at line 123
newtype WorkIndex = WorkIndex (Map CreditTo (NonEmpty Interval)) deriving (Show, Eq)newtype WorkIndex t = WorkIndex (Map CreditTo (NonEmpty (Interval t))) deriving (Show, Eq) - replacement in lib/Aftok/TimeLog.hs at line 134[5.52658]→[5.16169:16238](∅→∅),[5.1422]→[5.16169:16238](∅→∅),[5.16238]→[5.9530:9600](∅→∅),[5.1502]→[5.9530:9600](∅→∅)
workCredit :: (Foldable f) => DepF -> C.UTCTime -> f Interval -> NDTworkCredit df ptime ivals = getSum $ F.foldMap (Sum . df ptime) ivalsworkCredit :: (Foldable f, HasLogEntry le) => DepF -> C.UTCTime -> f (Interval le) -> NDTworkCredit df ptime ivals = getSum $ F.foldMap (Sum . df ptime . fmap (view $ event . eventTime)) ivals - replacement in lib/Aftok/TimeLog.hs at line 141
payouts :: DepF -> C.UTCTime -> WorkIndex -> FractionalPayoutspayouts :: forall le. (HasLogEntry le) => DepF -> C.UTCTime -> WorkIndex le -> FractionalPayouts - replacement in lib/Aftok/TimeLog.hs at line 143
let addIntervalDiff :: (Foldable f) => NDT -> f Interval -> (NDT, NDT)let addIntervalDiff :: (Foldable f) => NDT -> f (Interval le) -> (NDT, NDT) - replacement in lib/Aftok/TimeLog.hs at line 149[5.1582]→[5.90817:90868](∅→∅),[5.3156]→[5.4957:4980](∅→∅),[5.12496]→[5.4957:4980](∅→∅),[5.32225]→[5.4957:4980](∅→∅),[5.53058]→[5.4957:4980](∅→∅),[5.90868]→[5.4957:4980](∅→∅),[5.4396]→[5.4957:4980](∅→∅),[5.4980]→[5.1403:1461](∅→∅),[5.4420]→[5.1403:1461](∅→∅)
workIndex :: Foldable f => f LogEntry -> WorkIndexworkIndex logEntries =let sortedEntries = F.foldr H.insert H.empty logEntriesworkIndex :: (Foldable f, HasLogEntry le, Ord o) => (le -> o) -> f le -> WorkIndex leworkIndex cmp logEntries =let sortedEntries = sortWith cmp $ toList logEntries - replacement in lib/Aftok/TimeLog.hs at line 155
[Either LogEvent Interval] ->Map CreditTo (NonEmpty Interval) ->Map CreditTo (NonEmpty Interval)[Either le (Interval le)] ->Map CreditTo (NonEmpty (Interval le)) ->Map CreditTo (NonEmpty (Interval le)) - replacement in lib/Aftok/TimeLog.hs at line 167
type RawIndex = Map CreditTo [Either LogEvent Interval]type RawIndex le = Map CreditTo [Either le (Interval le)] - replacement in lib/Aftok/TimeLog.hs at line 169[5.2170]→[5.91033:91084](∅→∅),[5.32357]→[5.5030:5069](∅→∅),[5.91084]→[5.5030:5069](∅→∅),[5.2117]→[5.5030:5069](∅→∅),[5.5069]→[5.12738:12804](∅→∅),[5.12804]→[5.53574:53649](∅→∅),[5.53649]→[5.12880:12978](∅→∅),[5.12880]→[5.12880:12978](∅→∅),[5.12978]→[5.53650:53773](∅→∅),[5.53773]→[5.2490:2655](∅→∅),[5.2490]→[5.2490:2655](∅→∅),[5.2655]→[5.53774:53846](∅→∅),[5.53846]→[5.13201:13244](∅→∅),[5.13201]→[5.13201:13244](∅→∅),[5.13244]→[5.2752:2782](∅→∅),[5.2752]→[5.2752:2782](∅→∅)
appendLogEntry :: RawIndex -> LogEntry -> RawIndexappendLogEntry idx (LogEntry k ev _) =let combine :: LogEvent -> LogEvent -> Either LogEvent Intervalcombine (StartWork t) (StopWork t') | t' > t = Right $ Interval t t'combine (e1@(StartWork _)) (e2@(StartWork _)) = Left $ max e1 e2 -- ignore redundant startscombine (e1@(StopWork _)) (e2@(StopWork _)) = Left $ min e1 e2 -- ignore redundant endscombine _ e2 = Left e2-- if the interval includes the timestamp of a start event, then allow the extension of the intervalextension :: Interval -> LogEvent -> Maybe LogEventextension ival (StartWork t)| containsInclusive t ival =Just $ StartWork (ival ^. start)extension _ _ = NothingappendLogEntry ::forall le.HasLogEntry le =>RawIndex le ->le ->RawIndex leappendLogEntry idx logEvent =let k = logEvent ^. logEntry . creditTo - replacement in lib/Aftok/TimeLog.hs at line 180
Just (Right ival : xs) -> case extension ival ev ofJust e' -> Left e' : xsNothing -> Left ev : Right ival : xsJust (Right ival : xs) ->case extension (view (event . eventTime) <$> ival) logEvent ofJust e' -> Left e' : xsNothing -> Left logEvent : Right ival : xs - replacement in lib/Aftok/TimeLog.hs at line 185
Just (Left ev' : xs) -> combine ev' ev : xs_ -> [Left ev]Just (Left ev' : xs) ->combine ev' logEvent : xs_ -> [Left logEvent] - edit in lib/Aftok/TimeLog.hs at line 189
wherecombine :: le -> le -> Either le (Interval le)combine e e' = case (e ^. event, e' ^. event) of(StartWork t, StopWork t') | t' > t -> Right $ Interval e e' -- complete interval found(StartWork t, StartWork t') -> Left $ if t > t' then e else e' -- ignore redundant starts(StopWork t, StopWork t') -> Left $ if t <= t' then e else e' -- ignore redundant ends_ -> Left e'-- if the interval includes the timestamp of a start event, then allow the extension of the intervalextension :: (Interval C.UTCTime) -> le -> Maybe leextension ival newEvent@(view event -> StartWork t)| containsInclusive t ival =Just newEvent -- replace the end of the interval with the new eventextension _ _ =Nothing - file addition: 2021-01-16_05-04-32_event-replacement.txt[94.1]
Description: (Describe migration here.)Created: 2021-01-16 05:04:54.586280477 UTCDepends: 2017-06-08_04-37-31_event-metadata-ids 2016-10-14_02-49-36_event-amendmentsApply: |CREATE TYPE amendment_t AS ENUM ('amend_event_time', 'amend_credit_to', 'amend_metadata');ALTER TABLE work_events ADD COLUMN replacement_id uuid REFERENCES work_events(id);ALTER TABLE work_events ADD COLUMN amended_by_type amendment_t;ALTER TABLE work_events ADD COLUMN amended_by_id uuid; - file addition: amend.sh[5.1220]
#!/bin/bashif [ -f ".env" ]; thensource .envfiif [ -z "${AFTOK_HOST}" ]; thenAFTOK_HOST="aftok.com"fiif [ -z "${USER}" ]; thenread -p "Username: " USERechofiread -p "Event ID: " EIDwhile [ -z "${ATYPE}" ]doread -p "Amendment Type: " ATYPEcase $ATYPE in# "CREDIT_TO")# AVALUE="creditToChange"# read -p "Raise amount, in Bitcoin satoshis: " AMOUNT# ;;"TIME")AVALUE="timeChange"read -p "Event Timestamp (yyyy-MM-ddTHH:mm:ssZ): " ATIME;;*)echo "$ATYPE is not a amendment type. Please choose \"TIME\"" # or \"CREDIT_TO\""ATYPE="";;esacdoneBODY=$(cat <<END_BODY{"schemaVersion": "2.0","amendment": "timeChange","eventTime": "$ATIME"}END_BODY)curl --verbose \${ALLOW_INSECURE} \--user $USER \--header "Content-Type: application/json" \--request PUT \--data "$BODY" \"https://$AFTOK_HOST/api/events/$EID/amend" - file addition: create_auction.sh[5.1220]
#!/bin/bashif [ -f ".env" ]; thensource .envfiif [ -z "${AFTOK_HOST}" ]; thenAFTOK_HOST="aftok.com"fiif [ -z "${PID}" ]; thenread -p "Project UUID: " PIDechofiif [ -z "${USER}" ]; thenread -p "Username: " USERechofiread -p "Auction Name: " NAMEread -p "Description: " DESCwhile [ -z "${CCY}" ]doread -p "Currency: " CCYcase $CCY in"BTC")CCY="satoshi"read -p "Raise amount, in Bitcoin satoshis: " AMOUNT;;"ZEC")CCY="zatoshi"read -p "Raise amount, in Zcash zatoshis: " AMOUNT;;*)echo "$CCY is not a supported currency. Please choose \"BTC\" or \"ZEC\""CCY="";;esacdoneechoread -p "Auction start date (yyyy-MM-ddThh:mm:ssZ): " STARTread -p "Auction end date (yyyy-MM-ddThh:mm:ssZ): " ENDBODY=$(cat <<END_BODY{"auction_name": "$NAME","auction_desc": "$DESC","raise_amount": {"$CCY": $AMOUNT},"auction_start": "$START","auction_end": "$END"}END_BODY)curl --verbose \${ALLOW_INSECURE} \--user $USER \--header "Content-Type: application/json" \--data "$BODY" \"https://$AFTOK_HOST/api/projects/$PID/auctions" - edit in server/Aftok/Snaplet/Auctions.hs at line 8
auctionJSON,bidIdJSON, - replacement in server/Aftok/Snaplet/Auctions.hs at line 14
( Auction (..),( Auction (Auction), - replacement in server/Aftok/Snaplet/Auctions.hs at line 16
Bid (..),Bid (Bid), - edit in server/Aftok/Snaplet/Auctions.hs at line 18
_BidId,auctionEnd,auctionStart,description,initiator,name,projectId,raiseAmount, - replacement in server/Aftok/Snaplet/Auctions.hs at line 39
import Aftok.Types (UserId)import Aftok.Types (UserId, _ProjectId, _UserId) - edit in server/Aftok/Snaplet/Auctions.hs at line 41
import Control.Lens ((^.), to) - replacement in server/Aftok/Snaplet/Auctions.hs at line 51
{ name :: Text,description :: Maybe Text,raiseAmount :: Amount,auctionStart :: C.UTCTime,auctionEnd :: C.UTCTime{ acrName :: Text,acrDescription :: Maybe Text,acrRaiseAmount :: Amount,acrAuctionStart :: C.UTCTime,acrAuctionEnd :: C.UTCTime - replacement in server/Aftok/Snaplet/Auctions.hs at line 92
(name req)(description req)(raiseAmount $ req)(auctionStart req)(auctionEnd req)(acrName req)(acrDescription req)(acrRaiseAmount $ req)(acrAuctionStart req)(acrAuctionEnd req) - edit in server/Aftok/Snaplet/Auctions.hs at line 124[5.1576]
auctionJSON :: Auction Amount -> ValueauctionJSON x =v1 $obj[ "projectId" .= idValue (projectId . _ProjectId) x,"initiator" .= idValue (initiator . _UserId) x,"name" .= (x ^. name),"description" .= (x ^. description),"raiseAmount" .= (x ^. (raiseAmount . to amountJSON)),"auctionStart" .= (x ^. auctionStart),"auctionEnd" .= (x ^. auctionEnd)]bidIdJSON :: BidId -> ValuebidIdJSON pid = v1 $ obj ["bidId" .= (pid ^. _BidId)] - replacement in server/Aftok/Snaplet/Billing.hs at line 10
import Aftok.Billingimport Aftok.Billing as B - edit in server/Aftok/Snaplet/Billing.hs at line 27
import qualified Data.HashMap.Strict as O - edit in server/Aftok/Snaplet/Billing.hs at line 76[5.35830]
-- subscriptionJSON :: B.Subscription -> Value-- subscriptionJSON = v1 . obj . subscriptionKV---- subscriptionKV :: (KeyValue kv) => B.Subscription -> [kv]-- subscriptionKV sub =-- [ "user_id" .= idValue (B.customer . _UserId) sub,-- "billable_id" .= idValue (B.billable . B._BillableId) sub,-- "start_time" .= view B.startTime sub,-- "end_time" .= view B.endTime sub-- ]-- paymentRequestDetailsJSON :: [PaymentRequestDetail Amount] -> Value-- paymentRequestDetailsJSON r = v1 $ obj ["payment_requests" .= fmap paymentRequestDetailJSON r]---- paymentRequestDetailJSON :: PaymentRequestDetail Amount -> Object-- paymentRequestDetailJSON r = obj $ concat-- [ ["payment_request_id" .= view () r]-- , paymentRequestKV $ view _2 r-- , subscriptionKV $ view _3 r-- , billableKV $ view _4 r-- ]parseRecurrence :: Object -> Parser B.RecurrenceparseRecurrence o =let parseAnnually o' = const (pure B.Annually) <$> O.lookup "annually" o'parseMonthly o' = fmap B.Monthly . parseJSON <$> O.lookup "monthly" o'parseWeekly o' = fmap B.Weekly . parseJSON <$> O.lookup "weekly" o'parseOneTime o' = const (pure B.OneTime) <$> O.lookup "one-time" o'notFound =fail $ "Value " <> show o <> " does not represent a Recurrence value."parseV val =parseAnnually val<|> parseMonthly val<|> parseWeekly val<|> parseOneTime valin fromMaybe notFound $ parseV oparseRecurrence' :: Value -> Parser B.RecurrenceparseRecurrence' = \case(Object o) -> parseRecurrence oval -> fail $ "Value " <> show val <> " is not a JSON object." - file addition: Json.hs[5.2082]
module Aftok.Snaplet.Json( idJSON,)whereimport Aftok.Json (idValue, obj, v1)import Control.Lens (Getter)import Data.Aeson ((.=), Value)import Data.UUID (UUID)idJSON :: forall a. Text -> Getter a UUID -> a -> ValueidJSON t l a = v1 $ obj [t .= idValue l a] - edit in server/Aftok/Snaplet/Projects.hs at line 11
projectJSON,qdbProjectJSON, - replacement in server/Aftok/Snaplet/Projects.hs at line 18
import Aftok.Json (idValue)import Aftok.Json (idValue, identifiedJSON, obj, v1) - replacement in server/Aftok/Snaplet/Projects.hs at line 26
import Control.Lens ((^.))import Control.Lens ((^.), _1, _2, to) - edit in server/Aftok/Snaplet/Projects.hs at line 140[5.65787]
projectJSON :: Project -> ValueprojectJSON p =v1 $obj[ "projectName" .= (p ^. projectName),"inceptionDate" .= (p ^. inceptionDate),"initiator" .= (p ^. initiator . _UserId)]qdbProjectJSON :: (ProjectId, Project) -> ValueqdbProjectJSON = identifiedJSON "project" (_1 . _ProjectId) (_2 . to projectJSON) - edit in server/Aftok/Snaplet/WorkLog.hs at line 1
{-# LANGUAGE InstanceSigs #-}{-# LANGUAGE TemplateHaskell #-} - edit in server/Aftok/Snaplet/WorkLog.hs at line 4
{-# LANGUAGE TypeApplications #-} - edit in server/Aftok/Snaplet/WorkLog.hs at line 9
import Aftok.Interval( Interval (..),intervalJSON,) - edit in server/Aftok/Snaplet/WorkLog.hs at line 19
( AmendmentId,EventAmendment (..),EventId (..),FractionalPayouts,LogEntry (LogEntry),LogEvent,ModTime (..),Payouts (..),WorkIndex (..),_AmendmentId,_EventId,eventName,eventTime,payouts,toDepF,workIndex,) - replacement in server/Aftok/Snaplet/WorkLog.hs at line 37
( _ProjectId,( CreditTo (..),UserId,ProjectId,_ProjectId, - replacement in server/Aftok/Snaplet/WorkLog.hs at line 44
import Control.Lens ((^.))import Control.Lens ((^.), view) - replacement in server/Aftok/Snaplet/WorkLog.hs at line 46
import Data.Aeson ((.=))import qualified Data.Aeson as Aimport qualified Data.Aeson.Types as Aimport Data.Aeson ((.:), (.=), Value (Object), eitherDecode, object)import Data.Aeson.Types (Pair, Parser, parseEither)import qualified Data.List.NonEmpty as Limport qualified Data.Map.Strict as MS - edit in server/Aftok/Snaplet/WorkLog.hs at line 56
------------------------ Handlers---------------------- - replacement in server/Aftok/Snaplet/WorkLog.hs at line 62
S.Handler App App (EventId, KeyedLogEntry)S.Handler App App (ProjectId, UserId, KeyedLogEntry) - replacement in server/Aftok/Snaplet/WorkLog.hs at line 68
case (A.eitherDecode requestBody >>= A.parseEither (parseLogEntry uid evCtr)) ofcase (eitherDecode requestBody >>= parseEither (parseLogEntry uid evCtr)) of - replacement in server/Aftok/Snaplet/WorkLog.hs at line 82
(pure . (eid,))pure - replacement in server/Aftok/Snaplet/WorkLog.hs at line 85
projectWorkIndex :: S.Handler App App WorkIndexamendEventHandler :: S.Handler App App (EventId, AmendmentId)amendEventHandler = douid <- requireUserIdeventIdBytes <- getParam "eventId"eventId <-maybe(snapError 400 "eventId parameter is required")(pure . EventId)(eventIdBytes >>= U.fromASCIIBytes)modTime <- ModTime <$> liftIO C.getCurrentTimerequestJSON <- readRequestJSON 4096either(snapError 400 . T.pack)(snapEval . amendEvent uid eventId)(parseEither (parseEventAmendment modTime) requestJSON)projectWorkIndex :: S.Handler App App (WorkIndex KeyedLogEntry) - replacement in server/Aftok/Snaplet/WorkLog.hs at line 107
userEvents :: S.Handler App App [LogEntry]userEvents :: S.Handler App App [KeyedLogEntry] - replacement in server/Aftok/Snaplet/WorkLog.hs at line 115
userWorkIndex :: S.Handler App App WorkIndexuserWorkIndex = workIndex <$> userEventsuserWorkIndex :: S.Handler App App (WorkIndex KeyedLogEntry)userWorkIndex = workIndex (view logEntry) <$> userEvents - replacement in server/Aftok/Snaplet/WorkLog.hs at line 130[5.3782]→[5.18702:18753](∅→∅),[5.18753]→[5.8732:8755](∅→∅),[5.8732]→[5.8732:8755](∅→∅),[5.8755]→[5.72412:72435](∅→∅),[5.13339]→[5.8828:8865](∅→∅),[5.36174]→[5.8828:8865](∅→∅),[5.59594]→[5.8828:8865](∅→∅),[5.72461]→[5.8828:8865](∅→∅),[5.8828]→[5.8828:8865](∅→∅),[5.8865]→[5.72462:72653](∅→∅),[5.59821]→[5.9195:9233](∅→∅),[5.72653]→[5.9195:9233](∅→∅),[5.9195]→[5.9195:9233](∅→∅),[5.9233]→[5.72654:72732](∅→∅),[5.72732]→[5.104341:104403](∅→∅)
amendEventHandler :: S.Handler App App AmendmentIdamendEventHandler = douid <- requireUserIdeventIdBytes <- getParam "eventId"eventId <-maybe(snapError 400 "eventId parameter is required")(pure . EventId)(eventIdBytes >>= U.fromASCIIBytes)modTime <- ModTime <$> liftIO C.getCurrentTimerequestJSON <- readRequestJSON 4096either(snapError 400 . T.pack)(snapEval . amendEvent uid eventId)(A.parseEither (parseEventAmendment modTime) requestJSON)------------------------ Parsing----------------------parseEventAmendment ::ModTime ->Value ->Parser EventAmendmentparseEventAmendment t = \caseObject o ->let parseA :: Text -> Parser EventAmendmentparseA "timeChange" = TimeChange t <$> o .: "eventTime"parseA "creditToChange" = CreditToChange t <$> parseCreditToV2 oparseA "metadataChange" = MetadataChange t <$> o .: "eventMeta"parseA tid =fail . T.unpack $ "Amendment type " <> tid <> " not recognized."in o .: "amendment" >>= parseAval ->fail $ "Value " <> show val <> " is not a JSON object."------------------------ Rendering----------------------logEventJSON :: LogEvent -> ValuelogEventJSON ev =object [eventName ev .= object ["eventTime" .= (ev ^. eventTime)]]logEntryFields :: LogEntry -> [Pair]logEntryFields (LogEntry c ev m) =[ "creditTo" .= creditToJSON c,"event" .= logEventJSON ev,"eventMeta" .= m]keyedLogEntryFields :: KeyedLogEntry -> [Pair]keyedLogEntryFields (KeyedLogEntry eid le) =["eventId" .= idValue _EventId eid] <> logEntryFields lekeyedLogEntryJSON :: KeyedLogEntry -> ValuekeyedLogEntryJSON kle =object (keyedLogEntryFields kle) - replacement in server/Aftok/Snaplet/WorkLog.hs at line 173
keyedLogEntryJSON :: (EventId, KeyedLogEntry) -> A.ValuekeyedLogEntryJSON (eid, (pid, uid, ev)) =v2extendedLogEntryJSON :: (ProjectId, UserId, KeyedLogEntry) -> ValueextendedLogEntryJSON (pid, uid, le) =v1 - replacement in server/Aftok/Snaplet/WorkLog.hs at line 177
$ [ "eventId" .= idValue _EventId eid,"projectId" .= idValue _ProjectId pid,$ [ "projectId" .= idValue _ProjectId pid, - replacement in server/Aftok/Snaplet/WorkLog.hs at line 180
<> logEntryFields ev[5.73047]<> keyedLogEntryFields lepayoutsJSON :: FractionalPayouts -> ValuepayoutsJSON (Payouts m) =v1 $let payoutsRec :: (CreditTo, Rational) -> ValuepayoutsRec (c, r) =object ["creditTo" .= creditToJSON c,"payoutRatio" .= r,"payoutPercentage" .= (fromRational @Double r * 100)]in obj $ ["payouts" .= fmap payoutsRec (MS.assocs m)]workIndexJSON :: forall t. (t -> Value) -> WorkIndex t -> ValueworkIndexJSON leJSON (WorkIndex widx) =v1 $obj ["workIndex" .= fmap widxRec (MS.assocs widx)]wherewidxRec :: (CreditTo, NonEmpty (Interval t)) -> ValuewidxRec (c, l) =object[ "creditTo" .= creditToJSON c,"intervals" .= (intervalJSON leJSON <$> L.toList l)]amendEventResultJSON :: (EventId, AmendmentId) -> ValueamendEventResultJSON (eid, aid) =object[ "replacement_event" .= idValue _EventId eid,"amendment_id" .= idValue _AmendmentId aid] - edit in server/Main.hs at line 5[96.1461][5.75241]
import Aftok.Auction (_AuctionId)import Aftok.Billing (_BillableId, _SubscriptionId) - edit in server/Main.hs at line 11
import Aftok.Payments.Types (_PaymentId) - edit in server/Main.hs at line 17
import Aftok.Snaplet.Json (idJSON) - edit in server/Main.hs at line 23
import Aftok.Types (_ProjectId) - replacement in server/Main.hs at line 87
serveJSON projectIdJSON $ method POST projectCreateHandlerserveJSON (idJSON "projectId" _ProjectId) $ method POST projectCreateHandler - replacement in server/Main.hs at line 92
serveJSON workIndexJSON $ method GET projectWorkIndexserveJSON (workIndexJSON keyedLogEntryJSON) $ method GET projectWorkIndex - replacement in server/Main.hs at line 96
serveJSON keyedLogEntryJSON $ method POST (logWorkHandler f)amendEventRoute = serveJSON amendmentIdJSON $ method PUT amendEventHandlerserveJSON extendedLogEntryJSON $ method POST (logWorkHandler f)amendEventRoute = serveJSON amendEventResultJSON $ method PUT amendEventHandler - replacement in server/Main.hs at line 99
serveJSON (fmap logEntryJSON) $ method GET userEventsserveJSON (fmap keyedLogEntryJSON) $ method GET userEvents - replacement in server/Main.hs at line 101
serveJSON workIndexJSON $ method GET userWorkIndexserveJSON (workIndexJSON keyedLogEntryJSON) $ method GET userWorkIndex - replacement in server/Main.hs at line 103
serveJSON auctionIdJSON $ method POST auctionCreateHandlerserveJSON (idJSON "auctionId" _AuctionId) $ method POST auctionCreateHandler - replacement in server/Main.hs at line 109
serveJSON billableIdJSON $ method POST billableCreateHandlerserveJSON (idJSON "billableId" _BillableId) $ method POST billableCreateHandler - replacement in server/Main.hs at line 113
serveJSON subscriptionIdJSON $ method POST subscribeHandlerserveJSON (idJSON "subscriptionId" _SubscriptionId) $ method POST subscribeHandler - replacement in server/Main.hs at line 124
serveJSON paymentIdJSON $serveJSON (idJSON "paymentId" _PaymentId) $