Begin adding parsing for project detail.

[?]
Jan 30, 2021, 2:05 AM
7TQPQW3NPNUK6CMTOT5ZE4MDENJ5SUOJ2VF2M4JGKHLZHXVX4F3QC

Dependencies

  • [2] GLQSD33Y Use mock capability for overview init.
  • [3] QU5FW67R Add project selection to time tracker.
  • [4] JXG3FCXY Upgrade ps + halogen versions.
  • [5] RSF6UAJK Break out api module for timeline.
  • [6] B4MTB6UO Persist project across pages.
  • [7] Z5KNL332 Add skeleton of project overview HTML.
  • [8] QMEYU4MW Add display for prior intervals.
  • [9] WRPIYG3E Use project listing functionality to check for whether we have a cookie.
  • [10] NJNMO72S Add zcash.com submodule and update client to modern halogen.
  • [11] IR75ZMX3 Return actual events for interval ends, not just timestamps.
  • [12] QH4UB73N Format with purty.
  • [13] OUR4PAOT Use local dates for display of intervals.
  • [14] PPW6ROC5 Render project data
  • [15] QAC2QJ32 Add project overview page to client.
  • [16] ZIG57EE6 Fix project selection, end log end on project switch.
  • [17] NAFJ6RB3 Minor module reorg.
  • [18] SAESJLLY Initial experiments in hash routing.
  • [19] O2BZOX7M Add signup form, captcha check.
  • [20] J6S23MDG Use server timestamps for interval start and end.
  • [*] RB2ETNIF Add skeletal PureScript client project.

