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 exposing
( Incomes
, Income
, IncomeId
, incomesDecoder
, incomeIdDecoder
, incomeDefinedForAll
, userCumulativeIncomeSince
, cumulativeIncomesSince
)
import Dict exposing (Dict)
import Json.Decode as Decode exposing (Decoder)
import List exposing (..)
import Maybe.Extra as Maybe
import Time exposing (Time, hour)
import Utils.Json as Json
import Model.Date exposing (timeDecoder)
import Model.User exposing (UserId, userIdDecoder)
type alias Incomes = Dict IncomeId Income
type alias IncomeId = Int
type alias Income =
{ userId : UserId
, time : Float
, amount : Int
}
incomesDecoder : Decoder Incomes
incomesDecoder =
Json.dictDecoder (Decode.field "id" incomeIdDecoder) <|
Decode.map3 Income
(Decode.field "userId" userIdDecoder)
(Decode.field "date" timeDecoder)
(Decode.field "amount" Decode.int)
incomeIdDecoder : Decoder IncomeId
incomeIdDecoder = Decode.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 Maybe.isJust firstIncomes
then head << reverse << List.sort << map .time << Maybe.values <| 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 (Maybe.toList 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 = x1.userId, time = time, amount = x1.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
|