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
|
module Model.Income
( Incomes
, Income
, IncomeId
, incomesDecoder
, incomeIdDecoder
, incomeDefinedForAll
, cumulativeIncomesSince
) where
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
, creation : Time
, 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)
("creation" := 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 .creation) userIncomes
in if all isJust firstIncomes
then head << reverse << List.sort << map .creation << catMaybes <| firstIncomes
else Nothing
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.creation >= time) incomes
in (maybeToList mbStarterIncome) ++ orderedIncomesSince
getIncomeAt : Time -> List Income -> Maybe Income
getIncomeAt time incomes =
case incomes of
[x] ->
if x.creation < time
then Just { userId = x.userId, creation = time, amount = x.amount }
else Nothing
x1 :: x2 :: xs ->
if x1.creation < time && x2.creation > time
then Just { userId = x2.userId, creation = time, amount = x2.amount }
else getIncomeAt time (x2 :: xs)
[] ->
Nothing
cumulativeIncome : Time -> List Income -> Int
cumulativeIncome currentTime incomes =
getIncomesWithDuration currentTime (List.sortBy .creation incomes)
|> map durationIncome
|> sum
getIncomesWithDuration : Time -> List Income -> List (Float, Int)
getIncomesWithDuration currentTime incomes =
case incomes of
[] ->
[]
[income] ->
[(currentTime - income.creation, income.amount)]
(income1 :: income2 :: xs) ->
(income2.creation - income1.creation, income1.amount) :: (getIncomesWithDuration currentTime (income2 :: xs))
durationIncome : (Float, Int) -> Int
durationIncome (duration, income) =
duration * toFloat income / (hour * 24 * 365 / 12)
|> truncate
|