Change contents

  • edit in client/src/Aftok/Api/Json.purs at line 75
    [3.2782]
    [3.2782]
    other -> throwError $ Error { status: Just other, message: r.statusText }
    parseDatedResponseMay ::
    forall t.
    Traversable t =>
    DecodeJson (t String) =>
    Either AJAX.Error (Response Json) ->
    ExceptT APIError Effect (Maybe (t Instant))
    parseDatedResponseMay = case _ of
    Left err -> throwError $ Error { status: Nothing, message: printError err }
    Right r -> case r.status of
    StatusCode 403 -> throwError $ Forbidden
    StatusCode 404 -> pure Nothing
    StatusCode 200 -> withExceptT (ParseFailure r.body) $ Just <<< map fromDateTime <$> decodeDatedJson r.body
  • replacement in client/src/Aftok/Api/Project.purs at line 4
    [3.2947][3.2947:3103]()
    import Control.Monad.Except.Trans (ExceptT, runExceptT, except, withExceptT)
    import Control.Monad.Error.Class (throwError)
    import Data.Argonaut.Core (Json)
    [3.2947]
    [3.3103]
    import Control.Monad.Except.Trans (runExceptT)
    -- import Control.Monad.Except.Trans (ExceptT, runExceptT, except, withExceptT)
    -- import Control.Monad.Error.Class (throwError)
    -- import Data.Argonaut.Core (Json)
  • replacement in client/src/Aftok/Api/Project.purs at line 9
    [3.3168][3.3168:3197]()
    import Data.Bifunctor (lmap)
    [3.3168]
    [3.3197]
    -- import Data.Bifunctor (lmap)
  • replacement in client/src/Aftok/Api/Project.purs at line 11
    [3.3229][3.3229:3297]()
    import Data.Either (Either(..), note)
    import Data.Maybe (Maybe(..))
    [3.3229]
    [3.5]
    import Data.DateTime.Instant (toDateTime)
    import Data.Either (Either(..))
    import Data.Foldable (class Foldable, foldMapDefaultR)
  • replacement in client/src/Aftok/Api/Project.purs at line 16
    [3.3333][3.3333:3365](),[3.3365][3.27:67](),[3.67][3.3399:3434](),[3.3399][3.3399:3434](),[3.3434][3.68:97](),[3.97][3.3469:3492](),[3.3469][3.3469:3492]()
    import Data.Rational (Rational)
    import Data.Time.Duration (Hours, Days)
    import Data.Traversable (traverse)
    import Data.UUID (parseUUID)
    import Effect (Effect)
    [3.3333]
    [3.3492]
    import Data.Rational (Rational, (%))
    import Data.Time.Duration (Hours(..), Days(..))
    import Data.Traversable (class Traversable, traverse)
    -- import Effect (Effect)
  • replacement in client/src/Aftok/Api/Project.purs at line 22
    [3.3542][3.3542:3616]()
    import Affjax (get, printError)
    import Affjax.StatusCode (StatusCode(..))
    [3.3542]
    [3.3616]
    import Affjax (get)
  • replacement in client/src/Aftok/Api/Project.purs at line 25
    [3.3670][3.98:113](),[3.113][3.3681:3699](),[3.3681][3.3681:3699]()
    ( UserId(..)
    , ProjectId(..)
    [3.3670]
    [3.3699]
    ( UserId
    , ProjectId
  • replacement in client/src/Aftok/Api/Project.purs at line 29
    [3.3726][3.3726:3779]()
    ( APIError(..) )
    import Aftok.Api.Json (parseDate)
    [3.3726]
    [3.3779]
    ( APIError )
    import Aftok.Api.Json
    ( decompose
    , parseDatedResponse
    )
    data DepreciationFn
    = LinearDepreciation { undep :: Days, dep :: Days }
    instance decodeDepreciationFn :: DecodeJson DepreciationFn where
    decodeJson json = do
    x <- decodeJson json
    dtype <- x .: "type"
    args <- x .: "arguments"
    case dtype of
    "LinearDepreciation" -> do
    undep <- Days <$> args .: "undep"
    dep <- Days <$> args .: "dep"
    pure $ LinearDepreciation { undep, dep }
    other -> Left $ "Unrecognized depreciation function: " <> other
  • edit in client/src/Aftok/Api/Project.purs at line 56
    [3.138]
    [3.3916]
    , depFn :: DepreciationFn
  • replacement in client/src/Aftok/Api/Project.purs at line 59
    [3.3921][3.3921:3978]()
    derive instance newtypeProject :: Newtype (Project' a) _
    [3.3921]
    [3.3978]
    derive instance projectNewtype :: Newtype (Project' a) _
    derive instance projectFunctor :: Functor Project'
    instance projectFoldable :: Foldable Project' where
    foldr f b (Project' p) = f (p.inceptionDate) b
    foldl f b (Project' p) = f b (p.inceptionDate)
    foldMap = foldMapDefaultR
    instance projectTraversable :: Traversable Project' where
    traverse f (Project' p) = Project' <<< (\b -> p { inceptionDate = b }) <$> f (p.inceptionDate)
    sequence = traverse identity
  • replacement in client/src/Aftok/Api/Project.purs at line 75
    [3.140][3.140:214]()
    data DepreciationFn
    = LinearDepreciation { undep :: Days, dep :: Days }
    [3.140]
    [3.4014]
    instance decodeJsonProject :: DecodeJson (Project' String) where
    decodeJson json = do
    x <- decodeJson json
    project <- x .: "project"
    projectId <- x .: "projectId"
    projectName <- project .: "projectName"
    inceptionDate <- project .: "inceptionDate"
    initiator <- project .: "initiator"
    depFn <- project .: "depreciationFn"
    pure $ Project' { projectId, projectName, inceptionDate, initiator, depFn }
  • replacement in client/src/Aftok/Api/Project.purs at line 86
    [3.4015][3.215:290]()
    newtype ProjectUserData' date
    = ProjectUserData'
    { userName :: String
    [3.4015]
    [3.290]
    newtype Contributor' date
    = Contributor'
    { userId :: UserId
    , handle :: String
  • replacement in client/src/Aftok/Api/Project.purs at line 91
    [3.311][3.311:377]()
    , totalContribution :: Hours
    , currentPayoutRatio :: Rational
    [3.311]
    [3.377]
    , timeDevoted :: Hours
    , revShare :: Rational
  • replacement in client/src/Aftok/Api/Project.purs at line 95
    [3.342][3.342:391]()
    type ProjectUserData = ProjectUserData' DateTime
    [3.342]
    [3.381]
    instance decodeJsonContributor :: DecodeJson (Contributor' String) where
    decodeJson json = do
    x <- decodeJson json
    userId <- x .: "userId"
    handle <- x .: "username"
    joinedOn <- x .: "joinedOn"
    timeDevoted <- Hours <$> x .: "timeDevoted"
    revShareObj <- x .: "revenueShare"
    num <- revShareObj .: "numerator"
    den <- revShareObj .: "denominator"
    let revShare = num % den
    pure $ Contributor' { userId, handle, joinedOn, timeDevoted, revShare }
  • replacement in client/src/Aftok/Api/Project.purs at line 111
    [3.458][3.458:550]()
    , depreciation :: DepreciationFn
    , contributors :: M.Map UserId (ProjectUserData' date)
    [3.458]
    [3.550]
    , contributors :: M.Map UserId (Contributor' date)
  • replacement in client/src/Aftok/Api/Project.purs at line 116
    [3.601][3.4015:4059](),[3.4015][3.4015:4059]()
    data ProjectEvent
    = ProjectChange Project
    [3.601]
    [3.4059]
    type ProjectDetailJson date =
    { project :: Project' date
    , contributors :: Array (Contributor' date)
    }
  • replacement in client/src/Aftok/Api/Project.purs at line 121
    [3.4060][3.4060:4125]()
    instance decodeJsonProject :: DecodeJson (Project' String) where
    [3.4060]
    [3.4125]
    instance decodeJsonProjectDetail :: DecodeJson (ProjectDetail' String) where
  • replacement in client/src/Aftok/Api/Project.purs at line 125
    [3.4203][3.4203:4470](),[3.4470][3.602:696](),[3.696][3.4551:4624](),[3.4551][3.4551:4624]()
    projectIdStr <- x .: "projectId"
    projectId <- ProjectId <$> (note "Failed to decode project UUID" $ parseUUID projectIdStr)
    projectName <- project .: "projectName"
    inceptionDate <- project .: "inceptionDate"
    initiatorStr <- project .: "initiator"
    initiator <- UserId <$> (note "Failed to decode initiator UUID" $ parseUUID initiatorStr)
    pure $ Project' { projectId, projectName, inceptionDate, initiator }
    [3.4203]
    [3.4624]
    contributors <- x .: "contributors"
    pure $ ProjectDetail' { project, contributors }
  • edit in client/src/Aftok/Api/Project.purs at line 128
    [3.4625][3.4625:4780]()
    newtype Member' date
    = Member'
    { userId :: UserId
    , handle :: String
    , joinedOn :: date
    , timeDevoted :: Hours
    , revShareFrac :: Rational
    }
  • replacement in client/src/Aftok/Api/Project.purs at line 130
    [3.4852][3.4852:5349]()
    result <- get RF.json "/api/projects"
    EC.liftEffect <<< runExceptT
    $ case result of
    Left err -> throwError $ Error { status: Nothing, message: printError err }
    Right r -> case r.status of
    StatusCode 403 -> throwError Forbidden
    StatusCode 200 -> do
    records <- except $ lmap (ParseFailure r.body) (decodeJson r.body)
    traverse parseProject records
    other -> throwError $ Error { status: Just other, message: r.statusText }
    [3.4852]
    [3.5349]
    response <- get RF.json "/api/projects"
    EC.liftEffect
    <<< runExceptT
    <<< map decompose
    <<< map (map toDateTime)
    $ parseDatedResponse response
  • replacement in client/src/Aftok/Api/Project.purs at line 137
    [3.5350][3.5350:5618]()
    parseProject :: Json -> ExceptT APIError Effect Project
    parseProject json = do
    Project' p <- except <<< lmap (ParseFailure json) $ decodeJson json
    pdate <- withExceptT (ParseFailure json) $ parseDate p.inceptionDate
    pure $ Project' (p { inceptionDate = pdate })
    [3.5350]
    [3.5618]
    -- getProjectDetail :: ProjectId -> Aff (Maybe ProjectDetail)
    -- getProjectDetail pid = do
    -- response <- get RF.json ("/api/user/projects/" <> pidStr pid)
    -- EC.liftEffect
    -- <<< map (\dt -> ProjectDetail' {
    -- project: dt.project,
    -- contributors: M.fromFoldable $ map (\c -> (Tuple c.userId c)) dt.contributors
    -- })
    -- $ parsed
  • edit in client/src/Aftok/Api/Project.purs at line 147
    [3.5619]
  • replacement in client/src/Aftok/Overview.purs at line 13
    [3.535][2.45:73]()
    import Data.DateTime (date)
    [3.535]
    [3.578]
    import Data.DateTime (DateTime, date)
  • replacement in client/src/Aftok/Overview.purs at line 63
    [3.899][2.316:394]()
    (Project, Project'(..), ProjectEvent(..), ProjectDetail, ProjectDetail'(..)
    [3.899]
    [3.986]
    (Project, Project'(..), ProjectDetail, ProjectDetail'(..)
  • replacement in client/src/Aftok/Overview.purs at line 65
    [3.1009][3.1009:1034](),[3.1034][3.850:870]()
    , ProjectUserData'(..)
    , ProjectUserData
    [3.1009]
    [3.1034]
    , Contributor'(..)
  • replacement in client/src/Aftok/Overview.purs at line 86
    [3.9781][3.9781:9828]()
    = forall query. H.Slot query ProjectEvent id
    [3.9781]
    [3.2454]
    = forall query. H.Slot query ProjectList.Event id
  • replacement in client/src/Aftok/Overview.purs at line 104
    [3.6692][3.10048:10105](),[3.10048][3.10048:10105]()
    H.Component HH.HTML query OverviewInput ProjectEvent m
    [3.6692]
    [3.10105]
    H.Component HH.HTML query OverviewInput ProjectList.Event m
  • replacement in client/src/Aftok/Overview.purs at line 136
    [3.1491][3.1491:1614]()
    [ HH.slot _projectList unit (ProjectList.component system pcaps) st.selectedProject (Just <<< ProjectSelected) ]
    [3.1491]
    [3.1614]
    [ HH.slot
    _projectList
    unit
    (ProjectList.component system pcaps)
    st.selectedProject
    (Just <<< (\(ProjectList.ProjectChange p) -> ProjectSelected p))
    ]
  • replacement in client/src/Aftok/Overview.purs at line 168
    [3.1655][3.2660:2822](),[3.2660][3.2660:2822]()
    depreciationCols detail.depreciation <>
    [ colmd2 ((\(ProjectUserData' p) -> p.userName) <$> M.lookup project.initiator detail.contributors)
    [3.1655]
    [3.1656]
    depreciationCols project.depFn <>
    [ colmd2 ((\(Contributor' p) -> p.handle) <$> M.lookup project.initiator detail.contributors)
  • replacement in client/src/Aftok/Overview.purs at line 195
    [3.2303][3.2303:2571]()
    contributorCols :: ProjectUserData -> H.ComponentHTML OverviewAction Slots m
    contributorCols (ProjectUserData' pud) =
    let pct = maybe "N/A" (\f -> F.toString (f * F.fromInt 100)) (F.fromNumber (R.toNumber pud.currentPayoutRatio) :: Maybe (F.Fixed F.P10000))
    [3.2303]
    [3.2571]
    contributorCols :: Contributor' DateTime -> H.ComponentHTML OverviewAction Slots m
    contributorCols (Contributor' pud) =
    let pct = maybe "N/A" (\f -> F.toString (f * F.fromInt 100)) (F.fromNumber (R.toNumber pud.revShare) :: Maybe (F.Fixed F.P10000))
  • replacement in client/src/Aftok/Overview.purs at line 200
    [3.2647][3.2647:2682]()
    [ colmd2 (Just pud.userName)
    [3.2647]
    [3.2682]
    [ colmd2 (Just pud.handle)
  • replacement in client/src/Aftok/Overview.purs at line 202
    [3.2734][3.2734:2794]()
    , colmd2 (Just $ show (unwrap pud.totalContribution))
    [3.2734]
    [3.2794]
    , colmd2 (Just $ show (unwrap pud.timeDevoted))
  • replacement in client/src/Aftok/Overview.purs at line 339
    [3.3921][3.11331:11425]()
    eval :: OverviewAction -> H.HalogenM OverviewState OverviewAction Slots ProjectEvent m Unit
    [3.3921]
    [3.11425]
    eval :: OverviewAction -> H.HalogenM OverviewState OverviewAction Slots ProjectList.Event m Unit
  • replacement in client/src/Aftok/Overview.purs at line 351
    [3.11730][3.11730:11770]()
    H.raise (ProjectChange p)
    [3.11730]
    [3.11770]
    H.raise (ProjectList.ProjectChange p)
  • replacement in client/src/Aftok/Overview.purs at line 355
    [3.4105][2.681:782]()
    setProjectDetail :: ProjectId -> H.HalogenM OverviewState OverviewAction Slots ProjectEvent m Unit
    [3.4105]
    [2.782]
    setProjectDetail :: ProjectId -> H.HalogenM OverviewState OverviewAction Slots ProjectList.Event m Unit
  • edit in client/src/Aftok/Overview.purs at line 376
    [2.1255]
    [2.1255]
    , depFn: LinearDepreciation { undep: Days 30.0, dep: Days 300.0 }
  • replacement in client/src/Aftok/Overview.purs at line 378
    [2.1267][2.1267:1436]()
    , depreciation: LinearDepreciation { undep: Days 30.0, dep: Days 300.0 }
    , contributors: M.singleton uid $ ProjectUserData'
    { userName: "Joe"
    [2.1267]
    [2.1436]
    , contributors: M.singleton uid $ Contributor'
    { userId: uid
    , handle: "Joe"
  • replacement in client/src/Aftok/Overview.purs at line 382
    [2.1460][2.1460:1546]()
    , totalContribution: Hours 100.0
    , currentPayoutRatio: 55 R.% 100
    [2.1460]
    [2.1546]
    , timeDevoted: Hours 100.0
    , revShare: 55 R.% 100
  • replacement in client/src/Aftok/ProjectList.purs at line 33
    [3.365][3.7263:7288]()
    type Output
    = Project
    [3.365]
    [3.7288]
    data Event
    = ProjectChange Project
  • replacement in client/src/Aftok/ProjectList.purs at line 37
    [3.7302][3.7302:7344]()
    = forall query. H.Slot query Project id
    [3.7302]
    [3.7344]
    = forall query. H.Slot query Event id
  • replacement in client/src/Aftok/ProjectList.purs at line 57
    [3.12287][3.7403:7446]()
    H.Component HH.HTML query Input Output m
    [3.12287]
    [3.7446]
    H.Component HH.HTML query Input Event m
  • replacement in client/src/Aftok/ProjectList.purs at line 99
    [3.1519][3.7575:7638]()
    eval :: Action -> H.HalogenM CState Action () Project m Unit
    [3.1519]
    [3.13580]
    eval :: Action -> H.HalogenM CState Action () Event m Unit
  • replacement in client/src/Aftok/ProjectList.purs at line 109
    [3.13938][3.13938:13987]()
    traverse_ H.raise (index projects (i - 1))
    [3.13938]
    [3.1840]
    traverse_ (H.raise <<< ProjectChange) (index projects (i - 1))
  • edit in client/src/Aftok/Timeline.purs at line 66
    [3.7753][3.25769:25790](),[3.25769][3.25769:25790]()
    , ProjectEvent(..)
  • replacement in client/src/Aftok/Timeline.purs at line 113
    [3.26325][3.26325:26372]()
    = forall query. H.Slot query ProjectEvent id
    [3.26325]
    [3.3687]
    = forall query. H.Slot query ProjectList.Event id
  • replacement in client/src/Aftok/Timeline.purs at line 135
    [3.7840][3.7840:7889]()
    H.Component HH.HTML query Input ProjectEvent m
    [3.7840]
    [3.27025]
    H.Component HH.HTML query Input ProjectList.Event m
  • replacement in client/src/Aftok/Timeline.purs at line 169
    [3.28037][3.7932:8059]()
    [ HH.slot _projectList unit (ProjectList.component system pcaps) st.selectedProject (Just <<< ProjectSelected) ]
    [3.28037]
    [3.28171]
    [ HH.slot
    _projectList
    unit
    (ProjectList.component system pcaps)
    st.selectedProject
    (Just <<< (\(ProjectList.ProjectChange p) -> ProjectSelected p))
    ]
  • replacement in client/src/Aftok/Timeline.purs at line 198
    [3.4813][3.29110:29204]()
    eval :: TimelineAction -> H.HalogenM TimelineState TimelineAction Slots ProjectEvent m Unit
    [3.4813]
    [3.29204]
    eval :: TimelineAction -> H.HalogenM TimelineState TimelineAction Slots ProjectList.Event m Unit
  • replacement in client/src/Aftok/Timeline.purs at line 212
    [3.29767][3.29767:29801]()
    H.raise (ProjectChange p)
    [3.29767]
    [3.29801]
    H.raise (ProjectList.ProjectChange p)
  • replacement in client/src/Aftok/Timeline.purs at line 228
    [3.1970][3.30359:30450]()
    logStart :: Project -> H.HalogenM TimelineState TimelineAction Slots ProjectEvent m Unit
    [3.1970]
    [3.30450]
    logStart :: Project -> H.HalogenM TimelineState TimelineAction Slots ProjectList.Event m Unit
  • replacement in client/src/Aftok/Timeline.purs at line 235
    [3.2528][3.30667:30756]()
    logEnd :: Project -> H.HalogenM TimelineState TimelineAction Slots ProjectEvent m Unit
    [3.2528]
    [3.30756]
    logEnd :: Project -> H.HalogenM TimelineState TimelineAction Slots ProjectList.Event m Unit
  • replacement in client/src/Aftok/Timeline.purs at line 245
    [3.6289][3.31066:31167]()
    setStateForProject :: Project -> H.HalogenM TimelineState TimelineAction Slots ProjectEvent m Unit
    [3.6289]
    [3.31167]
    setStateForProject :: Project -> H.HalogenM TimelineState TimelineAction Slots ProjectList.Event m Unit
  • edit in client/src/Aftok/Types.purs at line 4
    [3.3258]
    [3.2966]
    import Data.Argonaut.Decode (class DecodeJson, decodeJson)
  • edit in client/src/Aftok/Types.purs at line 8
    [3.8100]
    [3.3009]
    import Data.Either (note)
  • replacement in client/src/Aftok/Types.purs at line 14
    [3.7262][3.8164:8198]()
    import Data.UUID (UUID, toString)
    [3.7262]
    [3.3526]
    import Data.UUID (UUID, toString, parseUUID)
  • edit in client/src/Aftok/Types.purs at line 89
    [3.10442]
    [3.7643]
    derive instance userIdNewtype :: Newtype UserId _
  • replacement in client/src/Aftok/Types.purs at line 91
    [3.7644][3.8271:8321]()
    derive instance userIdNewtype :: Newtype UserId _
    [3.7644]
    [3.5637]
    instance userIdDecodeJson :: DecodeJson UserId where
    decodeJson json = do
    uuidStr <- decodeJson json
    UserId <$> (note "Failed to decode user UUID" $ parseUUID uuidStr)
  • edit in client/src/Aftok/Types.purs at line 100
    [3.5717]
    [3.37239]
    derive instance projectIdOrd :: Ord ProjectId
    derive instance projectIdNewtype :: Newtype ProjectId _
  • replacement in client/src/Aftok/Types.purs at line 103
    [3.37240][3.5717:5773](),[3.5717][3.5717:5773]()
    derive instance projectIdNewtype :: Newtype ProjectId _
    [3.37240]
    [3.11908]
    instance projectIdDecodeJson :: DecodeJson ProjectId where
    decodeJson json = do
    uuidStr <- decodeJson json
    ProjectId <$> (note "Failed to decode project UUID" $ parseUUID uuidStr)
  • edit in client/src/Aftok/Types.purs at line 108
    [3.11909]
    [3.5774]
  • replacement in client/src/Main.purs at line 25
    [3.9265][3.8364:8428]()
    import Aftok.Api.Project (Project, ProjectEvent(ProjectChange))
    [3.9265]
    [3.1541]
    import Aftok.Api.Project (Project)
  • replacement in client/src/Main.purs at line 95
    [3.1894][3.7130:7161]()
    | ProjectAction ProjectEvent
    [3.1894]
    [3.1894]
    | ProjectAction ProjectList.Event
  • replacement in client/src/Main.purs at line 183
    [3.41804][3.41804:41886]()
    ProjectAction (ProjectChange p) -> H.modify_ (_ { selectedProject = Just p })
    [3.41804]
    [3.2454]
    ProjectAction (ProjectList.ProjectChange p) -> H.modify_ (_ { selectedProject = Just p })