From cfca18262c1ff48dcb683ddab7d03cf8e55573ff Mon Sep 17 00:00:00 2001 From: Joris Date: Fri, 24 Mar 2017 09:21:04 +0000 Subject: Features/categories --- src/client/elm/Model/Category.elm | 35 ++++++++++++++++++ src/client/elm/Model/Conf.elm | 4 +-- src/client/elm/Model/Date.elm | 8 ++--- src/client/elm/Model/Income.elm | 37 ++++++++----------- src/client/elm/Model/Init.elm | 22 +++++++----- src/client/elm/Model/InitResult.elm | 16 ++++----- src/client/elm/Model/Payer.elm | 9 +++-- src/client/elm/Model/Payment.elm | 61 ++++++++++++++++++-------------- src/client/elm/Model/PaymentCategory.elm | 48 +++++++++++++++++++++++++ src/client/elm/Model/Size.elm | 10 +++--- src/client/elm/Model/Translations.elm | 25 +++++++------ src/client/elm/Model/User.elm | 24 ++++++------- 12 files changed, 193 insertions(+), 106 deletions(-) create mode 100644 src/client/elm/Model/Category.elm create mode 100644 src/client/elm/Model/PaymentCategory.elm (limited to 'src/client/elm/Model') diff --git a/src/client/elm/Model/Category.elm b/src/client/elm/Model/Category.elm new file mode 100644 index 0000000..8b653a7 --- /dev/null +++ b/src/client/elm/Model/Category.elm @@ -0,0 +1,35 @@ +module Model.Category exposing + ( Categories + , Category + , CategoryId + , categoriesDecoder + , categoryIdDecoder + , empty + ) + +import Json.Decode as Decode exposing (Decoder) +import Utils.Json as Json +import Dict exposing (Dict) + +type alias Categories = Dict CategoryId Category + +type alias CategoryId = Int + +type alias Category = + { name : String + , color : String + } + +categoriesDecoder : Decoder Categories +categoriesDecoder = + Json.dictDecoder (Decode.field "id" categoryIdDecoder) <| + Decode.map2 + Category + (Decode.field "name" Decode.string) + (Decode.field "color" Decode.string) + +categoryIdDecoder : Decoder CategoryId +categoryIdDecoder = Decode.int + +empty : Categories +empty = Dict.empty diff --git a/src/client/elm/Model/Conf.elm b/src/client/elm/Model/Conf.elm index ec04622..308fa04 100644 --- a/src/client/elm/Model/Conf.elm +++ b/src/client/elm/Model/Conf.elm @@ -3,11 +3,11 @@ module Model.Conf exposing , confDecoder ) -import Json.Decode exposing (..) +import Json.Decode as Decode exposing (Decoder) type alias Conf = { currency : String } confDecoder : Decoder Conf -confDecoder = object1 Conf ("currency" := string) +confDecoder = Decode.map Conf (Decode.field "currency" Decode.string) diff --git a/src/client/elm/Model/Date.elm b/src/client/elm/Model/Date.elm index f3c9b91..bfba02f 100644 --- a/src/client/elm/Model/Date.elm +++ b/src/client/elm/Model/Date.elm @@ -4,12 +4,12 @@ module Model.Date exposing ) import Date as Date exposing (Date) +import Json.Decode as Decode exposing (Decoder) +import Json.Decode.Extra as Decode import Time exposing (Time) -import Json.Decode as Json exposing (..) - timeDecoder : Decoder Time -timeDecoder = Json.map Date.toTime dateDecoder +timeDecoder = Decode.map Date.toTime dateDecoder dateDecoder : Decoder Date -dateDecoder = customDecoder string Date.fromString +dateDecoder = Decode.string |> Decode.andThen (Date.fromString >> Decode.fromResult) diff --git a/src/client/elm/Model/Income.elm b/src/client/elm/Model/Income.elm index a5ca34b..34578c6 100644 --- a/src/client/elm/Model/Income.elm +++ b/src/client/elm/Model/Income.elm @@ -9,7 +9,8 @@ module Model.Income exposing , cumulativeIncomesSince ) -import Json.Decode as Json exposing ((:=)) +import Json.Decode as Decode exposing (Decoder) +import Utils.Json as Json import Time exposing (Time, hour) import List exposing (..) import Dict exposing (Dict) @@ -17,7 +18,7 @@ import Dict exposing (Dict) import Model.Date exposing (timeDecoder) import Model.User exposing (UserId, userIdDecoder) -import Utils.Maybe exposing (isJust, catMaybes, maybeToList) +import Utils.Maybe as Maybe type alias Incomes = Dict IncomeId Income @@ -29,31 +30,23 @@ type alias Income = , amount : Int } -incomesDecoder : Json.Decoder Incomes -incomesDecoder = Json.map Dict.fromList (Json.list incomeWithIdDecoder) +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) -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) +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 isJust firstIncomes - then head << reverse << List.sort << map .time << catMaybes <| firstIncomes + in if all Maybe.isJust firstIncomes + then head << reverse << List.sort << map .time << Maybe.cat <| firstIncomes else Nothing userCumulativeIncomeSince : Time -> Time -> Incomes -> UserId -> Int @@ -71,7 +64,7 @@ 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 + in (Maybe.toList mbStarterIncome) ++ orderedIncomesSince getIncomeAt : Time -> List Income -> Maybe Income getIncomeAt time incomes = diff --git a/src/client/elm/Model/Init.elm b/src/client/elm/Model/Init.elm index 3a86dba..db7069f 100644 --- a/src/client/elm/Model/Init.elm +++ b/src/client/elm/Model/Init.elm @@ -3,23 +3,29 @@ module Model.Init exposing , initDecoder ) -import Json.Decode as Json exposing ((:=)) +import Json.Decode as Decode exposing (Decoder) import Model.Payment exposing (Payments, paymentsDecoder) -import Model.Income exposing (Incomes, incomesDecoder) import Model.User exposing (Users, UserId, usersDecoder, userIdDecoder) +import Model.Income exposing (Incomes, incomesDecoder) +import Model.Category exposing (Categories, categoriesDecoder) +import Model.PaymentCategory exposing (PaymentCategories, paymentCategoriesDecoder) type alias Init = { users : Users , me : UserId , payments : Payments , incomes : Incomes + , categories : Categories + , paymentCategories : PaymentCategories } -initDecoder : Json.Decoder Init +initDecoder : Decoder Init initDecoder = - Json.object4 Init - ("users" := usersDecoder) - ("me" := userIdDecoder) - ("payments" := paymentsDecoder) - ("incomes" := incomesDecoder) + Decode.map6 Init + (Decode.field "users" usersDecoder) + (Decode.field "me" userIdDecoder) + (Decode.field "payments" paymentsDecoder) + (Decode.field "incomes" incomesDecoder) + (Decode.field "categories" categoriesDecoder) + (Decode.field "paymentCategories" paymentCategoriesDecoder) diff --git a/src/client/elm/Model/InitResult.elm b/src/client/elm/Model/InitResult.elm index c8da533..7ce0be2 100644 --- a/src/client/elm/Model/InitResult.elm +++ b/src/client/elm/Model/InitResult.elm @@ -3,7 +3,7 @@ module Model.InitResult exposing , initResultDecoder ) -import Json.Decode as Json exposing ((:=)) +import Json.Decode as Decode exposing (Decoder) import Model.Init exposing (Init, initDecoder) @@ -12,17 +12,17 @@ type InitResult = | InitSuccess Init | InitError String -initResultDecoder : Json.Decoder InitResult -initResultDecoder = ("tag" := Json.string) `Json.andThen` initResultDecoderWithTag +initResultDecoder : Decoder InitResult +initResultDecoder = (Decode.field "tag" Decode.string) |> Decode.andThen initResultDecoderWithTag -initResultDecoderWithTag : String -> Json.Decoder InitResult +initResultDecoderWithTag : String -> Decoder InitResult initResultDecoderWithTag tag = case tag of "InitEmpty" -> - Json.succeed InitEmpty + Decode.succeed InitEmpty "InitSuccess" -> - Json.map InitSuccess ("contents" := initDecoder) + Decode.map InitSuccess (Decode.field "contents" initDecoder) "InitError" -> - Json.map InitError ("contents" := Json.string) + Decode.map InitError (Decode.field "contents" Decode.string) _ -> - Json.fail <| "got " ++ tag ++ " for InitResult" + Decode.fail <| "got " ++ tag ++ " for InitResult" diff --git a/src/client/elm/Model/Payer.elm b/src/client/elm/Model/Payer.elm index e5a4b65..1663273 100644 --- a/src/client/elm/Model/Payer.elm +++ b/src/client/elm/Model/Payer.elm @@ -6,7 +6,6 @@ module Model.Payer exposing , useIncomesFrom ) -import Json.Decode as Json exposing (..) import Dict exposing (..) import List import Maybe @@ -54,7 +53,7 @@ getOrderedExceedingPayers currentTime users incomes payments = mbMaxRatio = postPaymentPayers |> Dict.toList - |> List.map (.ratio << snd) + |> List.map (.ratio << Tuple.second) |> List.maximum in case mbMaxRatio of Just maxRatio -> @@ -110,15 +109,15 @@ getPayers currentTime users incomes payments = exceedingPayersFromAmounts : List (UserId, Int) -> List ExceedingPayer exceedingPayersFromAmounts userAmounts = - let mbMinAmount = List.minimum << List.map snd <| userAmounts + let mbMinAmount = List.minimum << List.map Tuple.second <| userAmounts in case mbMinAmount of Nothing -> [] Just minAmount -> userAmounts |> List.map (\userAmount -> - { userId = fst userAmount - , amount = snd userAmount - minAmount + { userId = Tuple.first userAmount + , amount = Tuple.second userAmount - minAmount } ) |> List.filter (\payer -> payer.amount > 0) diff --git a/src/client/elm/Model/Payment.elm b/src/client/elm/Model/Payment.elm index 5109b2f..f61ded8 100644 --- a/src/client/elm/Model/Payment.elm +++ b/src/client/elm/Model/Payment.elm @@ -6,6 +6,7 @@ module Model.Payment exposing , Frequency(..) , paymentsDecoder , paymentIdDecoder + , find , edit , delete , totalPayments @@ -18,15 +19,16 @@ module Model.Payment exposing import Date exposing (..) import Date.Extra.Core exposing (monthToInt, intToMonth) -import Json.Decode as Json exposing ((:=)) -import String +import Json.Decode as Decode exposing (Decoder) +import Json.Decode.Extra as Decode import List import Form.Validate as Validate exposing (Validation) -import Model.User exposing (UserId, userIdDecoder) import Model.Date exposing (dateDecoder) +import Model.User exposing (UserId, userIdDecoder) import Utils.List as List +import Utils.Search as Search perPage : Int perPage = 7 @@ -46,31 +48,36 @@ type alias PaymentId = Int type Frequency = Punctual | Monthly -paymentsDecoder : Json.Decoder Payments -paymentsDecoder = Json.list paymentDecoder +paymentsDecoder : Decoder Payments +paymentsDecoder = Decode.list paymentDecoder -paymentDecoder : Json.Decoder Payment +paymentDecoder : Decoder Payment paymentDecoder = - Json.object6 Payment - ("id" := paymentIdDecoder) - ("name" := Json.string) - ("cost" := Json.int) - ("date" := dateDecoder) - ("userId" := userIdDecoder) - ("frequency" := frequencyDecoder) - -paymentIdDecoder : Json.Decoder PaymentId -paymentIdDecoder = Json.int - -frequencyDecoder : Json.Decoder Frequency + Decode.map6 Payment + (Decode.field "id" paymentIdDecoder) + (Decode.field "name" Decode.string) + (Decode.field "cost" Decode.int) + (Decode.field "date" dateDecoder) + (Decode.field "userId" userIdDecoder) + (Decode.field "frequency" frequencyDecoder) + +paymentIdDecoder : Decoder PaymentId +paymentIdDecoder = Decode.int + +frequencyDecoder : Decoder Frequency frequencyDecoder = - Json.customDecoder - Json.string - (\input -> case input of - "Punctual" -> Ok Punctual - "Monthly" -> Ok Monthly - _ -> Err ("Could not deduce Punctual nor Monthly from " ++ input) - ) + let frequencyResult input = + case input of + "Punctual" -> Ok Punctual + "Monthly" -> Ok Monthly + _ -> Err ("Could not deduce Punctual nor Monthly from " ++ input) + in Decode.string |> Decode.andThen (Decode.fromResult << frequencyResult) + +find : PaymentId -> Payments -> Maybe Payment +find paymentId payments = + payments + |> List.filter (\p -> p.id == paymentId) + |> List.head edit : Payment -> Payments -> Payments edit payment payments = payment :: delete payment.id payments @@ -98,7 +105,7 @@ groupAndSortByMonth : Payments -> List ((Month, Int), Payments) groupAndSortByMonth payments = payments |> List.groupBy (\payment -> (Date.year payment.date, monthToInt << Date.month <| payment.date)) - |> List.sortBy fst + |> List.sortBy Tuple.first |> List.map (\((year, month), payments) -> ((intToMonth month, year), payments)) |> List.reverse @@ -118,7 +125,7 @@ paymentSort frequency = searchSuccess : String -> Payment -> Bool searchSuccess search { name, cost } = let searchSuccessWord word = - ( String.contains (String.toLower word) (String.toLower name) + ( String.contains (Search.format word) (Search.format name) || String.contains word (toString cost) ) in List.all searchSuccessWord (String.words search) diff --git a/src/client/elm/Model/PaymentCategory.elm b/src/client/elm/Model/PaymentCategory.elm new file mode 100644 index 0000000..87678fe --- /dev/null +++ b/src/client/elm/Model/PaymentCategory.elm @@ -0,0 +1,48 @@ +module Model.PaymentCategory exposing + ( PaymentCategories + , paymentCategoriesDecoder + , search + , isCategoryUnused + , set + , update + ) + +import Dict exposing (Dict) +import Json.Decode as Decode exposing (Decoder) + +import Model.Category exposing (CategoryId, categoryIdDecoder) +import Utils.Json as Json +import Utils.Search as Search + +type alias PaymentCategories = List PaymentCategory + +type alias PaymentCategory = + { name : String + , category : CategoryId + } + +paymentCategoriesDecoder : Decoder PaymentCategories +paymentCategoriesDecoder = + Decode.list <| Decode.map2 PaymentCategory + (Decode.field "name" Decode.string) + (Decode.field "category" categoryIdDecoder) + +search : String -> PaymentCategories -> Maybe CategoryId +search paymentName paymentCategories = + paymentCategories + |> List.filter (\pc -> Search.format pc.name == Search.format paymentName) + |> List.head + |> Maybe.map .category + +isCategoryUnused : CategoryId -> PaymentCategories -> Bool +isCategoryUnused category paymentCategories = + paymentCategories + |> List.filter ((==) category << .category) + |> List.isEmpty + +set : String -> CategoryId -> PaymentCategories -> PaymentCategories +set name category paymentCategories = update name name category paymentCategories + +update : String -> String -> CategoryId -> PaymentCategories -> PaymentCategories +update oldName newName category paymentCategories = + { name = newName, category = category } :: List.filter (\pc -> not <| Search.format pc.name == Search.format oldName) paymentCategories diff --git a/src/client/elm/Model/Size.elm b/src/client/elm/Model/Size.elm index b29e90b..f40fb01 100644 --- a/src/client/elm/Model/Size.elm +++ b/src/client/elm/Model/Size.elm @@ -3,15 +3,15 @@ module Model.Size exposing , sizeDecoder ) -import Json.Decode as Json exposing ((:=)) +import Json.Decode as Decode exposing (Decoder) type alias Size = { width: Int , height: Int } -sizeDecoder : Json.Decoder Size +sizeDecoder : Decoder Size sizeDecoder = - Json.object2 Size - ("width" := Json.int) - ("height" := Json.int) + Decode.map2 Size + (Decode.field "width" Decode.int) + (Decode.field "height" Decode.int) diff --git a/src/client/elm/Model/Translations.elm b/src/client/elm/Model/Translations.elm index 57409b0..9b314e1 100644 --- a/src/client/elm/Model/Translations.elm +++ b/src/client/elm/Model/Translations.elm @@ -7,13 +7,13 @@ module Model.Translations exposing ) import Maybe exposing (withDefault) -import Json.Decode as Json exposing ((:=)) +import Json.Decode as Decode exposing (Decoder) import String type alias Translations = List Translation -translationsDecoder : Json.Decoder Translations -translationsDecoder = Json.list translationDecoder +translationsDecoder : Decoder Translations +translationsDecoder = Decode.list translationDecoder type alias Translation = { key : String @@ -27,25 +27,24 @@ getTranslation key translations = |> List.head |> Maybe.map .message -translationDecoder : Json.Decoder Translation +translationDecoder : Decoder Translation translationDecoder = - Json.object2 Translation - ("key" := Json.string) - ("message" := Json.list partDecoder) + Decode.map2 Translation + (Decode.field "key" Decode.string) + (Decode.field "message" (Decode.list partDecoder)) type MessagePart = Order Int | Str String -partDecoder : Json.Decoder MessagePart -partDecoder = - ("tag" := Json.string) `Json.andThen` partDecoderWithTag +partDecoder : Decoder MessagePart +partDecoder = (Decode.field "tag" Decode.string) |> Decode.andThen partDecoderWithTag -partDecoderWithTag : String -> Json.Decoder MessagePart +partDecoderWithTag : String -> Decoder MessagePart partDecoderWithTag tag = case tag of - "Order" -> Json.object1 Order ("contents" := Json.int) - _ -> Json.object1 Str ("contents" := Json.string) + "Order" -> Decode.map Order (Decode.field "contents" Decode.int) + _ -> Decode.map Str (Decode.field "contents" Decode.string) ----- diff --git a/src/client/elm/Model/User.elm b/src/client/elm/Model/User.elm index 02f2cea..f6e8147 100644 --- a/src/client/elm/Model/User.elm +++ b/src/client/elm/Model/User.elm @@ -8,7 +8,7 @@ module Model.User exposing , getUserName ) -import Json.Decode as Json exposing ((:=)) +import Json.Decode as Decode exposing (Decoder) import Dict exposing (Dict) type alias Users = Dict UserId User @@ -20,23 +20,23 @@ type alias User = , email : String } -usersDecoder : Json.Decoder Users -usersDecoder = Json.map Dict.fromList (Json.list userWithIdDecoder) +usersDecoder : Decoder Users +usersDecoder = Decode.map Dict.fromList (Decode.list userWithIdDecoder) -userWithIdDecoder : Json.Decoder (UserId, User) +userWithIdDecoder : Decode.Decoder (UserId, User) userWithIdDecoder = - Json.object2 (,) - ("id" := userIdDecoder) + Decode.map2 (,) + (Decode.field "id" userIdDecoder) userDecoder -userIdDecoder : Json.Decoder UserId -userIdDecoder = Json.int +userIdDecoder : Decoder UserId +userIdDecoder = Decode.int -userDecoder : Json.Decoder User +userDecoder : Decoder User userDecoder = - Json.object2 User - ("name" := Json.string) - ("email" := Json.string) + Decode.map2 User + (Decode.field "name" Decode.string) + (Decode.field "email" Decode.string) getUserName : Users -> UserId -> Maybe String getUserName users userId = -- cgit v1.2.3