aboutsummaryrefslogtreecommitdiff
path: root/src/client/elm/Model/Income.elm
blob: 06ba77219865a4cbcdc9822bea0d9dc1cc935409 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
module Model.Income exposing
  ( Incomes
  , Income
  , IncomeId
  , incomesDecoder
  , incomeIdDecoder
  , incomeDefinedForAll
  , userCumulativeIncomeSince
  , cumulativeIncomesSince
  )

import Json.Decode as Json exposing ((:=))
import Time exposing (Time, hour)
import List exposing (..)
import Dict exposing (Dict)

import Model.Date exposing (timeDecoder)
import Model.User exposing (UserId, userIdDecoder)

import Utils.Maybe exposing (isJust, catMaybes, maybeToList)

type alias Incomes = Dict IncomeId Income

type alias IncomeId = Int

type alias Income =
  { userId : UserId
  , time : Float
  , amount : Int
  }

incomesDecoder : Json.Decoder Incomes
incomesDecoder = Json.map Dict.fromList (Json.list incomeWithIdDecoder)

incomeWithIdDecoder : Json.Decoder (IncomeId, Income)
incomeWithIdDecoder =
  Json.object2 (,)
    ("id" := incomeIdDecoder)
    incomeDecoder

incomeIdDecoder : Json.Decoder IncomeId
incomeIdDecoder = Json.int

incomeDecoder : Json.Decoder Income
incomeDecoder =
  Json.object3 Income
    ("userId" := userIdDecoder)
    ("date" := timeDecoder)
    ("amount" := Json.int)

incomeDefinedForAll : List UserId -> Incomes -> Maybe Time
incomeDefinedForAll userIds incomes =
  let userIncomes = List.map (\userId -> List.filter ((==) userId << .userId) << Dict.values <| incomes) userIds
      firstIncomes = map (head << sortBy .time) userIncomes
  in  if all isJust firstIncomes
        then head << reverse << List.sort << map .time << catMaybes <| firstIncomes
        else Nothing

userCumulativeIncomeSince : Time -> Time -> Incomes -> UserId -> Int
userCumulativeIncomeSince currentTime since incomes userId =
  incomes
    |> Dict.values
    |> List.filter (\income -> income.userId == userId)
    |> cumulativeIncomesSince currentTime since

cumulativeIncomesSince : Time -> Time -> (List Income) -> Int
cumulativeIncomesSince currentTime since incomes =
  cumulativeIncome currentTime (getOrderedIncomesSince since incomes)

getOrderedIncomesSince : Time -> List Income -> List Income
getOrderedIncomesSince time incomes =
  let mbStarterIncome = getIncomeAt time incomes
      orderedIncomesSince = filter (\income -> income.time >= time) incomes
  in  (maybeToList mbStarterIncome) ++ orderedIncomesSince

getIncomeAt : Time -> List Income -> Maybe Income
getIncomeAt time incomes =
  case incomes of
    [x] ->
      if x.time < time
        then Just { userId = x.userId, time = time, amount = x.amount }
        else Nothing
    x1 :: x2 :: xs ->
      if x1.time < time && x2.time > time
        then Just { userId = x2.userId, time = time, amount = x2.amount }
        else getIncomeAt time (x2 :: xs)
    [] ->
      Nothing

cumulativeIncome : Time -> List Income -> Int
cumulativeIncome currentTime incomes =
  getIncomesWithDuration currentTime (List.sortBy .time incomes)
    |> map durationIncome
    |> sum

getIncomesWithDuration : Time -> List Income -> List (Float, Int)
getIncomesWithDuration currentTime incomes =
  case incomes of
    [] ->
      []
    [income] ->
      [(currentTime - income.time, income.amount)]
    (income1 :: income2 :: xs) ->
      (income2.time - income1.time, income1.amount) :: (getIncomesWithDuration currentTime (income2 :: xs))

durationIncome : (Float, Int) -> Int
durationIncome (duration, income) =
  duration * toFloat income / (hour * 24 * 365 / 12)
    |> truncate