From 02a979380f80abe719f030c4fdb7652bcbaa0cd3 Mon Sep 17 00:00:00 2001 From: Nicolas Perriault Date: Tue, 21 Jan 2025 10:14:36 +0100 Subject: [PATCH] refactor: merge component and process dbs in-memory. (#903) Merge the 3 process and component JSON db files in-memory, dynamically add scopes when decoding JSON contents to ease filtering them at runtime when required --- src/Data/Bookmark.elm | 4 +- src/Data/Common/Db.elm | 8 +- src/Data/Component.elm | 19 ++-- src/Data/Food/Db.elm | 35 +++---- src/Data/Food/Recipe.elm | 14 +-- src/Data/Object/Db.elm | 28 ++---- src/Data/Process.elm | 14 ++- src/Data/Scope.elm | 16 ++-- src/Data/Textile/Db.elm | 50 ++++------ src/Data/Textile/Simulator.elm | 2 +- src/Page/Explore.elm | 8 +- src/Page/Explore/Components.elm | 7 +- src/Page/Explore/TextileExamples.elm | 2 +- src/Page/Food.elm | 12 +-- src/Page/Object.elm | 4 +- src/Page/Textile.elm | 4 +- src/Request/Auth.elm | 9 +- src/Server.elm | 11 ++- src/Server/Query.elm | 24 ++--- src/Static/Db.elm | 135 ++++++++++++--------------- src/Static/Json.elm-template | 66 ++++++------- src/Views/CountrySelect.elm | 2 +- src/Views/Textile/Step.elm | 2 +- tests/Data/ScopeTest.elm | 32 +++++++ tests/TestUtils.elm | 6 +- 25 files changed, 248 insertions(+), 266 deletions(-) create mode 100644 tests/Data/ScopeTest.elm diff --git a/src/Data/Bookmark.elm b/src/Data/Bookmark.elm index 0f3f9f24c..a1d482ce4 100644 --- a/src/Data/Bookmark.elm +++ b/src/Data/Bookmark.elm @@ -204,7 +204,7 @@ toQueryDescription db bookmark = Object objectQuery -> objectQuery - |> ObjectQuery.toString db.object.components db.object.processes + |> ObjectQuery.toString db.components db.processes |> Result.withDefault "N/A" Textile textileQuery -> @@ -215,5 +215,5 @@ toQueryDescription db bookmark = Veli objectQuery -> objectQuery - |> ObjectQuery.toString db.object.components db.object.processes + |> ObjectQuery.toString db.components db.processes |> Result.withDefault "N/A" diff --git a/src/Data/Common/Db.elm b/src/Data/Common/Db.elm index b6009aa2a..369374537 100644 --- a/src/Data/Common/Db.elm +++ b/src/Data/Common/Db.elm @@ -6,7 +6,7 @@ module Data.Common.Db exposing import Data.Country as Country exposing (Country) import Data.Impact.Definition as Definition exposing (Definitions) -import Data.Textile.Db as TextileDb +import Data.Process exposing (Process) import Data.Transport as Transport exposing (Distances) import Json.Decode as Decode @@ -17,9 +17,9 @@ impactsFromJson = >> Result.mapError Decode.errorToString -countriesFromJson : TextileDb.Db -> String -> Result String (List Country) -countriesFromJson textile = - Decode.decodeString (Country.decodeList textile.processes) +countriesFromJson : List Process -> String -> Result String (List Country) +countriesFromJson processes = + Decode.decodeString (Country.decodeList processes) >> Result.mapError Decode.errorToString diff --git a/src/Data/Component.elm b/src/Data/Component.elm index 27dbe4d7b..d7bece36e 100644 --- a/src/Data/Component.elm +++ b/src/Data/Component.elm @@ -34,6 +34,7 @@ module Data.Component exposing import Data.Impact as Impact exposing (Impacts) import Data.Process as Process exposing (Process) +import Data.Scope as Scope exposing (Scope) import Data.Split as Split import Data.Uuid as Uuid exposing (Uuid) import Energy @@ -55,6 +56,7 @@ type alias Component = { elements : List Element , id : Id , name : String + , scopes : List Scope } @@ -255,17 +257,18 @@ computeItemResults { components, processes } { id, quantity } = ) -decode : Decoder Component -decode = +decode : List Scope -> Decoder Component +decode scopes = Decode.succeed Component |> Decode.required "elements" (Decode.list decodeElement) |> Decode.required "id" (Decode.map Id Uuid.decoder) |> Decode.required "name" Decode.string + |> Decode.optional "scopes" (Decode.list Scope.decode) scopes -decodeList : Decoder (List Component) -decodeList = - Decode.list decode +decodeList : List Scope -> Decoder (List Component) +decodeList scopes = + Decode.list (decode scopes) decodeElement : Decoder Element @@ -283,9 +286,9 @@ decodeItem = |> Decode.required "quantity" (Decode.map Quantity Decode.int) -decodeListFromJsonString : String -> Result String (List Component) -decodeListFromJsonString = - Decode.decodeString decodeList +decodeListFromJsonString : List Scope -> String -> Result String (List Component) +decodeListFromJsonString scopes = + Decode.decodeString (decodeList scopes) >> Result.mapError Decode.errorToString diff --git a/src/Data/Food/Db.elm b/src/Data/Food/Db.elm index 3265dd03f..2ebbba09c 100644 --- a/src/Data/Food/Db.elm +++ b/src/Data/Food/Db.elm @@ -7,8 +7,7 @@ import Data.Example as Example exposing (Example) import Data.Food.Ingredient as Ingredient exposing (Ingredient) import Data.Food.Query as Query exposing (Query) import Data.Food.WellKnown as WellKnown exposing (WellKnown) -import Data.Impact as Impact -import Data.Process as Process exposing (Process) +import Data.Process exposing (Process) import Json.Decode as Decode import Result.Extra as RE @@ -16,28 +15,20 @@ import Result.Extra as RE type alias Db = { examples : List (Example Query) , ingredients : List Ingredient - , processes : List Process , wellKnown : WellKnown } -buildFromJson : String -> String -> String -> Result String Db -buildFromJson exampleProductsJson foodProcessesJson ingredientsJson = - foodProcessesJson - |> Decode.decodeString (Process.decodeList Impact.decodeImpacts) - |> Result.mapError Decode.errorToString - |> Result.andThen - (\processes -> - Ok Db - |> RE.andMap - (exampleProductsJson - |> Example.decodeListFromJsonString Query.decode - ) - |> RE.andMap - (ingredientsJson - |> Decode.decodeString (Ingredient.decodeIngredients processes) - |> Result.mapError Decode.errorToString - ) - |> RE.andMap (Ok processes) - |> RE.andMap (WellKnown.load processes) +buildFromJson : String -> String -> List Process -> Result String Db +buildFromJson exampleProductsJson ingredientsJson processes = + Ok Db + |> RE.andMap + (exampleProductsJson + |> Example.decodeListFromJsonString Query.decode ) + |> RE.andMap + (ingredientsJson + |> Decode.decodeString (Ingredient.decodeIngredients processes) + |> Result.mapError Decode.errorToString + ) + |> RE.andMap (WellKnown.load processes) diff --git a/src/Data/Food/Recipe.elm b/src/Data/Food/Recipe.elm index 268bad401..ce7c0799a 100644 --- a/src/Data/Food/Recipe.elm +++ b/src/Data/Food/Recipe.elm @@ -24,7 +24,6 @@ module Data.Food.Recipe exposing ) import Data.Country as Country exposing (Country) -import Data.Food.Db as Food import Data.Food.EcosystemicServices as EcosystemicServices exposing (EcosystemicServices) import Data.Food.Ingredient as Ingredient exposing (Ingredient) import Data.Food.Origin as Origin @@ -457,9 +456,9 @@ fromQuery db query = Ok Recipe |> RE.andMap (Ok query.distribution) |> RE.andMap (ingredientListFromQuery db query) - |> RE.andMap (packagingListFromQuery db.food query) + |> RE.andMap (packagingListFromQuery db query) |> RE.andMap (preparationListFromQuery query) - |> RE.andMap (transformFromQuery db.food query) + |> RE.andMap (transformFromQuery db query) getMassAtPackaging : Recipe -> Mass @@ -604,16 +603,13 @@ ingredientQueryFromIngredient ingredient = } -packagingListFromQuery : - Food.Db - -> { a | packaging : List BuilderQuery.ProcessQuery } - -> Result String (List Packaging) +packagingListFromQuery : Db -> { a | packaging : List BuilderQuery.ProcessQuery } -> Result String (List Packaging) packagingListFromQuery db query = query.packaging |> RE.combineMap (packagingFromQuery db) -packagingFromQuery : Food.Db -> BuilderQuery.ProcessQuery -> Result String Packaging +packagingFromQuery : Db -> BuilderQuery.ProcessQuery -> Result String Packaging packagingFromQuery { processes } { id, mass } = processes |> Process.findById id @@ -684,7 +680,7 @@ toString { ingredients, packaging, transform } = transformFromQuery : - Food.Db + Db -> { a | transform : Maybe BuilderQuery.ProcessQuery } -> Result String (Maybe Transform) transformFromQuery { processes } query = diff --git a/src/Data/Object/Db.elm b/src/Data/Object/Db.elm index 6b2f919c0..4cfe87c35 100644 --- a/src/Data/Object/Db.elm +++ b/src/Data/Object/Db.elm @@ -3,34 +3,20 @@ module Data.Object.Db exposing , buildFromJson ) -import Data.Component as Component exposing (Component) import Data.Example as Example exposing (Example) -import Data.Impact as Impact import Data.Object.Query as Query exposing (Query) -import Data.Process as Process exposing (Process) -import Json.Decode as Decode import Result.Extra as RE type alias Db = - { components : List Component - , examples : List (Example Query) - , processes : List Process + { examples : List (Example Query) } -buildFromJson : String -> String -> String -> Result String Db -buildFromJson objectComponentsJson objectExamplesJson objectProcessesJson = - objectProcessesJson - |> Decode.decodeString (Process.decodeList Impact.decodeImpacts) - |> Result.mapError Decode.errorToString - |> Result.andThen - (\processes -> - Ok Db - |> RE.andMap (Component.decodeListFromJsonString objectComponentsJson) - |> RE.andMap - (objectExamplesJson - |> Example.decodeListFromJsonString Query.decode - ) - |> RE.andMap (Ok processes) +buildFromJson : String -> Result String Db +buildFromJson objectExamplesJson = + Ok Db + |> RE.andMap + (objectExamplesJson + |> Example.decodeListFromJsonString Query.decode ) diff --git a/src/Data/Process.elm b/src/Data/Process.elm index 6984c54d6..7df841409 100644 --- a/src/Data/Process.elm +++ b/src/Data/Process.elm @@ -19,6 +19,7 @@ import Data.Common.DecodeUtils as DU import Data.Impact as Impact exposing (Impacts) import Data.Impact.Definition as Definition import Data.Process.Category as Category exposing (Category) +import Data.Scope as Scope exposing (Scope) import Data.Split as Split exposing (Split) import Data.Unit as Unit import Data.Uuid as Uuid exposing (Uuid) @@ -46,6 +47,7 @@ type alias Process = , id : Id , impacts : Impacts , name : String + , scopes : List Scope , source : String , sourceId : Maybe SourceId , unit : String @@ -78,8 +80,8 @@ sourceIdToString (SourceId string) = string -decodeProcess : Decoder Impact.Impacts -> Decoder Process -decodeProcess impactsDecoder = +decodeProcess : List Scope -> Decoder Impact.Impacts -> Decoder Process +decodeProcess scopes impactsDecoder = Decode.succeed Process |> Pipe.required "categories" Category.decodeList |> Pipe.required "comment" Decode.string @@ -90,6 +92,7 @@ decodeProcess impactsDecoder = |> Pipe.required "id" decodeId |> Pipe.required "impacts" impactsDecoder |> Pipe.required "name" Decode.string + |> Pipe.hardcoded scopes |> Pipe.required "source" Decode.string |> DU.strictOptional "sourceId" decodeSourceId |> Pipe.required "unit" Decode.string @@ -108,6 +111,7 @@ encode process = , ( "id", encodeId process.id ) , ( "impacts", Impact.encode process.impacts ) , ( "name", Encode.string process.name ) + , ( "scopes", process.scopes |> Encode.list Scope.encode ) , ( "source", Encode.string process.source ) , ( "sourceId", EncodeExtra.maybe encodeSourceId process.sourceId ) , ( "unit", Encode.string process.unit ) @@ -126,9 +130,9 @@ decodeSourceId = |> Decode.map sourceIdFromString -decodeList : Decoder Impact.Impacts -> Decoder (List Process) -decodeList = - decodeProcess >> Decode.list +decodeList : List Scope -> Decoder Impact.Impacts -> Decoder (List Process) +decodeList scopes = + decodeProcess scopes >> Decode.list encodeId : Id -> Encode.Value diff --git a/src/Data/Scope.elm b/src/Data/Scope.elm index d1d7a4dd4..d8ae83c1a 100644 --- a/src/Data/Scope.elm +++ b/src/Data/Scope.elm @@ -1,8 +1,8 @@ module Data.Scope exposing ( Scope(..) + , anyOf , decode , encode - , only , parse , toLabel , toString @@ -21,6 +21,15 @@ type Scope | Veli +{-| Filter a list of scoped records against any passed allowed scopes +-} +anyOf : List Scope -> List { a | scopes : List Scope } -> List { a | scopes : List Scope } +anyOf scopes = + List.filter <| + .scopes + >> List.any (\scope -> List.member scope scopes) + + decode : Decoder Scope decode = Decode.string @@ -51,11 +60,6 @@ fromString string = Err <| "Couldn't decode unknown scope " ++ string -only : Scope -> List { a | scopes : List Scope } -> List { a | scopes : List Scope } -only scope = - List.filter (.scopes >> List.member scope) - - parse : Parser (Scope -> a) a parse = Parser.custom "SCOPE" <| diff --git a/src/Data/Textile/Db.elm b/src/Data/Textile/Db.elm index 2e729bd88..cfc291951 100644 --- a/src/Data/Textile/Db.elm +++ b/src/Data/Textile/Db.elm @@ -3,10 +3,8 @@ module Data.Textile.Db exposing , buildFromJson ) -import Data.Component as Component exposing (Component) import Data.Example as Example exposing (Example) -import Data.Impact as Impact -import Data.Process as Process exposing (Process) +import Data.Process exposing (Process) import Data.Textile.Material as Material exposing (Material) import Data.Textile.Product as Product exposing (Product) import Data.Textile.Query as Query exposing (Query) @@ -16,38 +14,28 @@ import Result.Extra as RE type alias Db = - { components : List Component - , examples : List (Example Query) + { examples : List (Example Query) , materials : List Material - , processes : List Process , products : List Product , wellKnown : WellKnown } -buildFromJson : String -> String -> String -> String -> String -> Result String Db -buildFromJson textileComponentsJson exampleProductsJson materialsJson productsJson processesJson = - processesJson - |> Decode.decodeString (Process.decodeList Impact.decodeImpacts) - |> Result.mapError Decode.errorToString - |> Result.andThen - (\processes -> - Ok Db - |> RE.andMap (Component.decodeListFromJsonString textileComponentsJson) - |> RE.andMap - (exampleProductsJson - |> Example.decodeListFromJsonString Query.decode - ) - |> RE.andMap - (materialsJson - |> Decode.decodeString (Material.decodeList processes) - |> Result.mapError Decode.errorToString - ) - |> RE.andMap (Ok processes) - |> RE.andMap - (productsJson - |> Decode.decodeString (Product.decodeList processes) - |> Result.mapError Decode.errorToString - ) - |> RE.andMap (WellKnown.load processes) +buildFromJson : String -> String -> String -> List Process -> Result String Db +buildFromJson exampleProductsJson textileMaterialsJson productsJson processes = + Ok Db + |> RE.andMap + (exampleProductsJson + |> Example.decodeListFromJsonString Query.decode ) + |> RE.andMap + (textileMaterialsJson + |> Decode.decodeString (Material.decodeList processes) + |> Result.mapError Decode.errorToString + ) + |> RE.andMap + (productsJson + |> Decode.decodeString (Product.decodeList processes) + |> Result.mapError Decode.errorToString + ) + |> RE.andMap (WellKnown.load processes) diff --git a/src/Data/Textile/Simulator.elm b/src/Data/Textile/Simulator.elm index b8896d944..874e1db18 100644 --- a/src/Data/Textile/Simulator.elm +++ b/src/Data/Textile/Simulator.elm @@ -741,7 +741,7 @@ computeTotalTransportImpacts simulator = computeTrims : Db -> Simulator -> Result String Simulator computeTrims db ({ durability } as simulator) = simulator.inputs.trims - |> Component.compute db.textile + |> Component.compute db |> Result.map Component.extractImpacts |> Result.map (\trimsImpacts -> diff --git a/src/Page/Explore.elm b/src/Page/Explore.elm index 3f2ec034f..3512c3eb9 100644 --- a/src/Page/Explore.elm +++ b/src/Page/Explore.elm @@ -48,7 +48,7 @@ import Page.Explore.TextileExamples as TextileExamples import Page.Explore.TextileMaterials as TextileMaterials import Page.Explore.TextileProducts as TextileProducts import Route exposing (Route) -import Static.Db as Db exposing (Db) +import Static.Db exposing (Db) import Table as SortableTable import Views.Alert as Alert import Views.Container as Container @@ -392,7 +392,8 @@ processesExplorer : processesExplorer session scope tableConfig tableState maybeId = let scopedProcesses = - Db.scopedProcesses scope session.db + session.db.processes + |> Scope.anyOf [ scope ] in [ scopedProcesses |> List.sortBy .name @@ -424,7 +425,8 @@ componentsExplorer : componentsExplorer db scope tableConfig tableState maybeId = let scopedComponents = - Db.scopedComponents scope db + db.components + |> Scope.anyOf [ scope ] in [ scopedComponents |> List.sortBy .name diff --git a/src/Page/Explore/Components.elm b/src/Page/Explore/Components.elm index dc7ce38ce..284455ff5 100644 --- a/src/Page/Explore/Components.elm +++ b/src/Page/Explore/Components.elm @@ -5,13 +5,13 @@ import Data.Dataset as Dataset import Data.Impact as Impact import Data.Impact.Definition as Definition import Data.Process as Process exposing (Process) -import Data.Scope exposing (Scope) +import Data.Scope as Scope exposing (Scope) import Data.Unit as Unit import Html exposing (..) import Html.Attributes exposing (..) import Page.Explore.Table as Table exposing (Table) import Route -import Static.Db as Db exposing (Db) +import Static.Db exposing (Db) import Views.Alert as Alert import Views.Format as Format @@ -20,7 +20,8 @@ table : Db -> { detailed : Bool, scope : Scope } -> Table Component.Component St table db { detailed, scope } = let scopedProcesses = - Db.scopedProcesses scope db + db.processes + |> Scope.anyOf [ scope ] expandElements = Component.expandElements scopedProcesses diff --git a/src/Page/Explore/TextileExamples.elm b/src/Page/Explore/TextileExamples.elm index c318a0bbd..e320d9341 100644 --- a/src/Page/Explore/TextileExamples.elm +++ b/src/Page/Explore/TextileExamples.elm @@ -40,7 +40,7 @@ table db { maxScore, maxPer100g } { detailed, scope } = , toCell = \( example, _ ) -> example.query.trims - |> List.map (Component.itemToString db.textile) + |> List.map (Component.itemToString db) |> RE.combine |> Result.map (String.join ", " diff --git a/src/Page/Food.elm b/src/Page/Food.elm index f01fa2403..e06288001 100644 --- a/src/Page/Food.elm +++ b/src/Page/Food.elm @@ -207,7 +207,7 @@ update ({ db, queries } as session) msg model = AddPackaging -> let firstPackaging = - db.food.processes + db.processes |> Recipe.availablePackagings (List.map .id query.packaging) |> List.sortBy Process.getDisplayName |> List.head @@ -232,7 +232,7 @@ update ({ db, queries } as session) msg model = query.ingredients |> List.map .mass |> Quantity.sum firstTransform = - db.food.processes + db.processes |> Process.listByCategory ProcessCategory.Transform |> List.sortBy Process.getDisplayName |> List.head @@ -686,7 +686,7 @@ createElementSelectorConfig db ingredientQuery { excluded, recipeIngredient, imp { elements = db.food.ingredients , countries = db.countries - |> Scope.only Scope.Food + |> Scope.anyOf [ Scope.Food ] |> List.sortBy .name , definitions = db.definitions } @@ -1007,7 +1007,7 @@ packagingListView : Db -> Definition -> Recipe -> Recipe.Results -> List (Html M packagingListView db selectedImpact recipe results = let availablePackagings = - db.food.processes + db.processes |> Recipe.availablePackagings (recipe.packaging |> List.map (.process >> .id) @@ -1044,7 +1044,7 @@ packagingListView db selectedImpact recipe results = (\packaging -> updateProcessFormView { processes = - db.food.processes + db.processes |> Process.listByCategory ProcessCategory.Packaging , excluded = recipe.packaging |> List.map (.process >> .id) , processQuery = { id = packaging.process.id, mass = packaging.mass } @@ -1488,7 +1488,7 @@ transformView db selectedImpact recipe results = Just transform -> updateProcessFormView { processes = - db.food.processes + db.processes |> Process.listByCategory ProcessCategory.Transform , excluded = [ transform.process.id ] , processQuery = { id = transform.process.id, mass = transform.mass } diff --git a/src/Page/Object.elm b/src/Page/Object.elm index 73ff1d84f..b8f31d955 100644 --- a/src/Page/Object.elm +++ b/src/Page/Object.elm @@ -193,7 +193,7 @@ suggestBookmarkName { db, store } examples query = name _ -> - Query.toString db.object.components db.object.processes query + Query.toString db.components db.processes query |> Result.withDefault "N/A" @@ -450,7 +450,7 @@ simulatorView session model = , ComponentView.editorView { addLabel = "Ajouter un composant" , allowExpandDetails = True - , db = session.db.object + , db = session.db , detailed = model.detailedComponents , impact = model.impact , items = diff --git a/src/Page/Textile.elm b/src/Page/Textile.elm index 1176f4713..ec759a5e2 100644 --- a/src/Page/Textile.elm +++ b/src/Page/Textile.elm @@ -1016,7 +1016,7 @@ simulatorFormView session model ({ inputs } as simulator) = , TrimView.editorView { addLabel = "Ajouter un accessoire" , allowExpandDetails = True - , db = session.db.textile + , db = session.db , detailed = model.detailedTrims , impact = model.impact , items = session.queries.textile.trims @@ -1025,7 +1025,7 @@ simulatorFormView session model ({ inputs } as simulator) = , removeItem = RemoveTrim , results = session.queries.textile.trims - |> Trim.compute session.db.textile + |> Trim.compute session.db |> Result.withDefault Trim.emptyResults , scope = Scope.Textile , setDetailed = SetDetailedTrims diff --git a/src/Request/Auth.elm b/src/Request/Auth.elm index 4a0651cac..04a4d332e 100644 --- a/src/Request/Auth.elm +++ b/src/Request/Auth.elm @@ -14,7 +14,6 @@ import Http import Json.Decode as Decode exposing (Decoder) import Json.Decode.Pipeline as JDP import Json.Encode as Encode -import Static.Db as Db import Static.Json exposing (RawJsonProcesses) @@ -65,7 +64,13 @@ processes : (Result Http.Error RawJsonProcesses -> msg) -> String -> Cmd msg processes event token = Http.request { body = Http.emptyBody - , expect = Http.expectJson event Db.decodeRawJsonProcesses + , expect = + Http.expectJson event + (Decode.succeed RawJsonProcesses + |> JDP.required "foodProcesses" Decode.string + |> JDP.required "objectProcesses" Decode.string + |> JDP.required "textileProcesses" Decode.string + ) , headers = [ Http.header "token" token ] , method = "GET" , timeout = Nothing diff --git a/src/Server.elm b/src/Server.elm index dbc326542..452cb6928 100644 --- a/src/Server.elm +++ b/src/Server.elm @@ -216,7 +216,7 @@ handleRequest db request = -- GET routes Just Route.FoodGetCountryList -> db.countries - |> Scope.only Scope.Food + |> Scope.anyOf [ Scope.Food ] |> Encode.list encodeCountry |> respondWith 200 @@ -226,13 +226,13 @@ handleRequest db request = |> respondWith 200 Just Route.FoodGetPackagingList -> - db.food.processes + db.processes |> List.filter (.categories >> List.member ProcessCategory.Packaging) |> encodeProcessList |> respondWith 200 Just Route.FoodGetTransformList -> - db.food.processes + db.processes |> List.filter (.categories >> List.member ProcessCategory.Transform) |> encodeProcessList |> respondWith 200 @@ -247,7 +247,7 @@ handleRequest db request = Just Route.TextileGetCountryList -> db.countries - |> Scope.only Scope.Textile + |> Scope.anyOf [ Scope.Textile ] |> Encode.list encodeCountry |> respondWith 200 @@ -286,7 +286,8 @@ handleRequest db request = |> respondWith 400 Just Route.TextileGetTrimList -> - db.textile.components + db.components + |> Scope.anyOf [ Scope.Textile ] |> Encode.list encodeComponent |> respondWith 200 diff --git a/src/Server/Query.elm b/src/Server/Query.elm index 506ecab89..1d638c6ce 100644 --- a/src/Server/Query.elm +++ b/src/Server/Query.elm @@ -65,13 +65,13 @@ succeed = parseFoodQuery : Db -> Parser (Result Errors BuilderQuery.Query) -parseFoodQuery { countries, food } = +parseFoodQuery db = succeed (Ok BuilderQuery.Query) |> apply (distributionParser "distribution") - |> apply (ingredientListParser "ingredients" countries food) - |> apply (packagingListParser "packaging" food.processes) + |> apply (ingredientListParser "ingredients" db.countries db.food) + |> apply (packagingListParser "packaging" db.processes) |> apply (preparationListParser "preparation") - |> apply (maybeTransformParser "transform" food.processes) + |> apply (maybeTransformParser "transform" db.processes) ingredientListParser : String -> List Country -> Food.Db -> Parser (ParseResult (List BuilderQuery.IngredientQuery)) @@ -387,14 +387,14 @@ parseTransform_ transforms string = parseTextileQuery : Db -> Parser (Result Errors TextileQuery.Query) -parseTextileQuery { countries, textile } = +parseTextileQuery db = succeed (Ok TextileQuery.Query) |> apply (maybeSplitParser "airTransportRatio") |> apply (maybeBusiness "business") - |> apply (maybeTextileCountryParser "countryDyeing" countries) - |> apply (maybeTextileCountryParser "countryFabric" countries) - |> apply (maybeTextileCountryParser "countryMaking" countries) - |> apply (maybeTextileCountryParser "countrySpinning" countries) + |> apply (maybeTextileCountryParser "countryDyeing" db.countries) + |> apply (maybeTextileCountryParser "countryFabric" db.countries) + |> apply (maybeTextileCountryParser "countryMaking" db.countries) + |> apply (maybeTextileCountryParser "countrySpinning" db.countries) |> apply (maybeDisabledStepsParser "disabledSteps") |> apply (maybeDyeingMedium "dyeingMedium") |> apply (maybeFabricParser "fabricProcess") @@ -403,15 +403,15 @@ parseTextileQuery { countries, textile } = |> apply (maybeMakingDeadStockParser "makingDeadStock") |> apply (maybeMakingWasteParser "makingWaste") |> apply (massParserInKilograms "mass") - |> apply (materialListParser "materials" textile.materials countries) + |> apply (materialListParser "materials" db.textile.materials db.countries) |> apply (maybeIntParser "numberOfReferences") |> apply (maybePhysicalDurabilityParser "physicalDurability") |> apply (maybePriceParser "price") |> apply (maybePrinting "printing") - |> apply (productParser "product" textile.products) + |> apply (productParser "product" db.textile.products) |> apply (maybeSurfaceMassParser "surfaceMass") |> apply (maybeBoolParser "traceability") - |> apply (componentItemListParser textile.components "trims") + |> apply (componentItemListParser db.components "trims") |> apply (boolParser { default = False } "upcycled") |> apply (maybeYarnSizeParser "yarnSize") diff --git a/src/Static/Db.elm b/src/Static/Db.elm index df9d68358..fee384c95 100644 --- a/src/Static/Db.elm +++ b/src/Static/Db.elm @@ -1,25 +1,22 @@ module Static.Db exposing ( Db , db - , decodeRawJsonProcesses - , scopedComponents - , scopedProcesses ) import Data.Common.Db as Common -import Data.Component exposing (Component) +import Data.Component as Component exposing (Component) import Data.Country exposing (Country) import Data.Food.Db as FoodDb +import Data.Impact as Impact import Data.Impact.Definition exposing (Definitions) import Data.Object.Db as ObjectDb -import Data.Process exposing (Process) +import Data.Process as Process exposing (Process) import Data.Scope as Scope exposing (Scope) import Data.Textile.Db as TextileDb import Data.Transport exposing (Distances) -import Json.Decode as Decode exposing (Decoder) -import Json.Decode.Pipeline as JDP +import Json.Decode as Decode import Result.Extra as RE -import Static.Json as StaticJson exposing (RawJsonProcesses) +import Static.Json as StaticJson type alias Db = @@ -35,89 +32,75 @@ type alias Db = db : StaticJson.RawJsonProcesses -> Result String Db -db procs = - StaticJson.db procs - |> Result.andThen - (\{ foodDb, objectDb, textileDb } -> +db = + decodeRawProcesses + >> Result.andThen + (\processes -> Ok Db - |> RE.andMap (Ok []) - |> RE.andMap (countries textileDb) + |> RE.andMap (decodeRawComponents StaticJson.rawJsonComponents) + |> RE.andMap (countries processes) |> RE.andMap impactDefinitions |> RE.andMap distances - |> RE.andMap (Ok foodDb) - |> RE.andMap (Ok objectDb) - |> RE.andMap (Ok []) - |> RE.andMap (Ok textileDb) + |> RE.andMap + (processes + |> FoodDb.buildFromJson + StaticJson.foodProductExamplesJson + StaticJson.foodIngredientsJson + ) + |> RE.andMap (ObjectDb.buildFromJson StaticJson.objectExamplesJson) + |> RE.andMap (Ok processes) + |> RE.andMap + (processes + |> TextileDb.buildFromJson + StaticJson.textileProductExamplesJson + StaticJson.textileMaterialsJson + StaticJson.textileProductsJson + ) ) - |> Result.map - (\db_ -> - { db_ - | components = - List.concat - [ db_.object.components - , db_.textile.components - ] - , processes = - List.concat - [ db_.food.processes - , db_.object.processes - , db_.textile.processes - ] - } - ) - -decodeRawJsonProcesses : Decoder RawJsonProcesses -decodeRawJsonProcesses = - Decode.succeed RawJsonProcesses - |> JDP.required "foodProcesses" Decode.string - |> JDP.required "objectProcesses" Decode.string - |> JDP.required "textileProcesses" Decode.string +decodeRawComponents : StaticJson.RawJsonComponents -> Result String (List Component) +decodeRawComponents { objectComponents, textileComponents } = + [ ( objectComponents, [ Scope.Object, Scope.Veli ] ) + , ( textileComponents, [ Scope.Textile ] ) + ] + |> List.map (\( json, scopes ) -> decodeScopedComponents scopes json) + |> RE.combine + |> Result.map List.concat -impactDefinitions : Result String Definitions -impactDefinitions = - Common.impactsFromJson StaticJson.impactsJson +decodeRawProcesses : StaticJson.RawJsonProcesses -> Result String (List Process) +decodeRawProcesses { foodProcesses, objectProcesses, textileProcesses } = + [ ( foodProcesses, [ Scope.Food ] ) + , ( objectProcesses, [ Scope.Object, Scope.Veli ] ) + , ( textileProcesses, [ Scope.Textile ] ) + ] + |> List.map (\( json, scopes ) -> decodeScopedProcesses scopes json) + |> RE.combine + |> Result.map List.concat -countries : TextileDb.Db -> Result String (List Country) -countries textileDb = - Common.countriesFromJson textileDb StaticJson.countriesJson - -distances : Result String Distances -distances = - Common.transportsFromJson StaticJson.transportsJson +decodeScopedComponents : List Scope -> String -> Result String (List Component) +decodeScopedComponents scopes = + Component.decodeListFromJsonString scopes -scopedComponents : Scope -> Db -> List Component -scopedComponents scope { object, textile } = - case scope of - Scope.Food -> - -- Note: we don't have any food components yet - [] +decodeScopedProcesses : List Scope -> String -> Result String (List Process) +decodeScopedProcesses scopes = + Decode.decodeString (Process.decodeList scopes Impact.decodeImpacts) + >> Result.mapError Decode.errorToString - Scope.Object -> - object.components - - Scope.Textile -> - textile.components - - Scope.Veli -> - object.components +impactDefinitions : Result String Definitions +impactDefinitions = + Common.impactsFromJson StaticJson.impactsJson -scopedProcesses : Scope -> Db -> List Process -scopedProcesses scope { food, object, textile } = - case scope of - Scope.Food -> - food.processes - Scope.Object -> - object.processes +countries : List Process -> Result String (List Country) +countries processes = + Common.countriesFromJson processes StaticJson.countriesJson - Scope.Textile -> - textile.processes - Scope.Veli -> - object.processes +distances : Result String Distances +distances = + Common.transportsFromJson StaticJson.transportsJson diff --git a/src/Static/Json.elm-template b/src/Static/Json.elm-template index bcf805221..dcb4a2010 100644 --- a/src/Static/Json.elm-template +++ b/src/Static/Json.elm-template @@ -1,21 +1,30 @@ module Static.Json exposing - ( RawJsonProcesses + ( RawJsonComponents + , RawJsonProcesses , countriesJson - , db + , foodIngredientsJson + , foodProductExamplesJson , impactsJson + , objectExamplesJson + , rawJsonComponents , rawJsonProcesses + , textileMaterialsJson + , textileProductExamplesJson + , textileProductsJson , transportsJson ) -import Data.Food.Db as FoodDb -import Data.Object.Db as ObjectDb -import Data.Textile.Db as TextileDb + +type alias RawJsonComponents = + { objectComponents : String + , textileComponents : String + } -type alias Db = - { foodDb : FoodDb.Db - , objectDb : ObjectDb.Db - , textileDb : TextileDb.Db +rawJsonComponents : RawJsonComponents +rawJsonComponents = + { objectComponents = objectComponentsJson + , textileComponents = textileComponentsJson } @@ -26,6 +35,14 @@ type alias RawJsonProcesses = } +rawJsonProcesses : RawJsonProcesses +rawJsonProcesses = + { foodProcesses = foodProcessesJson + , objectProcesses = objectProcessesJson + , textileProcesses = textileProcessesJson + } + + impactsJson : String impactsJson = """%impactsJson%""" @@ -56,11 +73,6 @@ textileComponentsJson = """%textileComponentsJson%""" -textileDb : String -> Result String TextileDb.Db -textileDb textileProcesses = - TextileDb.buildFromJson textileComponentsJson textileProductExamplesJson textileMaterialsJson textileProductsJson textileProcesses - - foodProductExamplesJson : String foodProductExamplesJson = """%foodProductExamplesJson%""" @@ -76,11 +88,6 @@ foodIngredientsJson = """%foodIngredientsJson%""" -foodDb : String -> Result String FoodDb.Db -foodDb foodProcesses = - FoodDb.buildFromJson foodProductExamplesJson foodProcesses foodIngredientsJson - - countriesJson : String countriesJson = """%countriesJson%""" @@ -104,24 +111,3 @@ objectExamplesJson = objectProcessesJson : String objectProcessesJson = """%objectProcessesJson%""" - - -objectDb : String -> Result String ObjectDb.Db -objectDb objectProcesses = - ObjectDb.buildFromJson objectComponentsJson objectExamplesJson objectProcesses - - -rawJsonProcesses : RawJsonProcesses -rawJsonProcesses = - { foodProcesses = foodProcessesJson - , objectProcesses = objectProcessesJson - , textileProcesses = textileProcessesJson - } - - -db : RawJsonProcesses -> Result String Db -db { foodProcesses, objectProcesses, textileProcesses } = - Result.map3 Db - (foodDb foodProcesses) - (objectDb objectProcesses) - (textileDb textileProcesses) diff --git a/src/Views/CountrySelect.elm b/src/Views/CountrySelect.elm index 21ae18374..8dd670c7c 100644 --- a/src/Views/CountrySelect.elm +++ b/src/Views/CountrySelect.elm @@ -19,7 +19,7 @@ type alias Config msg = view : Config msg -> Html msg view { attributes, countries, onSelect, scope, selectedCountry } = countries - |> Scope.only scope + |> Scope.anyOf [ scope ] |> List.sortBy .name |> List.map (\{ code, name } -> diff --git a/src/Views/Textile/Step.elm b/src/Views/Textile/Step.elm index 9f27fd579..0640cdc4b 100644 --- a/src/Views/Textile/Step.elm +++ b/src/Views/Textile/Step.elm @@ -680,7 +680,7 @@ createElementSelectorConfig cfg materialInput = , db = { countries = cfg.db.countries - |> Scope.only Scope.Textile + |> Scope.anyOf [ Scope.Textile ] |> List.sortBy .name , definitions = cfg.db.definitions , elements = cfg.db.textile.materials diff --git a/tests/Data/ScopeTest.elm b/tests/Data/ScopeTest.elm new file mode 100644 index 000000000..bb9736541 --- /dev/null +++ b/tests/Data/ScopeTest.elm @@ -0,0 +1,32 @@ +module Data.ScopeTest exposing (..) + +import Data.Scope as Scope +import Expect +import Test exposing (..) +import TestUtils exposing (asTest) + + +suite : Test +suite = + describe "Data.Scope" + [ describe "anyOf should filter records against any of passed scopes" + [ asTest "test1" + ([ { scopes = [ Scope.Food ] }, { scopes = [ Scope.Food, Scope.Textile ] } ] + |> Scope.anyOf [ Scope.Food ] + |> List.length + |> Expect.equal 2 + ) + , asTest "test2" + ([ { scopes = [ Scope.Textile ] }, { scopes = [ Scope.Food, Scope.Textile ] } ] + |> Scope.anyOf [ Scope.Food ] + |> List.length + |> Expect.equal 1 + ) + , asTest "test3" + ([ { scopes = [ Scope.Object ] }, { scopes = [ Scope.Object, Scope.Textile ] } ] + |> Scope.anyOf [ Scope.Food ] + |> List.length + |> Expect.equal 0 + ) + ] + ] diff --git a/tests/TestUtils.elm b/tests/TestUtils.elm index 11f68de27..fd350dc16 100644 --- a/tests/TestUtils.elm +++ b/tests/TestUtils.elm @@ -68,9 +68,9 @@ createServerRequest dbs method body url = , jsResponseHandler = Encode.null , method = method , processes = - { foodProcesses = dbs.food.processes |> encode Process.encode - , objectProcesses = dbs.object.processes |> encode Process.encode - , textileProcesses = dbs.textile.processes |> encode Process.encode + { foodProcesses = dbs.processes |> encode Process.encode + , objectProcesses = dbs.processes |> encode Process.encode + , textileProcesses = dbs.processes |> encode Process.encode } , url = url }