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
|