From 23ae8a564854ab6cdb4f38bc62f04a04a47ca3c4 Mon Sep 17 00:00:00 2001 From: Vincent Jousse Date: Tue, 10 Sep 2024 11:46:22 +0200 Subject: [PATCH 01/26] fix(ci): check for ecobalyse-private when extracting the branch name (#733) ## :wrench: Problem Currently, we are checking for `ecobalyse_data` in the content of a PR to synchronize withe the `ecobalyse-private` repo. For consistency, check for `ecobalyse-private`. ## :rotating_light: Points to watch / comments Github token was changed to my personal token to avoid 403 responses. We may need to use an app token at some point. Tests have been moved to the package directory for consistency. ## :desert_island: How to test Merge this PR in a PR requiring a different `ecobalyse-private` branch and check that everything is working as expected. --- packages/python/ecobalyse/ecobalyse/github.py | 2 +- .../ecobalyse/ecobalyse/tests}/test_data_branch.py | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) rename {tests => packages/python/ecobalyse/ecobalyse/tests}/test_data_branch.py (59%) diff --git a/packages/python/ecobalyse/ecobalyse/github.py b/packages/python/ecobalyse/ecobalyse/github.py index 1c392e031..8c178e69f 100644 --- a/packages/python/ecobalyse/ecobalyse/github.py +++ b/packages/python/ecobalyse/ecobalyse/github.py @@ -14,7 +14,7 @@ def extract_branch_name(content: str) -> str | None: # (it should be part of the body) # Branch names format: https://docs.github.com/en/get-started/using-git/dealing-with-special-characters-in-branch-and-tag-names#naming-branches-and-tags # The English alphabet (a to z and A to Z), Numbers (0 to 9), period (.), hyphen (-), underscore (_), forward slash (/) - result = re.search(r"ecobalyse_data: ([0-9a-zA-Z./_-]+)", content, re.M | re.I) + result = re.search(r"ecobalyse-private: ([0-9a-zA-Z./_-]+)", content, re.M | re.I) if result: return result.group(1) diff --git a/tests/test_data_branch.py b/packages/python/ecobalyse/ecobalyse/tests/test_data_branch.py similarity index 59% rename from tests/test_data_branch.py rename to packages/python/ecobalyse/ecobalyse/tests/test_data_branch.py index 3b979cb50..8a1f2efab 100644 --- a/tests/test_data_branch.py +++ b/packages/python/ecobalyse/ecobalyse/tests/test_data_branch.py @@ -2,25 +2,27 @@ def test_extract_data_branch_name(client): - branch_name = extract_branch_name("ecobalyse_data: test_branch_name") + branch_name = extract_branch_name("ecobalyse-private: test_branch_name") assert branch_name == "test_branch_name" - branch_name = extract_branch_name("nstnsnecobalyse_data: test_branch_name nstnstn") + branch_name = extract_branch_name( + "nstnsnecobalyse-private: test_branch_name nstnstn" + ) assert branch_name == "test_branch_name" branch_name = extract_branch_name("""tnsrtntntsr - nstnsnecobalyse_data: test_branch_name nstnstn + nstnsnecobalyse-private: test_branch_name nstnstn eaiuiuea""") assert branch_name == "test_branch_name" branch_name = extract_branch_name( - "nstnsnecobalyse_data: test_branch_name/9./_- nstnstn" + "nstnsnecobalyse-private: test_branch_name/9./_- nstnstn" ) assert branch_name == "test_branch_name/9./_-" # We should not include invalid characters in the match branch_name = extract_branch_name( - "nstnsnecobalyse_data: test_bran Date: Tue, 10 Sep 2024 15:34:49 +0200 Subject: [PATCH 02/26] feat: add holistic durability in exploratory mode (#721) ## :wrench: Problem Add physical durability in exploratory mode. [Notion card](https://www.notion.so/Introduire-la-durabilit-physique-en-mode-exploratoire-7466a0fd69f140f984cf85b272bbe5ae) ## :book: TODO - [x] Add physical durability concept to elm data types - [x] Add physical durability to the API ## :desert_island: How to test Play with the new durability slider and check that everything is ok. Checking the behavior of the API may be a good idea too. --------- Co-authored-by: Nicolas Perriault --- openapi.yaml | 18 +++ src/Data/Textile/Economics.elm | 12 +- src/Data/Textile/Inputs.elm | 3 + src/Data/Textile/Query.elm | 201 ++++++++++++++------------- src/Data/Textile/Simulator.elm | 32 +++-- src/Data/Textile/Step.elm | 6 +- src/Data/Unit.elm | 90 ++++++++---- src/Page/Api.elm | 7 +- src/Page/Textile.elm | 42 +++++- src/Server/Query.elm | 81 ++++++++--- src/Views/RangeSlider.elm | 70 ++++++++-- tests/Data/Textile/EconomicsTest.elm | 4 +- tests/Data/UnitTest.elm | 2 +- tests/Server/RouteTest.elm | 5 + tests/server.spec.js | 8 ++ 15 files changed, 393 insertions(+), 188 deletions(-) diff --git a/openapi.yaml b/openapi.yaml index bc67bac13..aa15caad2 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -104,6 +104,7 @@ paths: - $ref: "#/components/parameters/priceParam" - $ref: "#/components/parameters/traceabilityParam" - $ref: "#/components/parameters/upcycledParam" + - $ref: "#/components/parameters/physicalDurabilityParam" responses: 200: description: Opération réussie @@ -176,6 +177,7 @@ paths: - $ref: "#/components/parameters/priceParam" - $ref: "#/components/parameters/traceabilityParam" - $ref: "#/components/parameters/upcycledParam" + - $ref: "#/components/parameters/physicalDurabilityParam" responses: 200: description: successful operation @@ -250,6 +252,7 @@ paths: - $ref: "#/components/parameters/priceParam" - $ref: "#/components/parameters/traceabilityParam" - $ref: "#/components/parameters/upcycledParam" + - $ref: "#/components/parameters/physicalDurabilityParam" responses: 200: description: Opération réussie @@ -1099,6 +1102,16 @@ components: freezingAndOven: summary: Congélation et cuisson au four value: ["freezing", "oven"] + physicalDurabilityParam: + name: physicalDurability + in: query + description: La durabilité physique du produit. + required: false + style: form + schema: + type: number + minimum: 0.67 + maximum: 1.45 schemas: Impacts: type: object @@ -1283,6 +1296,11 @@ components: description: | Produit remanufacturé type: boolean + physicalDurability: + description: La durabilité physique du produit. + type: number + minimum: 0.67 + maximum: 1.45 TextileQueryMaterial: type: object additionalProperties: false diff --git a/src/Data/Textile/Economics.elm b/src/Data/Textile/Economics.elm index fe0b3839c..1484624b8 100644 --- a/src/Data/Textile/Economics.elm +++ b/src/Data/Textile/Economics.elm @@ -5,7 +5,7 @@ module Data.Textile.Economics exposing , businessFromString , businessToLabel , businessToString - , computeDurabilityIndex + , computeNonPhysicalDurabilityIndex , computeNumberOfReferencesIndex , computeRepairCostIndex , decode @@ -95,12 +95,12 @@ businessToString business = "large-business-without-services" -computeDurabilityIndex : Economics -> Unit.Durability -computeDurabilityIndex economics = +computeNonPhysicalDurabilityIndex : Economics -> Unit.NonPhysicalDurability +computeNonPhysicalDurabilityIndex economics = let ( minDurability, maxDurability ) = - ( Unit.durabilityToFloat Unit.minDurability - , Unit.durabilityToFloat Unit.maxDurability + ( Unit.nonPhysicalDurabilityToFloat (Unit.minDurability Unit.NonPhysicalDurability) + , Unit.nonPhysicalDurabilityToFloat (Unit.maxDurability Unit.NonPhysicalDurability) ) finalIndex = @@ -119,7 +119,7 @@ computeDurabilityIndex economics = + finalIndex * (maxDurability - minDurability) |> formatIndex - |> Unit.durability + |> Unit.nonPhysicalDurability computeRepairCostIndex : Business -> Price -> Price -> Unit.Ratio diff --git a/src/Data/Textile/Inputs.elm b/src/Data/Textile/Inputs.elm index 1cfe22fab..26cfe9667 100644 --- a/src/Data/Textile/Inputs.elm +++ b/src/Data/Textile/Inputs.elm @@ -77,6 +77,7 @@ type alias Inputs = , price : Maybe Economics.Price , traceability : Maybe Bool , upcycled : Bool + , physicalDurability : Maybe Unit.PhysicalDurability } @@ -198,6 +199,7 @@ fromQuery { countries, textile } query = |> RE.andMap (Ok query.price) |> RE.andMap (Ok query.traceability) |> RE.andMap (Ok query.upcycled) + |> RE.andMap (Ok query.physicalDurability) toQuery : Inputs -> Query @@ -225,6 +227,7 @@ toQuery inputs = , price = inputs.price , traceability = inputs.traceability , upcycled = inputs.upcycled + , physicalDurability = inputs.physicalDurability } diff --git a/src/Data/Textile/Query.elm b/src/Data/Textile/Query.elm index f7f0d39c3..8e2776ceb 100644 --- a/src/Data/Textile/Query.elm +++ b/src/Data/Textile/Query.elm @@ -45,37 +45,38 @@ import Url.Parser as Parser exposing (Parser) type alias MaterialQuery = - { id : Material.Id + { country : Maybe Country.Code + , id : Material.Id , share : Split , spinning : Maybe Spinning - , country : Maybe Country.Code } type alias Query = - { mass : Mass - , materials : List MaterialQuery - , product : Product.Id - , countrySpinning : Maybe Country.Code - , countryFabric : Maybe Country.Code + { airTransportRatio : Maybe Split + , business : Maybe Economics.Business , countryDyeing : Maybe Country.Code + , countryFabric : Maybe Country.Code , countryMaking : Maybe Country.Code - , airTransportRatio : Maybe Split - , makingWaste : Maybe Split - , makingDeadStock : Maybe Split - , makingComplexity : Maybe MakingComplexity - , yarnSize : Maybe Unit.YarnSize - , surfaceMass : Maybe Unit.SurfaceMass - , fabricProcess : Maybe Fabric + , countrySpinning : Maybe Country.Code , disabledSteps : List Label - , fading : Maybe Bool , dyeingMedium : Maybe DyeingMedium - , printing : Maybe Printing - , business : Maybe Economics.Business + , fabricProcess : Maybe Fabric + , fading : Maybe Bool + , makingComplexity : Maybe MakingComplexity + , makingDeadStock : Maybe Split + , makingWaste : Maybe Split + , mass : Mass + , materials : List MaterialQuery , numberOfReferences : Maybe Int + , physicalDurability : Maybe Unit.PhysicalDurability , price : Maybe Economics.Price + , printing : Maybe Printing + , product : Product.Id + , surfaceMass : Maybe Unit.SurfaceMass , traceability : Maybe Bool , upcycled : Bool + , yarnSize : Maybe Unit.YarnSize } @@ -109,56 +110,49 @@ buildApiQuery clientUrl query = decode : Decoder Query decode = Decode.succeed Query - |> Pipe.required "mass" (Decode.map Mass.kilograms Decode.float) - |> Pipe.required "materials" (Decode.list decodeMaterialQuery) - |> Pipe.required "product" (Decode.map Product.Id Decode.string) - |> Pipe.optional "countrySpinning" (Decode.maybe Country.decodeCode) Nothing - |> Pipe.optional "countryFabric" (Decode.maybe Country.decodeCode) Nothing + |> Pipe.optional "airTransportRatio" (Decode.maybe Split.decodeFloat) Nothing + |> Pipe.optional "business" (Decode.maybe Economics.decodeBusiness) Nothing |> Pipe.optional "countryDyeing" (Decode.maybe Country.decodeCode) Nothing + |> Pipe.optional "countryFabric" (Decode.maybe Country.decodeCode) Nothing |> Pipe.optional "countryMaking" (Decode.maybe Country.decodeCode) Nothing - |> Pipe.optional "airTransportRatio" (Decode.maybe Split.decodeFloat) Nothing - |> Pipe.optional "makingWaste" (Decode.maybe Split.decodeFloat) Nothing - |> Pipe.optional "makingDeadStock" (Decode.maybe Split.decodeFloat) Nothing - |> Pipe.optional "makingComplexity" (Decode.maybe MakingComplexity.decode) Nothing - |> Pipe.optional "yarnSize" (Decode.maybe Unit.decodeYarnSize) Nothing - |> Pipe.optional "surfaceMass" (Decode.maybe Unit.decodeSurfaceMass) Nothing - |> Pipe.optional "fabricProcess" (Decode.maybe Fabric.decode) Nothing + |> Pipe.optional "countrySpinning" (Decode.maybe Country.decodeCode) Nothing |> Pipe.optional "disabledSteps" (Decode.list Label.decodeFromCode) [] - |> Pipe.optional "fading" (Decode.maybe Decode.bool) Nothing |> Pipe.optional "dyeingMedium" (Decode.maybe DyeingMedium.decode) Nothing - |> Pipe.optional "printing" (Decode.maybe Printing.decode) Nothing - |> Pipe.optional "business" (Decode.maybe Economics.decodeBusiness) Nothing + |> Pipe.optional "fabricProcess" (Decode.maybe Fabric.decode) Nothing + |> Pipe.optional "fading" (Decode.maybe Decode.bool) Nothing + |> Pipe.optional "makingComplexity" (Decode.maybe MakingComplexity.decode) Nothing + |> Pipe.optional "makingDeadStock" (Decode.maybe Split.decodeFloat) Nothing + |> Pipe.optional "makingWaste" (Decode.maybe Split.decodeFloat) Nothing + |> Pipe.required "mass" (Decode.map Mass.kilograms Decode.float) + |> Pipe.required "materials" (Decode.list decodeMaterialQuery) |> Pipe.optional "numberOfReferences" (Decode.maybe Decode.int) Nothing + |> Pipe.optional "physicalDurability" (Decode.maybe Unit.decodePhysicalDurability) Nothing |> Pipe.optional "price" (Decode.maybe Economics.decodePrice) Nothing + |> Pipe.optional "printing" (Decode.maybe Printing.decode) Nothing + |> Pipe.required "product" (Decode.map Product.Id Decode.string) + |> Pipe.optional "surfaceMass" (Decode.maybe Unit.decodeSurfaceMass) Nothing |> Pipe.optional "traceability" (Decode.maybe Decode.bool) Nothing |> Pipe.optional "upcycled" Decode.bool False + |> Pipe.optional "yarnSize" (Decode.maybe Unit.decodeYarnSize) Nothing decodeMaterialQuery : Decoder MaterialQuery decodeMaterialQuery = Decode.succeed MaterialQuery + |> Pipe.optional "country" (Decode.maybe Country.decodeCode) Nothing |> Pipe.required "id" (Decode.map Material.Id Decode.string) |> Pipe.required "share" Split.decodeFloat |> Pipe.optional "spinning" (Decode.maybe Spinning.decode) Nothing - |> Pipe.optional "country" (Decode.maybe Country.decodeCode) Nothing encode : Query -> Encode.Value encode query = - [ ( "mass", query.mass |> Mass.inKilograms |> Encode.float |> Just ) - , ( "materials", query.materials |> Encode.list encodeMaterialQuery |> Just ) - , ( "product", query.product |> Product.idToString |> Encode.string |> Just ) - , ( "countrySpinning", query.countrySpinning |> Maybe.map Country.encodeCode ) - , ( "countryFabric", query.countryFabric |> Maybe.map Country.encodeCode ) + [ ( "airTransportRatio", query.airTransportRatio |> Maybe.map Split.encodeFloat ) + , ( "business", query.business |> Maybe.map Economics.encodeBusiness ) , ( "countryDyeing", query.countryDyeing |> Maybe.map Country.encodeCode ) + , ( "countryFabric", query.countryFabric |> Maybe.map Country.encodeCode ) , ( "countryMaking", query.countryMaking |> Maybe.map Country.encodeCode ) - , ( "airTransportRatio", query.airTransportRatio |> Maybe.map Split.encodeFloat ) - , ( "makingWaste", query.makingWaste |> Maybe.map Split.encodeFloat ) - , ( "makingDeadStock", query.makingDeadStock |> Maybe.map Split.encodeFloat ) - , ( "makingComplexity", query.makingComplexity |> Maybe.map (MakingComplexity.toString >> Encode.string) ) - , ( "yarnSize", query.yarnSize |> Maybe.map Unit.encodeYarnSize ) - , ( "surfaceMass", query.surfaceMass |> Maybe.map Unit.encodeSurfaceMass ) - , ( "fabricProcess", query.fabricProcess |> Maybe.map Fabric.encode ) + , ( "countrySpinning", query.countrySpinning |> Maybe.map Country.encodeCode ) , ( "disabledSteps" , case query.disabledSteps of [] -> @@ -167,14 +161,22 @@ encode query = list -> Encode.list Label.encode list |> Just ) - , ( "fading", query.fading |> Maybe.map Encode.bool ) , ( "dyeingMedium", query.dyeingMedium |> Maybe.map DyeingMedium.encode ) - , ( "printing", query.printing |> Maybe.map Printing.encode ) - , ( "business", query.business |> Maybe.map Economics.encodeBusiness ) + , ( "fabricProcess", query.fabricProcess |> Maybe.map Fabric.encode ) + , ( "fading", query.fading |> Maybe.map Encode.bool ) + , ( "makingComplexity", query.makingComplexity |> Maybe.map (MakingComplexity.toString >> Encode.string) ) + , ( "makingDeadStock", query.makingDeadStock |> Maybe.map Split.encodeFloat ) + , ( "makingWaste", query.makingWaste |> Maybe.map Split.encodeFloat ) + , ( "mass", query.mass |> Mass.inKilograms |> Encode.float |> Just ) + , ( "materials", query.materials |> Encode.list encodeMaterialQuery |> Just ) , ( "numberOfReferences", query.numberOfReferences |> Maybe.map Encode.int ) , ( "price", query.price |> Maybe.map Economics.encodePrice ) + , ( "printing", query.printing |> Maybe.map Printing.encode ) + , ( "product", query.product |> Product.idToString |> Encode.string |> Just ) + , ( "surfaceMass", query.surfaceMass |> Maybe.map Unit.encodeSurfaceMass ) , ( "traceability", query.traceability |> Maybe.map Encode.bool ) , ( "upcycled", Encode.bool query.upcycled |> Just ) + , ( "yarnSize", query.yarnSize |> Maybe.map Unit.encodeYarnSize ) ] -- For concision, drop keys where no param is defined |> List.filterMap (\( key, maybeVal ) -> maybeVal |> Maybe.map (\val -> ( key, val ))) @@ -183,10 +185,10 @@ encode query = encodeMaterialQuery : MaterialQuery -> Encode.Value encodeMaterialQuery v = - [ ( "id", Material.encodeId v.id |> Just ) + [ ( "country", v.country |> Maybe.map Country.encodeCode ) + , ( "id", Material.encodeId v.id |> Just ) , ( "share", Split.encodeFloat v.share |> Just ) , ( "spinning", v.spinning |> Maybe.map Spinning.encode ) - , ( "country", v.country |> Maybe.map Country.encodeCode ) ] |> List.filterMap (\( key, maybeVal ) -> maybeVal |> Maybe.map (\val -> ( key, val ))) |> Encode.object @@ -226,15 +228,15 @@ handleUpcycling query = isAdvancedQuery : Query -> Bool isAdvancedQuery query = List.any identity - [ query.materials |> List.any (.spinning >> (/=) Nothing) - , query.makingWaste /= Nothing - , query.makingDeadStock /= Nothing + [ query.dyeingMedium /= Nothing + , query.fabricProcess /= Nothing , query.makingComplexity /= Nothing - , query.yarnSize /= Nothing + , query.makingDeadStock /= Nothing + , query.makingWaste /= Nothing + , query.materials |> List.any (.spinning >> (/=) Nothing) , query.surfaceMass /= Nothing - , query.fabricProcess /= Nothing , not query.upcycled && List.length query.disabledSteps > 0 - , query.dyeingMedium /= Nothing + , query.yarnSize /= Nothing ] @@ -243,15 +245,15 @@ isAdvancedQuery query = regulatory : Query -> Query regulatory query = { query - | materials = query.materials |> List.map (\m -> { m | spinning = Nothing }) - , makingWaste = Nothing - , makingDeadStock = Nothing + | disabledSteps = [] + , dyeingMedium = Nothing + , fabricProcess = Nothing , makingComplexity = Nothing - , yarnSize = Nothing + , makingDeadStock = Nothing + , makingWaste = Nothing + , materials = query.materials |> List.map (\m -> { m | spinning = Nothing }) , surfaceMass = Nothing - , fabricProcess = Nothing - , disabledSteps = [] - , dyeingMedium = Nothing + , yarnSize = Nothing } @@ -272,10 +274,10 @@ updateMaterial oldMaterialId newMaterial = updateMaterialQuery oldMaterialId (\materialQuery -> { materialQuery - | id = newMaterial.id + | country = newMaterial.country + , id = newMaterial.id , share = newMaterial.share , spinning = Nothing - , country = newMaterial.country } ) @@ -312,15 +314,15 @@ updateProduct product query = if product.id /= query.product then -- Product category has changed, reset a bunch of related query params { query - | product = product.id - , makingWaste = Nothing - , makingDeadStock = Nothing - , makingComplexity = Nothing - , yarnSize = Nothing - , surfaceMass = Nothing + | dyeingMedium = Nothing , fabricProcess = Nothing - , dyeingMedium = Nothing + , makingComplexity = Nothing + , makingDeadStock = Nothing + , makingWaste = Nothing , printing = Nothing + , product = product.id + , surfaceMass = Nothing + , yarnSize = Nothing } else @@ -338,15 +340,12 @@ updateStepCountry label code query = Just code in case label of - Label.Spinning -> - { query | countrySpinning = maybeCode } + Label.Ennobling -> + { query | countryDyeing = maybeCode } Label.Fabric -> { query | countryFabric = maybeCode } - Label.Ennobling -> - { query | countryDyeing = maybeCode } - Label.Making -> { query | countryMaking = maybeCode @@ -359,6 +358,9 @@ updateStepCountry label code query = query.airTransportRatio } + Label.Spinning -> + { query | countrySpinning = maybeCode } + _ -> query @@ -392,7 +394,20 @@ validateMaterials materials = default : Query default = - { mass = Mass.kilograms 0.17 + { airTransportRatio = Nothing + , business = Nothing + , countryDyeing = Just (Country.Code "CN") + , countryFabric = Just (Country.Code "CN") + , countryMaking = Just (Country.Code "CN") + , countrySpinning = Just (Country.Code "CN") + , disabledSteps = [] + , dyeingMedium = Nothing + , fabricProcess = Nothing + , fading = Nothing + , makingComplexity = Nothing + , makingDeadStock = Nothing + , makingWaste = Nothing + , mass = Mass.kilograms 0.17 , materials = [ { id = Material.Id "ei-coton" , share = Split.full @@ -400,46 +415,34 @@ default = , country = Nothing } ] - , product = Product.Id "tshirt" - , countrySpinning = Just (Country.Code "CN") - , countryFabric = Just (Country.Code "CN") - , countryDyeing = Just (Country.Code "CN") - , countryMaking = Just (Country.Code "CN") - , airTransportRatio = Nothing - , makingWaste = Nothing - , makingDeadStock = Nothing - , makingComplexity = Nothing - , yarnSize = Nothing - , surfaceMass = Nothing - , fabricProcess = Nothing - , disabledSteps = [] - , fading = Nothing - , dyeingMedium = Nothing - , printing = Nothing - , business = Nothing , numberOfReferences = Nothing + , physicalDurability = Nothing , price = Nothing + , printing = Nothing + , product = Product.Id "tshirt" + , surfaceMass = Nothing , traceability = Nothing , upcycled = False + , yarnSize = Nothing } jupeCotonAsie : Query jupeCotonAsie = { default - | mass = Mass.kilograms 0.3 + | fabricProcess = Just Fabric.Weaving + , mass = Mass.kilograms 0.3 , product = Product.Id "jupe" - , fabricProcess = Just Fabric.Weaving } tShirtCotonFrance : Query tShirtCotonFrance = { default - | countrySpinning = Just (Country.Code "FR") + | countryDyeing = Just (Country.Code "FR") , countryFabric = Just (Country.Code "FR") - , countryDyeing = Just (Country.Code "FR") , countryMaking = Just (Country.Code "FR") + , countrySpinning = Just (Country.Code "FR") } diff --git a/src/Data/Textile/Simulator.elm b/src/Data/Textile/Simulator.elm index f57631dd1..b6089ce65 100644 --- a/src/Data/Textile/Simulator.elm +++ b/src/Data/Textile/Simulator.elm @@ -40,7 +40,7 @@ type alias Simulator = , lifeCycle : LifeCycle , impacts : Impacts , complementsImpacts : Impact.ComplementsImpacts - , durability : Unit.Durability + , durability : Unit.HolisticDurability , transport : Transport , daysOfWear : Duration , useNbCycles : Int @@ -55,7 +55,7 @@ encode v = , ( "impacts", Impact.encode v.impacts ) , ( "complementsImpacts", Impact.encodeComplementsImpacts v.complementsImpacts ) , ( "transport", Transport.encode v.transport ) - , ( "durability", v.durability |> Unit.durabilityToFloat |> Encode.float ) + , ( "durability", v.durability |> Unit.floatDurabilityFromHolistic |> Encode.float ) , ( "daysOfWear", v.daysOfWear |> Duration.inDays |> round |> Encode.int ) , ( "useNbCycles", Encode.int v.useNbCycles ) ] @@ -74,7 +74,10 @@ init db = , lifeCycle = lifeCycle , impacts = Impact.empty , complementsImpacts = Impact.noComplementsImpacts - , durability = Unit.standardDurability + , durability = + { nonPhysical = Unit.standardDurability Unit.NonPhysicalDurability + , physical = inputs.physicalDurability |> Maybe.withDefault (Unit.maxDurability Unit.PhysicalDurability) + } , transport = Transport.default Impact.empty , daysOfWear = inputs.product.use.daysOfWear , useNbCycles = Product.customDaysOfWear product.use @@ -175,8 +178,8 @@ initializeFinalMass ({ inputs } as simulator) = computeDurability : Simulator -> Simulator computeDurability ({ inputs } as simulator) = let - durability = - Economics.computeDurabilityIndex + nonPhysicalDurability = + Economics.computeNonPhysicalDurabilityIndex { business = inputs.business |> Maybe.withDefault inputs.product.economics.business @@ -191,13 +194,16 @@ computeDurability ({ inputs } as simulator) = inputs.traceability |> Maybe.withDefault inputs.product.economics.traceability } + + newDurability = + { physical = simulator.durability.physical, nonPhysical = nonPhysicalDurability } in { simulator - | durability = durability + | durability = newDurability , daysOfWear = - simulator.daysOfWear |> Quantity.multiplyBy (Unit.durabilityToFloat durability) + simulator.daysOfWear |> Quantity.multiplyBy (Unit.floatDurabilityFromHolistic newDurability) , useNbCycles = - round (toFloat simulator.useNbCycles * Unit.durabilityToFloat durability) + round (toFloat simulator.useNbCycles * Unit.floatDurabilityFromHolistic newDurability) } @@ -683,14 +689,14 @@ computeFinalImpacts ({ durability, lifeCycle } as simulator) = lifeCycle |> Array.filter .enabled |> LifeCycle.sumComplementsImpacts - |> Impact.divideComplementsImpactsBy (Unit.durabilityToFloat durability) + |> Impact.divideComplementsImpactsBy (Unit.floatDurabilityFromHolistic durability) in { simulator | complementsImpacts = complementsImpacts , impacts = lifeCycle |> LifeCycle.computeFinalImpacts - |> Impact.divideBy (Unit.durabilityToFloat durability) + |> Impact.divideBy (Unit.floatDurabilityFromHolistic durability) |> Impact.impactsWithComplements complementsImpacts } @@ -702,7 +708,7 @@ getTotalImpactsWithoutComplements { durability, lifeCycle } = |> Array.map Step.getTotalImpactsWithoutComplements |> Array.toList |> Impact.sumImpacts - |> Impact.divideBy (Unit.durabilityToFloat durability) + |> Impact.divideBy (Unit.floatDurabilityFromHolistic durability) updateLifeCycle : (LifeCycle -> LifeCycle) -> Simulator -> Simulator @@ -739,7 +745,7 @@ toStepsImpacts trigram simulator = Maybe.map (Quantity.minus (complementImpact - |> Quantity.multiplyBy (Unit.durabilityToFloat simulator.durability) + |> Quantity.multiplyBy (Unit.floatDurabilityFromHolistic simulator.durability) ) ) @@ -767,4 +773,4 @@ toStepsImpacts trigram simulator = |> getImpact |> applyComplement simulator.complementsImpacts.outOfEuropeEOL } - |> Impact.divideStepsImpactsBy (Unit.durabilityToFloat simulator.durability) + |> Impact.divideStepsImpactsBy (Unit.floatDurabilityFromHolistic simulator.durability) diff --git a/src/Data/Textile/Step.elm b/src/Data/Textile/Step.elm index 8570bf453..df0e64f14 100644 --- a/src/Data/Textile/Step.elm +++ b/src/Data/Textile/Step.elm @@ -60,7 +60,7 @@ type alias Step = , kwh : Energy , processInfo : ProcessInfo , airTransportRatio : Split -- FIXME: why not Maybe? - , durability : Unit.Durability + , durability : Unit.NonPhysicalDurability , makingComplexity : Maybe MakingComplexity , makingWaste : Maybe Split , makingDeadStock : Maybe Split @@ -114,7 +114,7 @@ create { label, editable, country, enabled } = , kwh = Quantity.zero , processInfo = defaultProcessInfo , airTransportRatio = Split.zero -- Note: this depends on next step country, so we can't set an accurate default value initially - , durability = Unit.standardDurability + , durability = Unit.standardDurability Unit.NonPhysicalDurability , makingComplexity = Nothing , makingWaste = Nothing , makingDeadStock = Nothing @@ -497,7 +497,7 @@ encode v = , ( "elec_kWh", Encode.float (Energy.inKilowattHours v.kwh) ) , ( "processInfo", encodeProcessInfo v.processInfo ) , ( "airTransportRatio", Split.encodeFloat v.airTransportRatio ) - , ( "durability", Unit.encodeDurability v.durability ) + , ( "durability", Unit.encodeNonPhysicalDurability v.durability ) , ( "makingWaste", v.makingWaste |> Maybe.map Split.encodeFloat |> Maybe.withDefault Encode.null ) , ( "makingDeadStock", v.makingDeadStock |> Maybe.map Split.encodeFloat |> Maybe.withDefault Encode.null ) , ( "picking", v.picking |> Maybe.map Unit.encodePickPerMeter |> Maybe.withDefault Encode.null ) diff --git a/src/Data/Unit.elm b/src/Data/Unit.elm index 4fc5c7823..df02365ce 100644 --- a/src/Data/Unit.elm +++ b/src/Data/Unit.elm @@ -1,26 +1,27 @@ module Data.Unit exposing - ( Durability(..) + ( HolisticDurability , Impact , ImpactUnit(..) + , NonPhysicalDurability(..) + , PhysicalDurability(..) , PickPerMeter(..) , Ratio(..) , SurfaceMass , ThreadDensity(..) , YarnSize - , decodeDurability , decodeImpact + , decodePhysicalDurability , decodeRatio , decodeSurfaceMass , decodeYarnSize - , durability - , durabilityToFloat - , encodeDurability , encodeImpact + , encodeNonPhysicalDurability , encodePickPerMeter , encodeRatio , encodeSurfaceMass , encodeThreadDensity , encodeYarnSize + , floatDurabilityFromHolistic , forKWh , forKg , forKgAndDistance @@ -35,6 +36,10 @@ module Data.Unit exposing , minDurability , minSurfaceMass , minYarnSize + , nonPhysicalDurability + , nonPhysicalDurabilityToFloat + , physicalDurability + , physicalDurabilityToFloat , pickPerMeter , pickPerMeterToFloat , ratio @@ -110,59 +115,84 @@ encodeRatio = -- Durability -type Durability - = Durability Float +type PhysicalDurability + = PhysicalDurability Float -minDurability : Durability -minDurability = - Durability 0.67 +type NonPhysicalDurability + = NonPhysicalDurability Float -standardDurability : Durability -standardDurability = - Durability 1 +type alias HolisticDurability = + { physical : PhysicalDurability + , nonPhysical : NonPhysicalDurability + } -maxDurability : Durability -maxDurability = - Durability 1.45 +minDurability : (Float -> a) -> a +minDurability dur = + dur 0.67 -durability : Float -> Durability -durability = - Durability +standardDurability : (Float -> a) -> a +standardDurability dur = + dur 1 -durabilityToFloat : Durability -> Float -durabilityToFloat (Durability float) = +maxDurability : (Float -> a) -> a +maxDurability dur = + dur 1.45 + + +physicalDurability : Float -> PhysicalDurability +physicalDurability value = + PhysicalDurability value + + +nonPhysicalDurability : Float -> NonPhysicalDurability +nonPhysicalDurability value = + NonPhysicalDurability value + + +floatDurabilityFromHolistic : HolisticDurability -> Float +floatDurabilityFromHolistic { physical, nonPhysical } = + min (physicalDurabilityToFloat physical) (nonPhysicalDurabilityToFloat nonPhysical) + + +physicalDurabilityToFloat : PhysicalDurability -> Float +physicalDurabilityToFloat (PhysicalDurability float) = + float + + +nonPhysicalDurabilityToFloat : NonPhysicalDurability -> Float +nonPhysicalDurabilityToFloat (NonPhysicalDurability float) = float -decodeDurability : Decoder Durability -decodeDurability = +decodePhysicalDurability : Decoder PhysicalDurability +decodePhysicalDurability = Decode.float |> Decode.andThen (\float -> - if float < durabilityToFloat minDurability || float > durabilityToFloat maxDurability then + if float < physicalDurabilityToFloat (minDurability PhysicalDurability) || float > physicalDurabilityToFloat (maxDurability PhysicalDurability) then Decode.fail ("Le coefficient de durabilité spécifié (" ++ String.fromFloat float - ++ ") doit être comprise entre " - ++ String.fromFloat (durabilityToFloat minDurability) + ++ ") doit être compris entre " + ++ String.fromFloat (physicalDurabilityToFloat (minDurability PhysicalDurability)) ++ " et " - ++ String.fromFloat (durabilityToFloat maxDurability) + ++ String.fromFloat (physicalDurabilityToFloat (maxDurability PhysicalDurability)) ++ "." ) else Decode.succeed float ) - |> Decode.map durability + |> Decode.map physicalDurability -encodeDurability : Durability -> Encode.Value -encodeDurability (Durability float) = +encodeNonPhysicalDurability : NonPhysicalDurability -> Encode.Value +encodeNonPhysicalDurability (NonPhysicalDurability float) = Encode.float float diff --git a/src/Page/Api.elm b/src/Page/Api.elm index ddc1089a1..7d4acd6eb 100644 --- a/src/Page/Api.elm +++ b/src/Page/Api.elm @@ -55,7 +55,12 @@ getApiServerUrl { clientUrl } = changelog : List News changelog = - [ { date = "29 août 2024" + [ { date = "5 septembre 2024" + , level = "minor" + , domains = [ "Textile" ] + , md = "Ajout du champ `physicalDurability` qui permet de préciser la durabilité physique d'un vêtement." + } + , { date = "29 août 2024" , level = "minor" , domains = [ "Textile" ] , md = """Ajout du champ booléen `upcycled` qui permet de préciser si un vêtement est remanufacturé. diff --git a/src/Page/Textile.elm b/src/Page/Textile.elm index 67241006a..ae47267dc 100644 --- a/src/Page/Textile.elm +++ b/src/Page/Textile.elm @@ -66,6 +66,7 @@ import Views.Icon as Icon import Views.ImpactTabs as ImpactTabs import Views.Markdown as Markdown import Views.Modal as ModalView +import Views.RangeSlider as RangeSlider import Views.Sidebar as SidebarView import Views.Textile.Step as StepView @@ -130,6 +131,7 @@ type Msg | UpdateBusiness (Result String Economics.Business) | UpdateDyeingMedium DyeingMedium | UpdateFabricProcess Fabric + | UpdatePhysicalDurability (Maybe Unit.PhysicalDurability) | UpdateMakingComplexity MakingComplexity | UpdateMakingWaste (Maybe Split) | UpdateMakingDeadStock (Maybe Split) @@ -533,6 +535,10 @@ update ({ queries, navKey } as session) msg model = ( model, session, Cmd.none ) |> updateQuery { query | fabricProcess = Just fabricProcess } + ( UpdatePhysicalDurability physicalDurability, _ ) -> + ( model, session, Cmd.none ) + |> updateQuery { query | physicalDurability = physicalDurability } + ( UpdateMakingComplexity makingComplexity, _ ) -> ( model, session, Cmd.none ) |> updateQuery { query | makingComplexity = Just makingComplexity } @@ -782,6 +788,19 @@ productPriceField productPrice = ] +physicalDurabilityField : Unit.PhysicalDurability -> Html Msg +physicalDurabilityField durability = + div [ class "input-group" ] + [ RangeSlider.physicalDurability + { id = "physicalDurability" + , update = UpdatePhysicalDurability + , value = durability + , toString = Unit.physicalDurabilityToFloat >> String.fromFloat + , disabled = False + } + ] + + businessField : Economics.Business -> Html Msg businessField business = [ Economics.SmallBusiness @@ -932,11 +951,11 @@ simulatorFormView session model ({ inputs } as simulator) = ] , div [ class "card shadow-sm pb-2 mb-3" ] [ div [ class "card-header d-flex justify-content-between align-items-center" ] - [ h2 [ class "h5 mb-1 text-truncate" ] [ text "Durabilité non-physique" ] + [ h2 [ class "h5 mb-1 text-truncate" ] [ text "Durabilité" ] , div [ class "d-flex align-items-center gap-2" ] [ span [ class "d-none d-sm-flex text-truncate" ] [ text "Coefficient de durabilité\u{00A0}:" ] , simulator.durability - |> Unit.durabilityToFloat + |> Unit.floatDurabilityFromHolistic |> Format.formatFloat 2 |> text , Button.docsPillLink @@ -994,6 +1013,25 @@ simulatorFormView session model ({ inputs } as simulator) = |> traceabilityField ] ] + , if model.activeTab == ExploratoryTab then + div [] + [ div [ class "card-body py-2 row g-3 align-items-start flex-md-columns" ] + [ div [ class "col-md-4" ] [ text "Durabilité non physique" ] + , div [ class "col-md-8" ] + [ simulator.durability.nonPhysical + |> Unit.nonPhysicalDurabilityToFloat + |> String.fromFloat + |> text + ] + ] + , div [ class "card-body py-2 row g-3 align-items-start flex-md-columns" ] + [ div [ class "col-md-4" ] [ text "Durabilité physique" ] + , div [ class "col-md-8" ] [ physicalDurabilityField simulator.durability.physical ] + ] + ] + + else + text "" ] , div [] [ lifeCycleStepsView session.db model simulator diff --git a/src/Server/Query.elm b/src/Server/Query.elm index 0ffc90685..fe991ce63 100644 --- a/src/Server/Query.elm +++ b/src/Server/Query.elm @@ -257,6 +257,35 @@ validateMassInGrams string = |> Result.map Mass.grams +validatePhysicalDurability : String -> Result String Unit.PhysicalDurability +validatePhysicalDurability string = + string + |> String.toFloat + |> Result.fromMaybe ("Durabilité invalide\u{202F}: " ++ string) + |> Result.andThen + (\durability -> + let + minFloatDurability = + Unit.minDurability Unit.PhysicalDurability |> Unit.physicalDurabilityToFloat + + maxFloatDurability = + Unit.maxDurability Unit.PhysicalDurability |> Unit.physicalDurabilityToFloat + in + if durability < minFloatDurability || durability > maxFloatDurability then + Err + ("La durabilité doit être comprise entre " + ++ String.fromFloat minFloatDurability + ++ " et " + ++ String.fromFloat maxFloatDurability + ++ "." + ) + + else + Ok durability + ) + |> Result.map Unit.PhysicalDurability + + maybeTransformParser : String -> List FoodProcess.Process -> Parser (ParseResult (Maybe BuilderQuery.ProcessQuery)) maybeTransformParser key transforms = Query.string key @@ -286,6 +315,20 @@ maybePriceParser key = ) +maybePhysicalDurabilityParser : String -> Parser (ParseResult (Maybe Unit.PhysicalDurability)) +maybePhysicalDurabilityParser key = + Query.string key + |> Query.map + (Maybe.map + (\durability -> + validatePhysicalDurability durability + |> Result.map Just + |> Result.mapError (\err -> ( key, err )) + ) + >> Maybe.withDefault (Ok Nothing) + ) + + distributionParser : String -> Parser (ParseResult (Maybe Distribution)) distributionParser key = Query.string key @@ -344,29 +387,30 @@ parseTransform_ transforms string = parseTextileQuery : List Country -> Textile.Db -> Parser (Result Errors TextileQuery.Query) parseTextileQuery countries textile = succeed (Ok TextileQuery.Query) - |> apply (massParserInKilograms "mass") - |> apply (materialListParser "materials" textile.materials countries) - |> apply (productParser "product" textile.products) - |> apply (maybeTextileCountryParser "countrySpinning" countries) - |> apply (maybeTextileCountryParser "countryFabric" countries) + |> apply (maybeSplitParser "airTransportRatio") + |> apply (maybeBusiness "business") |> apply (maybeTextileCountryParser "countryDyeing" countries) + |> apply (maybeTextileCountryParser "countryFabric" countries) |> apply (maybeTextileCountryParser "countryMaking" countries) - |> apply (maybeSplitParser "airTransportRatio") - |> apply (maybeMakingWasteParser "makingWaste") - |> apply (maybeMakingDeadStockParser "makingDeadStock") - |> apply (maybeMakingComplexityParser "makingComplexity") - |> apply (maybeYarnSizeParser "yarnSize") - |> apply (maybeSurfaceMassParser "surfaceMass") - |> apply (maybeFabricParser "fabricProcess") + |> apply (maybeTextileCountryParser "countrySpinning" countries) |> apply (maybeDisabledStepsParser "disabledSteps") - |> apply (maybeBoolParser "fading") |> apply (maybeDyeingMedium "dyeingMedium") - |> apply (maybePrinting "printing") - |> apply (maybeBusiness "business") + |> apply (maybeFabricParser "fabricProcess") + |> apply (maybeBoolParser "fading") + |> apply (maybeMakingComplexityParser "makingComplexity") + |> apply (maybeMakingDeadStockParser "makingDeadStock") + |> apply (maybeMakingWasteParser "makingWaste") + |> apply (massParserInKilograms "mass") + |> apply (materialListParser "materials" textile.materials countries) |> apply (maybeIntParser "numberOfReferences") + |> apply (maybePhysicalDurabilityParser "physicalDurability") |> apply (maybePriceParser "price") + |> apply (maybePrinting "printing") + |> apply (productParser "product" textile.products) + |> apply (maybeSurfaceMassParser "surfaceMass") |> apply (maybeBoolParser "traceability") |> apply (boolParser { default = False } "upcycled") + |> apply (maybeYarnSizeParser "yarnSize") toErrors : ParseResult a -> Result Errors a @@ -467,10 +511,10 @@ parseMaterial_ materials countries string = |> Result.andThen (\material -> Ok TextileQuery.MaterialQuery + |> RE.andMap (countryParser countries Scope.Textile countryCode) |> RE.andMap (Ok material.id) |> RE.andMap (parseSplit share) |> RE.andMap (parseSpinning material spinningString) - |> RE.andMap (countryParser countries Scope.Textile countryCode) ) [ id, share, spinningString ] -> @@ -479,17 +523,18 @@ parseMaterial_ materials countries string = |> Result.andThen (\material -> Ok TextileQuery.MaterialQuery + |> RE.andMap (Ok Nothing) |> RE.andMap (Ok material.id) |> RE.andMap (parseSplit share) |> RE.andMap (parseSpinning material spinningString) - |> RE.andMap (Ok Nothing) ) [ id, share ] -> Ok TextileQuery.MaterialQuery + |> RE.andMap (Ok Nothing) |> RE.andMap (parseMaterialId_ materials id) |> RE.andMap (parseSplit share) - |> Result.map (\partiallyApplied -> partiallyApplied Nothing Nothing) + |> RE.andMap (Ok Nothing) [ "" ] -> Err <| "Format de matière vide." diff --git a/src/Views/RangeSlider.elm b/src/Views/RangeSlider.elm index 0dd6f820e..18712e535 100644 --- a/src/Views/RangeSlider.elm +++ b/src/Views/RangeSlider.elm @@ -1,5 +1,6 @@ module Views.RangeSlider exposing ( percent + , physicalDurability , surfaceMass , yarnSize ) @@ -24,7 +25,7 @@ type alias PercentConfig msg = percent : PercentConfig msg -> Html msg percent config = - layout + narrowLayout { id = config.id , label = config.toString config.value , attributes = @@ -52,7 +53,7 @@ type alias SurfaceMassConfig msg = surfaceMass : SurfaceMassConfig msg -> Html msg surfaceMass config = - layout + narrowLayout { id = config.id , label = config.toString config.value , attributes = @@ -80,7 +81,7 @@ type alias YarnSizeConfig msg = yarnSize : YarnSizeConfig msg -> Html msg yarnSize config = - layout + narrowLayout { id = config.id , label = config.toString config.value , attributes = @@ -97,21 +98,64 @@ yarnSize config = } -layout : { id : String, label : String, attributes : List (Attribute msg) } -> Html msg -layout { id, label, attributes } = +type alias PhysicalDurabilityConfig msg = + { id : String + , update : Maybe Unit.PhysicalDurability -> msg + , value : Unit.PhysicalDurability + , toString : Unit.PhysicalDurability -> String + , disabled : Bool + } + + +physicalDurability : PhysicalDurabilityConfig msg -> Html msg +physicalDurability config = + wideLayout + { id = config.id + , label = config.toString config.value + , attributes = + [ onInput (String.toFloat >> Maybe.map Unit.physicalDurability >> config.update) + , Attr.min (String.fromFloat (Unit.physicalDurabilityToFloat (Unit.minDurability Unit.PhysicalDurability))) + , Attr.max (String.fromFloat (Unit.physicalDurabilityToFloat (Unit.maxDurability Unit.PhysicalDurability))) + + -- WARNING: be careful when reordering attributes: for obscure reasons, + -- the `value` one MUST be set AFTER the `step` one. + , step "0.01" + , value (String.fromFloat (Unit.physicalDurabilityToFloat config.value)) + , Attr.disabled config.disabled + ] + } + + +narrowLayout : { id : String, label : String, attributes : List (Attribute msg) } -> Html msg +narrowLayout { id, label, attributes } = div [ class "RangeSlider row" ] [ div [ class "col-xxl-6" ] [ Html.label [ for id, class "form-label text-nowrap fs-7 mb-0" ] [ text label ] ] , div [ class "col-xxl-6" ] - [ input - (type_ "range" - :: class "d-block form-range" - :: style "margin-top" "2px" - :: Attr.id id - :: attributes - ) - [] + [ rangeInput (Attr.id id :: attributes) ] + ] + + +wideLayout : { id : String, label : String, attributes : List (Attribute msg) } -> Html msg +wideLayout { id, label, attributes } = + div [ class "RangeSlider row", style "flex-grow" "1" ] + [ div [ class "col-xxl-2" ] + [ Html.label [ for id, class "form-label text-nowrap mb-0" ] + [ text label ] ] + , div [ class "col-xxl-10" ] + [ rangeInput (Attr.id id :: attributes) ] ] + + +rangeInput : List (Attribute msg) -> Html msg +rangeInput attributes = + input + (type_ "range" + :: class "d-block form-range" + :: style "margin-top" "2px" + :: attributes + ) + [] diff --git a/tests/Data/Textile/EconomicsTest.elm b/tests/Data/Textile/EconomicsTest.elm index 1e988395a..33da548ab 100644 --- a/tests/Data/Textile/EconomicsTest.elm +++ b/tests/Data/Textile/EconomicsTest.elm @@ -17,14 +17,14 @@ suite : Test suite = describe "Data.Textile.Economics" [ describe "computeDurabilityIndex" - [ Economics.computeDurabilityIndex + [ Economics.computeNonPhysicalDurabilityIndex { business = SmallBusiness , numberOfReferences = 20000 , price = priceFromFloat 100 , repairCost = priceFromFloat 10 , traceability = False } - |> Unit.durabilityToFloat + |> Unit.nonPhysicalDurabilityToFloat |> Expect.within (Expect.Absolute 0.01) 1.1 |> asTest "should compute durability index" ] diff --git a/tests/Data/UnitTest.elm b/tests/Data/UnitTest.elm index a340e35ec..f52fce313 100644 --- a/tests/Data/UnitTest.elm +++ b/tests/Data/UnitTest.elm @@ -20,7 +20,7 @@ suite = describe "Data.Unit" [ describe "Decoder validation" [ "-7" - |> Decode.decodeString Unit.decodeDurability + |> Decode.decodeString Unit.decodePhysicalDurability |> Result.mapError Decode.errorToString |> Expect.err |> asTest "should discard erroneous Durability value" diff --git a/tests/Server/RouteTest.elm b/tests/Server/RouteTest.elm index ab62ef2c3..59e116c93 100644 --- a/tests/Server/RouteTest.elm +++ b/tests/Server/RouteTest.elm @@ -330,6 +330,11 @@ textileEndpoints db = |> Maybe.andThen (Dict.get "countryDyeing") |> Expect.equal (Just "Le code pays US n'est pas utilisable dans un contexte Textile.") |> asTest "should validate that an ingredient country scope is valid" + , testEndpoint db "GET" Encode.null "/textile/simulator?physicalDurability=99" + |> Maybe.andThen extractTextileErrors + |> Maybe.andThen (Dict.get "physicalDurability") + |> Expect.equal (Just "La durabilité doit être comprise entre 0.67 et 1.45.") + |> asTest "should validate that the physical durability param is invalid" ] , describe "multiple parameters checks" [ testEndpoint db "GET" Encode.null "/textile/simulator" diff --git a/tests/server.spec.js b/tests/server.spec.js index af8e88318..677ee0303 100644 --- a/tests/server.spec.js +++ b/tests/server.spec.js @@ -229,6 +229,14 @@ describe("API", () => { ); }); + it("should validate the physicalDurability param range", async () => { + expectFieldErrorMessage( + await makeRequest("/api/textile/simulator", ["physicalDurability=2"]), + "physicalDurability", + /doit être comprise entre/, + ); + }); + it("should accept the yarnSize param without any unit", async () => { const response = await makeRequest("/api/textile/simulator", ["yarnSize=9"]); }); From dd148466aabfcf137c013f4387d8917d247d8a42 Mon Sep 17 00:00:00 2001 From: Vincent Jousse Date: Thu, 12 Sep 2024 09:53:40 +0200 Subject: [PATCH 03/26] chore: optimize scalingo build (#734) ## :wrench: Problem Builds are some time taking ages on Scalingo and the image size is more than 1GB. ## :cake: Solution Remove unneeded files from the image using [Scalingo `.slugignore`](https://doc.scalingo.com/platform/app/slugignore), cache elm files, move python packages not needed for production in `dev-packages` and remove .map files from old versions. ## :rotating_light: Points to watch / comments The env variable `ELM_HOME` has been set to `.elm` in scalingo so that it can be properly cached. Moving python packages into `dev-packages` requires to install them with `pipenv install -d` locally. When doing `npm install` we should force the `NODE_ENV` to `development` so that `devDependencies` are always installed (by default, it is set to `production` in the `.env` file) ## :desert_island: How to test Scalingo should be green and image size smaller. --- .github/workflows/node.js.yml | 4 +- .github/workflows/score_history.yml | 4 +- .slugignore | 12 + Pipfile | 13 +- Pipfile.lock | 952 ++++++++++++++-------------- README.md | 4 +- buildpack-run.sh | 4 + package-lock.json | 392 ++++++++++-- package.json | 19 +- 9 files changed, 868 insertions(+), 536 deletions(-) create mode 100644 .slugignore diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index 265b0b078..dfe8e28bb 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -19,7 +19,7 @@ jobs: strategy: matrix: node-version: [20.x] - python-version: [3.11] + python-version: [3.12] steps: - uses: actions/checkout@v4 @@ -61,7 +61,7 @@ jobs: run: npm ci --prefer-offline --no-audit - name: Install Python dependencies - run: pip install pipenv && pipenv install + run: pip install pipenv && pipenv install -d - name: Install Ubuntu dependencies run: | diff --git a/.github/workflows/score_history.yml b/.github/workflows/score_history.yml index 44a174214..486188c1e 100644 --- a/.github/workflows/score_history.yml +++ b/.github/workflows/score_history.yml @@ -17,7 +17,7 @@ jobs: strategy: matrix: node-version: [20.x] - python-version: [3.11] + python-version: [3.12] # Run the job manually, on push to master or if the commit contains "[score_history]" if: ${{ github.event_name == 'workflow_dispatch' || github.ref == 'refs/heads/master' || contains(github.event.head_commit.message, '[score_history]') }} @@ -65,7 +65,7 @@ jobs: run: npm ci --prefer-offline --no-audit - name: Install Python dependencies - run: pip install pipenv && pipenv install + run: pip install pipenv && pipenv install -d - name: Install Ubuntu dependencies diff --git a/.slugignore b/.slugignore new file mode 100644 index 000000000..c09e1e80b --- /dev/null +++ b/.slugignore @@ -0,0 +1,12 @@ +elm-stuff/ +data/ +.elm/ +src/ +ecobalyse-private/.git +tests/ +styles.scss +review/ + +node_modules/@parcel +node_modules/@swc +node_modules/@elm_binaries diff --git a/Pipfile b/Pipfile index 4867d049c..9591cd542 100644 --- a/Pipfile +++ b/Pipfile @@ -3,18 +3,16 @@ url = "https://pypi.org/simple" verify_ssl = true name = "pypi" +[requires] +python_version = "3.12" + [packages] django = ">=5.0.8,<6" django-mail-auth = ">=3.2,<3.3" gunicorn = ">=22,<23" psycopg2-binary = "2.9.9" python-decouple = ">=3,<4" -ruff = "*" -SQLAlchemy = "2.0.30" -pandas = "1.5.3" GitPython = "3.1.43" -pre-commit = "*" -numpy = ">=1,<2" pytest-django = "*" pygithub = "*" ecobalyse = {file = "packages/python/ecobalyse"} @@ -22,3 +20,8 @@ typer = "*" pytest-mock = "*" [dev-packages] +numpy = ">=1,<2" +pre-commit = "*" +pandas = "1.5.3" +ruff = "*" +SQLAlchemy = "2.0.30" diff --git a/Pipfile.lock b/Pipfile.lock index 5b225a94a..04a3517f2 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,10 +1,12 @@ { "_meta": { "hash": { - "sha256": "9fc750a355fe9b80eda270865ec2d3366737413d689c16781ff0739e6aa7661d" + "sha256": "94f9971a86b6cb8853c5c8be6433eefe477315fc07bd0132d62c3c1d7fadbbe8" }, "pipfile-spec": 6, - "requires": {}, + "requires": { + "python_version": "3.12" + }, "sources": [ { "name": "pypi", @@ -24,92 +26,84 @@ }, "certifi": { "hashes": [ - "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b", - "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90" + "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8", + "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9" ], "markers": "python_version >= '3.6'", - "version": "==2024.7.4" + "version": "==2024.8.30" }, "cffi": { "hashes": [ - "sha256:011aff3524d578a9412c8b3cfaa50f2c0bd78e03eb7af7aa5e0df59b158efb2f", - "sha256:0a048d4f6630113e54bb4b77e315e1ba32a5a31512c31a273807d0027a7e69ab", - "sha256:0bb15e7acf8ab35ca8b24b90af52c8b391690ef5c4aec3d31f38f0d37d2cc499", - "sha256:0d46ee4764b88b91f16661a8befc6bfb24806d885e27436fdc292ed7e6f6d058", - "sha256:0e60821d312f99d3e1569202518dddf10ae547e799d75aef3bca3a2d9e8ee693", - "sha256:0fdacad9e0d9fc23e519efd5ea24a70348305e8d7d85ecbb1a5fa66dc834e7fb", - "sha256:14b9cbc8f7ac98a739558eb86fabc283d4d564dafed50216e7f7ee62d0d25377", - "sha256:17c6d6d3260c7f2d94f657e6872591fe8733872a86ed1345bda872cfc8c74885", - "sha256:1a2ddbac59dc3716bc79f27906c010406155031a1c801410f1bafff17ea304d2", - "sha256:2404f3de742f47cb62d023f0ba7c5a916c9c653d5b368cc966382ae4e57da401", - "sha256:24658baf6224d8f280e827f0a50c46ad819ec8ba380a42448e24459daf809cf4", - "sha256:24aa705a5f5bd3a8bcfa4d123f03413de5d86e497435693b638cbffb7d5d8a1b", - "sha256:2770bb0d5e3cc0e31e7318db06efcbcdb7b31bcb1a70086d3177692a02256f59", - "sha256:331ad15c39c9fe9186ceaf87203a9ecf5ae0ba2538c9e898e3a6967e8ad3db6f", - "sha256:3aa9d43b02a0c681f0bfbc12d476d47b2b2b6a3f9287f11ee42989a268a1833c", - "sha256:41f4915e09218744d8bae14759f983e466ab69b178de38066f7579892ff2a555", - "sha256:4304d4416ff032ed50ad6bb87416d802e67139e31c0bde4628f36a47a3164bfa", - "sha256:435a22d00ec7d7ea533db494da8581b05977f9c37338c80bc86314bec2619424", - "sha256:45f7cd36186db767d803b1473b3c659d57a23b5fa491ad83c6d40f2af58e4dbb", - "sha256:48b389b1fd5144603d61d752afd7167dfd205973a43151ae5045b35793232aa2", - "sha256:4e67d26532bfd8b7f7c05d5a766d6f437b362c1bf203a3a5ce3593a645e870b8", - "sha256:516a405f174fd3b88829eabfe4bb296ac602d6a0f68e0d64d5ac9456194a5b7e", - "sha256:5ba5c243f4004c750836f81606a9fcb7841f8874ad8f3bf204ff5e56332b72b9", - "sha256:5bdc0f1f610d067c70aa3737ed06e2726fd9d6f7bfee4a351f4c40b6831f4e82", - "sha256:6107e445faf057c118d5050560695e46d272e5301feffda3c41849641222a828", - "sha256:6327b572f5770293fc062a7ec04160e89741e8552bf1c358d1a23eba68166759", - "sha256:669b29a9eca6146465cc574659058ed949748f0809a2582d1f1a324eb91054dc", - "sha256:6ce01337d23884b21c03869d2f68c5523d43174d4fc405490eb0091057943118", - "sha256:6d872186c1617d143969defeadac5a904e6e374183e07977eedef9c07c8953bf", - "sha256:6f76a90c345796c01d85e6332e81cab6d70de83b829cf1d9762d0a3da59c7932", - "sha256:70d2aa9fb00cf52034feac4b913181a6e10356019b18ef89bc7c12a283bf5f5a", - "sha256:7cbc78dc018596315d4e7841c8c3a7ae31cc4d638c9b627f87d52e8abaaf2d29", - "sha256:856bf0924d24e7f93b8aee12a3a1095c34085600aa805693fb7f5d1962393206", - "sha256:8a98748ed1a1df4ee1d6f927e151ed6c1a09d5ec21684de879c7ea6aa96f58f2", - "sha256:93a7350f6706b31f457c1457d3a3259ff9071a66f312ae64dc024f049055f72c", - "sha256:964823b2fc77b55355999ade496c54dde161c621cb1f6eac61dc30ed1b63cd4c", - "sha256:a003ac9edc22d99ae1286b0875c460351f4e101f8c9d9d2576e78d7e048f64e0", - "sha256:a0ce71725cacc9ebf839630772b07eeec220cbb5f03be1399e0457a1464f8e1a", - "sha256:a47eef975d2b8b721775a0fa286f50eab535b9d56c70a6e62842134cf7841195", - "sha256:a8b5b9712783415695663bd463990e2f00c6750562e6ad1d28e072a611c5f2a6", - "sha256:a9015f5b8af1bb6837a3fcb0cdf3b874fe3385ff6274e8b7925d81ccaec3c5c9", - "sha256:aec510255ce690d240f7cb23d7114f6b351c733a74c279a84def763660a2c3bc", - "sha256:b00e7bcd71caa0282cbe3c90966f738e2db91e64092a877c3ff7f19a1628fdcb", - "sha256:b50aaac7d05c2c26dfd50c3321199f019ba76bb650e346a6ef3616306eed67b0", - "sha256:b7b6ea9e36d32582cda3465f54c4b454f62f23cb083ebc7a94e2ca6ef011c3a7", - "sha256:bb9333f58fc3a2296fb1d54576138d4cf5d496a2cc118422bd77835e6ae0b9cb", - "sha256:c1c13185b90bbd3f8b5963cd8ce7ad4ff441924c31e23c975cb150e27c2bf67a", - "sha256:c3b8bd3133cd50f6b637bb4322822c94c5ce4bf0d724ed5ae70afce62187c492", - "sha256:c5d97162c196ce54af6700949ddf9409e9833ef1003b4741c2b39ef46f1d9720", - "sha256:c815270206f983309915a6844fe994b2fa47e5d05c4c4cef267c3b30e34dbe42", - "sha256:cab2eba3830bf4f6d91e2d6718e0e1c14a2f5ad1af68a89d24ace0c6b17cced7", - "sha256:d1df34588123fcc88c872f5acb6f74ae59e9d182a2707097f9e28275ec26a12d", - "sha256:d6bdcd415ba87846fd317bee0774e412e8792832e7805938987e4ede1d13046d", - "sha256:db9a30ec064129d605d0f1aedc93e00894b9334ec74ba9c6bdd08147434b33eb", - "sha256:dbc183e7bef690c9abe5ea67b7b60fdbca81aa8da43468287dae7b5c046107d4", - "sha256:dca802c8db0720ce1c49cce1149ff7b06e91ba15fa84b1d59144fef1a1bc7ac2", - "sha256:dec6b307ce928e8e112a6bb9921a1cb00a0e14979bf28b98e084a4b8a742bd9b", - "sha256:df8bb0010fdd0a743b7542589223a2816bdde4d94bb5ad67884348fa2c1c67e8", - "sha256:e4094c7b464cf0a858e75cd14b03509e84789abf7b79f8537e6a72152109c76e", - "sha256:e4760a68cab57bfaa628938e9c2971137e05ce48e762a9cb53b76c9b569f1204", - "sha256:eb09b82377233b902d4c3fbeeb7ad731cdab579c6c6fda1f763cd779139e47c3", - "sha256:eb862356ee9391dc5a0b3cbc00f416b48c1b9a52d252d898e5b7696a5f9fe150", - "sha256:ef9528915df81b8f4c7612b19b8628214c65c9b7f74db2e34a646a0a2a0da2d4", - "sha256:f3157624b7558b914cb039fd1af735e5e8049a87c817cc215109ad1c8779df76", - "sha256:f3e0992f23bbb0be00a921eae5363329253c3b86287db27092461c887b791e5e", - "sha256:f9338cc05451f1942d0d8203ec2c346c830f8e86469903d5126c1f0a13a2bcbb", - "sha256:ffef8fd58a36fb5f1196919638f73dd3ae0db1a878982b27a9a5a176ede4ba91" - ], - "markers": "platform_python_implementation != 'PyPy'", - "version": "==1.17.0" - }, - "cfgv": { - "hashes": [ - "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9", - "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560" + "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8", + "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2", + "sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1", + "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15", + "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36", + "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", + "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8", + "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36", + "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17", + "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf", + "sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc", + "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3", + "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed", + "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702", + "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1", + "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8", + "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903", + "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6", + "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d", + "sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b", + "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e", + "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be", + "sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c", + "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683", + "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9", + "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c", + "sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8", + "sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1", + "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4", + "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655", + "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67", + "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595", + "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0", + "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65", + "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41", + "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6", + "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401", + "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6", + "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3", + "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16", + "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93", + "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e", + "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4", + "sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964", + "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c", + "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576", + "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0", + "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3", + "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662", + "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3", + "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff", + "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5", + "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd", + "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f", + "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5", + "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14", + "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d", + "sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9", + "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7", + "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382", + "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a", + "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e", + "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a", + "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4", + "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99", + "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87", + "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b" ], "markers": "python_version >= '3.8'", - "version": "==3.4.0" + "version": "==1.17.1" }, "charset-normalizer": { "hashes": [ @@ -245,7 +239,6 @@ "sha256:f98bf604c82c416bc829e490c700ca1553eafdf2912a91e23a79d97d9801372a", "sha256:fba1007b3ef89946dbbb515aeeb41e30203b004f0b4b00e5e16078b518563289" ], - "index": "pypi", "markers": "python_version >= '3.7'", "version": "==43.0.1" }, @@ -257,21 +250,14 @@ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==1.2.14" }, - "distlib": { - "hashes": [ - "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784", - "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64" - ], - "version": "==0.3.8" - }, "django": { "hashes": [ - "sha256:848a5980e8efb76eea70872fb0e4bc5e371619c70fffbe48e3e1b50b2c09455d", - "sha256:d3b811bf5371a26def053d7ee42a9df1267ef7622323fe70a601936725aa4557" + "sha256:021ffb7fdab3d2d388bc8c7c2434eb9c1f6f4d09e6119010bbb1694dda286bc2", + "sha256:71603f27dac22a6533fb38d83072eea9ddb4017fead6f67f2562a40402d61c3f" ], "index": "pypi", "markers": "python_version >= '3.10'", - "version": "==5.1" + "version": "==5.1.1" }, "django-mail-auth": { "hashes": [ @@ -285,14 +271,6 @@ "ecobalyse": { "file": "packages/python/ecobalyse" }, - "filelock": { - "hashes": [ - "sha256:2207938cbc1844345cb01a5a95524dae30f0ce089eba5b00378295a17e3e90cb", - "sha256:6ca1fffae96225dab4c6eaf1c4f4f28cd2568d3ec2a44e15a08520504de468e7" - ], - "markers": "python_version >= '3.8'", - "version": "==3.15.4" - }, "gitdb": { "hashes": [ "sha256:81a3407ddd2ee8df444cbacea00e2d038e40150acfa3001696fe0dcf1d3adfa4", @@ -310,70 +288,6 @@ "markers": "python_version >= '3.7'", "version": "==3.1.43" }, - "greenlet": { - "hashes": [ - "sha256:01bc7ea167cf943b4c802068e178bbf70ae2e8c080467070d01bfa02f337ee67", - "sha256:0448abc479fab28b00cb472d278828b3ccca164531daab4e970a0458786055d6", - "sha256:086152f8fbc5955df88382e8a75984e2bb1c892ad2e3c80a2508954e52295257", - "sha256:098d86f528c855ead3479afe84b49242e174ed262456c342d70fc7f972bc13c4", - "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676", - "sha256:1551a8195c0d4a68fac7a4325efac0d541b48def35feb49d803674ac32582f61", - "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc", - "sha256:1996cb9306c8595335bb157d133daf5cf9f693ef413e7673cb07e3e5871379ca", - "sha256:1a7191e42732df52cb5f39d3527217e7ab73cae2cb3694d241e18f53d84ea9a7", - "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728", - "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305", - "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6", - "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379", - "sha256:2dd6e660effd852586b6a8478a1d244b8dc90ab5b1321751d2ea15deb49ed414", - "sha256:3ddc0f794e6ad661e321caa8d2f0a55ce01213c74722587256fb6566049a8b04", - "sha256:3ed7fb269f15dc662787f4119ec300ad0702fa1b19d2135a37c2c4de6fadfd4a", - "sha256:419b386f84949bf0e7c73e6032e3457b82a787c1ab4a0e43732898a761cc9dbf", - "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491", - "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559", - "sha256:57e8974f23e47dac22b83436bdcf23080ade568ce77df33159e019d161ce1d1e", - "sha256:5b51e85cb5ceda94e79d019ed36b35386e8c37d22f07d6a751cb659b180d5274", - "sha256:649dde7de1a5eceb258f9cb00bdf50e978c9db1b996964cd80703614c86495eb", - "sha256:64d7675ad83578e3fc149b617a444fab8efdafc9385471f868eb5ff83e446b8b", - "sha256:68834da854554926fbedd38c76e60c4a2e3198c6fbed520b106a8986445caaf9", - "sha256:6b66c9c1e7ccabad3a7d037b2bcb740122a7b17a53734b7d72a344ce39882a1b", - "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be", - "sha256:7170375bcc99f1a2fbd9c306f5be8764eaf3ac6b5cb968862cad4c7057756506", - "sha256:73a411ef564e0e097dbe7e866bb2dda0f027e072b04da387282b02c308807405", - "sha256:77457465d89b8263bca14759d7c1684df840b6811b2499838cc5b040a8b5b113", - "sha256:7f362975f2d179f9e26928c5b517524e89dd48530a0202570d55ad6ca5d8a56f", - "sha256:81bb9c6d52e8321f09c3d165b2a78c680506d9af285bfccbad9fb7ad5a5da3e5", - "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230", - "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d", - "sha256:99bf650dc5d69546e076f413a87481ee1d2d09aaaaaca058c9251b6d8c14783f", - "sha256:9da2bd29ed9e4f15955dd1595ad7bc9320308a3b766ef7f837e23ad4b4aac31a", - "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e", - "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61", - "sha256:b37eef18ea55f2ffd8f00ff8fe7c8d3818abd3e25fb73fae2ca3b672e333a7a6", - "sha256:b542be2440edc2d48547b5923c408cbe0fc94afb9f18741faa6ae970dbcb9b6d", - "sha256:b7dcbe92cc99f08c8dd11f930de4d99ef756c3591a5377d1d9cd7dd5e896da71", - "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22", - "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2", - "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3", - "sha256:c5ee858cfe08f34712f548c3c363e807e7186f03ad7a5039ebadb29e8c6be067", - "sha256:c9db1c18f0eaad2f804728c67d6c610778456e3e1cc4ab4bbd5eeb8e6053c6fc", - "sha256:d353cadd6083fdb056bb46ed07e4340b0869c305c8ca54ef9da3421acbdf6881", - "sha256:d46677c85c5ba00a9cb6f7a00b2bfa6f812192d2c9f7d9c4f6a55b60216712f3", - "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e", - "sha256:d73a9fe764d77f87f8ec26a0c85144d6a951a6c438dfe50487df5595c6373eac", - "sha256:da70d4d51c8b306bb7a031d5cff6cc25ad253affe89b70352af5f1cb68e74b53", - "sha256:daf3cb43b7cf2ba96d614252ce1684c1bccee6b2183a01328c98d36fcd7d5cb0", - "sha256:dca1e2f3ca00b84a396bc1bce13dd21f680f035314d2379c4160c98153b2059b", - "sha256:dd4f49ae60e10adbc94b45c0b5e6a179acc1736cf7a90160b404076ee283cf83", - "sha256:e1f145462f1fa6e4a4ae3c0f782e580ce44d57c8f2c7aae1b6fa88c0b2efdb41", - "sha256:e3391d1e16e2a5a1507d83e4a8b100f4ee626e8eca43cf2cadb543de69827c4c", - "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf", - "sha256:fd096eb7ffef17c456cfa587523c5f92321ae02427ff955bebe9e3c63bc9f0da", - "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33" - ], - "markers": "platform_machine == 'aarch64' or (platform_machine == 'ppc64le' or (platform_machine == 'x86_64' or (platform_machine == 'amd64' or (platform_machine == 'AMD64' or (platform_machine == 'win32' or platform_machine == 'WIN32')))))", - "version": "==3.0.3" - }, "gunicorn": { "hashes": [ "sha256:350679f91b24062c86e386e198a15438d53a7a8207235a78ba1b53df4c4378d9", @@ -383,21 +297,13 @@ "markers": "python_version >= '3.7'", "version": "==22.0.0" }, - "identify": { - "hashes": [ - "sha256:cb171c685bdc31bcc4c1734698736a7d5b6c8bf2e0c15117f4d469c8640ae5cf", - "sha256:e79ae4406387a9d300332b5fd366d8994f1525e8414984e1a59e058b2eda2dd0" - ], - "markers": "python_version >= '3.8'", - "version": "==2.6.0" - }, "idna": { "hashes": [ - "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc", - "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0" + "sha256:050b4e5baadcd44d760cedbd2b8e639f2ff89bbc7a5730fcc662954303377aac", + "sha256:d838c2c0ed6fced7693d5e8ab8e734d5f8fda53a039c0164afb0b82e771e3603" ], - "markers": "python_version >= '3.5'", - "version": "==3.7" + "markers": "python_version >= '3.6'", + "version": "==3.8" }, "iniconfig": { "hashes": [ @@ -423,57 +329,6 @@ "markers": "python_version >= '3.7'", "version": "==0.1.2" }, - "nodeenv": { - "hashes": [ - "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", - "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6'", - "version": "==1.9.1" - }, - "numpy": { - "hashes": [ - "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b", - "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818", - "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20", - "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0", - "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010", - "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a", - "sha256:3373d5d70a5fe74a2c1bb6d2cfd9609ecf686d47a2d7b1d37a8f3b6bf6003aea", - "sha256:47711010ad8555514b434df65f7d7b076bb8261df1ca9bb78f53d3b2db02e95c", - "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71", - "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110", - "sha256:52b8b60467cd7dd1e9ed082188b4e6bb35aa5cdd01777621a1658910745b90be", - "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a", - "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a", - "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5", - "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed", - "sha256:679b0076f67ecc0138fd2ede3a8fd196dddc2ad3254069bcb9faf9a79b1cebcd", - "sha256:7349ab0fa0c429c82442a27a9673fc802ffdb7c7775fad780226cb234965e53c", - "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e", - "sha256:7e50d0a0cc3189f9cb0aeb3a6a6af18c16f59f004b866cd2be1c14b36134a4a0", - "sha256:95a7476c59002f2f6c590b9b7b998306fba6a5aa646b1e22ddfeaf8f78c3a29c", - "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a", - "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b", - "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0", - "sha256:a354325ee03388678242a4d7ebcd08b5c727033fcff3b2f536aea978e15ee9e6", - "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2", - "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a", - "sha256:afedb719a9dcfc7eaf2287b839d8198e06dcd4cb5d276a3df279231138e83d30", - "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218", - "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5", - "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07", - "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2", - "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4", - "sha256:d5241e0a80d808d70546c697135da2c613f30e28251ff8307eb72ba696945764", - "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef", - "sha256:f870204a840a60da0b12273ef34f7051e98c3b5961b61b0c2c1be6dfd64fbcd3", - "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f" - ], - "index": "pypi", - "markers": "python_version >= '3.9'", - "version": "==1.26.4" - }, "packaging": { "hashes": [ "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002", @@ -482,48 +337,6 @@ "markers": "python_version >= '3.8'", "version": "==24.1" }, - "pandas": { - "hashes": [ - "sha256:14e45300521902689a81f3f41386dc86f19b8ba8dd5ac5a3c7010ef8d2932813", - "sha256:26d9c71772c7afb9d5046e6e9cf42d83dd147b5cf5bcb9d97252077118543792", - "sha256:3749077d86e3a2f0ed51367f30bf5b82e131cc0f14260c4d3e499186fccc4406", - "sha256:41179ce559943d83a9b4bbacb736b04c928b095b5f25dd2b7389eda08f46f373", - "sha256:478ff646ca42b20376e4ed3fa2e8d7341e8a63105586efe54fa2508ee087f328", - "sha256:50869a35cbb0f2e0cd5ec04b191e7b12ed688874bd05dd777c19b28cbea90996", - "sha256:565fa34a5434d38e9d250af3c12ff931abaf88050551d9fbcdfafca50d62babf", - "sha256:5f2b952406a1588ad4cad5b3f55f520e82e902388a6d5a4a91baa8d38d23c7f6", - "sha256:5fbcb19d6fceb9e946b3e23258757c7b225ba450990d9ed63ccceeb8cae609f7", - "sha256:6973549c01ca91ec96199e940495219c887ea815b2083722821f1d7abfa2b4dc", - "sha256:74a3fd7e5a7ec052f183273dc7b0acd3a863edf7520f5d3a1765c04ffdb3b0b1", - "sha256:7a0a56cef15fd1586726dace5616db75ebcfec9179a3a55e78f72c5639fa2a23", - "sha256:7cec0bee9f294e5de5bbfc14d0573f65526071029d036b753ee6507d2a21480a", - "sha256:87bd9c03da1ac870a6d2c8902a0e1fd4267ca00f13bc494c9e5a9020920e1d51", - "sha256:972d8a45395f2a2d26733eb8d0f629b2f90bebe8e8eddbb8829b180c09639572", - "sha256:9842b6f4b8479e41968eced654487258ed81df7d1c9b7b870ceea24ed9459b31", - "sha256:9f69c4029613de47816b1bb30ff5ac778686688751a5e9c99ad8c7031f6508e5", - "sha256:a50d9a4336a9621cab7b8eb3fb11adb82de58f9b91d84c2cd526576b881a0c5a", - "sha256:bc4c368f42b551bf72fac35c5128963a171b40dce866fb066540eeaf46faa003", - "sha256:c39a8da13cede5adcd3be1182883aea1c925476f4e84b2807a46e2775306305d", - "sha256:c3ac844a0fe00bfaeb2c9b51ab1424e5c8744f89860b138434a363b1f620f354", - "sha256:c4c00e0b0597c8e4f59e8d461f797e5d70b4d025880516a8261b2817c47759ee", - "sha256:c74a62747864ed568f5a82a49a23a8d7fe171d0c69038b38cedf0976831296fa", - "sha256:dd05f7783b3274aa206a1af06f0ceed3f9b412cf665b7247eacd83be41cf7bf0", - "sha256:dfd681c5dc216037e0b0a2c821f5ed99ba9f03ebcf119c7dac0e9a7b960b9ec9", - "sha256:e474390e60ed609cec869b0da796ad94f420bb057d86784191eefc62b65819ae", - "sha256:f76d097d12c82a535fda9dfe5e8dd4127952b45fea9b0276cb30cca5ea313fbc" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==1.5.3" - }, - "platformdirs": { - "hashes": [ - "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee", - "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3" - ], - "markers": "python_version >= '3.8'", - "version": "==4.2.2" - }, "pluggy": { "hashes": [ "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", @@ -532,15 +345,6 @@ "markers": "python_version >= '3.8'", "version": "==1.5.0" }, - "pre-commit": { - "hashes": [ - "sha256:8bb6494d4a20423842e198980c9ecf9f96607a07ea29549e180eef9ae80fe7af", - "sha256:9a90a53bf82fdd8778d58085faf8d83df56e40dfe18f45b19446e26bf1b3a63f" - ], - "index": "pypi", - "markers": "python_version >= '3.9'", - "version": "==3.8.0" - }, "psycopg2-binary": { "hashes": [ "sha256:03ef7df18daf2c4c07e2695e8cfd5ee7f748a1d54d802330985a78d2a5a6dca9", @@ -630,12 +434,12 @@ }, "pygithub": { "hashes": [ - "sha256:0148d7347a1cdeed99af905077010aef81a4dad988b0ba51d4108bf66b443f7e", - "sha256:65b499728be3ce7b0cd2cd760da3b32f0f4d7bc55e5e0677617f90f6564e793e" + "sha256:6601e22627e87bac192f1e2e39c6e6f69a43152cfb8f307cee575879320b3051", + "sha256:81935aa4bdc939fba98fee1cb47422c09157c56a27966476ff92775602b9ee24" ], "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==2.3.0" + "markers": "python_version >= '3.8'", + "version": "==2.4.0" }, "pygments": { "hashes": [ @@ -674,20 +478,20 @@ }, "pytest": { "hashes": [ - "sha256:4ba08f9ae7dcf84ded419494d229b48d0903ea6407b030eaec46df5e6a73bba5", - "sha256:c132345d12ce551242c87269de812483f5bcc87cdbb4722e48487ba194f9fdce" + "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181", + "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2" ], "markers": "python_version >= '3.8'", - "version": "==8.3.2" + "version": "==8.3.3" }, "pytest-django": { "hashes": [ - "sha256:5d054fe011c56f3b10f978f41a8efb2e5adfc7e680ef36fb571ada1f24779d90", - "sha256:ca1ddd1e0e4c227cf9e3e40a6afc6d106b3e70868fd2ac5798a22501271cd0c7" + "sha256:1d83692cb39188682dbb419ff0393867e9904094a549a7d38a3154d5731b2b99", + "sha256:8bf7bc358c9ae6f6fc51b6cebb190fe20212196e6807121f11bd6a3b03428314" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==4.8.0" + "version": "==4.9.0" }, "pytest-mock": { "hashes": [ @@ -698,14 +502,6 @@ "markers": "python_version >= '3.8'", "version": "==3.14.0" }, - "python-dateutil": { - "hashes": [ - "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", - "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.9.0.post0" - }, "python-decouple": { "hashes": [ "sha256:ba6e2657d4f376ecc46f77a3a615e058d93ba5e465c01bbe57289bfb7cce680f", @@ -714,72 +510,6 @@ "index": "pypi", "version": "==3.8" }, - "pytz": { - "hashes": [ - "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812", - "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319" - ], - "version": "==2024.1" - }, - "pyyaml": { - "hashes": [ - "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff", - "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", - "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", - "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e", - "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", - "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", - "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", - "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", - "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", - "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", - "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a", - "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", - "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", - "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8", - "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", - "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19", - "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", - "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a", - "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", - "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", - "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", - "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631", - "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d", - "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", - "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", - "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", - "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", - "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", - "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", - "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706", - "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", - "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", - "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", - "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083", - "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", - "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", - "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", - "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f", - "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725", - "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", - "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", - "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", - "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", - "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", - "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5", - "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d", - "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290", - "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", - "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", - "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", - "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", - "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12", - "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4" - ], - "markers": "python_version >= '3.8'", - "version": "==6.0.2" - }, "requests": { "hashes": [ "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", @@ -790,36 +520,11 @@ }, "rich": { "hashes": [ - "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222", - "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432" + "sha256:1760a3c0848469b97b558fc61c85233e3dafb69c7a071b4d60c38099d3cd4c06", + "sha256:8260cda28e3db6bf04d2d1ef4dbc03ba80a824c88b0e7668a0f23126a424844a" ], "markers": "python_full_version >= '3.7.0'", - "version": "==13.7.1" - }, - "ruff": { - "hashes": [ - "sha256:2c7477c3b9da822e2db0b4e0b59e61b8a23e87886e727b327e7dcaf06213c5cf", - "sha256:392688dbb50fecf1bf7126731c90c11a9df1c3a4cdc3f481b53e851da5634fa5", - "sha256:3a0af7ab3f86e3dc9f157a928e08e26c4b40707d0612b01cd577cc84b8905cc9", - "sha256:3bc81074971b0ffad1bd0c52284b22411f02a11a012082a76ac6da153536e014", - "sha256:45efaae53b360c81043e311cdec8a7696420b3d3e8935202c2846e7a97d4edae", - "sha256:5278d3e095ccc8c30430bcc9bc550f778790acc211865520f3041910a28d0024", - "sha256:99d7ae0df47c62729d58765c593ea54c2546d5de213f2af2a19442d50a10cec9", - "sha256:9eb18dfd7b613eec000e3738b3f0e4398bf0153cb80bfa3e351b3c1c2f6d7b15", - "sha256:9fb4c4e8b83f19c9477a8745e56d2eeef07a7ff50b68a6998f7d9e2e3887bdc4", - "sha256:af3ffd8c6563acb8848d33cd19a69b9bfe943667f0419ca083f8ebe4224a3436", - "sha256:b2e0dd11e2ae553ee5c92a81731d88a9883af8db7408db47fc81887c1f8b672e", - "sha256:b4bb7de6a24169dc023f992718a9417380301b0c2da0fe85919f47264fb8add9", - "sha256:bc60c7d71b732c8fa73cf995efc0c836a2fd8b9810e115be8babb24ae87e0850", - "sha256:c2ebfc8f51ef4aca05dad4552bbcf6fe8d1f75b2f6af546cc47cc1c1ca916b5b", - "sha256:c62bc04c6723a81e25e71715aa59489f15034d69bf641df88cb38bdc32fd1dbb", - "sha256:d812615525a34ecfc07fd93f906ef5b93656be01dfae9a819e31caa6cfe758a1", - "sha256:faaa4060f4064c3b7aaaa27328080c932fa142786f8142aff095b42b6a2eb631", - "sha256:fe6d5f65d6f276ee7a0fc50a0cecaccb362d30ef98a110f99cac1c7872df2f18" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==0.6.1" + "version": "==13.8.1" }, "shellingham": { "hashes": [ @@ -829,14 +534,6 @@ "markers": "python_version >= '3.7'", "version": "==1.5.4" }, - "six": { - "hashes": [ - "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", - "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.16.0" - }, "smmap": { "hashes": [ "sha256:dceeb6c0028fdb6734471eb07c0cd2aae706ccaecab45965ee83f11c8d3b1f62", @@ -845,63 +542,7 @@ "markers": "python_version >= '3.7'", "version": "==5.0.1" }, - "sqlalchemy": { - "hashes": [ - "sha256:0094c5dc698a5f78d3d1539853e8ecec02516b62b8223c970c86d44e7a80f6c7", - "sha256:0138c5c16be3600923fa2169532205d18891b28afa817cb49b50e08f62198bb8", - "sha256:0a089e218654e740a41388893e090d2e2c22c29028c9d1353feb38638820bbeb", - "sha256:0b3f4c438e37d22b83e640f825ef0f37b95db9aa2d68203f2c9549375d0b2260", - "sha256:16863e2b132b761891d6c49f0a0f70030e0bcac4fd208117f6b7e053e68668d0", - "sha256:1f9a727312ff6ad5248a4367358e2cf7e625e98b1028b1d7ab7b806b7d757513", - "sha256:2383146973a15435e4717f94c7509982770e3e54974c71f76500a0136f22810b", - "sha256:2753743c2afd061bb95a61a51bbb6a1a11ac1c44292fad898f10c9839a7f75b2", - "sha256:296230899df0b77dec4eb799bcea6fbe39a43707ce7bb166519c97b583cfcab3", - "sha256:2a4f4da89c74435f2bc61878cd08f3646b699e7d2eba97144030d1be44e27584", - "sha256:2b1708916730f4830bc69d6f49d37f7698b5bd7530aca7f04f785f8849e95255", - "sha256:2ecabd9ccaa6e914e3dbb2aa46b76dede7eadc8cbf1b8083c94d936bcd5ffb49", - "sha256:311710f9a2ee235f1403537b10c7687214bb1f2b9ebb52702c5aa4a77f0b3af7", - "sha256:37a4b4fb0dd4d2669070fb05b8b8824afd0af57587393015baee1cf9890242d9", - "sha256:3a365eda439b7a00732638f11072907c1bc8e351c7665e7e5da91b169af794af", - "sha256:3b48154678e76445c7ded1896715ce05319f74b1e73cf82d4f8b59b46e9c0ddc", - "sha256:3b69e934f0f2b677ec111b4d83f92dc1a3210a779f69bf905273192cf4ed433e", - "sha256:3cb5a646930c5123f8461f6468901573f334c2c63c795b9af350063a736d0134", - "sha256:408f8b0e2c04677e9c93f40eef3ab22f550fecb3011b187f66a096395ff3d9fd", - "sha256:40ad017c672c00b9b663fcfcd5f0864a0a97828e2ee7ab0c140dc84058d194cf", - "sha256:5a79d65395ac5e6b0c2890935bad892eabb911c4aa8e8015067ddb37eea3d56c", - "sha256:5a8e3b0a7e09e94be7510d1661339d6b52daf202ed2f5b1f9f48ea34ee6f2d57", - "sha256:69c9db1ce00e59e8dd09d7bae852a9add716efdc070a3e2068377e6ff0d6fdaa", - "sha256:7108d569d3990c71e26a42f60474b4c02c8586c4681af5fd67e51a044fdea86a", - "sha256:77d2edb1f54aff37e3318f611637171e8ec71472f1fdc7348b41dcb226f93d90", - "sha256:7d74336c65705b986d12a7e337ba27ab2b9d819993851b140efdf029248e818e", - "sha256:8409de825f2c3b62ab15788635ccaec0c881c3f12a8af2b12ae4910a0a9aeef6", - "sha256:955991a09f0992c68a499791a753523f50f71a6885531568404fa0f231832aa0", - "sha256:99650e9f4cf3ad0d409fed3eec4f071fadd032e9a5edc7270cd646a26446feeb", - "sha256:9a5baf9267b752390252889f0c802ea13b52dfee5e369527da229189b8bd592e", - "sha256:a0ef36b28534f2a5771191be6edb44cc2673c7b2edf6deac6562400288664221", - "sha256:a1429a4b0f709f19ff3b0cf13675b2b9bfa8a7e79990003207a011c0db880a13", - "sha256:a7bfc726d167f425d4c16269a9a10fe8630ff6d14b683d588044dcef2d0f6be7", - "sha256:a943d297126c9230719c27fcbbeab57ecd5d15b0bd6bfd26e91bfcfe64220621", - "sha256:ae8c62fe2480dd61c532ccafdbce9b29dacc126fe8be0d9a927ca3e699b9491a", - "sha256:b60203c63e8f984df92035610c5fb76d941254cf5d19751faab7d33b21e5ddc0", - "sha256:b6bf767d14b77f6a18b6982cbbf29d71bede087edae495d11ab358280f304d8e", - "sha256:b6c7ec2b1f4969fc19b65b7059ed00497e25f54069407a8701091beb69e591a5", - "sha256:bba002a9447b291548e8d66fd8c96a6a7ed4f2def0bb155f4f0a1309fd2735d5", - "sha256:bc0c53579650a891f9b83fa3cecd4e00218e071d0ba00c4890f5be0c34887ed3", - "sha256:c4f61ada6979223013d9ab83a3ed003ded6959eae37d0d685db2c147e9143797", - "sha256:c62d401223f468eb4da32627bffc0c78ed516b03bb8a34a58be54d618b74d472", - "sha256:e42203d8d20dc704604862977b1470a122e4892791fe3ed165f041e4bf447a1b", - "sha256:edc16a50f5e1b7a06a2dcc1f2205b0b961074c123ed17ebda726f376a5ab0953", - "sha256:efedba7e13aa9a6c8407c48facfdfa108a5a4128e35f4c68f20c3407e4376aa9", - "sha256:f1dc3eabd8c0232ee8387fbe03e0a62220a6f089e278b1f0aaf5e2d6210741ad", - "sha256:f69e4c756ee2686767eb80f94c0125c8b0a0b87ede03eacc5c8ae3b54b99dc46", - "sha256:f7703c2010355dd28f53deb644a05fc30f796bd8598b43f0ba678878780b6e4c", - "sha256:fa561138a64f949f3e889eb9ab8c58e1504ab351d6cf55259dc4c248eaa19da6" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==2.0.30" - }, - "sqlparse": { + "sqlparse": { "hashes": [ "sha256:773dcbf9a5ab44a090f3441e2180efe2560220203dc2f8c0b0fa141e18b505e4", "sha256:bb6b4df465655ef332548e24f08e205afc81b9ab86cb1c45657a7ff173a3a00e" @@ -911,12 +552,12 @@ }, "typer": { "hashes": [ - "sha256:819aa03699f438397e876aa12b0d63766864ecba1b579092cc9fe35d886e34b6", - "sha256:c9c1613ed6a166162705b3347b8d10b661ccc5d95692654d0fb628118f2c34e6" + "sha256:62fe4e471711b147e3365034133904df3e235698399bc4de2b36c8579298d52b", + "sha256:f592f089bedcc8ec1b974125d64851029c3b1af145f04aca64d69410f0c9b722" ], "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==0.12.4" + "version": "==0.12.5" }, "typing-extensions": { "hashes": [ @@ -934,14 +575,6 @@ "markers": "python_version >= '3.8'", "version": "==2.2.2" }, - "virtualenv": { - "hashes": [ - "sha256:4c43a2a236279d9ea36a0d76f98d84bd6ca94ac4e0f4a3b9d46d05e10fea542a", - "sha256:8cc4a31139e796e9a7de2cd5cf2489de1217193116a8fd42328f1bd65f434589" - ], - "markers": "python_version >= '3.7'", - "version": "==20.26.3" - }, "wrapt": { "hashes": [ "sha256:0d2691979e93d06a95a26257adb7bfd0c93818e89b1406f5a28f36e0d8c1e1fc", @@ -1019,5 +652,390 @@ "version": "==1.16.0" } }, - "develop": {} + "develop": { + "cfgv": { + "hashes": [ + "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9", + "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560" + ], + "markers": "python_version >= '3.8'", + "version": "==3.4.0" + }, + "distlib": { + "hashes": [ + "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784", + "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64" + ], + "version": "==0.3.8" + }, + "filelock": { + "hashes": [ + "sha256:81de9eb8453c769b63369f87f11131a7ab04e367f8d97ad39dc230daa07e3bec", + "sha256:f6ed4c963184f4c84dd5557ce8fece759a3724b37b80c6c4f20a2f63a4dc6609" + ], + "markers": "python_version >= '3.8'", + "version": "==3.16.0" + }, + "greenlet": { + "hashes": [ + "sha256:01059afb9b178606b4b6e92c3e710ea1635597c3537e44da69f4531e111dd5e9", + "sha256:037d9ac99540ace9424cb9ea89f0accfaff4316f149520b4ae293eebc5bded17", + "sha256:0e49a65d25d7350cca2da15aac31b6f67a43d867448babf997fe83c7505f57bc", + "sha256:13ff8c8e54a10472ce3b2a2da007f915175192f18e6495bad50486e87c7f6637", + "sha256:1544b8dd090b494c55e60c4ff46e238be44fdc472d2589e943c241e0169bcea2", + "sha256:184258372ae9e1e9bddce6f187967f2e08ecd16906557c4320e3ba88a93438c3", + "sha256:1ddc7bcedeb47187be74208bc652d63d6b20cb24f4e596bd356092d8000da6d6", + "sha256:221169d31cada333a0c7fd087b957c8f431c1dba202c3a58cf5a3583ed973e9b", + "sha256:243a223c96a4246f8a30ea470c440fe9db1f5e444941ee3c3cd79df119b8eebf", + "sha256:24fc216ec7c8be9becba8b64a98a78f9cd057fd2dc75ae952ca94ed8a893bf27", + "sha256:2651dfb006f391bcb240635079a68a261b227a10a08af6349cba834a2141efa1", + "sha256:26811df4dc81271033a7836bc20d12cd30938e6bd2e9437f56fa03da81b0f8fc", + "sha256:26d9c1c4f1748ccac0bae1dbb465fb1a795a75aba8af8ca871503019f4285e2a", + "sha256:28fe80a3eb673b2d5cc3b12eea468a5e5f4603c26aa34d88bf61bba82ceb2f9b", + "sha256:2cd8518eade968bc52262d8c46727cfc0826ff4d552cf0430b8d65aaf50bb91d", + "sha256:2d004db911ed7b6218ec5c5bfe4cf70ae8aa2223dffbb5b3c69e342bb253cb28", + "sha256:3d07c28b85b350564bdff9f51c1c5007dfb2f389385d1bc23288de51134ca303", + "sha256:3e7e6ef1737a819819b1163116ad4b48d06cfdd40352d813bb14436024fcda99", + "sha256:44151d7b81b9391ed759a2f2865bbe623ef00d648fed59363be2bbbd5154656f", + "sha256:44cd313629ded43bb3b98737bba2f3e2c2c8679b55ea29ed73daea6b755fe8e7", + "sha256:4a3dae7492d16e85ea6045fd11cb8e782b63eac8c8d520c3a92c02ac4573b0a6", + "sha256:4b5ea3664eed571779403858d7cd0a9b0ebf50d57d2cdeafc7748e09ef8cd81a", + "sha256:4c3446937be153718250fe421da548f973124189f18fe4575a0510b5c928f0cc", + "sha256:5415b9494ff6240b09af06b91a375731febe0090218e2898d2b85f9b92abcda0", + "sha256:5fd6e94593f6f9714dbad1aaba734b5ec04593374fa6638df61592055868f8b8", + "sha256:619935a44f414274a2c08c9e74611965650b730eb4efe4b2270f91df5e4adf9a", + "sha256:655b21ffd37a96b1e78cc48bf254f5ea4b5b85efaf9e9e2a526b3c9309d660ca", + "sha256:665b21e95bc0fce5cab03b2e1d90ba9c66c510f1bb5fdc864f3a377d0f553f6b", + "sha256:6a4bf607f690f7987ab3291406e012cd8591a4f77aa54f29b890f9c331e84989", + "sha256:6cea1cca3be76c9483282dc7760ea1cc08a6ecec1f0b6ca0a94ea0d17432da19", + "sha256:713d450cf8e61854de9420fb7eea8ad228df4e27e7d4ed465de98c955d2b3fa6", + "sha256:726377bd60081172685c0ff46afbc600d064f01053190e4450857483c4d44484", + "sha256:76b3e3976d2a452cba7aa9e453498ac72240d43030fdc6d538a72b87eaff52fd", + "sha256:76dc19e660baea5c38e949455c1181bc018893f25372d10ffe24b3ed7341fb25", + "sha256:76e5064fd8e94c3f74d9fd69b02d99e3cdb8fc286ed49a1f10b256e59d0d3a0b", + "sha256:7f346d24d74c00b6730440f5eb8ec3fe5774ca8d1c9574e8e57c8671bb51b910", + "sha256:81eeec4403a7d7684b5812a8aaa626fa23b7d0848edb3a28d2eb3220daddcbd0", + "sha256:90b5bbf05fe3d3ef697103850c2ce3374558f6fe40fd57c9fac1bf14903f50a5", + "sha256:9730929375021ec90f6447bff4f7f5508faef1c02f399a1953870cdb78e0c345", + "sha256:9eb4a1d7399b9f3c7ac68ae6baa6be5f9195d1d08c9ddc45ad559aa6b556bce6", + "sha256:a0409bc18a9f85321399c29baf93545152d74a49d92f2f55302f122007cfda00", + "sha256:a22f4e26400f7f48faef2d69c20dc055a1f3043d330923f9abe08ea0aecc44df", + "sha256:a53dfe8f82b715319e9953330fa5c8708b610d48b5c59f1316337302af5c0811", + "sha256:a771dc64fa44ebe58d65768d869fcfb9060169d203446c1d446e844b62bdfdca", + "sha256:a814dc3100e8a046ff48faeaa909e80cdb358411a3d6dd5293158425c684eda8", + "sha256:a8870983af660798dc1b529e1fd6f1cefd94e45135a32e58bd70edd694540f33", + "sha256:ac0adfdb3a21dc2a24ed728b61e72440d297d0fd3a577389df566651fcd08f97", + "sha256:b395121e9bbe8d02a750886f108d540abe66075e61e22f7353d9acb0b81be0f0", + "sha256:b9505a0c8579899057cbefd4ec34d865ab99852baf1ff33a9481eb3924e2da0b", + "sha256:c0a5b1c22c82831f56f2f7ad9bbe4948879762fe0d59833a4a71f16e5fa0f682", + "sha256:c3967dcc1cd2ea61b08b0b276659242cbce5caca39e7cbc02408222fb9e6ff39", + "sha256:c6f4c2027689093775fd58ca2388d58789009116844432d920e9147f91acbe64", + "sha256:c9d86401550b09a55410f32ceb5fe7efcd998bd2dad9e82521713cb148a4a15f", + "sha256:cd468ec62257bb4544989402b19d795d2305eccb06cde5da0eb739b63dc04665", + "sha256:cfcfb73aed40f550a57ea904629bdaf2e562c68fa1164fa4588e752af6efdc3f", + "sha256:d0dd943282231480aad5f50f89bdf26690c995e8ff555f26d8a5b9887b559bcc", + "sha256:d3c59a06c2c28a81a026ff11fbf012081ea34fb9b7052f2ed0366e14896f0a1d", + "sha256:d45b75b0f3fd8d99f62eb7908cfa6d727b7ed190737dec7fe46d993da550b81a", + "sha256:d46d5069e2eeda111d6f71970e341f4bd9aeeee92074e649ae263b834286ecc0", + "sha256:d58ec349e0c2c0bc6669bf2cd4982d2f93bf067860d23a0ea1fe677b0f0b1e09", + "sha256:db1b3ccb93488328c74e97ff888604a8b95ae4f35f4f56677ca57a4fc3a4220b", + "sha256:dd65695a8df1233309b701dec2539cc4b11e97d4fcc0f4185b4a12ce54db0491", + "sha256:f9482c2ed414781c0af0b35d9d575226da6b728bd1a720668fa05837184965b7", + "sha256:f9671e7282d8c6fcabc32c0fb8d7c0ea8894ae85cee89c9aadc2d7129e1a9954", + "sha256:fad7a051e07f64e297e6e8399b4d6a3bdcad3d7297409e9a06ef8cbccff4f501", + "sha256:ffb08f2a1e59d38c7b8b9ac8083c9c8b9875f0955b1e9b9b9a965607a51f8e54" + ], + "markers": "platform_machine == 'aarch64' or (platform_machine == 'ppc64le' or (platform_machine == 'x86_64' or (platform_machine == 'amd64' or (platform_machine == 'AMD64' or (platform_machine == 'win32' or platform_machine == 'WIN32')))))", + "version": "==3.1.0" + }, + "identify": { + "hashes": [ + "sha256:cb171c685bdc31bcc4c1734698736a7d5b6c8bf2e0c15117f4d469c8640ae5cf", + "sha256:e79ae4406387a9d300332b5fd366d8994f1525e8414984e1a59e058b2eda2dd0" + ], + "markers": "python_version >= '3.8'", + "version": "==2.6.0" + }, + "nodeenv": { + "hashes": [ + "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", + "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6'", + "version": "==1.9.1" + }, + "numpy": { + "hashes": [ + "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b", + "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818", + "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20", + "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0", + "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010", + "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a", + "sha256:3373d5d70a5fe74a2c1bb6d2cfd9609ecf686d47a2d7b1d37a8f3b6bf6003aea", + "sha256:47711010ad8555514b434df65f7d7b076bb8261df1ca9bb78f53d3b2db02e95c", + "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71", + "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110", + "sha256:52b8b60467cd7dd1e9ed082188b4e6bb35aa5cdd01777621a1658910745b90be", + "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a", + "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a", + "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5", + "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed", + "sha256:679b0076f67ecc0138fd2ede3a8fd196dddc2ad3254069bcb9faf9a79b1cebcd", + "sha256:7349ab0fa0c429c82442a27a9673fc802ffdb7c7775fad780226cb234965e53c", + "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e", + "sha256:7e50d0a0cc3189f9cb0aeb3a6a6af18c16f59f004b866cd2be1c14b36134a4a0", + "sha256:95a7476c59002f2f6c590b9b7b998306fba6a5aa646b1e22ddfeaf8f78c3a29c", + "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a", + "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b", + "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0", + "sha256:a354325ee03388678242a4d7ebcd08b5c727033fcff3b2f536aea978e15ee9e6", + "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2", + "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a", + "sha256:afedb719a9dcfc7eaf2287b839d8198e06dcd4cb5d276a3df279231138e83d30", + "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218", + "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5", + "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07", + "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2", + "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4", + "sha256:d5241e0a80d808d70546c697135da2c613f30e28251ff8307eb72ba696945764", + "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef", + "sha256:f870204a840a60da0b12273ef34f7051e98c3b5961b61b0c2c1be6dfd64fbcd3", + "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f" + ], + "index": "pypi", + "markers": "python_version >= '3.9'", + "version": "==1.26.4" + }, + "pandas": { + "hashes": [ + "sha256:14e45300521902689a81f3f41386dc86f19b8ba8dd5ac5a3c7010ef8d2932813", + "sha256:26d9c71772c7afb9d5046e6e9cf42d83dd147b5cf5bcb9d97252077118543792", + "sha256:3749077d86e3a2f0ed51367f30bf5b82e131cc0f14260c4d3e499186fccc4406", + "sha256:41179ce559943d83a9b4bbacb736b04c928b095b5f25dd2b7389eda08f46f373", + "sha256:478ff646ca42b20376e4ed3fa2e8d7341e8a63105586efe54fa2508ee087f328", + "sha256:50869a35cbb0f2e0cd5ec04b191e7b12ed688874bd05dd777c19b28cbea90996", + "sha256:565fa34a5434d38e9d250af3c12ff931abaf88050551d9fbcdfafca50d62babf", + "sha256:5f2b952406a1588ad4cad5b3f55f520e82e902388a6d5a4a91baa8d38d23c7f6", + "sha256:5fbcb19d6fceb9e946b3e23258757c7b225ba450990d9ed63ccceeb8cae609f7", + "sha256:6973549c01ca91ec96199e940495219c887ea815b2083722821f1d7abfa2b4dc", + "sha256:74a3fd7e5a7ec052f183273dc7b0acd3a863edf7520f5d3a1765c04ffdb3b0b1", + "sha256:7a0a56cef15fd1586726dace5616db75ebcfec9179a3a55e78f72c5639fa2a23", + "sha256:7cec0bee9f294e5de5bbfc14d0573f65526071029d036b753ee6507d2a21480a", + "sha256:87bd9c03da1ac870a6d2c8902a0e1fd4267ca00f13bc494c9e5a9020920e1d51", + "sha256:972d8a45395f2a2d26733eb8d0f629b2f90bebe8e8eddbb8829b180c09639572", + "sha256:9842b6f4b8479e41968eced654487258ed81df7d1c9b7b870ceea24ed9459b31", + "sha256:9f69c4029613de47816b1bb30ff5ac778686688751a5e9c99ad8c7031f6508e5", + "sha256:a50d9a4336a9621cab7b8eb3fb11adb82de58f9b91d84c2cd526576b881a0c5a", + "sha256:bc4c368f42b551bf72fac35c5128963a171b40dce866fb066540eeaf46faa003", + "sha256:c39a8da13cede5adcd3be1182883aea1c925476f4e84b2807a46e2775306305d", + "sha256:c3ac844a0fe00bfaeb2c9b51ab1424e5c8744f89860b138434a363b1f620f354", + "sha256:c4c00e0b0597c8e4f59e8d461f797e5d70b4d025880516a8261b2817c47759ee", + "sha256:c74a62747864ed568f5a82a49a23a8d7fe171d0c69038b38cedf0976831296fa", + "sha256:dd05f7783b3274aa206a1af06f0ceed3f9b412cf665b7247eacd83be41cf7bf0", + "sha256:dfd681c5dc216037e0b0a2c821f5ed99ba9f03ebcf119c7dac0e9a7b960b9ec9", + "sha256:e474390e60ed609cec869b0da796ad94f420bb057d86784191eefc62b65819ae", + "sha256:f76d097d12c82a535fda9dfe5e8dd4127952b45fea9b0276cb30cca5ea313fbc" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==1.5.3" + }, + "platformdirs": { + "hashes": [ + "sha256:9e5e27a08aa095dd127b9f2e764d74254f482fef22b0970773bfba79d091ab8c", + "sha256:eb1c8582560b34ed4ba105009a4badf7f6f85768b30126f351328507b2beb617" + ], + "markers": "python_version >= '3.8'", + "version": "==4.3.2" + }, + "pre-commit": { + "hashes": [ + "sha256:8bb6494d4a20423842e198980c9ecf9f96607a07ea29549e180eef9ae80fe7af", + "sha256:9a90a53bf82fdd8778d58085faf8d83df56e40dfe18f45b19446e26bf1b3a63f" + ], + "index": "pypi", + "markers": "python_version >= '3.9'", + "version": "==3.8.0" + }, + "python-dateutil": { + "hashes": [ + "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", + "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==2.9.0.post0" + }, + "pytz": { + "hashes": [ + "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a", + "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725" + ], + "version": "==2024.2" + }, + "pyyaml": { + "hashes": [ + "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff", + "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", + "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", + "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e", + "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", + "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", + "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", + "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", + "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", + "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", + "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a", + "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", + "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", + "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8", + "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", + "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19", + "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", + "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a", + "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", + "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", + "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", + "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631", + "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d", + "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", + "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", + "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", + "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", + "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", + "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", + "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706", + "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", + "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", + "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", + "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083", + "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", + "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", + "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", + "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f", + "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725", + "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", + "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", + "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", + "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", + "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", + "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5", + "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d", + "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290", + "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", + "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", + "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", + "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", + "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12", + "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4" + ], + "markers": "python_version >= '3.8'", + "version": "==6.0.2" + }, + "ruff": { + "hashes": [ + "sha256:0308610470fcc82969082fc83c76c0d362f562e2f0cdab0586516f03a4e06ec6", + "sha256:0b52387d3289ccd227b62102c24714ed75fbba0b16ecc69a923a37e3b5e0aaaa", + "sha256:0ea086601b22dc5e7693a78f3fcfc460cceabfdf3bdc36dc898792aba48fbad6", + "sha256:34d5efad480193c046c86608dbba2bccdc1c5fd11950fb271f8086e0c763a5d1", + "sha256:50e30b437cebef547bd5c3edf9ce81343e5dd7c737cb36ccb4fe83573f3d392e", + "sha256:549daccee5227282289390b0222d0fbee0275d1db6d514550d65420053021a58", + "sha256:66dbfea86b663baab8fcae56c59f190caba9398df1488164e2df53e216248baa", + "sha256:7862f42fc1a4aca1ea3ffe8a11f67819d183a5693b228f0bb3a531f5e40336fc", + "sha256:803b96dea21795a6c9d5bfa9e96127cc9c31a1987802ca68f35e5c95aed3fc0d", + "sha256:932063a03bac394866683e15710c25b8690ccdca1cf192b9a98260332ca93408", + "sha256:ac3b5bfbee99973f80aa1b7cbd1c9cbce200883bdd067300c22a6cc1c7fba212", + "sha256:ac4b75e898ed189b3708c9ab3fc70b79a433219e1e87193b4f2b77251d058d14", + "sha256:bedff9e4f004dad5f7f76a9d39c4ca98af526c9b1695068198b3bda8c085ef60", + "sha256:c44536df7b93a587de690e124b89bd47306fddd59398a0fb12afd6133c7b3818", + "sha256:c4b153fc152af51855458e79e835fb6b933032921756cec9af7d0ba2aa01a258", + "sha256:d02a4127a86de23002e694d7ff19f905c51e338c72d8e09b56bfb60e1681724f", + "sha256:eebe4ff1967c838a1a9618a5a59a3b0a00406f8d7eefee97c70411fefc353617", + "sha256:f0f8968feea5ce3777c0d8365653d5e91c40c31a81d95824ba61d871a11b8523" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==0.6.4" + }, + "six": { + "hashes": [ + "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", + "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==1.16.0" + }, + "sqlalchemy": { + "hashes": [ + "sha256:0094c5dc698a5f78d3d1539853e8ecec02516b62b8223c970c86d44e7a80f6c7", + "sha256:0138c5c16be3600923fa2169532205d18891b28afa817cb49b50e08f62198bb8", + "sha256:0a089e218654e740a41388893e090d2e2c22c29028c9d1353feb38638820bbeb", + "sha256:0b3f4c438e37d22b83e640f825ef0f37b95db9aa2d68203f2c9549375d0b2260", + "sha256:16863e2b132b761891d6c49f0a0f70030e0bcac4fd208117f6b7e053e68668d0", + "sha256:1f9a727312ff6ad5248a4367358e2cf7e625e98b1028b1d7ab7b806b7d757513", + "sha256:2383146973a15435e4717f94c7509982770e3e54974c71f76500a0136f22810b", + "sha256:2753743c2afd061bb95a61a51bbb6a1a11ac1c44292fad898f10c9839a7f75b2", + "sha256:296230899df0b77dec4eb799bcea6fbe39a43707ce7bb166519c97b583cfcab3", + "sha256:2a4f4da89c74435f2bc61878cd08f3646b699e7d2eba97144030d1be44e27584", + "sha256:2b1708916730f4830bc69d6f49d37f7698b5bd7530aca7f04f785f8849e95255", + "sha256:2ecabd9ccaa6e914e3dbb2aa46b76dede7eadc8cbf1b8083c94d936bcd5ffb49", + "sha256:311710f9a2ee235f1403537b10c7687214bb1f2b9ebb52702c5aa4a77f0b3af7", + "sha256:37a4b4fb0dd4d2669070fb05b8b8824afd0af57587393015baee1cf9890242d9", + "sha256:3a365eda439b7a00732638f11072907c1bc8e351c7665e7e5da91b169af794af", + "sha256:3b48154678e76445c7ded1896715ce05319f74b1e73cf82d4f8b59b46e9c0ddc", + "sha256:3b69e934f0f2b677ec111b4d83f92dc1a3210a779f69bf905273192cf4ed433e", + "sha256:3cb5a646930c5123f8461f6468901573f334c2c63c795b9af350063a736d0134", + "sha256:408f8b0e2c04677e9c93f40eef3ab22f550fecb3011b187f66a096395ff3d9fd", + "sha256:40ad017c672c00b9b663fcfcd5f0864a0a97828e2ee7ab0c140dc84058d194cf", + "sha256:5a79d65395ac5e6b0c2890935bad892eabb911c4aa8e8015067ddb37eea3d56c", + "sha256:5a8e3b0a7e09e94be7510d1661339d6b52daf202ed2f5b1f9f48ea34ee6f2d57", + "sha256:69c9db1ce00e59e8dd09d7bae852a9add716efdc070a3e2068377e6ff0d6fdaa", + "sha256:7108d569d3990c71e26a42f60474b4c02c8586c4681af5fd67e51a044fdea86a", + "sha256:77d2edb1f54aff37e3318f611637171e8ec71472f1fdc7348b41dcb226f93d90", + "sha256:7d74336c65705b986d12a7e337ba27ab2b9d819993851b140efdf029248e818e", + "sha256:8409de825f2c3b62ab15788635ccaec0c881c3f12a8af2b12ae4910a0a9aeef6", + "sha256:955991a09f0992c68a499791a753523f50f71a6885531568404fa0f231832aa0", + "sha256:99650e9f4cf3ad0d409fed3eec4f071fadd032e9a5edc7270cd646a26446feeb", + "sha256:9a5baf9267b752390252889f0c802ea13b52dfee5e369527da229189b8bd592e", + "sha256:a0ef36b28534f2a5771191be6edb44cc2673c7b2edf6deac6562400288664221", + "sha256:a1429a4b0f709f19ff3b0cf13675b2b9bfa8a7e79990003207a011c0db880a13", + "sha256:a7bfc726d167f425d4c16269a9a10fe8630ff6d14b683d588044dcef2d0f6be7", + "sha256:a943d297126c9230719c27fcbbeab57ecd5d15b0bd6bfd26e91bfcfe64220621", + "sha256:ae8c62fe2480dd61c532ccafdbce9b29dacc126fe8be0d9a927ca3e699b9491a", + "sha256:b60203c63e8f984df92035610c5fb76d941254cf5d19751faab7d33b21e5ddc0", + "sha256:b6bf767d14b77f6a18b6982cbbf29d71bede087edae495d11ab358280f304d8e", + "sha256:b6c7ec2b1f4969fc19b65b7059ed00497e25f54069407a8701091beb69e591a5", + "sha256:bba002a9447b291548e8d66fd8c96a6a7ed4f2def0bb155f4f0a1309fd2735d5", + "sha256:bc0c53579650a891f9b83fa3cecd4e00218e071d0ba00c4890f5be0c34887ed3", + "sha256:c4f61ada6979223013d9ab83a3ed003ded6959eae37d0d685db2c147e9143797", + "sha256:c62d401223f468eb4da32627bffc0c78ed516b03bb8a34a58be54d618b74d472", + "sha256:e42203d8d20dc704604862977b1470a122e4892791fe3ed165f041e4bf447a1b", + "sha256:edc16a50f5e1b7a06a2dcc1f2205b0b961074c123ed17ebda726f376a5ab0953", + "sha256:efedba7e13aa9a6c8407c48facfdfa108a5a4128e35f4c68f20c3407e4376aa9", + "sha256:f1dc3eabd8c0232ee8387fbe03e0a62220a6f089e278b1f0aaf5e2d6210741ad", + "sha256:f69e4c756ee2686767eb80f94c0125c8b0a0b87ede03eacc5c8ae3b54b99dc46", + "sha256:f7703c2010355dd28f53deb644a05fc30f796bd8598b43f0ba678878780b6e4c", + "sha256:fa561138a64f949f3e889eb9ab8c58e1504ab351d6cf55259dc4c248eaa19da6" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==2.0.30" + }, + "typing-extensions": { + "hashes": [ + "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", + "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8" + ], + "markers": "python_version >= '3.8'", + "version": "==4.12.2" + }, + "virtualenv": { + "hashes": [ + "sha256:48f2695d9809277003f30776d155615ffc11328e6a0a8c1f0ec80188d7874a55", + "sha256:c17f4e0f3e6036e9f26700446f85c76ab11df65ff6d8a9cbfad9f71aabfcf23c" + ], + "markers": "python_version >= '3.7'", + "version": "==20.26.4" + } + } } diff --git a/README.md b/README.md index d28f5a083..72b5df20f 100644 --- a/README.md +++ b/README.md @@ -14,11 +14,11 @@ Le frontend de cette application est écrite en [Elm](https://elm-lang.org/). Vo ### Frontend - $ npm install + $ NODE_ENV=development npm install ### Backend - $ pipenv install + $ pipenv install -d Assurez-vous d'avoir un PostgreSQL >=16 qui tourne localement si vous souhaitez vous rapprocher de l'environnement de production. À défaut, `sqlite` sera utilisé. diff --git a/buildpack-run.sh b/buildpack-run.sh index af3e5d38f..86ae96d9d 100755 --- a/buildpack-run.sh +++ b/buildpack-run.sh @@ -6,3 +6,7 @@ git clone git@github.com:MTES-MCT/ecobalyse-private.git ./bin/checkout-ecobalyse-private-branch.sh $SOURCE_VERSION ./bin/download_github_releases.py + +# Remove big map files from old versions for a slimer scalingo image +find versions/ -type f -name "*.js.map" -delete +find versions/ -type f -name "*.css.map" -delete diff --git a/package-lock.json b/package-lock.json index d7e27062d..b93a58445 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,35 +9,35 @@ "version": "2.1.1", "license": "MIT", "dependencies": { - "@parcel/transformer-elm": "^2.12.0", - "@parcel/transformer-image": "^2.12.0", - "@parcel/transformer-sass": "^2.12.0", "@sentry/browser": "^8.27.0", "@sentry/node": "^8.27.0", "@sentry/profiling-node": "^8.28.0", "@sentry/tracing": "^7.114.0", - "bootstrap": "^5.3.3", "cors": "^2.8.5", "dotenv": "^16.4.5", - "elm": "^0.19.1-6", "express": "^4.19.2", "express-rate-limit": "^7.4.0", "helmet": "^7.1.0", - "highcharts": "^11.4.8", "js-yaml": "^4.1.0", - "parcel": "^2.12.0", "piwik": "^1.0.9" }, "devDependencies": { "@apidevtools/swagger-cli": "^4.0.4", + "@parcel/transformer-elm": "^2.12.0", + "@parcel/transformer-image": "^2.12.0", + "@parcel/transformer-sass": "^2.12.0", + "bootstrap": "^5.3.3", "concurrently": "^8.2.2", + "elm": "^0.19.1-6", "elm-format": "^0.8.7", "elm-json": "^0.2.13", "elm-review": "^2.12.0", "elm-test": "0.19.1-revision12", + "highcharts": "^11.4.8", "jest": "^29.7.0", "nodemon": "^3.1.4", "npm-check-updates": "^17.1.0", + "parcel": "^2.12.0", "prettier": "^3.3.3", "process": "^0.11.10", "rimraf": "^6.0.1", @@ -354,6 +354,7 @@ "version": "7.23.5", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", + "dev": true, "dependencies": { "@babel/highlight": "^7.23.4", "chalk": "^2.4.2" @@ -366,6 +367,7 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, "dependencies": { "color-convert": "^1.9.0" }, @@ -377,6 +379,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -390,6 +393,7 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, "dependencies": { "color-name": "1.1.3" } @@ -397,12 +401,14 @@ "node_modules/@babel/code-frame/node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true }, "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, "engines": { "node": ">=0.8.0" } @@ -411,6 +417,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, "engines": { "node": ">=4" } @@ -419,6 +426,7 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, "dependencies": { "has-flag": "^3.0.0" }, @@ -648,6 +656,7 @@ "version": "7.22.20", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "dev": true, "engines": { "node": ">=6.9.0" } @@ -679,6 +688,7 @@ "version": "7.23.4", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", + "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.22.20", "chalk": "^2.4.2", @@ -692,6 +702,7 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, "dependencies": { "color-convert": "^1.9.0" }, @@ -703,6 +714,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -716,6 +728,7 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, "dependencies": { "color-name": "1.1.3" } @@ -723,12 +736,14 @@ "node_modules/@babel/highlight/node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true }, "node_modules/@babel/highlight/node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, "engines": { "node": ">=0.8.0" } @@ -737,6 +752,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, "engines": { "node": ">=4" } @@ -745,6 +761,7 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, "dependencies": { "has-flag": "^3.0.0" }, @@ -1038,6 +1055,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "darwin" @@ -1050,6 +1068,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "darwin" @@ -1062,6 +1081,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -1074,6 +1094,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "win32" @@ -1517,6 +1538,7 @@ "version": "0.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "dev": true, "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", @@ -1530,6 +1552,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, "engines": { "node": ">=6.0.0" } @@ -1538,6 +1561,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, "engines": { "node": ">=6.0.0" } @@ -1546,6 +1570,7 @@ "version": "0.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", + "dev": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.0", "@jridgewell/trace-mapping": "^0.3.9" @@ -1554,12 +1579,14 @@ "node_modules/@jridgewell/sourcemap-codec": { "version": "1.4.15", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.25", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -1575,12 +1602,14 @@ "node_modules/@lezer/common": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.1.tgz", - "integrity": "sha512-yemX0ZD2xS/73llMZIK6KplkjIjf2EvAHcinDi/TfJ9hS25G0388+ClHt6/3but0oOxinTcQHJLDXh6w1crzFQ==" + "integrity": "sha512-yemX0ZD2xS/73llMZIK6KplkjIjf2EvAHcinDi/TfJ9hS25G0388+ClHt6/3but0oOxinTcQHJLDXh6w1crzFQ==", + "dev": true }, "node_modules/@lezer/lr": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.0.tgz", "integrity": "sha512-Wst46p51km8gH0ZUmeNrtpRYmdlRHUpN1DQd3GFAyKANi8WVz8c2jHYTf1CVScFaCjQw1iO3ZZdqGDxQPRErTg==", + "dev": true, "dependencies": { "@lezer/common": "^1.0.0" } @@ -1592,6 +1621,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "darwin" @@ -1604,6 +1634,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "darwin" @@ -1616,6 +1647,7 @@ "cpu": [ "arm" ], + "dev": true, "optional": true, "os": [ "linux" @@ -1628,6 +1660,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -1640,6 +1673,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -1652,6 +1686,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "win32" @@ -1661,6 +1696,7 @@ "version": "0.1.1", "resolved": "https://registry.npmjs.org/@mischnic/json-sourcemap/-/json-sourcemap-0.1.1.tgz", "integrity": "sha512-iA7+tyVqfrATAIsIRWQG+a7ZLLD0VaOCKV2Wd/v4mqIU3J9c4jx9p7S0nw1XH3gJCKNBOOwACOPYYSUu9pgT+w==", + "dev": true, "dependencies": { "@lezer/common": "^1.0.0", "@lezer/lr": "^1.0.0", @@ -1677,6 +1713,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "darwin" @@ -1689,6 +1726,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "darwin" @@ -1701,6 +1739,7 @@ "cpu": [ "arm" ], + "dev": true, "optional": true, "os": [ "linux" @@ -1713,6 +1752,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -1725,6 +1765,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -1737,6 +1778,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "win32" @@ -2230,6 +2272,7 @@ "version": "2.12.0", "resolved": "https://registry.npmjs.org/@parcel/bundler-default/-/bundler-default-2.12.0.tgz", "integrity": "sha512-3ybN74oYNMKyjD6V20c9Gerdbh7teeNvVMwIoHIQMzuIFT6IGX53PyOLlOKRLbjxMc0TMimQQxIt2eQqxR5LsA==", + "dev": true, "dependencies": { "@parcel/diagnostic": "2.12.0", "@parcel/graph": "3.2.0", @@ -2251,6 +2294,7 @@ "version": "2.12.0", "resolved": "https://registry.npmjs.org/@parcel/cache/-/cache-2.12.0.tgz", "integrity": "sha512-FX5ZpTEkxvq/yvWklRHDESVRz+c7sLTXgFuzz6uEnBcXV38j6dMSikflNpHA6q/L4GKkCqRywm9R6XQwhwIMyw==", + "dev": true, "dependencies": { "@parcel/fs": "2.12.0", "@parcel/logger": "2.12.0", @@ -2272,6 +2316,7 @@ "version": "2.12.0", "resolved": "https://registry.npmjs.org/@parcel/codeframe/-/codeframe-2.12.0.tgz", "integrity": "sha512-v2VmneILFiHZJTxPiR7GEF1wey1/IXPdZMcUlNXBiPZyWDfcuNgGGVQkx/xW561rULLIvDPharOMdxz5oHOKQg==", + "dev": true, "dependencies": { "chalk": "^4.1.0" }, @@ -2287,6 +2332,7 @@ "version": "2.12.0", "resolved": "https://registry.npmjs.org/@parcel/compressor-raw/-/compressor-raw-2.12.0.tgz", "integrity": "sha512-h41Q3X7ZAQ9wbQ2csP8QGrwepasLZdXiuEdpUryDce6rF9ZiHoJ97MRpdLxOhOPyASTw/xDgE1xyaPQr0Q3f5A==", + "dev": true, "dependencies": { "@parcel/plugin": "2.12.0" }, @@ -2303,6 +2349,7 @@ "version": "2.12.0", "resolved": "https://registry.npmjs.org/@parcel/config-default/-/config-default-2.12.0.tgz", "integrity": "sha512-dPNe2n9eEsKRc1soWIY0yToMUPirPIa2QhxcCB3Z5RjpDGIXm0pds+BaiqY6uGLEEzsjhRO0ujd4v2Rmm0vuFg==", + "dev": true, "dependencies": { "@parcel/bundler-default": "2.12.0", "@parcel/compressor-raw": "2.12.0", @@ -2348,6 +2395,7 @@ "version": "2.12.0", "resolved": "https://registry.npmjs.org/@parcel/core/-/core-2.12.0.tgz", "integrity": "sha512-s+6pwEj+GfKf7vqGUzN9iSEPueUssCCQrCBUlcAfKrJe0a22hTUCjewpB0I7lNrCIULt8dkndD+sMdOrXsRl6Q==", + "dev": true, "dependencies": { "@mischnic/json-sourcemap": "^0.1.0", "@parcel/cache": "2.12.0", @@ -2387,6 +2435,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-7.0.0.tgz", "integrity": "sha512-M3NhsLbV1i6HuGzBUH8vXrtxOk+tWmzWKDMbAVSUp3Zsjm7ywFeuwrUXhmhQyRK1q5B5GGy7hcXPbj3bnfZg2g==", + "dev": true, "engines": { "node": ">=6" } @@ -2395,6 +2444,7 @@ "version": "2.12.0", "resolved": "https://registry.npmjs.org/@parcel/diagnostic/-/diagnostic-2.12.0.tgz", "integrity": "sha512-8f1NOsSFK+F4AwFCKynyIu9Kr/uWHC+SywAv4oS6Bv3Acig0gtwUjugk0C9UaB8ztBZiW5TQZhw+uPZn9T/lJA==", + "dev": true, "dependencies": { "@mischnic/json-sourcemap": "^0.1.0", "nullthrows": "^1.1.1" @@ -2411,6 +2461,7 @@ "version": "2.12.0", "resolved": "https://registry.npmjs.org/@parcel/events/-/events-2.12.0.tgz", "integrity": "sha512-nmAAEIKLjW1kB2cUbCYSmZOGbnGj8wCzhqnK727zCCWaA25ogzAtt657GPOeFyqW77KyosU728Tl63Fc8hphIA==", + "dev": true, "engines": { "node": ">= 12.0.0" }, @@ -2423,6 +2474,7 @@ "version": "2.12.0", "resolved": "https://registry.npmjs.org/@parcel/fs/-/fs-2.12.0.tgz", "integrity": "sha512-NnFkuvou1YBtPOhTdZr44WN7I60cGyly2wpHzqRl62yhObyi1KvW0SjwOMa0QGNcBOIzp4G0CapoZ93hD0RG5Q==", + "dev": true, "dependencies": { "@parcel/rust": "2.12.0", "@parcel/types": "2.12.0", @@ -2445,6 +2497,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/@parcel/graph/-/graph-3.2.0.tgz", "integrity": "sha512-xlrmCPqy58D4Fg5umV7bpwDx5Vyt7MlnQPxW68vae5+BA4GSWetfZt+Cs5dtotMG2oCHzZxhIPt7YZ7NRyQzLA==", + "dev": true, "dependencies": { "nullthrows": "^1.1.1" }, @@ -2460,6 +2513,7 @@ "version": "2.12.0", "resolved": "https://registry.npmjs.org/@parcel/logger/-/logger-2.12.0.tgz", "integrity": "sha512-cJ7Paqa7/9VJ7C+KwgJlwMqTQBOjjn71FbKk0G07hydUEBISU2aDfmc/52o60ErL9l+vXB26zTrIBanbxS8rVg==", + "dev": true, "dependencies": { "@parcel/diagnostic": "2.12.0", "@parcel/events": "2.12.0" @@ -2476,6 +2530,7 @@ "version": "2.12.0", "resolved": "https://registry.npmjs.org/@parcel/markdown-ansi/-/markdown-ansi-2.12.0.tgz", "integrity": "sha512-WZz3rzL8k0H3WR4qTHX6Ic8DlEs17keO9gtD4MNGyMNQbqQEvQ61lWJaIH0nAtgEetu0SOITiVqdZrb8zx/M7w==", + "dev": true, "dependencies": { "chalk": "^4.1.0" }, @@ -2491,6 +2546,7 @@ "version": "2.12.0", "resolved": "https://registry.npmjs.org/@parcel/namer-default/-/namer-default-2.12.0.tgz", "integrity": "sha512-9DNKPDHWgMnMtqqZIMiEj/R9PNWW16lpnlHjwK3ciRlMPgjPJ8+UNc255teZODhX0T17GOzPdGbU/O/xbxVPzA==", + "dev": true, "dependencies": { "@parcel/diagnostic": "2.12.0", "@parcel/plugin": "2.12.0", @@ -2509,6 +2565,7 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/@parcel/node-resolver-core/-/node-resolver-core-3.3.0.tgz", "integrity": "sha512-rhPW9DYPEIqQBSlYzz3S0AjXxjN6Ub2yS6tzzsW/4S3Gpsgk/uEq4ZfxPvoPf/6TgZndVxmKwpmxaKtGMmf3cA==", + "dev": true, "dependencies": { "@mischnic/json-sourcemap": "^0.1.0", "@parcel/diagnostic": "2.12.0", @@ -2530,6 +2587,7 @@ "version": "2.12.0", "resolved": "https://registry.npmjs.org/@parcel/optimizer-css/-/optimizer-css-2.12.0.tgz", "integrity": "sha512-ifbcC97fRzpruTjaa8axIFeX4MjjSIlQfem3EJug3L2AVqQUXnM1XO8L0NaXGNLTW2qnh1ZjIJ7vXT/QhsphsA==", + "dev": true, "dependencies": { "@parcel/diagnostic": "2.12.0", "@parcel/plugin": "2.12.0", @@ -2552,6 +2610,7 @@ "version": "2.12.0", "resolved": "https://registry.npmjs.org/@parcel/optimizer-htmlnano/-/optimizer-htmlnano-2.12.0.tgz", "integrity": "sha512-MfPMeCrT8FYiOrpFHVR+NcZQlXAptK2r4nGJjfT+ndPBhEEZp4yyL7n1y7HfX9geg5altc4WTb4Gug7rCoW8VQ==", + "dev": true, "dependencies": { "@parcel/plugin": "2.12.0", "htmlnano": "^2.0.0", @@ -2572,6 +2631,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, "engines": { "node": ">= 10" } @@ -2580,6 +2640,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "dev": true, "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.0.1", @@ -2595,6 +2656,7 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "dev": true, "dependencies": { "mdn-data": "2.0.14", "source-map": "^0.6.1" @@ -2607,6 +2669,7 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "dev": true, "dependencies": { "css-tree": "^1.1.2" }, @@ -2617,12 +2680,14 @@ "node_modules/@parcel/optimizer-htmlnano/node_modules/mdn-data": { "version": "2.0.14", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", - "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", + "dev": true }, "node_modules/@parcel/optimizer-htmlnano/node_modules/svgo": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", + "dev": true, "dependencies": { "@trysound/sax": "0.2.0", "commander": "^7.2.0", @@ -2643,6 +2708,7 @@ "version": "2.12.0", "resolved": "https://registry.npmjs.org/@parcel/optimizer-image/-/optimizer-image-2.12.0.tgz", "integrity": "sha512-bo1O7raeAIbRU5nmNVtx8divLW9Xqn0c57GVNGeAK4mygnQoqHqRZ0mR9uboh64pxv6ijXZHPhKvU9HEpjPjBQ==", + "dev": true, "dependencies": { "@parcel/diagnostic": "2.12.0", "@parcel/plugin": "2.12.0", @@ -2666,6 +2732,7 @@ "version": "2.12.0", "resolved": "https://registry.npmjs.org/@parcel/optimizer-svgo/-/optimizer-svgo-2.12.0.tgz", "integrity": "sha512-Kyli+ZZXnoonnbeRQdoWwee9Bk2jm/49xvnfb+2OO8NN0d41lblBoRhOyFiScRnJrw7eVl1Xrz7NTkXCIO7XFQ==", + "dev": true, "dependencies": { "@parcel/diagnostic": "2.12.0", "@parcel/plugin": "2.12.0", @@ -2685,6 +2752,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, "engines": { "node": ">= 10" } @@ -2693,6 +2761,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "dev": true, "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.0.1", @@ -2708,6 +2777,7 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "dev": true, "dependencies": { "mdn-data": "2.0.14", "source-map": "^0.6.1" @@ -2720,6 +2790,7 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "dev": true, "dependencies": { "css-tree": "^1.1.2" }, @@ -2730,12 +2801,14 @@ "node_modules/@parcel/optimizer-svgo/node_modules/mdn-data": { "version": "2.0.14", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", - "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", + "dev": true }, "node_modules/@parcel/optimizer-svgo/node_modules/svgo": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", + "dev": true, "dependencies": { "@trysound/sax": "0.2.0", "commander": "^7.2.0", @@ -2756,6 +2829,7 @@ "version": "2.12.0", "resolved": "https://registry.npmjs.org/@parcel/optimizer-swc/-/optimizer-swc-2.12.0.tgz", "integrity": "sha512-iBi6LZB3lm6WmbXfzi8J3DCVPmn4FN2lw7DGXxUXu7MouDPVWfTsM6U/5TkSHJRNRogZ2gqy5q9g34NPxHbJcw==", + "dev": true, "dependencies": { "@parcel/diagnostic": "2.12.0", "@parcel/plugin": "2.12.0", @@ -2777,6 +2851,7 @@ "version": "2.12.0", "resolved": "https://registry.npmjs.org/@parcel/package-manager/-/package-manager-2.12.0.tgz", "integrity": "sha512-0nvAezcjPx9FT+hIL+LS1jb0aohwLZXct7jAh7i0MLMtehOi0z1Sau+QpgMlA9rfEZZ1LIeFdnZZwqSy7Ccspw==", + "dev": true, "dependencies": { "@parcel/diagnostic": "2.12.0", "@parcel/fs": "2.12.0", @@ -2803,6 +2878,7 @@ "version": "2.12.0", "resolved": "https://registry.npmjs.org/@parcel/packager-css/-/packager-css-2.12.0.tgz", "integrity": "sha512-j3a/ODciaNKD19IYdWJT+TP+tnhhn5koBGBWWtrKSu0UxWpnezIGZetit3eE+Y9+NTePalMkvpIlit2eDhvfJA==", + "dev": true, "dependencies": { "@parcel/diagnostic": "2.12.0", "@parcel/plugin": "2.12.0", @@ -2824,6 +2900,7 @@ "version": "2.12.0", "resolved": "https://registry.npmjs.org/@parcel/packager-html/-/packager-html-2.12.0.tgz", "integrity": "sha512-PpvGB9hFFe+19NXGz2ApvPrkA9GwEqaDAninT+3pJD57OVBaxB8U+HN4a5LICKxjUppPPqmrLb6YPbD65IX4RA==", + "dev": true, "dependencies": { "@parcel/plugin": "2.12.0", "@parcel/types": "2.12.0", @@ -2844,6 +2921,7 @@ "version": "2.12.0", "resolved": "https://registry.npmjs.org/@parcel/packager-js/-/packager-js-2.12.0.tgz", "integrity": "sha512-viMF+FszITRRr8+2iJyk+4ruGiL27Y6AF7hQ3xbJfzqnmbOhGFtLTQwuwhOLqN/mWR2VKdgbLpZSarWaO3yAMg==", + "dev": true, "dependencies": { "@parcel/diagnostic": "2.12.0", "@parcel/plugin": "2.12.0", @@ -2867,6 +2945,7 @@ "version": "13.24.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, "dependencies": { "type-fest": "^0.20.2" }, @@ -2881,6 +2960,7 @@ "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, "engines": { "node": ">=10" }, @@ -2892,6 +2972,7 @@ "version": "2.12.0", "resolved": "https://registry.npmjs.org/@parcel/packager-raw/-/packager-raw-2.12.0.tgz", "integrity": "sha512-tJZqFbHqP24aq1F+OojFbQIc09P/u8HAW5xfndCrFnXpW4wTgM3p03P0xfw3gnNq+TtxHJ8c3UFE5LnXNNKhYA==", + "dev": true, "dependencies": { "@parcel/plugin": "2.12.0" }, @@ -2908,6 +2989,7 @@ "version": "2.12.0", "resolved": "https://registry.npmjs.org/@parcel/packager-svg/-/packager-svg-2.12.0.tgz", "integrity": "sha512-ldaGiacGb2lLqcXas97k8JiZRbAnNREmcvoY2W2dvW4loVuDT9B9fU777mbV6zODpcgcHWsLL3lYbJ5Lt3y9cg==", + "dev": true, "dependencies": { "@parcel/plugin": "2.12.0", "@parcel/types": "2.12.0", @@ -2927,6 +3009,7 @@ "version": "2.12.0", "resolved": "https://registry.npmjs.org/@parcel/packager-wasm/-/packager-wasm-2.12.0.tgz", "integrity": "sha512-fYqZzIqO9fGYveeImzF8ll6KRo2LrOXfD+2Y5U3BiX/wp9wv17dz50QLDQm9hmTcKGWxK4yWqKQh+Evp/fae7A==", + "dev": true, "dependencies": { "@parcel/plugin": "2.12.0" }, @@ -2943,6 +3026,7 @@ "version": "2.12.0", "resolved": "https://registry.npmjs.org/@parcel/plugin/-/plugin-2.12.0.tgz", "integrity": "sha512-nc/uRA8DiMoe4neBbzV6kDndh/58a4wQuGKw5oEoIwBCHUvE2W8ZFSu7ollSXUGRzfacTt4NdY8TwS73ScWZ+g==", + "dev": true, "dependencies": { "@parcel/types": "2.12.0" }, @@ -2958,6 +3042,7 @@ "version": "2.12.0", "resolved": "https://registry.npmjs.org/@parcel/profiler/-/profiler-2.12.0.tgz", "integrity": "sha512-q53fvl5LDcFYzMUtSusUBZSjQrKjMlLEBgKeQHFwkimwR1mgoseaDBDuNz0XvmzDzF1UelJ02TUKCGacU8W2qA==", + "dev": true, "dependencies": { "@parcel/diagnostic": "2.12.0", "@parcel/events": "2.12.0", @@ -2975,6 +3060,7 @@ "version": "2.12.0", "resolved": "https://registry.npmjs.org/@parcel/reporter-cli/-/reporter-cli-2.12.0.tgz", "integrity": "sha512-TqKsH4GVOLPSCanZ6tcTPj+rdVHERnt5y4bwTM82cajM21bCX1Ruwp8xOKU+03091oV2pv5ieB18pJyRF7IpIw==", + "dev": true, "dependencies": { "@parcel/plugin": "2.12.0", "@parcel/types": "2.12.0", @@ -2995,6 +3081,7 @@ "version": "2.12.0", "resolved": "https://registry.npmjs.org/@parcel/reporter-dev-server/-/reporter-dev-server-2.12.0.tgz", "integrity": "sha512-tIcDqRvAPAttRlTV28dHcbWT5K2r/MBFks7nM4nrEDHWtnrCwimkDmZTc1kD8QOCCjGVwRHcQybpHvxfwol6GA==", + "dev": true, "dependencies": { "@parcel/plugin": "2.12.0", "@parcel/utils": "2.12.0" @@ -3012,6 +3099,7 @@ "version": "2.12.0", "resolved": "https://registry.npmjs.org/@parcel/reporter-tracer/-/reporter-tracer-2.12.0.tgz", "integrity": "sha512-g8rlu9GxB8Ut/F8WGx4zidIPQ4pcYFjU9bZO+fyRIPrSUFH2bKijCnbZcr4ntqzDGx74hwD6cCG4DBoleq2UlQ==", + "dev": true, "dependencies": { "@parcel/plugin": "2.12.0", "@parcel/utils": "2.12.0", @@ -3031,6 +3119,7 @@ "version": "2.12.0", "resolved": "https://registry.npmjs.org/@parcel/resolver-default/-/resolver-default-2.12.0.tgz", "integrity": "sha512-uuhbajTax37TwCxu7V98JtRLiT6hzE4VYSu5B7Qkauy14/WFt2dz6GOUXPgVsED569/hkxebPx3KCMtZW6cHHA==", + "dev": true, "dependencies": { "@parcel/node-resolver-core": "3.3.0", "@parcel/plugin": "2.12.0" @@ -3048,6 +3137,7 @@ "version": "2.12.0", "resolved": "https://registry.npmjs.org/@parcel/runtime-browser-hmr/-/runtime-browser-hmr-2.12.0.tgz", "integrity": "sha512-4ZLp2FWyD32r0GlTulO3+jxgsA3oO1P1b5oO2IWuWilfhcJH5LTiazpL5YdusUjtNn9PGN6QLAWfxmzRIfM+Ow==", + "dev": true, "dependencies": { "@parcel/plugin": "2.12.0", "@parcel/utils": "2.12.0" @@ -3065,6 +3155,7 @@ "version": "2.12.0", "resolved": "https://registry.npmjs.org/@parcel/runtime-js/-/runtime-js-2.12.0.tgz", "integrity": "sha512-sBerP32Z1crX5PfLNGDSXSdqzlllM++GVnVQVeM7DgMKS8JIFG3VLi28YkX+dYYGtPypm01JoIHCkvwiZEcQJg==", + "dev": true, "dependencies": { "@parcel/diagnostic": "2.12.0", "@parcel/plugin": "2.12.0", @@ -3084,6 +3175,7 @@ "version": "2.12.0", "resolved": "https://registry.npmjs.org/@parcel/runtime-react-refresh/-/runtime-react-refresh-2.12.0.tgz", "integrity": "sha512-SCHkcczJIDFTFdLTzrHTkQ0aTrX3xH6jrA4UsCBL6ji61+w+ohy4jEEe9qCgJVXhnJfGLE43HNXek+0MStX+Mw==", + "dev": true, "dependencies": { "@parcel/plugin": "2.12.0", "@parcel/utils": "2.12.0", @@ -3103,6 +3195,7 @@ "version": "2.12.0", "resolved": "https://registry.npmjs.org/@parcel/runtime-service-worker/-/runtime-service-worker-2.12.0.tgz", "integrity": "sha512-BXuMBsfiwpIEnssn+jqfC3jkgbS8oxeo3C7xhSQsuSv+AF2FwY3O3AO1c1RBskEW3XrBLNINOJujroNw80VTKA==", + "dev": true, "dependencies": { "@parcel/plugin": "2.12.0", "@parcel/utils": "2.12.0", @@ -3121,6 +3214,7 @@ "version": "2.12.0", "resolved": "https://registry.npmjs.org/@parcel/rust/-/rust-2.12.0.tgz", "integrity": "sha512-005cldMdFZFDPOjbDVEXcINQ3wT4vrxvSavRWI3Az0e3E18exO/x/mW9f648KtXugOXMAqCEqhFHcXECL9nmMw==", + "dev": true, "engines": { "node": ">= 12.0.0" }, @@ -3133,6 +3227,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/@parcel/source-map/-/source-map-2.1.1.tgz", "integrity": "sha512-Ejx1P/mj+kMjQb8/y5XxDUn4reGdr+WyKYloBljpppUy8gs42T+BNoEOuRYqDVdgPc6NxduzIDoJS9pOFfV5Ew==", + "dev": true, "dependencies": { "detect-libc": "^1.0.3" }, @@ -3144,6 +3239,7 @@ "version": "2.12.0", "resolved": "https://registry.npmjs.org/@parcel/transformer-babel/-/transformer-babel-2.12.0.tgz", "integrity": "sha512-zQaBfOnf/l8rPxYGnsk/ufh/0EuqvmnxafjBIpKZ//j6rGylw5JCqXSb1QvvAqRYruKeccxGv7+HrxpqKU6V4A==", + "dev": true, "dependencies": { "@parcel/diagnostic": "2.12.0", "@parcel/plugin": "2.12.0", @@ -3167,6 +3263,7 @@ "version": "2.12.0", "resolved": "https://registry.npmjs.org/@parcel/transformer-css/-/transformer-css-2.12.0.tgz", "integrity": "sha512-vXhOqoAlQGATYyQ433Z1DXKmiKmzOAUmKysbYH3FD+LKEKLMEl/pA14goqp00TW+A/EjtSKKyeMyHlMIIUqj4Q==", + "dev": true, "dependencies": { "@parcel/diagnostic": "2.12.0", "@parcel/plugin": "2.12.0", @@ -3189,6 +3286,7 @@ "version": "2.12.0", "resolved": "https://registry.npmjs.org/@parcel/transformer-elm/-/transformer-elm-2.12.0.tgz", "integrity": "sha512-qqVTsP860FghYTLBrJkrFKD7qbNUq/EqUCofFKLWgWSrMXfzTmsNwMCOStTF4NyclLWcC9KBVjNU+fGS5I519Q==", + "dev": true, "dependencies": { "@parcel/diagnostic": "2.12.0", "@parcel/plugin": "2.12.0", @@ -3215,6 +3313,7 @@ "version": "2.12.0", "resolved": "https://registry.npmjs.org/@parcel/transformer-html/-/transformer-html-2.12.0.tgz", "integrity": "sha512-5jW4dFFBlYBvIQk4nrH62rfA/G/KzVzEDa6S+Nne0xXhglLjkm64Ci9b/d4tKZfuGWUbpm2ASAq8skti/nfpXw==", + "dev": true, "dependencies": { "@parcel/diagnostic": "2.12.0", "@parcel/plugin": "2.12.0", @@ -3239,6 +3338,7 @@ "version": "2.12.0", "resolved": "https://registry.npmjs.org/@parcel/transformer-image/-/transformer-image-2.12.0.tgz", "integrity": "sha512-8hXrGm2IRII49R7lZ0RpmNk27EhcsH+uNKsvxuMpXPuEnWgC/ha/IrjaI29xCng1uGur74bJF43NUSQhR4aTdw==", + "dev": true, "dependencies": { "@parcel/plugin": "2.12.0", "@parcel/utils": "2.12.0", @@ -3257,6 +3357,7 @@ "version": "2.12.0", "resolved": "https://registry.npmjs.org/@parcel/transformer-js/-/transformer-js-2.12.0.tgz", "integrity": "sha512-OSZpOu+FGDbC/xivu24v092D9w6EGytB3vidwbdiJ2FaPgfV7rxS0WIUjH4I0OcvHAcitArRXL0a3+HrNTdQQw==", + "dev": true, "dependencies": { "@parcel/diagnostic": "2.12.0", "@parcel/plugin": "2.12.0", @@ -3285,12 +3386,14 @@ "node_modules/@parcel/transformer-js/node_modules/regenerator-runtime": { "version": "0.13.11", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "dev": true }, "node_modules/@parcel/transformer-json": { "version": "2.12.0", "resolved": "https://registry.npmjs.org/@parcel/transformer-json/-/transformer-json-2.12.0.tgz", "integrity": "sha512-Utv64GLRCQILK5r0KFs4o7I41ixMPllwOLOhkdjJKvf1hZmN6WqfOmB1YLbWS/y5Zb/iB52DU2pWZm96vLFQZQ==", + "dev": true, "dependencies": { "@parcel/plugin": "2.12.0", "json5": "^2.2.0" @@ -3308,6 +3411,7 @@ "version": "2.12.0", "resolved": "https://registry.npmjs.org/@parcel/transformer-postcss/-/transformer-postcss-2.12.0.tgz", "integrity": "sha512-FZqn+oUtiLfPOn67EZxPpBkfdFiTnF4iwiXPqvst3XI8H+iC+yNgzmtJkunOOuylpYY6NOU5jT8d7saqWSDv2Q==", + "dev": true, "dependencies": { "@parcel/diagnostic": "2.12.0", "@parcel/plugin": "2.12.0", @@ -3331,6 +3435,7 @@ "version": "2.12.0", "resolved": "https://registry.npmjs.org/@parcel/transformer-posthtml/-/transformer-posthtml-2.12.0.tgz", "integrity": "sha512-z6Z7rav/pcaWdeD+2sDUcd0mmNZRUvtHaUGa50Y2mr+poxrKilpsnFMSiWBT+oOqPt7j71jzDvrdnAF4XkCljg==", + "dev": true, "dependencies": { "@parcel/plugin": "2.12.0", "@parcel/utils": "2.12.0", @@ -3353,6 +3458,7 @@ "version": "2.12.0", "resolved": "https://registry.npmjs.org/@parcel/transformer-raw/-/transformer-raw-2.12.0.tgz", "integrity": "sha512-Ht1fQvXxix0NncdnmnXZsa6hra20RXYh1VqhBYZLsDfkvGGFnXIgO03Jqn4Z8MkKoa0tiNbDhpKIeTjyclbBxQ==", + "dev": true, "dependencies": { "@parcel/plugin": "2.12.0" }, @@ -3369,6 +3475,7 @@ "version": "2.12.0", "resolved": "https://registry.npmjs.org/@parcel/transformer-react-refresh-wrap/-/transformer-react-refresh-wrap-2.12.0.tgz", "integrity": "sha512-GE8gmP2AZtkpBIV5vSCVhewgOFRhqwdM5Q9jNPOY5PKcM3/Ff0qCqDiTzzGLhk0/VMBrdjssrfZkVx6S/lHdJw==", + "dev": true, "dependencies": { "@parcel/plugin": "2.12.0", "@parcel/utils": "2.12.0", @@ -3387,6 +3494,7 @@ "version": "2.12.0", "resolved": "https://registry.npmjs.org/@parcel/transformer-sass/-/transformer-sass-2.12.0.tgz", "integrity": "sha512-xLLoSLPST+2AHJwFRLl4foArDjjy6P1RChP3TxMU2MVS1sbKGJnfFhFpHAacH8ASjuGtu5rbpfpHRZePlvoZxw==", + "dev": true, "dependencies": { "@parcel/plugin": "2.12.0", "@parcel/source-map": "^2.1.1", @@ -3405,6 +3513,7 @@ "version": "2.12.0", "resolved": "https://registry.npmjs.org/@parcel/transformer-svg/-/transformer-svg-2.12.0.tgz", "integrity": "sha512-cZJqGRJ4JNdYcb+vj94J7PdOuTnwyy45dM9xqbIMH+HSiiIkfrMsdEwYft0GTyFTdsnf+hdHn3tau7Qa5hhX+A==", + "dev": true, "dependencies": { "@parcel/diagnostic": "2.12.0", "@parcel/plugin": "2.12.0", @@ -3428,6 +3537,7 @@ "version": "2.12.0", "resolved": "https://registry.npmjs.org/@parcel/types/-/types-2.12.0.tgz", "integrity": "sha512-8zAFiYNCwNTQcglIObyNwKfRYQK5ELlL13GuBOrSMxueUiI5ylgsGbTS1N7J3dAGZixHO8KhHGv5a71FILn9rQ==", + "dev": true, "dependencies": { "@parcel/cache": "2.12.0", "@parcel/diagnostic": "2.12.0", @@ -3442,6 +3552,7 @@ "version": "2.12.0", "resolved": "https://registry.npmjs.org/@parcel/utils/-/utils-2.12.0.tgz", "integrity": "sha512-z1JhLuZ8QmDaYoEIuUCVZlhcFrS7LMfHrb2OCRui5SQFntRWBH2fNM6H/fXXUkT9SkxcuFP2DUA6/m4+Gkz72g==", + "dev": true, "dependencies": { "@parcel/codeframe": "2.12.0", "@parcel/diagnostic": "2.12.0", @@ -3464,6 +3575,7 @@ "version": "2.4.1", "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.4.1.tgz", "integrity": "sha512-HNjmfLQEVRZmHRET336f20H/8kOozUGwk7yajvsonjNxbj2wBTK1WsQuHkD5yYh9RxFGL2EyDHryOihOwUoKDA==", + "dev": true, "dependencies": { "detect-libc": "^1.0.3", "is-glob": "^4.0.3", @@ -3499,6 +3611,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "android" @@ -3518,6 +3631,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "darwin" @@ -3537,6 +3651,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "darwin" @@ -3556,6 +3671,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "freebsd" @@ -3575,6 +3691,7 @@ "cpu": [ "arm" ], + "dev": true, "optional": true, "os": [ "linux" @@ -3594,6 +3711,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -3613,6 +3731,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -3632,6 +3751,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -3651,6 +3771,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -3670,6 +3791,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "win32" @@ -3689,6 +3811,7 @@ "cpu": [ "ia32" ], + "dev": true, "optional": true, "os": [ "win32" @@ -3708,6 +3831,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "win32" @@ -3724,6 +3848,7 @@ "version": "2.12.0", "resolved": "https://registry.npmjs.org/@parcel/workers/-/workers-2.12.0.tgz", "integrity": "sha512-zv5We5Jmb+ZWXlU6A+AufyjY4oZckkxsZ8J4dvyWL0W8IQvGO1JB4FGeryyttzQv3RM3OxcN/BpTGPiDG6keBw==", + "dev": true, "dependencies": { "@parcel/diagnostic": "2.12.0", "@parcel/logger": "2.12.0", @@ -3758,6 +3883,7 @@ "version": "2.11.8", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "dev": true, "peer": true, "funding": { "type": "opencollective", @@ -4181,6 +4307,7 @@ "version": "1.4.6", "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.4.6.tgz", "integrity": "sha512-A7iK9+1qzTCIuc3IYcS8gPHCm9bZVKUJrfNnwveZYyo6OFp3jLno4WOM2yBy5uqedgYATEiWgBYHKq37KrU6IA==", + "dev": true, "hasInstallScript": true, "dependencies": { "@swc/counter": "^0.1.2", @@ -4221,6 +4348,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "darwin" @@ -4236,6 +4364,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "darwin" @@ -4251,6 +4380,7 @@ "cpu": [ "arm" ], + "dev": true, "optional": true, "os": [ "linux" @@ -4266,6 +4396,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -4281,6 +4412,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -4296,6 +4428,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -4311,6 +4444,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -4326,6 +4460,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "win32" @@ -4341,6 +4476,7 @@ "cpu": [ "ia32" ], + "dev": true, "optional": true, "os": [ "win32" @@ -4356,6 +4492,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "win32" @@ -4367,12 +4504,14 @@ "node_modules/@swc/counter": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", - "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==" + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", + "dev": true }, "node_modules/@swc/helpers": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.6.tgz", "integrity": "sha512-aYX01Ke9hunpoCexYAgQucEpARGQ5w/cqHFrIR+e9gdKb1QWTsVJuTJ2ozQzIAxLyRQe/m+2RqzkyOOGiMKRQA==", + "dev": true, "dependencies": { "tslib": "^2.4.0" } @@ -4380,7 +4519,8 @@ "node_modules/@swc/types": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.5.tgz", - "integrity": "sha512-myfUej5naTBWnqOCc/MdVOLVjXUXtIA+NpDrDBKJtLLg2shUjBu3cZmB/85RyitKc55+lUUyl7oRfLOvkr2hsw==" + "integrity": "sha512-myfUej5naTBWnqOCc/MdVOLVjXUXtIA+NpDrDBKJtLLg2shUjBu3cZmB/85RyitKc55+lUUyl7oRfLOvkr2hsw==", + "dev": true }, "node_modules/@szmarczak/http-timer": { "version": "4.0.6", @@ -4398,6 +4538,7 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", + "dev": true, "engines": { "node": ">=10.13.0" } @@ -4594,7 +4735,8 @@ "node_modules/abortcontroller-polyfill": { "version": "1.7.5", "resolved": "https://registry.npmjs.org/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.5.tgz", - "integrity": "sha512-JMJ5soJWP18htbbxJjG7bG6yuI6pRhgJ0scHHTfkUjf6wjP912xZWvM+A4sJK3gqd9E8fcPbDnOefbA9Th/FIQ==" + "integrity": "sha512-JMJ5soJWP18htbbxJjG7bG6yuI6pRhgJ0scHHTfkUjf6wjP912xZWvM+A4sJK3gqd9E8fcPbDnOefbA9Th/FIQ==", + "dev": true }, "node_modules/accepts": { "version": "1.3.8", @@ -4682,6 +4824,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -4696,6 +4839,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -4888,12 +5032,14 @@ "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true }, "node_modules/base-x": { "version": "3.0.9", "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz", "integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==", + "dev": true, "dependencies": { "safe-buffer": "^5.0.1" } @@ -4944,6 +5090,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, "engines": { "node": ">=8" } @@ -5001,12 +5148,14 @@ "node_modules/boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true }, "node_modules/bootstrap": { "version": "5.3.3", "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.3.tgz", "integrity": "sha512-8HLCdWgyoMguSO9o+aH+iuZ+aht+mzW0u3HIMzVu7Srrpv7EBBxTnrFlSCskwdY1+EOFQSm7uMJhNQHkdPcmjg==", + "dev": true, "funding": [ { "type": "github", @@ -5025,6 +5174,7 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -5034,6 +5184,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, "dependencies": { "fill-range": "^7.1.1" }, @@ -5045,6 +5196,7 @@ "version": "4.23.0", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", + "dev": true, "funding": [ { "type": "opencollective", @@ -5108,7 +5260,8 @@ "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true }, "node_modules/buffers": { "version": "0.1.1", @@ -5183,6 +5336,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, "engines": { "node": ">=6" } @@ -5200,6 +5354,7 @@ "version": "1.0.30001657", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001657.tgz", "integrity": "sha512-DPbJAlP8/BAXy3IgiWmZKItubb3TYGP0WscQQlVGIfT4s/YlFYVuJgyOsQNP7rJRChx/qdMeLJQJP0Sgg2yjNA==", + "dev": true, "funding": [ { "type": "opencollective", @@ -5238,6 +5393,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -5262,6 +5418,7 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -5294,6 +5451,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "dev": true, "engines": { "node": ">=6.0" } @@ -5377,6 +5535,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "dev": true, "engines": { "node": ">=0.8" } @@ -5413,6 +5572,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "dependencies": { "color-name": "~1.1.4" }, @@ -5423,7 +5583,8 @@ "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "node_modules/combined-stream": { "version": "1.0.8", @@ -5440,7 +5601,8 @@ "node_modules/command-exists": { "version": "1.2.9", "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz", - "integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==" + "integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==", + "dev": true }, "node_modules/commander": { "version": "9.5.0", @@ -5464,7 +5626,8 @@ "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true }, "node_modules/concurrently": { "version": "8.2.2", @@ -5575,6 +5738,7 @@ "version": "8.3.6", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", + "dev": true, "dependencies": { "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", @@ -5621,6 +5785,7 @@ "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -5634,6 +5799,7 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "dev": true, "optional": true, "peer": true, "dependencies": { @@ -5651,6 +5817,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, "optional": true, "peer": true, "dependencies": { @@ -5666,6 +5833,7 @@ "version": "5.0.3", "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, "optional": true, "peer": true, "dependencies": { @@ -5682,6 +5850,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "dev": true, "optional": true, "peer": true, "dependencies": { @@ -5697,6 +5866,7 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, "optional": true, "peer": true, "engines": { @@ -5710,6 +5880,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", + "dev": true, "optional": true, "peer": true, "dependencies": { @@ -5724,6 +5895,7 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "dev": true, "engines": { "node": ">= 6" }, @@ -5735,6 +5907,7 @@ "version": "5.0.5", "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", + "dev": true, "optional": true, "peer": true, "dependencies": { @@ -5749,6 +5922,7 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", + "dev": true, "optional": true, "peer": true, "dependencies": { @@ -5764,6 +5938,7 @@ "version": "2.0.28", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", + "dev": true, "optional": true, "peer": true }, @@ -5939,6 +6114,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "dev": true, "bin": { "detect-libc": "bin/detect-libc.js" }, @@ -5992,6 +6168,7 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dev": true, "dependencies": { "domelementtype": "^2.0.1", "domhandler": "^4.2.0", @@ -6005,6 +6182,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, "funding": { "url": "https://github.com/fb55/entities?sponsor=1" } @@ -6013,6 +6191,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, "funding": [ { "type": "github", @@ -6024,6 +6203,7 @@ "version": "4.3.1", "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dev": true, "dependencies": { "domelementtype": "^2.2.0" }, @@ -6038,6 +6218,7 @@ "version": "2.8.0", "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dev": true, "dependencies": { "dom-serializer": "^1.0.1", "domelementtype": "^2.2.0", @@ -6061,7 +6242,8 @@ "node_modules/dotenv-expand": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", - "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==" + "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==", + "dev": true }, "node_modules/eastasianwidth": { "version": "0.2.0", @@ -6094,12 +6276,14 @@ "node_modules/electron-to-chromium": { "version": "1.4.698", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.698.tgz", - "integrity": "sha512-f9iZD1t3CLy1AS6vzM5EKGa6p9pRcOeEFXRFbaG2Ta+Oe7MkfRQ3fsvPYidzHe1h4i0JvIvpcY55C+B6BZNGtQ==" + "integrity": "sha512-f9iZD1t3CLy1AS6vzM5EKGa6p9pRcOeEFXRFbaG2Ta+Oe7MkfRQ3fsvPYidzHe1h4i0JvIvpcY55C+B6BZNGtQ==", + "dev": true }, "node_modules/elm": { "version": "0.19.1-6", "resolved": "https://registry.npmjs.org/elm/-/elm-0.19.1-6.tgz", "integrity": "sha512-mKYyierHICPdMx/vhiIacdPmTPnh889gjHOZ75ZAoCxo3lZmSWbGP8HMw78wyctJH0HwvTmeKhlYSWboQNYPeQ==", + "dev": true, "hasInstallScript": true, "bin": { "elm": "bin/elm" @@ -6134,7 +6318,8 @@ "node_modules/elm-hot": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/elm-hot/-/elm-hot-1.1.6.tgz", - "integrity": "sha512-zYZJlfs7Gt4BdjA+D+857K+XAWzwwySJmXCgFpHW1dIEfaHSZCIPYPf7/jinZBLfKRkOAlKzI32AA84DY50g7Q==" + "integrity": "sha512-zYZJlfs7Gt4BdjA+D+857K+XAWzwwySJmXCgFpHW1dIEfaHSZCIPYPf7/jinZBLfKRkOAlKzI32AA84DY50g7Q==", + "dev": true }, "node_modules/elm-json": { "version": "0.2.13", @@ -6388,6 +6573,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", + "dev": true, "engines": { "node": ">=0.12" }, @@ -6399,6 +6585,7 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, "dependencies": { "is-arrayish": "^0.2.1" } @@ -6426,6 +6613,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "dev": true, "engines": { "node": ">=6" } @@ -6671,6 +6859,7 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, "dependencies": { "to-regex-range": "^5.0.1" }, @@ -6699,6 +6888,7 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/find-elm-dependencies/-/find-elm-dependencies-2.0.4.tgz", "integrity": "sha512-x/4w4fVmlD2X4PD9oQ+yh9EyaQef6OtEULdMGBTuWx0Nkppvo2Z/bAiQioW2n+GdRYKypME2b9OmYTw5tw5qDg==", + "dev": true, "dependencies": { "firstline": "^1.2.0", "lodash": "^4.17.19" @@ -6727,6 +6917,7 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/firstline/-/firstline-1.3.1.tgz", "integrity": "sha512-ycwgqtoxujz1dm0kjkBFOPQMESxB9uKc/PlD951dQDIG+tBXRpYZC2UmJb0gDxopQ1ZX6oyRQN3goRczYu7Deg==", + "dev": true, "engines": { "node": ">=6.4.0" } @@ -6873,12 +7064,14 @@ "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, "hasInstallScript": true, "optional": true, "os": [ @@ -6945,6 +7138,7 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/get-port/-/get-port-4.2.0.tgz", "integrity": "sha512-/b3jarXkH8KJoOMQc3uVGHASwGLPq3gSFJ7tgJm2diza+bydJPTGOibin2steecKeOylE8oY2JERlVWkAJO6yw==", + "dev": true, "engines": { "node": ">=6" } @@ -6977,6 +7171,7 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -6996,6 +7191,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, "dependencies": { "is-glob": "^4.0.1" }, @@ -7007,6 +7203,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -7125,6 +7322,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, "engines": { "node": ">=8" } @@ -7195,6 +7393,7 @@ "version": "11.4.8", "resolved": "https://registry.npmjs.org/highcharts/-/highcharts-11.4.8.tgz", "integrity": "sha512-5Tke9LuzZszC4osaFisxLIcw7xgNGz4Sy3Jc9pRMV+ydm6sYqsPYdU8ELOgpzGNrbrRNDRBtveoR5xS3SzneEA==", + "dev": true, "license": "https://www.highcharts.com/license" }, "node_modules/html-escaper": { @@ -7207,6 +7406,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/htmlnano/-/htmlnano-2.1.0.tgz", "integrity": "sha512-jVGRE0Ep9byMBKEu0Vxgl8dhXYOUk0iNQ2pjsG+BcRB0u0oDF5A9p/iBGMg/PGKYUyMD0OAGu8dVT5Lzj8S58g==", + "dev": true, "dependencies": { "cosmiconfig": "^8.0.0", "posthtml": "^0.16.5", @@ -7253,6 +7453,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz", "integrity": "sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==", + "dev": true, "funding": [ "https://github.com/fb55/htmlparser2?sponsor=1", { @@ -7383,12 +7584,14 @@ "node_modules/immutable": { "version": "4.3.5", "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.5.tgz", - "integrity": "sha512-8eabxkth9gZatlwl5TBuJnCsoTADlL6ftEr7A4qgdaTsPyreilDSnUk57SO+jfKcNtxPa22U5KK6DSeAYhpBJw==" + "integrity": "sha512-8eabxkth9gZatlwl5TBuJnCsoTADlL6ftEr7A4qgdaTsPyreilDSnUk57SO+jfKcNtxPa22U5KK6DSeAYhpBJw==", + "dev": true }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -7404,6 +7607,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, "engines": { "node": ">=4" } @@ -7452,6 +7656,7 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -7473,12 +7678,14 @@ "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, "dependencies": { "binary-extensions": "^2.0.0" }, @@ -7501,6 +7708,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -7527,6 +7735,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, "dependencies": { "is-extglob": "^2.1.1" }, @@ -7546,12 +7755,14 @@ "node_modules/is-json": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-json/-/is-json-2.0.1.tgz", - "integrity": "sha512-6BEnpVn1rcf3ngfmViLM6vjUjGErbdrL4rwlv+u1NO1XO8kqT4YGL8+19Q+Z/bas8tY90BTWMk2+fW1g6hQjbA==" + "integrity": "sha512-6BEnpVn1rcf3ngfmViLM6vjUjGErbdrL4rwlv+u1NO1XO8kqT4YGL8+19Q+Z/bas8tY90BTWMk2+fW1g6hQjbA==", + "dev": true }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, "engines": { "node": ">=0.12.0" } @@ -7589,7 +7800,8 @@ "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true }, "node_modules/isstream": { "version": "0.1.2", @@ -8260,7 +8472,8 @@ "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true }, "node_modules/js-yaml": { "version": "4.1.0", @@ -8294,7 +8507,8 @@ "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true }, "node_modules/json-schema": { "version": "0.4.0", @@ -8318,6 +8532,7 @@ "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, "bin": { "json5": "lib/cli.js" }, @@ -8383,6 +8598,7 @@ "version": "1.24.0", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.24.0.tgz", "integrity": "sha512-y36QEEDVx4IM7/yIZNsZJMRREIu26WzTsauIysf5s76YeCmlSbRZS7aC97IGPuoFRnyZ5Wx43OBsQBFB5Ne7ng==", + "dev": true, "dependencies": { "detect-libc": "^1.0.3" }, @@ -8412,6 +8628,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "darwin" @@ -8431,6 +8648,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "darwin" @@ -8450,6 +8668,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "freebsd" @@ -8469,6 +8688,7 @@ "cpu": [ "arm" ], + "dev": true, "optional": true, "os": [ "linux" @@ -8488,6 +8708,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -8507,6 +8728,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -8526,6 +8748,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -8545,6 +8768,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -8564,6 +8788,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "win32" @@ -8579,12 +8804,14 @@ "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true }, "node_modules/lmdb": { "version": "2.8.5", "resolved": "https://registry.npmjs.org/lmdb/-/lmdb-2.8.5.tgz", "integrity": "sha512-9bMdFfc80S+vSldBmG3HOuLVHnxRdNTlpzR6QDnzqCQtCzGUEAGTzBKYMeIM+I/sU4oZfgbcbS7X7F65/z/oxQ==", + "dev": true, "hasInstallScript": true, "dependencies": { "msgpackr": "^1.9.5", @@ -8608,7 +8835,8 @@ "node_modules/lmdb/node_modules/node-addon-api": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", - "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==" + "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==", + "dev": true }, "node_modules/locate-path": { "version": "5.0.0", @@ -8625,7 +8853,8 @@ "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true }, "node_modules/log-symbols": { "version": "4.1.0", @@ -8689,6 +8918,7 @@ "version": "2.0.30", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", + "dev": true, "optional": true, "peer": true }, @@ -8733,6 +8963,7 @@ "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" @@ -8805,6 +9036,7 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -8876,6 +9108,7 @@ "version": "1.10.1", "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.10.1.tgz", "integrity": "sha512-r5VRLv9qouXuLiIBrLpl2d5ZvPt8svdQTl5/vMvE4nzDMyEX4sgW5yWhuBBj5UmgwOTWj8CIdSXn5sAfsHAWIQ==", + "dev": true, "optionalDependencies": { "msgpackr-extract": "^3.0.2" } @@ -8884,6 +9117,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.2.tgz", "integrity": "sha512-SdzXp4kD/Qf8agZ9+iTu6eql0m3kWm1A2y1hkpTeVNENutaB0BwHlSvAIaMxwntmRUAUjon2V4L8Z/njd0Ct8A==", + "dev": true, "hasInstallScript": true, "optional": true, "dependencies": { @@ -8905,6 +9139,7 @@ "version": "5.0.7", "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.0.7.tgz", "integrity": "sha512-YlCCc6Wffkx0kHkmam79GKvDQ6x+QZkMjFGrIMxgFNILFvGSbCp2fCBC55pGTT9gVaz8Na5CLmxt/urtzRv36w==", + "dev": true, "optional": true, "bin": { "node-gyp-build-optional-packages": "bin.js", @@ -8929,7 +9164,8 @@ "node_modules/nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true }, "node_modules/node-abi": { "version": "3.67.0", @@ -8947,6 +9183,7 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.0.tgz", "integrity": "sha512-mNcltoe1R8o7STTegSOHdnJNN7s5EUvhoS7ShnTHDyOSd+8H+UdWODq6qSv67PjC8Zc5JRT8+oLAMCr0SIXw7g==", + "dev": true, "engines": { "node": "^16 || ^18 || >= 20" } @@ -8955,6 +9192,7 @@ "version": "5.0.6", "resolved": "https://registry.npmjs.org/node-elm-compiler/-/node-elm-compiler-5.0.6.tgz", "integrity": "sha512-DWTRQR8b54rvschcZRREdsz7K84lnS8A6YJu8du3QLQ8f204SJbyTaA6NzYYbfUG97OTRKRv/0KZl82cTfpLhA==", + "dev": true, "dependencies": { "cross-spawn": "6.0.5", "find-elm-dependencies": "^2.0.4", @@ -8969,6 +9207,7 @@ "version": "6.0.5", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, "dependencies": { "nice-try": "^1.0.4", "path-key": "^2.0.1", @@ -8984,6 +9223,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "dev": true, "engines": { "node": ">=4" } @@ -8992,6 +9232,7 @@ "version": "5.7.2", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, "bin": { "semver": "bin/semver" } @@ -9000,6 +9241,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "dev": true, "dependencies": { "shebang-regex": "^1.0.0" }, @@ -9011,6 +9253,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -9019,6 +9262,7 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, "dependencies": { "isexe": "^2.0.0" }, @@ -9030,6 +9274,7 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.1.1.tgz", "integrity": "sha512-+P72GAjVAbTxjjwUmwjVrqrdZROD4nf8KgpBoDxqXXTiYZZt/ud60dE5yvCSr9lRO8e8yv6kgJIC0K0PfZFVQw==", + "dev": true, "dependencies": { "detect-libc": "^2.0.1" }, @@ -9043,6 +9288,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==", + "dev": true, "engines": { "node": ">=8" } @@ -9056,7 +9302,8 @@ "node_modules/node-releases": { "version": "2.0.14", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==" + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", + "dev": true }, "node_modules/nodemon": { "version": "3.1.4", @@ -9147,6 +9394,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -9194,6 +9442,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, "dependencies": { "boolbase": "^1.0.0" }, @@ -9204,7 +9453,8 @@ "node_modules/nullthrows": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz", - "integrity": "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==" + "integrity": "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==", + "dev": true }, "node_modules/oauth-sign": { "version": "0.9.0", @@ -9246,6 +9496,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, "dependencies": { "wrappy": "1" } @@ -9349,7 +9600,8 @@ "node_modules/ordered-binary": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/ordered-binary/-/ordered-binary-1.5.1.tgz", - "integrity": "sha512-5VyHfHY3cd0iza71JepYG50My+YUbrFtGoUz2ooEydPyPM7Aai/JW098juLr+RG6+rDJuzNNTsEQu2DZa1A41A==" + "integrity": "sha512-5VyHfHY3cd0iza71JepYG50My+YUbrFtGoUz2ooEydPyPM7Aai/JW098juLr+RG6+rDJuzNNTsEQu2DZa1A41A==", + "dev": true }, "node_modules/p-cancelable": { "version": "2.1.1", @@ -9422,6 +9674,7 @@ "version": "2.12.0", "resolved": "https://registry.npmjs.org/parcel/-/parcel-2.12.0.tgz", "integrity": "sha512-W+gxAq7aQ9dJIg/XLKGcRT0cvnStFAQHPaI0pvD0U2l6IVLueUAm3nwN7lkY62zZNmlvNx6jNtE4wlbS+CyqSg==", + "dev": true, "dependencies": { "@parcel/config-default": "2.12.0", "@parcel/core": "2.12.0", @@ -9453,6 +9706,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, "engines": { "node": ">= 10" } @@ -9461,6 +9715,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, "dependencies": { "callsites": "^3.0.0" }, @@ -9472,6 +9727,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", @@ -9506,6 +9762,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -9514,6 +9771,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, "engines": { "node": ">=8" } @@ -9556,6 +9814,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, "engines": { "node": ">=8" } @@ -9600,12 +9859,14 @@ "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, "engines": { "node": ">=8.6" }, @@ -9648,7 +9909,8 @@ "node_modules/postcss-value-parser": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true }, "node_modules/postgres-array": { "version": "2.0.0", @@ -9693,6 +9955,7 @@ "version": "0.16.6", "resolved": "https://registry.npmjs.org/posthtml/-/posthtml-0.16.6.tgz", "integrity": "sha512-JcEmHlyLK/o0uGAlj65vgg+7LIms0xKXe60lcDOTU7oVX/3LuEuLwrQpW3VJ7de5TaFKiW4kWkaIpJL42FEgxQ==", + "dev": true, "dependencies": { "posthtml-parser": "^0.11.0", "posthtml-render": "^3.0.0" @@ -9705,6 +9968,7 @@ "version": "0.10.2", "resolved": "https://registry.npmjs.org/posthtml-parser/-/posthtml-parser-0.10.2.tgz", "integrity": "sha512-PId6zZ/2lyJi9LiKfe+i2xv57oEjJgWbsHGGANwos5AvdQp98i6AtamAl8gzSVFGfQ43Glb5D614cvZf012VKg==", + "dev": true, "dependencies": { "htmlparser2": "^7.1.1" }, @@ -9716,6 +9980,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/posthtml-render/-/posthtml-render-3.0.0.tgz", "integrity": "sha512-z+16RoxK3fUPgwaIgH9NGnK1HKY9XIDpydky5eQGgAFVXTCSezalv9U2jQuNV+Z9qV1fDWNzldcw4eK0SSbqKA==", + "dev": true, "dependencies": { "is-json": "^2.0.1" }, @@ -9727,6 +9992,7 @@ "version": "0.11.0", "resolved": "https://registry.npmjs.org/posthtml-parser/-/posthtml-parser-0.11.0.tgz", "integrity": "sha512-QecJtfLekJbWVo/dMAA+OSwY79wpRmbqS5TeXvXSX+f0c6pW4/SE6inzZ2qkU7oAMCPqIDkZDvd/bQsSFUnKyw==", + "dev": true, "dependencies": { "htmlparser2": "^7.1.1" }, @@ -9929,7 +10195,8 @@ "node_modules/react-error-overlay": { "version": "6.0.9", "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.9.tgz", - "integrity": "sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew==" + "integrity": "sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew==", + "dev": true }, "node_modules/react-is": { "version": "18.2.0", @@ -9941,6 +10208,7 @@ "version": "0.9.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.9.0.tgz", "integrity": "sha512-Gvzk7OZpiqKSkxsQvO/mbTN1poglhmAV7gR/DdIrRrSMXraRQQlfikRJOr3Nb9GTMPC5kof948Zy6jJZIFtDvQ==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -9963,6 +10231,7 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, "dependencies": { "picomatch": "^2.2.1" }, @@ -10354,6 +10623,7 @@ "version": "1.71.1", "resolved": "https://registry.npmjs.org/sass/-/sass-1.71.1.tgz", "integrity": "sha512-wovtnV2PxzteLlfNzbgm1tFXPLoZILYAMJtvoXXkD7/+1uP41eKkIt1ypWq5/q2uT94qHjXehEYfmjKOvjL9sg==", + "dev": true, "dependencies": { "chokidar": ">=3.0.0 <4.0.0", "immutable": "^4.0.0", @@ -10469,6 +10739,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, "dependencies": { "shebang-regex": "^3.0.0" }, @@ -10480,6 +10751,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, "engines": { "node": ">=8" } @@ -10553,6 +10825,7 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -10561,6 +10834,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -10597,6 +10871,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/srcset/-/srcset-4.0.0.tgz", "integrity": "sha512-wvLeHgcVHKO8Sc/H/5lkGreJQVeYMm9rlmt8PuR1xE31rIuXhuzznUUqAt8MqLhB3MqJdFzlNAfpcWnxiFUcPw==", + "dev": true, "engines": { "node": ">=12" }, @@ -10639,7 +10914,8 @@ "version": "0.1.8", "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", - "deprecated": "Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility" + "deprecated": "Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility", + "dev": true }, "node_modules/stack-utils": { "version": "2.0.6", @@ -10861,6 +11137,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -10896,6 +11173,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.2.0.tgz", "integrity": "sha512-4PP6CMW/V7l/GmKRKzsLR8xxjdHTV4IMvhTnpuHwwBazSIlw5W/5SmPjN8Dwyt7lKbSJrRDgp4t9ph0HgChFBQ==", + "dev": true, "optional": true, "peer": true, "dependencies": { @@ -10922,6 +11200,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, "optional": true, "peer": true, "engines": { @@ -10979,6 +11258,7 @@ "version": "0.9.4", "resolved": "https://registry.npmjs.org/temp/-/temp-0.9.4.tgz", "integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==", + "dev": true, "dependencies": { "mkdirp": "^0.5.1", "rimraf": "~2.6.2" @@ -10991,6 +11271,7 @@ "version": "0.5.6", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, "dependencies": { "minimist": "^1.2.6" }, @@ -11002,6 +11283,7 @@ "version": "2.6.3", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, "dependencies": { "glob": "^7.1.3" }, @@ -11013,6 +11295,7 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz", "integrity": "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==", + "dev": true, "engines": { "node": ">=8" }, @@ -11040,6 +11323,7 @@ "version": "5.29.1", "resolved": "https://registry.npmjs.org/terser/-/terser-5.29.1.tgz", "integrity": "sha512-lZQ/fyaIGxsbGxApKmoPTODIzELy3++mXhS5hOqaAWZjQtpq/hFHAc+rm29NND1rYRxRWKcjuARNwULNXa5RtQ==", + "dev": true, "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.8.2", @@ -11056,12 +11340,14 @@ "node_modules/terser/node_modules/commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true }, "node_modules/terser/node_modules/source-map-support": { "version": "0.5.21", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" @@ -11090,7 +11376,8 @@ "node_modules/timsort": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", - "integrity": "sha512-qsdtZH+vMoCARQtyod4imc2nIJwg9Cc7lPRrw9CzF8ZKR0khdr8+2nX80PBhET3tcyTtJDxAffGh2rXH4tyU8A==" + "integrity": "sha512-qsdtZH+vMoCARQtyod4imc2nIJwg9Cc7lPRrw9CzF8ZKR0khdr8+2nX80PBhET3tcyTtJDxAffGh2rXH4tyU8A==", + "dev": true }, "node_modules/tmpl": { "version": "1.0.5", @@ -11111,6 +11398,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, "dependencies": { "is-number": "^7.0.0" }, @@ -11187,7 +11475,8 @@ "node_modules/tslib": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true }, "node_modules/tunnel-agent": { "version": "0.6.0", @@ -11294,6 +11583,7 @@ "version": "1.0.13", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "dev": true, "funding": [ { "type": "opencollective", @@ -11338,6 +11628,7 @@ "version": "3.11.0", "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.11.0.tgz", "integrity": "sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw==", + "dev": true, "engines": { "node": ">= 4" } @@ -11417,12 +11708,14 @@ "node_modules/weak-lru-cache": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/weak-lru-cache/-/weak-lru-cache-1.2.2.tgz", - "integrity": "sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw==" + "integrity": "sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw==", + "dev": true }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, "dependencies": { "isexe": "^2.0.0" }, @@ -11476,7 +11769,8 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true }, "node_modules/write-file-atomic": { "version": "4.0.2", diff --git a/package.json b/package.json index 54dec0684..ab5ed0525 100644 --- a/package.json +++ b/package.json @@ -50,23 +50,16 @@ "test": "npm run test:client && npm run test:backend && npm run test:server" }, "dependencies": { - "@parcel/transformer-elm": "^2.12.0", - "@parcel/transformer-image": "^2.12.0", - "@parcel/transformer-sass": "^2.12.0", "@sentry/browser": "^8.27.0", "@sentry/node": "^8.27.0", "@sentry/profiling-node": "^8.28.0", "@sentry/tracing": "^7.114.0", - "bootstrap": "^5.3.3", "cors": "^2.8.5", "dotenv": "^16.4.5", - "elm": "^0.19.1-6", "express": "^4.19.2", "express-rate-limit": "^7.4.0", "helmet": "^7.1.0", - "highcharts": "^11.4.8", "js-yaml": "^4.1.0", - "parcel": "^2.12.0", "piwik": "^1.0.9" }, "devDependencies": { @@ -82,10 +75,18 @@ "prettier": "^3.3.3", "process": "^0.11.10", "rimraf": "^6.0.1", - "supertest": "^7.0.0" + "supertest": "^7.0.0", + "parcel": "^2.12.0", + "@parcel/transformer-elm": "^2.12.0", + "@parcel/transformer-image": "^2.12.0", + "@parcel/transformer-sass": "^2.12.0", + "highcharts": "^11.4.8", + "bootstrap": "^5.3.3", + "elm": "^0.19.1-6" }, "cacheDirectories": [ "node_modules", - "~/.elm" + "elm-stuff", + ".elm" ] } From 61340c5abb9df76833650e7f989182ed2e3fe41d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 12 Sep 2024 08:23:05 +0000 Subject: [PATCH 04/26] chore(master): release 2.2.0 (#717) [2.2.0](https://github.com/MTES-MCT/ecobalyse/compare/v2.1.1...v2.2.0) (2024-09-12) ### Features * add app version to openapi docs in the root endpoint. ([#726](https://github.com/MTES-MCT/ecobalyse/issues/726)) ([5959c34](https://github.com/MTES-MCT/ecobalyse/commit/5959c3483600a2668390f6f0dd8a2778218436c0)) * add holistic durability in exploratory mode ([#721](https://github.com/MTES-MCT/ecobalyse/issues/721)) ([774faf3](https://github.com/MTES-MCT/ecobalyse/commit/774faf3ad553687e154d4f46bf1227ccd0571710)) * render app version details in the changelog. ([#725](https://github.com/MTES-MCT/ecobalyse/issues/725)) ([8f6ea50](https://github.com/MTES-MCT/ecobalyse/commit/8f6ea50aa1d11c37a0afc54e0de68a69cffbb1bb)) ### Bug Fixes * accept custom making complexity for upcycled garments. ([#723](https://github.com/MTES-MCT/ecobalyse/issues/723)) ([8f61547](https://github.com/MTES-MCT/ecobalyse/commit/8f61547f942b3fefd2129550125e9e7c0591cbaa)) * **ci:** check for ecobalyse-private when extracting the branch name ([#733](https://github.com/MTES-MCT/ecobalyse/issues/733)) ([23ae8a5](https://github.com/MTES-MCT/ecobalyse/commit/23ae8a564854ab6cdb4f38bc62f04a04a47ca3c4)) * don't add disabled step impacts to lifecycle totals. ([#719](https://github.com/MTES-MCT/ecobalyse/issues/719)) ([b6a7e1c](https://github.com/MTES-MCT/ecobalyse/commit/b6a7e1c4ff190acef8d0af0ee0f02b93b19ee32d)) * ensure express app is properly monitored by Sentry. ([#729](https://github.com/MTES-MCT/ecobalyse/issues/729)) ([84a39aa](https://github.com/MTES-MCT/ecobalyse/commit/84a39aa69a8771294195787401cd0e9e11403d1f)) * make scalingo not segfaulting. ([#728](https://github.com/MTES-MCT/ecobalyse/issues/728)) ([1de5140](https://github.com/MTES-MCT/ecobalyse/commit/1de5140c7e75bae20bd6afb58a0180d389dd3254)) * use fabric processes to compute fabric waste ([#712](https://github.com/MTES-MCT/ecobalyse/issues/712)) ([1cce55b](https://github.com/MTES-MCT/ecobalyse/commit/1cce55b229cc9e14de72037d381d06f2255fe0bd)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 19 +++++++++++++++++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1fa8e7962..da4586ff8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,24 @@ # Changelog +## [2.2.0](https://github.com/MTES-MCT/ecobalyse/compare/v2.1.1...v2.2.0) (2024-09-12) + + +### Features + +* add app version to openapi docs in the root endpoint. ([#726](https://github.com/MTES-MCT/ecobalyse/issues/726)) ([5959c34](https://github.com/MTES-MCT/ecobalyse/commit/5959c3483600a2668390f6f0dd8a2778218436c0)) +* add holistic durability in exploratory mode ([#721](https://github.com/MTES-MCT/ecobalyse/issues/721)) ([774faf3](https://github.com/MTES-MCT/ecobalyse/commit/774faf3ad553687e154d4f46bf1227ccd0571710)) +* render app version details in the changelog. ([#725](https://github.com/MTES-MCT/ecobalyse/issues/725)) ([8f6ea50](https://github.com/MTES-MCT/ecobalyse/commit/8f6ea50aa1d11c37a0afc54e0de68a69cffbb1bb)) + + +### Bug Fixes + +* accept custom making complexity for upcycled garments. ([#723](https://github.com/MTES-MCT/ecobalyse/issues/723)) ([8f61547](https://github.com/MTES-MCT/ecobalyse/commit/8f61547f942b3fefd2129550125e9e7c0591cbaa)) +* **ci:** check for ecobalyse-private when extracting the branch name ([#733](https://github.com/MTES-MCT/ecobalyse/issues/733)) ([23ae8a5](https://github.com/MTES-MCT/ecobalyse/commit/23ae8a564854ab6cdb4f38bc62f04a04a47ca3c4)) +* don't add disabled step impacts to lifecycle totals. ([#719](https://github.com/MTES-MCT/ecobalyse/issues/719)) ([b6a7e1c](https://github.com/MTES-MCT/ecobalyse/commit/b6a7e1c4ff190acef8d0af0ee0f02b93b19ee32d)) +* ensure express app is properly monitored by Sentry. ([#729](https://github.com/MTES-MCT/ecobalyse/issues/729)) ([84a39aa](https://github.com/MTES-MCT/ecobalyse/commit/84a39aa69a8771294195787401cd0e9e11403d1f)) +* make scalingo not segfaulting. ([#728](https://github.com/MTES-MCT/ecobalyse/issues/728)) ([1de5140](https://github.com/MTES-MCT/ecobalyse/commit/1de5140c7e75bae20bd6afb58a0180d389dd3254)) +* use fabric processes to compute fabric waste ([#712](https://github.com/MTES-MCT/ecobalyse/issues/712)) ([1cce55b](https://github.com/MTES-MCT/ecobalyse/commit/1cce55b229cc9e14de72037d381d06f2255fe0bd)) + ## [2.1.1](https://github.com/MTES-MCT/ecobalyse/compare/v2.1.0...v2.1.1) (2024-09-02) diff --git a/package-lock.json b/package-lock.json index b93a58445..9abbf4525 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ecobalyse", - "version": "2.1.1", + "version": "2.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ecobalyse", - "version": "2.1.1", + "version": "2.2.0", "license": "MIT", "dependencies": { "@sentry/browser": "^8.27.0", diff --git a/package.json b/package.json index ab5ed0525..77c351eca 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ecobalyse", - "version": "2.1.1", + "version": "2.2.0", "description": "Accélérer l'affichage environnemental de la filière textile française", "author": "Ecobalyse ", "license": "MIT", From bac3f365c31d50fbc2757af6a73848712e822eec Mon Sep 17 00:00:00 2001 From: Christophe Combelles Date: Thu, 12 Sep 2024 10:45:35 +0200 Subject: [PATCH 05/26] refactor: Removed duplicate identifier column in the food explorer (#738) The `source identifier` column is a duplicate of the `identifier` and can be removed --- src/Page/Explore/FoodProcesses.elm | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Page/Explore/FoodProcesses.elm b/src/Page/Explore/FoodProcesses.elm index 9de9a8bf7..0d923ae56 100644 --- a/src/Page/Explore/FoodProcesses.elm +++ b/src/Page/Explore/FoodProcesses.elm @@ -43,10 +43,6 @@ table _ { detailed, scope } = , toValue = Table.StringValue <| .source , toCell = .source >> text } - , { label = "Identifiant source" - , toValue = Table.StringValue <| .identifier >> FoodProcess.identifierToString - , toCell = \process -> code [] [ text (FoodProcess.identifierToString process.identifier) ] - } , { label = "Unité" , toValue = Table.StringValue <| .unit , toCell = .unit >> text From bc436c2d1520efb9de269cdadccf6587d1904468 Mon Sep 17 00:00:00 2001 From: Christophe Combelles Date: Thu, 12 Sep 2024 11:31:46 +0200 Subject: [PATCH 06/26] fix: fixed brightway explorer notebook error (wrong key) (#745) --- data/notebooks/explore.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/data/notebooks/explore.py b/data/notebooks/explore.py index 59ae9d1bd..1d190f5b0 100644 --- a/data/notebooks/explore.py +++ b/data/notebooks/explore.py @@ -411,7 +411,7 @@ def display_main_data(method, impact_category, activity): name = ", ".join(m[1:]) scores[name] = { "Indicateur": name, - "Amount": lca.score, + "Score": lca.score, "Unité": bw2data.methods[m].get("unit", "(no unit)"), } @@ -428,8 +428,8 @@ def display_main_data(method, impact_category, activity): } scores["Ecotoxicity, freshwater"] = { "Indicateur": "Ecotoxicity, freshwater", - "Amount": scores["Ecotoxicity, freshwater - part 1"]["Amount"] - + scores["Ecotoxicity, freshwater - part 2"]["Amount"], + "Score": scores["Ecotoxicity, freshwater - part 1"]["Score"] + + scores["Ecotoxicity, freshwater - part 2"]["Score"], "Unité": scores["Ecotoxicity, freshwater - part 1"]["Unité"], } for trigram in [ From 65d0ed547566c193c5617147a35604e26b0bbe6d Mon Sep 17 00:00:00 2001 From: Christophe Combelles Date: Thu, 12 Sep 2024 11:41:05 +0200 Subject: [PATCH 07/26] feat: displayName in the textile explorer, reordered columns (#737) [Story](https://www.notion.so/4f0841edbc29408eb147558e53fe2acd?v=40d0d544eb8e4c03891da1f9aad8b6cd&p=8001e835e8024b45a7014c4cb2e5b9e3&pm=s) ecobalyse-private: textile_explorer --- data/README.md | 2 +- public/data/textile/processes.json | 90 +++++++++++++++++++++++++-- src/Data/Food/Process.elm | 8 +-- src/Data/Textile/Process.elm | 10 +++ src/Page/Explore/FoodProcesses.elm | 14 +---- src/Page/Explore/TextileProcesses.elm | 22 ++++--- tests/Data/Textile/FormulaTest.elm | 1 + 7 files changed, 115 insertions(+), 32 deletions(-) diff --git a/data/README.md b/data/README.md index da2012c45..8f2060bcc 100644 --- a/data/README.md +++ b/data/README.md @@ -27,7 +27,7 @@ d'abord un `make clean_data` (qui supprime le volume docker). - `make import_method` : pour importer EF 3.1 adapted dans Brightway. Assurez-vous d'avoir le bon fichier de données dans `data/` - `make export_food` : pour exporter les json pour le builder alimentaire -- `make delete_database DB=` : pour supprimer une base de données +- `make delete_database DB=` : pour supprimer une base de données (Ex avec espace: make delete_database DB="Ecoinvent\ 3.9.1") - `make delete_method` : pour supprimer la méthode EF3.1 - `make sync_datapackages` : lance un fix parfois nécessaire pour la synchro brightway - `make import` : lance toutes les commandes d'import diff --git a/public/data/textile/processes.json b/public/data/textile/processes.json index 04d9ee547..4a59fee01 100644 --- a/public/data/textile/processes.json +++ b/public/data/textile/processes.json @@ -1,6 +1,7 @@ [ { "name": "market group for electricity, medium voltage, RAS", + "displayName": "Électricité moyenne tension, Asie", "info": "Energie > Electricité > Mix moyen", "unit": "kWh", "source": "Ecoinvent 3.9.1", @@ -38,6 +39,7 @@ }, { "name": "market group for electricity, medium voltage, RAF", + "displayName": "Électricité moyenne tension, Afrique", "info": "Energie > Electricité > Mix moyen", "unit": "kWh", "source": "Ecoinvent 3.9.1", @@ -75,6 +77,7 @@ }, { "name": "market group for electricity, medium voltage, RME", + "displayName": "Électricité moyenne tension, Moyen-Orient", "info": "Energie > Electricité > Mix moyen", "unit": "kWh", "source": "Ecoinvent 3.9.1", @@ -112,6 +115,7 @@ }, { "name": "market group for electricity, medium voltage, RLA", + "displayName": "Électricité moyenne tension, Amérique latine", "info": "Energie > Electricité > Mix moyen", "unit": "kWh", "source": "Ecoinvent 3.9.1", @@ -149,6 +153,7 @@ }, { "name": "market group for electricity, medium voltage, RNA", + "displayName": "Électricité moyenne tension, Amérique du nord", "info": "Energie > Electricité > Mix moyen", "unit": "kWh", "source": "Ecoinvent 3.9.1", @@ -186,6 +191,7 @@ }, { "name": "market for electricity, medium voltage, AU", + "displayName": "Électricité moyenne tension, Australie", "info": "Energie > Electricité > Mix moyen", "unit": "kWh", "source": "Ecoinvent 3.9.1", @@ -223,6 +229,7 @@ }, { "name": "market group for electricity, medium voltage, CN", + "displayName": "Électricité moyenne tension, Chine", "info": "Energie > Electricité > Mix moyen", "unit": "kWh", "source": "Ecoinvent 3.9.1", @@ -260,6 +267,7 @@ }, { "name": "market for electricity, medium voltage, AL", + "displayName": "Électricité moyenne tension, Albanie", "info": "Energie > Electricité > Mix moyen", "unit": "kWh", "source": "Ecoinvent 3.9.1", @@ -297,6 +305,7 @@ }, { "name": "market for electricity, medium voltage, PE", + "displayName": "Électricité moyenne tension, Pérou", "info": "Energie > Electricité > Mix moyen", "unit": "kWh", "source": "Ecoinvent 3.9.1", @@ -334,6 +343,7 @@ }, { "name": "market for electricity, medium voltage, NZ", + "displayName": "Électricité moyenne tension, Nouvelle-Zélande", "info": "Energie > Electricité > Mix moyen", "unit": "kWh", "source": "Ecoinvent 3.9.1", @@ -371,6 +381,7 @@ }, { "name": "market for electricity, medium voltage, MA", + "displayName": "Électricité moyenne tension, Maroc", "info": "Energie > Electricité > Mix moyen", "unit": "kWh", "source": "Ecoinvent 3.9.1", @@ -408,6 +419,7 @@ }, { "name": "market for electricity, medium voltage, KE", + "displayName": "Électricité moyenne tension, Kenya", "info": "Energie > Electricité > Mix moyen", "unit": "kWh", "source": "Ecoinvent 3.9.1", @@ -445,6 +457,7 @@ }, { "name": "market for electricity, medium voltage, IT", + "displayName": "Électricité moyenne tension, Italie", "info": "Energie > Electricité > Mix moyen", "unit": "kWh", "source": "Ecoinvent 3.9.1", @@ -482,6 +495,7 @@ }, { "name": "market for electricity, medium voltage, RER", + "displayName": "Électricité moyenne tension, Europe", "info": "Energie > Electricité > Mix moyen", "unit": "kWh", "source": "Ecoinvent 3.9.1", @@ -519,6 +533,7 @@ }, { "name": "market for electricity, medium voltage, ES", + "displayName": "Électricité moyenne tension, Espagne", "info": "Energie > Electricité > Mix moyen", "unit": "kWh", "source": "Ecoinvent 3.9.1", @@ -556,6 +571,7 @@ }, { "name": "market for electricity, medium voltage, FR", + "displayName": "Électricité moyenne tension, France", "info": "Energie > Electricité > Mix moyen", "unit": "kWh", "source": "Ecoinvent 3.9.1", @@ -593,6 +609,7 @@ }, { "name": "market group for electricity, medium voltage, MM", + "displayName": "Électricité moyenne tension, Myanmar", "info": "Energie > Electricité > Mix moyen", "unit": "kWh", "source": "Ecoinvent 3.9.1", @@ -630,6 +647,7 @@ }, { "name": "market group for electricity, medium voltage, IN", + "displayName": "Électricité moyenne tension, Inde", "info": "Energie > Electricité > Mix moyen", "unit": "kWh", "source": "Ecoinvent 3.9.1", @@ -667,6 +685,7 @@ }, { "name": "Elasthane (Lycra)", + "displayName": "Elasthane (Lycra)", "info": "Textile > Matières > Matières synthétiques", "unit": "kg", "source": "Ecobalyse", @@ -704,6 +723,7 @@ }, { "name": "polymethyl methacrylate production, beads [RoW]", + "displayName": "Production de plexiglas (Polyméthacrylate de méthyle)", "info": "Textile > Matières > Matières synthétiques", "unit": "kg", "source": "Ecoinvent 3.9.1", @@ -741,6 +761,7 @@ }, { "name": "fibre production, jute, retting [RoW]", + "displayName": "Production de jute, rouissage", "info": "Textile > Matières > Matières naturelles", "unit": "kg", "source": "Ecoinvent 3.9.1", @@ -778,6 +799,7 @@ }, { "name": "polypropylene production, granulate [RoW]", + "displayName": "Production de polypropylène, granulés", "info": "Textile > Matières > Matières synthétiques", "unit": "kg", "source": "Ecoinvent 3.9.1", @@ -815,6 +837,7 @@ }, { "name": "polyethylene terephthalate production, granulate, amorphous [RoW]", + "displayName": "Production de PET, granulés, amorphe", "info": "Textile > Matières > Matières synthétiques", "unit": "kg", "source": "Ecoinvent 3.9.1", @@ -852,6 +875,7 @@ }, { "name": "polyethylene terephthalate production, granulate, amorphous, recycled [RoW]", + "displayName": "Production de PET recyclé, granulés, amorphe", "info": "Textile > Matières > Matières synthétiques", "unit": "kg", "source": "Ecoinvent 3.9.1", @@ -889,6 +913,7 @@ }, { "name": "nylon 6-6 production [RoW]", + "displayName": "Production de nylon 6-6", "info": "Textile > Matières > Matières synthétiques", "unit": "kg", "source": "Ecoinvent 3.9.1", @@ -926,6 +951,7 @@ }, { "name": "viscose fibre, recycled, post-consumer, production [GLO]", + "displayName": "Production de fibres de viscose recyclées", "info": "Textile > Matières > Matières recyclées", "unit": "kg", "source": "Ecoinvent 3.9.1", @@ -963,6 +989,7 @@ }, { "name": "fibre production, flax, retting [RoW]", + "displayName": "Production de fibres de lin, rouissage", "info": "Textile > Matières > Matières naturelles", "unit": "kg", "source": "Ecoinvent 3.9.1", @@ -1000,6 +1027,7 @@ }, { "name": "Laine par défaut", + "displayName": "Laine par défaut", "info": "Textile > Matières > Matières naturelles", "unit": "kg", "source": "Ecobalyse", @@ -1037,6 +1065,7 @@ }, { "name": "Laine nouvelle filière", + "displayName": "Laine nouvelle filière", "info": "Textile > Matières > Matières naturelles uuid_bi=376bd165-d354-41aa-a6e3-fd3228413bb2", "unit": "kg", "source": "Ecobalyse", @@ -1074,6 +1103,7 @@ }, { "name": "fibre production, cotton, ginning, RoW", + "displayName": "Production de fibres de coton", "info": "Textile > Matières > Matières naturelles", "unit": "kg", "source": "Ecoinvent 3.9.1", @@ -1111,6 +1141,7 @@ }, { "name": "fibre production, cotton, organic, ginning [RoW] (Ecobalyse)", + "displayName": "Production de fibres de coton bio", "info": "Textile > Matières > Matières naturelles", "unit": "kg", "source": "Ecobalyse", @@ -1148,6 +1179,7 @@ }, { "name": "sunn hemp production [RoW]", + "displayName": "Production de chanvre", "info": "Textile > Matières > Matières naturelles", "unit": "kg", "source": "Ecoinvent 3.9.1", @@ -1185,6 +1217,7 @@ }, { "name": "market for fibre, viscose [GLO]", + "displayName": "Fibre de viscose", "info": "Textile > Matières > Matières synthétiques", "unit": "kg", "source": "Ecoinvent 3.9.1", @@ -1222,6 +1255,7 @@ }, { "name": "Production de coton recyclé (recyclage mécanique) pré-filature, traitement de déchets textiles post-consommation, inventaire partiellement agrégé", + "displayName": "Production de coton recyclé (déchets post-consommation)", "info": "Textile > Matières > Matières recyclées uuid_bi=4d23093d-1346-4018-8c0f-7aae33c67bcd", "unit": "kg", "source": "Ecobalyse", @@ -1259,6 +1293,7 @@ }, { "name": "Production de coton recyclé (recyclage mécanique) pré-filature, traitement de déchets de production textiles, inventaire partiellement agrégé", + "displayName": "Production de coton recyclé (déchets de production)", "info": "Textile > Matières > Matières recyclées uuid_bi=2b24abb0-c1ec-4298-9b58-350904a26104", "unit": "kg", "source": "Ecobalyse", @@ -1296,6 +1331,7 @@ }, { "name": "Tricotage moyen (mix de métiers circulaire & rectiligne)", + "displayName": "Tricotage moyen (mix de métiers circulaire & rectiligne)", "info": "Textile > Mise en forme > Tricotage", "unit": "kg", "source": "Ecobalyse", @@ -1333,6 +1369,7 @@ }, { "name": "Tricotage fully-fashioned", + "displayName": "Tricotage fully-fashioned", "info": "Textile > Mise en forme > Tricotage", "unit": "kg", "source": "Ecobalyse", @@ -1370,6 +1407,7 @@ }, { "name": "Tricotage seamless", + "displayName": "Tricotage seamless", "info": "Textile > Mise en forme > Tricotage", "unit": "kg", "source": "Ecobalyse", @@ -1407,6 +1445,7 @@ }, { "name": "Tissage (habillement)", + "displayName": "Tissage (habillement)", "info": "Textile > Mise en forme > Tissage", "unit": "kg", "source": "Ecobalyse", @@ -1444,6 +1483,7 @@ }, { "name": "Teinture sur étoffe", + "displayName": "Teinture sur étoffe", "info": "Textile > Ennoblissement > Teinture", "unit": "kg", "source": "Base Impacts 2.01", @@ -1481,6 +1521,7 @@ }, { "name": "Teinture sur pièce", + "displayName": "Teinture sur pièce", "info": "Textile > Ennoblissement > Teinture", "unit": "kg", "source": "Base Impacts 2.01", @@ -1518,6 +1559,7 @@ }, { "name": "Teinture sur fil", + "displayName": "Teinture sur fil", "info": "Textile > Ennoblissement > Teinture", "unit": "kg", "source": "Base Impacts 2.01", @@ -1555,6 +1597,7 @@ }, { "name": "transport, freight, sea, container ship//[GLO] market for transport, freight, sea, container ship", + "displayName": "transport maritime", "info": "Transport > Maritime > Flotte moyenne", "unit": "t*km", "source": "Ecoinvent 3.9.1", @@ -1592,6 +1635,7 @@ }, { "name": "transport, freight, aircraft, long haul//[GLO] market for transport, freight, aircraft, long haul", + "displayName": "transport aérien long-courrier", "info": "Transport > Aérien > Flotte moyenne", "unit": "t*km", "source": "Ecoinvent 3.9.1", @@ -1629,6 +1673,7 @@ }, { "name": "transport, freight train//[GLO] market group for transport, freight train", + "displayName": "transport ferroviaire", "info": "Transport > Train > Flotte moyenne", "unit": "t*km", "source": "Ecoinvent 3.9.1", @@ -1666,6 +1711,7 @@ }, { "name": "transport, freight, lorry, unspecified//[GLO] market group for transport, freight, lorry, unspecified", + "displayName": "transport routier", "info": "Transport > Routier > Flotte moyenne continentale", "unit": "t*km", "source": "Ecoinvent 3.9.1", @@ -1703,6 +1749,7 @@ }, { "name": "Transport en camion non spécifié France (dont parc, utilisation et infrastructure) (50%) [tkm], FR", + "displayName": "Transport en camion non spécifié France", "info": "Transport > Routier > Flotte moyenne française", "unit": "t*km", "source": "Base Impacts 2.01", @@ -1740,6 +1787,7 @@ }, { "name": "market for electricity, medium voltage, TN", + "displayName": "Électricité moyenne tension, Tunisie", "info": "Energie > Electricité > Mix moyen", "unit": "kWh", "source": "Ecoinvent 3.9.1", @@ -1777,6 +1825,7 @@ }, { "name": "market for electricity, medium voltage, TR", + "displayName": "Électricité moyenne tension, Turquie", "info": "Energie > Electricité > Mix moyen", "unit": "kWh", "source": "Ecoinvent 3.9.1", @@ -1814,6 +1863,7 @@ }, { "name": "market for electricity, medium voltage, BD", + "displayName": "Électricité moyenne tension, Bangladesh", "info": "Energie > Electricité > Mix moyen", "unit": "kWh", "source": "Ecoinvent 3.9.1", @@ -1851,6 +1901,7 @@ }, { "name": "market group for electricity, medium voltage, BR", + "displayName": "Électricité moyenne tension, Brésil", "info": "Energie > Electricité > Mix moyen", "unit": "kWh", "source": "Ecoinvent 3.9.1", @@ -1888,6 +1939,7 @@ }, { "name": "market for electricity, medium voltage, CZ", + "displayName": "Électricité moyenne tension, République Tchèque", "info": "Energie > Electricité > Mix moyen", "unit": "kWh", "source": "Ecoinvent 3.9.1", @@ -1925,6 +1977,7 @@ }, { "name": "market for electricity, medium voltage, ET", + "displayName": "Électricité moyenne tension, Éthiopie", "info": "Energie > Electricité > Mix moyen", "unit": "kWh", "source": "Ecoinvent 3.9.1", @@ -1962,6 +2015,7 @@ }, { "name": "market for electricity, medium voltage, KH", + "displayName": "Électricité moyenne tension, Cambodge", "info": "Energie > Electricité > Mix moyen", "unit": "kWh", "source": "Ecoinvent 3.9.1", @@ -1999,6 +2053,7 @@ }, { "name": "market for electricity, medium voltage, LK", + "displayName": "Électricité moyenne tension, Sri Lanka", "info": "Energie > Electricité > Mix moyen", "unit": "kWh", "source": "Ecoinvent 3.9.1", @@ -2036,6 +2091,7 @@ }, { "name": "market for electricity, medium voltage, PK", + "displayName": "Électricité moyenne tension, Pakistan", "info": "Energie > Electricité > Mix moyen", "unit": "kWh", "source": "Ecoinvent 3.9.1", @@ -2073,6 +2129,7 @@ }, { "name": "market group for electricity, medium voltage US", + "displayName": "Électricité moyenne tension, USA", "info": "Energie > Electricité > Mix moyen", "unit": "kWh", "source": "Ecoinvent 3.9.1", @@ -2110,6 +2167,7 @@ }, { "name": "market for electricity, medium voltage, VN", + "displayName": "Électricité moyenne tension, Viet Nam", "info": "Energie > Electricité > Mix moyen", "unit": "kWh", "source": "Ecoinvent 3.9.1", @@ -2147,10 +2205,11 @@ }, { "name": "Heat mix (Europe)", + "displayName": "Mix chaleur (Europe)", "info": "Energie > Chaleur > Vapeur par énergie primaire", "unit": "MJ", - "source": "Ecoinvent 3.9.1", - "correctif": "Reconstitution du mix", + "source": "Ecobalyse", + "correctif": "Reconstitution du mix depuis Ecoinvent 3.9.1", "step_usage": "Energie", "uuid": "heat-europe", "impacts": { @@ -2184,10 +2243,11 @@ }, { "name": "Heat mix (World)", + "displayName": "Mix chaleur (Monde)", "info": "Energie > Chaleur > Vapeur par énergie primaire", "unit": "MJ", - "source": "Ecoinvent 3.9.1", - "correctif": "Reconstitution du mix", + "source": "Ecobalyse", + "correctif": "Reconstitution du mix depuis Ecoinvent 3.9.1", "step_usage": "Energie", "uuid": "heat-row", "impacts": { @@ -2221,6 +2281,7 @@ }, { "name": "Utilisation : Impact hors repassage (électricité (lave-linge, sèche-linge), lessive liquide et traitement des eaux usées) (Chemisier) - Non ironing impact", + "displayName": "Utilisation : Impact hors repassage (Chemisier)", "info": "Utilisation > Aggrégation multi-impacts > Electricité (lave-linge, sèche-linge), lessive liquide et traitement des eaux usées", "unit": "kg", "source": "Ecobalyse", @@ -2258,6 +2319,7 @@ }, { "name": "Utilisation : Impact hors repassage (électricité (lave-linge, sèche-linge), lessive liquide et traitement des eaux usées) (Jean) - Non ironing impact", + "displayName": "Utilisation : Impact hors repassage (Jean)", "info": "Utilisation > Aggrégation multi-impacts > Electricité (lave-linge, sèche-linge), lessive liquide et traitement des eaux usées", "unit": "kg", "source": "Ecobalyse", @@ -2295,6 +2357,7 @@ }, { "name": "Utilisation : Impact hors repassage (électricité (lave-linge, sèche-linge), lessive liquide et traitement des eaux usées) (Jupe) - Non ironing impact", + "displayName": "Utilisation : Impact hors repassage (Jupe)", "info": "Utilisation > Aggrégation multi-impacts > Electricité (lave-linge, sèche-linge), lessive liquide et traitement des eaux usées", "unit": "kg", "source": "Ecobalyse", @@ -2332,6 +2395,7 @@ }, { "name": "Utilisation : Impact hors repassage (électricité (lave-linge, sèche-linge), lessive liquide et traitement des eaux usées) (Manteau) - Non ironing impact", + "displayName": "Utilisation : Impact hors repassage (Manteau)", "info": "Utilisation > Aggrégation multi-impacts > Electricité (lave-linge, sèche-linge), lessive liquide et traitement des eaux usées", "unit": "kg", "source": "Ecobalyse", @@ -2369,6 +2433,7 @@ }, { "name": "Utilisation : Impact hors repassage (électricité (lave-linge, sèche-linge), lessive liquide et traitement des eaux usées) (Pantalon) - Non ironing impact", + "displayName": "Utilisation : Impact hors repassage (Pantalon)", "info": "Utilisation > Aggrégation multi-impacts > Electricité (lave-linge, sèche-linge), lessive liquide et traitement des eaux usées", "unit": "kg", "source": "Ecobalyse", @@ -2406,6 +2471,7 @@ }, { "name": "Utilisation : Impact hors repassage (électricité (lave-linge, sèche-linge), lessive liquide et traitement des eaux usées) (Pull) - Non ironing impact", + "displayName": "Utilisation : Impact hors repassage (Pull)", "info": "Utilisation > Aggrégation multi-impacts > Electricité (lave-linge, sèche-linge), lessive liquide et traitement des eaux usées", "unit": "kg", "source": "Ecobalyse", @@ -2443,6 +2509,7 @@ }, { "name": "Utilisation : Impact hors repassage (électricité (lave-linge, sèche-linge), lessive liquide et traitement des eaux usées) (Robe) - Non ironing impact", + "displayName": "Utilisation : Impact hors repassage (Robe)", "info": "Utilisation > Aggrégation multi-impacts > Electricité (lave-linge, sèche-linge), lessive liquide et traitement des eaux usées", "unit": "kg", "source": "Ecobalyse", @@ -2480,6 +2547,7 @@ }, { "name": "Utilisation : Impact hors repassage (électricité (lave-linge, sèche-linge), lessive liquide et traitement des eaux usées) (T-shirt) - Non ironing impact", + "displayName": "Utilisation : Impact hors repassage (T-shirt)", "info": "Utilisation > Aggrégation multi-impacts > Electricité (lave-linge, sèche-linge), lessive liquide et traitement des eaux usées", "unit": "kg", "source": "Ecobalyse", @@ -2517,6 +2585,7 @@ }, { "name": "Transport en voiture jusqu'au point de collecte précalculé pour la fin de vie", + "displayName": "Transport en voiture jusqu'au point de collecte précalculé pour la fin de vie", "info": "Transport > Routier > Voiture individuelle", "unit": "Item(s)", "source": "Base Impacts 2.01", @@ -2554,6 +2623,7 @@ }, { "name": "Mise en décharge de textiles, FR", + "displayName": "Mise en décharge de textiles, FR", "info": "Traitement de fin de vie > Mise en décharge > Fractions de déchets", "unit": "kg", "source": "Ecobalyse", @@ -2591,6 +2661,7 @@ }, { "name": "Fin de vie hors voiture (transport en camion, incinération, mise en décharge)", + "displayName": "Fin de vie hors voiture (transport en camion, incinération, mise en décharge)", "info": "Fin de vie > Aggrégation multi-impacts > ", "unit": "kg", "source": "Ecobalyse", @@ -2628,6 +2699,7 @@ }, { "name": "Tricotage rectiligne, inventaire désagrégé", + "displayName": "Tricotage rectiligne", "info": "Textile > Mise en forme > Tricotage", "unit": "kg", "source": "Ecobalyse", @@ -2665,6 +2737,7 @@ }, { "name": "Tricotage circulaire, inventaire désagrégé", + "displayName": "Tricotage circulaire", "info": "Textile > Mise en forme > Tricotage", "unit": "kg", "source": "Ecobalyse", @@ -2702,6 +2775,7 @@ }, { "name": "Délavage chimique, procédé majorant, traitement inefficace des eaux usées", + "displayName": "Délavage chimique, procédé majorant, traitement inefficace des eaux usées", "info": "Textile > Ennoblissement > Delavage", "unit": "kg", "source": "Base Impacts 2.01", @@ -2739,6 +2813,7 @@ }, { "name": "Impression pigmentaire, procédé représentatif, traitement moyen des eaux usées", + "displayName": "Impression pigmentaire, procédé représentatif, traitement moyen des eaux usées", "info": "Textile > Ennoblissement > Impression", "unit": "m2", "source": "Base Impacts 2.01", @@ -2776,6 +2851,7 @@ }, { "name": "Impression fixé-lavé, procédé représentatif, traitement moyen des eaux usées", + "displayName": "Impression fixé-lavé, procédé représentatif, traitement moyen des eaux usées", "info": "Textile > Ennoblissement > Impression", "unit": "m2", "source": "Base Impacts 2.01", @@ -2813,6 +2889,7 @@ }, { "name": "Apprêt anti-tache, procédé représentatif, traitement moyen des eaux usées", + "displayName": "Apprêt anti-tache, procédé représentatif, traitement moyen des eaux usées", "info": "Textile > Ennoblissement > Appret chimique", "unit": "kg", "source": "Base Impacts 2.01", @@ -2850,6 +2927,7 @@ }, { "name": "Teinture fibres synthétiques", + "displayName": "Teinture fibres synthétiques", "info": "Textile > Ennoblissement > Teinture", "unit": "kg", "source": "Ecobalyse", @@ -2887,6 +2965,7 @@ }, { "name": "Teinture fibres cellulosiques", + "displayName": "Teinture fibres cellulosiques", "info": "Textile > Ennoblissement > Teinture", "unit": "kg", "source": "Ecobalyse", @@ -2924,6 +3003,7 @@ }, { "name": "Blanchiment", + "displayName": "Blanchiment", "info": "Textile > Ennoblissement > Blanchiment", "unit": "kg", "source": "Ecoinvent 3.9.1", @@ -2961,6 +3041,7 @@ }, { "name": "Impression (pigmentaire)", + "displayName": "Impression (pigmentaire)", "info": "Textile > Ennoblissement > Impression", "unit": "kg", "source": "Ecobalyse", @@ -2998,6 +3079,7 @@ }, { "name": "Impression fixé-lavé (colorants)", + "displayName": "Impression fixé-lavé (colorants)", "info": "Textile > Ennoblissement > Impression", "unit": "kg", "source": "Ecobalyse", diff --git a/src/Data/Food/Process.elm b/src/Data/Food/Process.elm index 52272529e..2f042f930 100644 --- a/src/Data/Food/Process.elm +++ b/src/Data/Food/Process.elm @@ -306,12 +306,8 @@ encodeStringUnit unit = getDisplayName : Process -> String getDisplayName process = - case process.displayName of - Just displayName -> - displayName - - Nothing -> - nameToString process.name + process.displayName + |> Maybe.withDefault (nameToString process.name) listByCategory : Category -> List Process -> List Process diff --git a/src/Data/Textile/Process.elm b/src/Data/Textile/Process.elm index e220abb19..5f63bd98e 100644 --- a/src/Data/Textile/Process.elm +++ b/src/Data/Textile/Process.elm @@ -8,6 +8,7 @@ module Data.Textile.Process exposing , encodeUuid , findByAlias , findByUuid + , getDisplayName , getImpact , uuidToString ) @@ -25,6 +26,7 @@ import Json.Encode.Extra as EncodeExtra type alias Process = { name : String + , displayName : Maybe String , info : String , unit : String , source : String @@ -92,6 +94,7 @@ decode : Decoder Impact.Impacts -> Decoder Process decode impactsDecoder = Decode.succeed Process |> Pipe.required "name" Decode.string + |> Pipe.optional "displayName" (Decode.maybe Decode.string) Nothing |> Pipe.required "info" Decode.string |> Pipe.required "unit" Decode.string |> Pipe.required "source" Decode.string @@ -106,6 +109,12 @@ decode impactsDecoder = |> Pipe.required "alias" (Decode.maybe decodeAlias) +getDisplayName : Process -> String +getDisplayName process = + process.displayName + |> Maybe.withDefault process.name + + decodeList : Decoder Impact.Impacts -> Decoder (List Process) decodeList impactsDecoder = Decode.list (decode impactsDecoder) @@ -135,6 +144,7 @@ encode : Process -> Encode.Value encode process = Encode.object [ ( "name", Encode.string process.name ) + , ( "displayName", EncodeExtra.maybe Encode.string process.displayName ) , ( "info", Encode.string process.info ) , ( "unit", Encode.string process.unit ) , ( "source", Encode.string process.source ) diff --git a/src/Page/Explore/FoodProcesses.elm b/src/Page/Explore/FoodProcesses.elm index 0d923ae56..e4223d08c 100644 --- a/src/Page/Explore/FoodProcesses.elm +++ b/src/Page/Explore/FoodProcesses.elm @@ -28,8 +28,8 @@ table _ { detailed, scope } = [ code [] [ text (FoodProcess.identifierToString process.identifier) ] ] } , { label = "Nom" - , toValue = Table.StringValue getDisplayName - , toCell = getDisplayName >> text + , toValue = Table.StringValue FoodProcess.getDisplayName + , toCell = FoodProcess.getDisplayName >> text } , { label = "Catégorie" , toValue = Table.StringValue <| .category >> FoodProcess.categoryToLabel @@ -57,13 +57,3 @@ table _ { detailed, scope } = } ] } - - -getDisplayName : FoodProcess.Process -> String -getDisplayName process = - case process.displayName of - Just displayName -> - displayName - - Nothing -> - FoodProcess.nameToString process.name diff --git a/src/Page/Explore/TextileProcesses.elm b/src/Page/Explore/TextileProcesses.elm index 51ec6e09c..73919e198 100644 --- a/src/Page/Explore/TextileProcesses.elm +++ b/src/Page/Explore/TextileProcesses.elm @@ -16,11 +16,7 @@ table { detailed, scope } = , toRoute = .uuid >> Just >> Dataset.TextileProcesses >> Route.Explore scope , legend = [] , columns = - [ { label = "Étape" - , toValue = Table.StringValue <| .stepUsage - , toCell = .stepUsage >> text - } - , { label = "Identifiant" + [ { label = "Identifiant" , toValue = Table.StringValue <| .uuid >> Process.uuidToString , toCell = .uuid @@ -35,6 +31,14 @@ table { detailed, scope } = ) } , { label = "Nom" + , toValue = Table.StringValue Process.getDisplayName + , toCell = Process.getDisplayName >> text + } + , { label = "Étape" + , toValue = Table.StringValue .stepUsage + , toCell = .stepUsage >> text + } + , { label = "Nom technique" , toValue = Table.StringValue .name , toCell = .name >> text } @@ -44,15 +48,15 @@ table { detailed, scope } = \process -> span [ title process.source ] [ text process.source ] } + , { label = "Unité" + , toValue = Table.StringValue .unit + , toCell = .unit >> text + } , { label = "Correctif" , toValue = Table.StringValue .correctif , toCell = \process -> span [ title process.correctif ] [ text process.correctif ] } - , { label = "Unité" - , toValue = Table.StringValue .unit - , toCell = .unit >> text - } ] } diff --git a/tests/Data/Textile/FormulaTest.elm b/tests/Data/Textile/FormulaTest.elm index 75149d62a..3665eb21e 100644 --- a/tests/Data/Textile/FormulaTest.elm +++ b/tests/Data/Textile/FormulaTest.elm @@ -29,6 +29,7 @@ km = noOpProcess : Process noOpProcess = { name = "Default" + , displayName = Just "Default" , info = "" , unit = "" , uuid = Process.Uuid "" From 1f3a6c5eb542971415be7632be227a90b34020c2 Mon Sep 17 00:00:00 2001 From: Nicolas Perriault Date: Thu, 12 Sep 2024 13:10:27 +0200 Subject: [PATCH 08/26] refactor: sort most record properties and constructors. (#736) The codebase is sometimes painful to read on long pattern matching cases, long records with many properties, especially as they're not always ordered alphabetically to easily visually scan and find what you're after. Sorting long list of things alphabetically is well known for helping seeking information in such long lists. Let's use [fysiweb/elm-review-sorted](https://package.elm-lang.org/packages/fysiweb/elm-review-sorted/latest/) to report CS violations when important files don't comply. Note: a few modules where excluded from applying the sorted record properties rule because we're using them as constructors, so position of properties used as arguments is required at given positions in the record definition, and were so left intact. --- review/elm.json | 5 +- review/src/ReviewConfig.elm | 11 +- src/ComputeAggregated.elm | 16 +- src/Data/AutocompleteSelector.elm | 4 +- src/Data/Bookmark.elm | 10 +- src/Data/Country.elm | 48 ++-- src/Data/Dataset.elm | 122 +++++----- src/Data/Example.elm | 6 +- src/Data/Food/Db.elm | 21 +- src/Data/Food/EcosystemicServices.elm | 38 ++-- src/Data/Food/Ingredient.elm | 40 ++-- src/Data/Food/Ingredient/Category.elm | 30 +-- src/Data/Food/Origin.elm | 16 +- src/Data/Food/Preparation.elm | 48 ++-- src/Data/Food/Process.elm | 40 ++-- src/Data/Food/Query.elm | 22 +- src/Data/Food/Recipe.elm | 119 +++++----- src/Data/Food/Retail.elm | 24 +- src/Data/Food/WellKnown.elm | 20 +- src/Data/Gitbook.elm | 18 +- src/Data/Impact.elm | 137 ++++++------ src/Data/Matomo.elm | 18 +- src/Data/Scoring.elm | 12 +- src/Data/Session.elm | 49 ++-- src/Data/Split.elm | 6 +- src/Data/Textile/Db.elm | 27 ++- src/Data/Textile/Economics.elm | 26 +-- src/Data/Textile/Fabric.elm | 38 ++-- src/Data/Textile/Formula.elm | 148 ++++++------ src/Data/Textile/Inputs.elm | 166 +++++++------- src/Data/Textile/LifeCycle.elm | 10 +- src/Data/Textile/MakingComplexity.elm | 72 +++--- src/Data/Textile/Material.elm | 26 +-- src/Data/Textile/Material/Spinning.elm | 36 +-- src/Data/Textile/Process.elm | 32 +-- src/Data/Textile/Product.elm | 46 ++-- src/Data/Textile/Query.elm | 12 +- src/Data/Textile/Simulator.elm | 184 +++++++-------- src/Data/Textile/Step.elm | 297 ++++++++++++------------- src/Data/Textile/Step/Label.elm | 154 ++++++------- src/Data/Textile/WellKnown.elm | 122 ++++------ src/Data/Transport.elm | 42 ++-- src/Data/Unit.elm | 6 +- src/Data/User.elm | 18 +- src/Main.elm | 152 ++++++------- src/Page/Auth.elm | 10 +- src/Page/Changelog.elm | 6 +- src/Page/Editorial.elm | 18 +- src/Page/Explore.elm | 82 +++---- src/Page/Explore/Table.elm | 8 +- src/Page/Explore/TextileMaterials.elm | 6 +- src/Page/Food.elm | 88 ++++---- src/Page/Home.elm | 8 +- src/Page/Stats.elm | 32 +-- src/Page/Textile.elm | 42 ++-- src/Request/Auth.elm | 28 +-- src/Request/Common.elm | 16 +- src/Request/Matomo.elm | 4 +- src/Request/Version.elm | 6 +- src/Route.elm | 30 +-- src/Server.elm | 67 +++--- src/Server/Query.elm | 34 +-- src/Server/Request.elm | 8 +- src/Server/Route.elm | 62 +++--- src/Static/Db.elm | 22 +- src/Views/Alert.elm | 14 +- src/Views/AutocompleteSelector.elm | 24 +- src/Views/BaseElement.elm | 18 +- src/Views/Bookmark.elm | 36 ++- src/Views/CardTabs.elm | 10 +- src/Views/Comparator.elm | 62 +++--- src/Views/Component/SplitInput.elm | 4 +- src/Views/Format.elm | 6 +- src/Views/ImpactTabs.elm | 120 +++++----- src/Views/Markdown.elm | 42 ++-- src/Views/Modal.elm | 14 +- src/Views/Page.elm | 20 +- src/Views/RangeSlider.elm | 60 ++--- src/Views/Score.elm | 4 +- src/Views/Sidebar.elm | 44 ++-- src/Views/Table.elm | 12 +- src/Views/Textile/Step.elm | 137 ++++++------ src/Views/Transport.elm | 18 +- tests/Server/RouteTest.elm | 30 +-- 84 files changed, 1863 insertions(+), 1883 deletions(-) diff --git a/review/elm.json b/review/elm.json index c7734475a..ba8761219 100644 --- a/review/elm.json +++ b/review/elm.json @@ -1,10 +1,13 @@ { "type": "application", - "source-directories": ["src"], + "source-directories": [ + "src" + ], "elm-version": "0.19.1", "dependencies": { "direct": { "elm/core": "1.0.5", + "fysiweb/elm-review-sorted": "1.0.3", "jfmengels/elm-review": "2.14.0", "jfmengels/elm-review-cognitive-complexity": "1.0.3", "jfmengels/elm-review-common": "1.3.3", diff --git a/review/src/ReviewConfig.elm b/review/src/ReviewConfig.elm index abaa9157a..1d4d9c029 100644 --- a/review/src/ReviewConfig.elm +++ b/review/src/ReviewConfig.elm @@ -18,7 +18,8 @@ import NoUnused.Variables import Review.Rule as Rule exposing (Rule) import Simplify import CognitiveComplexity - +import NoUnsortedRecordFields +import NoUnsortedConstructors config : List Rule config = @@ -44,6 +45,14 @@ config = |> Rule.ignoreErrorsForDirectories [ "tests/" ] , NoRedundantConcat.rule , NoRedundantCons.rule + , NoUnsortedRecordFields.rule + |> Rule.ignoreErrorsForDirectories [ "tests/" ] + |> Rule.ignoreErrorsForDirectories [ "src/Page" ] + |> Rule.ignoreErrorsForFiles [ "src/Data/Impact/Definition.elm" ] + |> Rule.ignoreErrorsForFiles [ "src/Views/Page.elm" ] + , NoUnsortedConstructors.rule + |> Rule.ignoreErrorsForDirectories [ "tests/" ] + |> Rule.ignoreErrorsForFiles [ "src/Data/Impact/Definition.elm" ] -- NoUnused , NoUnused.CustomTypeConstructors.rule [] |> Rule.ignoreErrorsForFiles [ "src/Views/Modal.elm" ] diff --git a/src/ComputeAggregated.elm b/src/ComputeAggregated.elm index 04b88dccd..57128635a 100644 --- a/src/ComputeAggregated.elm +++ b/src/ComputeAggregated.elm @@ -11,8 +11,8 @@ import Quantity type alias Flags = { definitionsString : String - , textileProcessesString : String , foodProcessesString : String + , textileProcessesString : String } @@ -49,7 +49,7 @@ keepOnlyAggregated processes = toExport : Flags -> Result Decode.Error Encode.Value -toExport { definitionsString, textileProcessesString, foodProcessesString } = +toExport { definitionsString, foodProcessesString, textileProcessesString } = definitionsString |> Decode.decodeString Definition.decode |> Result.andThen @@ -87,11 +87,6 @@ toExport { definitionsString, textileProcessesString, foodProcessesString } = init : Flags -> ( (), Cmd () ) init flags = case toExport flags of - Ok encodedValue -> - ( () - , export encodedValue - ) - Err error -> ( () , error @@ -100,13 +95,18 @@ init flags = |> logError ) + Ok encodedValue -> + ( () + , export encodedValue + ) + main : Program Flags () () main = Platform.worker { init = init - , update = \_ _ -> ( (), Cmd.none ) , subscriptions = always Sub.none + , update = \_ _ -> ( (), Cmd.none ) } diff --git a/src/Data/AutocompleteSelector.elm b/src/Data/AutocompleteSelector.elm index b691eec58..a14037e1c 100644 --- a/src/Data/AutocompleteSelector.elm +++ b/src/Data/AutocompleteSelector.elm @@ -8,9 +8,9 @@ import Task init : (element -> String) -> List element -> Autocomplete element init toString availableElements = Autocomplete.init - { query = "" - , choices = availableElements + { choices = availableElements , ignoreList = [] + , query = "" } (\lastChoices -> Task.succeed diff --git a/src/Data/Bookmark.elm b/src/Data/Bookmark.elm index 860bbbed9..a6f809f37 100644 --- a/src/Data/Bookmark.elm +++ b/src/Data/Bookmark.elm @@ -24,8 +24,8 @@ import Time exposing (Posix) type alias Bookmark = - { name : String - , created : Posix + { created : Posix + , name : String , query : Query } @@ -38,8 +38,8 @@ type Query decode : Decoder Bookmark decode = Decode.map3 Bookmark - (Decode.field "name" Decode.string) (Decode.field "created" (Decode.map Time.millisToPosix Decode.int)) + (Decode.field "name" Decode.string) (Decode.field "query" decodeQuery) @@ -54,8 +54,8 @@ decodeQuery = encode : Bookmark -> Encode.Value encode v = Encode.object - [ ( "name", Encode.string v.name ) - , ( "created", Encode.int <| Time.posixToMillis v.created ) + [ ( "created", Encode.int <| Time.posixToMillis v.created ) + , ( "name", Encode.string v.name ) , ( "query", encodeQuery v.query ) ] diff --git a/src/Data/Country.elm b/src/Data/Country.elm index 8a8b48c88..a8b649ba6 100644 --- a/src/Data/Country.elm +++ b/src/Data/Country.elm @@ -28,20 +28,20 @@ type Code type AquaticPollutionScenario - = Best - | Average + = Average + | Best | Worst type alias Country = - { code : Code - , name : String - , zone : Zone + { airTransportRatio : Split + , aquaticPollutionScenario : AquaticPollutionScenario + , code : Code , electricityProcess : Process , heatProcess : Process - , airTransportRatio : Split + , name : String , scopes : List Scope - , aquaticPollutionScenario : AquaticPollutionScenario + , zone : Zone } @@ -65,14 +65,14 @@ findByCode code = decode : List Process -> Decoder Country decode processes = Decode.succeed Country + |> Pipe.required "airTransportRatio" Split.decodeFloat + |> Pipe.required "aquaticPollutionScenario" decodeAquaticPollutionScenario |> Pipe.required "code" decodeCode - |> Pipe.required "name" Decode.string - |> Pipe.required "zone" Zone.decode |> Pipe.required "electricityProcessUuid" (Process.decodeFromUuid processes) |> Pipe.required "heatProcessUuid" (Process.decodeFromUuid processes) - |> Pipe.required "airTransportRatio" Split.decodeFloat + |> Pipe.required "name" Decode.string |> Pipe.optional "scopes" (Decode.list Scope.decode) [ Scope.Food, Scope.Textile ] - |> Pipe.required "aquaticPollutionScenario" decodeAquaticPollutionScenario + |> Pipe.required "zone" Zone.decode decodeCode : Decoder Code @@ -88,13 +88,13 @@ decodeList processes = encode : Country -> Encode.Value encode v = Encode.object - [ ( "code", encodeCode v.code ) - , ( "name", Encode.string v.name ) + [ ( "airTransportRatio", Split.encodeFloat v.airTransportRatio ) + , ( "aquaticPollutionScenario", v.aquaticPollutionScenario |> aquaticPollutionScenarioToString |> Encode.string ) + , ( "code", encodeCode v.code ) , ( "electricityProcessUuid", v.electricityProcess.uuid |> Process.uuidToString |> Encode.string ) , ( "heatProcessUuid", v.heatProcess.uuid |> Process.uuidToString |> Encode.string ) - , ( "airTransportRatio", Split.encodeFloat v.airTransportRatio ) + , ( "name", Encode.string v.name ) , ( "scopes", v.scopes |> Encode.list Scope.encode ) - , ( "aquaticPollutionScenario", v.aquaticPollutionScenario |> aquaticPollutionScenarioToString |> Encode.string ) ] @@ -113,12 +113,12 @@ decodeAquaticPollutionScenario = aquaticPollutionScenarioFromString : String -> Result String AquaticPollutionScenario aquaticPollutionScenarioFromString string = case string of - "Best" -> - Ok Best - "Average" -> Ok Average + "Best" -> + Ok Best + "Worst" -> Ok Worst @@ -129,12 +129,12 @@ aquaticPollutionScenarioFromString string = aquaticPollutionScenarioToString : AquaticPollutionScenario -> String aquaticPollutionScenarioToString scenario = case scenario of - Best -> - "Best" - Average -> "Average" + Best -> + "Best" + Worst -> "Worst" @@ -142,12 +142,12 @@ aquaticPollutionScenarioToString scenario = getAquaticPollutionRatio : AquaticPollutionScenario -> Split getAquaticPollutionRatio scenario = case scenario of - Best -> - Split.tenth - Average -> Split.fromPercent 36 |> Result.withDefault Split.full + Best -> + Split.tenth + Worst -> Split.fromPercent 65 |> Result.withDefault Split.full diff --git a/src/Data/Dataset.elm b/src/Data/Dataset.elm index d916b7594..518a1efe5 100644 --- a/src/Data/Dataset.elm +++ b/src/Data/Dataset.elm @@ -29,14 +29,14 @@ It's used by Page.Explore and related routes. -} type Dataset = Countries (Maybe Country.Code) - | Impacts (Maybe Definition.Trigram) | FoodExamples (Maybe Uuid) | FoodIngredients (Maybe Ingredient.Id) | FoodProcesses (Maybe FoodProcess.Identifier) + | Impacts (Maybe Definition.Trigram) | TextileExamples (Maybe Uuid) - | TextileProducts (Maybe Product.Id) | TextileMaterials (Maybe Material.Id) | TextileProcesses (Maybe Process.Uuid) + | TextileProducts (Maybe Product.Id) datasets : Scope -> List Dataset @@ -66,20 +66,17 @@ fromSlug string = "countries" -> Countries Nothing - "impacts" -> - Impacts Nothing - "food-examples" -> FoodExamples Nothing - "ingredients" -> - FoodIngredients Nothing - "food-processes" -> FoodProcesses Nothing - "products" -> - TextileProducts Nothing + "impacts" -> + Impacts Nothing + + "ingredients" -> + FoodIngredients Nothing "materials" -> TextileMaterials Nothing @@ -87,6 +84,9 @@ fromSlug string = "processes" -> TextileProcesses Nothing + "products" -> + TextileProducts Nothing + _ -> TextileExamples Nothing @@ -97,9 +97,6 @@ isDetailed dataset = Countries (Just _) -> True - Impacts (Just _) -> - True - FoodExamples (Just _) -> True @@ -109,10 +106,10 @@ isDetailed dataset = FoodProcesses (Just _) -> True - TextileExamples (Just _) -> + Impacts (Just _) -> True - TextileProducts (Just _) -> + TextileExamples (Just _) -> True TextileMaterials (Just _) -> @@ -121,6 +118,9 @@ isDetailed dataset = TextileProcesses (Just _) -> True + TextileProducts (Just _) -> + True + _ -> False @@ -141,9 +141,6 @@ reset dataset = Countries _ -> Countries Nothing - Impacts _ -> - Impacts Nothing - FoodExamples _ -> FoodExamples Nothing @@ -153,18 +150,21 @@ reset dataset = FoodProcesses _ -> FoodProcesses Nothing + Impacts _ -> + Impacts Nothing + TextileExamples _ -> TextileExamples Nothing - TextileProducts _ -> - TextileProducts Nothing - TextileMaterials _ -> TextileMaterials Nothing TextileProcesses _ -> TextileProcesses Nothing + TextileProducts _ -> + TextileProducts Nothing + same : Dataset -> Dataset -> Bool same a b = @@ -172,9 +172,6 @@ same a b = ( Countries _, Countries _ ) -> True - ( Impacts _, Impacts _ ) -> - True - ( FoodExamples _, FoodExamples _ ) -> True @@ -184,10 +181,10 @@ same a b = ( FoodProcesses _, FoodProcesses _ ) -> True - ( TextileExamples _, TextileExamples _ ) -> + ( Impacts _, Impacts _ ) -> True - ( TextileProducts _, TextileProducts _ ) -> + ( TextileExamples _, TextileExamples _ ) -> True ( TextileMaterials _, TextileMaterials _ ) -> @@ -196,6 +193,9 @@ same a b = ( TextileProcesses _, TextileProcesses _ ) -> True + ( TextileProducts _, TextileProducts _ ) -> + True + _ -> False @@ -206,9 +206,6 @@ setIdFromString idString dataset = Countries _ -> Countries (Just (Country.codeFromString idString)) - Impacts _ -> - Impacts (Definition.toTrigram idString |> Result.toMaybe) - FoodExamples _ -> FoodExamples (Uuid.fromString idString) @@ -218,108 +215,111 @@ setIdFromString idString dataset = FoodProcesses _ -> FoodProcesses (Just (FoodProcess.identifierFromString idString)) + Impacts _ -> + Impacts (Definition.toTrigram idString |> Result.toMaybe) + TextileExamples _ -> TextileExamples (Uuid.fromString idString) - TextileProducts _ -> - TextileProducts (Just (Product.Id idString)) - TextileMaterials _ -> TextileMaterials (Just (Material.Id idString)) TextileProcesses _ -> TextileProcesses (Just (Process.Uuid idString)) + TextileProducts _ -> + TextileProducts (Just (Product.Id idString)) + slug : Dataset -> String slug = strings >> .slug -strings : Dataset -> { slug : String, label : String } +strings : Dataset -> { label : String, slug : String } strings dataset = case dataset of Countries _ -> - { slug = "countries", label = "Pays" } - - Impacts _ -> - { slug = "impacts", label = "Impacts" } + { label = "Pays", slug = "countries" } FoodExamples _ -> - { slug = "food-examples", label = "Exemples" } + { label = "Exemples", slug = "food-examples" } FoodIngredients _ -> - { slug = "ingredients", label = "Ingrédients" } + { label = "Ingrédients", slug = "ingredients" } FoodProcesses _ -> - { slug = "food-processes", label = "Procédés" } + { label = "Procédés", slug = "food-processes" } - TextileExamples _ -> - { slug = "textile-examples", label = "Exemples" } + Impacts _ -> + { label = "Impacts", slug = "impacts" } - TextileProducts _ -> - { slug = "products", label = "Produits" } + TextileExamples _ -> + { label = "Exemples", slug = "textile-examples" } TextileMaterials _ -> - { slug = "materials", label = "Matières" } + { label = "Matières", slug = "materials" } TextileProcesses _ -> - { slug = "processes", label = "Procédés" } + { label = "Procédés", slug = "processes" } + + TextileProducts _ -> + { label = "Produits", slug = "products" } toRoutePath : Dataset -> List String toRoutePath dataset = case dataset of - Countries Nothing -> - [ slug dataset ] - Countries (Just code) -> [ slug dataset, Country.codeToString code ] - FoodExamples Nothing -> + Countries Nothing -> [ slug dataset ] FoodExamples (Just id) -> [ slug dataset, Uuid.toString id ] - FoodIngredients Nothing -> + FoodExamples Nothing -> [ slug dataset ] FoodIngredients (Just id) -> [ slug dataset, Ingredient.idToString id ] - FoodProcesses Nothing -> + FoodIngredients Nothing -> [ slug dataset ] FoodProcesses (Just id) -> [ slug dataset, FoodProcess.identifierToString id ] - Impacts Nothing -> + FoodProcesses Nothing -> [ slug dataset ] Impacts (Just trigram) -> [ slug dataset, Definition.toString trigram ] - TextileExamples Nothing -> + Impacts Nothing -> [ slug dataset ] TextileExamples (Just id) -> [ slug dataset, Uuid.toString id ] - TextileProducts Nothing -> + TextileExamples Nothing -> [ slug dataset ] - TextileProducts (Just id) -> - [ slug dataset, Product.idToString id ] + TextileMaterials (Just id) -> + [ slug dataset, Material.idToString id ] TextileMaterials Nothing -> [ slug dataset ] - TextileMaterials (Just id) -> - [ slug dataset, Material.idToString id ] + TextileProcesses (Just id) -> + [ slug dataset, Process.uuidToString id ] TextileProcesses Nothing -> [ slug dataset ] - TextileProcesses (Just id) -> - [ slug dataset, Process.uuidToString id ] + TextileProducts (Just id) -> + [ slug dataset, Product.idToString id ] + + TextileProducts Nothing -> + [ slug dataset ] diff --git a/src/Data/Example.elm b/src/Data/Example.elm index ca0d46c8a..adaaeafc7 100644 --- a/src/Data/Example.elm +++ b/src/Data/Example.elm @@ -15,9 +15,9 @@ import Url.Parser as Parser exposing (Parser) type alias Example query = - { id : Uuid + { category : String + , id : Uuid , name : String - , category : String , query : query } @@ -25,9 +25,9 @@ type alias Example query = decode : Decoder query -> Decoder (Example query) decode decodeQuery = Decode.map4 Example + (Decode.field "category" Decode.string) (Decode.field "id" Uuid.decoder) (Decode.field "name" Decode.string) - (Decode.field "category" Decode.string) (Decode.field "query" decodeQuery) diff --git a/src/Data/Food/Db.elm b/src/Data/Food/Db.elm index 858d5e3ac..115f32650 100644 --- a/src/Data/Food/Db.elm +++ b/src/Data/Food/Db.elm @@ -10,12 +10,13 @@ import Data.Food.Query as Query exposing (Query) import Data.Food.WellKnown as WellKnown exposing (WellKnown) import Data.Impact as Impact import Json.Decode as Decode +import Result.Extra as RE type alias Db = - { processes : List Process - , examples : List (Example Query) + { examples : List (Example Query) , ingredients : List Ingredient + , processes : List Process , wellKnown : WellKnown } @@ -27,8 +28,16 @@ buildFromJson exampleProductsJson foodProcessesJson ingredientsJson = |> Result.mapError Decode.errorToString |> Result.andThen (\processes -> - Result.map3 (Db processes) - (exampleProductsJson |> Example.decodeListFromJsonString Query.decode) - (ingredientsJson |> Decode.decodeString (Ingredient.decodeIngredients processes) |> Result.mapError Decode.errorToString) - (WellKnown.load 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) ) diff --git a/src/Data/Food/EcosystemicServices.elm b/src/Data/Food/EcosystemicServices.elm index 2714d1024..2c56e7ebb 100644 --- a/src/Data/Food/EcosystemicServices.elm +++ b/src/Data/Food/EcosystemicServices.elm @@ -24,49 +24,49 @@ type alias Labels = type alias AbstractEcosystemicServices a = - { hedges : a - , plotSize : a - , cropDiversity : a - , permanentPasture : a + { cropDiversity : a + , hedges : a , livestockDensity : a + , permanentPasture : a + , plotSize : a } coefficients : Coefficients coefficients = - { hedges = Unit.ratio 3 - , plotSize = Unit.ratio 4 - , cropDiversity = Unit.ratio 1.5 - , permanentPasture = Unit.ratio 7 + { cropDiversity = Unit.ratio 1.5 + , hedges = Unit.ratio 3 , livestockDensity = Unit.ratio 3000 + , permanentPasture = Unit.ratio 7 + , plotSize = Unit.ratio 4 } decode : Decoder EcosystemicServices decode = Decode.succeed AbstractEcosystemicServices - |> Pipe.optional "hedges" Unit.decodeImpact (Unit.impact 0) - |> Pipe.optional "plotSize" Unit.decodeImpact (Unit.impact 0) |> Pipe.optional "cropDiversity" Unit.decodeImpact (Unit.impact 0) - |> Pipe.optional "permanentPasture" Unit.decodeImpact (Unit.impact 0) + |> Pipe.optional "hedges" Unit.decodeImpact (Unit.impact 0) |> Pipe.optional "livestockDensity" Unit.decodeImpact (Unit.impact 0) + |> Pipe.optional "permanentPasture" Unit.decodeImpact (Unit.impact 0) + |> Pipe.optional "plotSize" Unit.decodeImpact (Unit.impact 0) empty : EcosystemicServices empty = - { hedges = Unit.impact 0 - , plotSize = Unit.impact 0 - , cropDiversity = Unit.impact 0 - , permanentPasture = Unit.impact 0 + { cropDiversity = Unit.impact 0 + , hedges = Unit.impact 0 , livestockDensity = Unit.impact 0 + , permanentPasture = Unit.impact 0 + , plotSize = Unit.impact 0 } labels : Labels labels = - { hedges = "Haies" - , plotSize = "Taille de parcelles" - , cropDiversity = "Diversité culturale" - , permanentPasture = "Prairies permanentes" + { cropDiversity = "Diversité culturale" + , hedges = "Haies" , livestockDensity = "Chargement territorial" + , permanentPasture = "Prairies permanentes" + , plotSize = "Taille de parcelles" } diff --git a/src/Data/Food/Ingredient.elm b/src/Data/Food/Ingredient.elm index b215b1006..7e765aaf2 100644 --- a/src/Data/Food/Ingredient.elm +++ b/src/Data/Food/Ingredient.elm @@ -33,17 +33,17 @@ import Length type alias Ingredient = - { id : Id - , name : String - , categories : List IngredientCategory.Category + { categories : List IngredientCategory.Category , default : Process , defaultOrigin : Origin + , density : Density + , ecosystemicServices : EcosystemicServices + , id : Id , inediblePart : Split + , name : String , rawToCookedRatio : Unit.Ratio - , density : Density , transportCooling : TransportCooling , visible : Bool - , ecosystemicServices : EcosystemicServices } @@ -52,15 +52,15 @@ type Id type PlaneTransport - = PlaneNotApplicable - | ByPlane + = ByPlane | NoPlane + | PlaneNotApplicable type TransportCooling - = NoCooling - | AlwaysCool + = AlwaysCool | CoolOnceTransformed + | NoCooling byPlaneAllowed : PlaneTransport -> Ingredient -> Result String PlaneTransport @@ -100,15 +100,15 @@ encodeId (Id str) = encodePlaneTransport : PlaneTransport -> Maybe Encode.Value encodePlaneTransport planeTransport = case planeTransport of - PlaneNotApplicable -> - Nothing - ByPlane -> Just <| Encode.string "byPlane" NoPlane -> Just <| Encode.string "noPlane" + PlaneNotApplicable -> + Nothing + idFromString : String -> Id idFromString str = @@ -134,17 +134,17 @@ decodeIngredients processes = decodeIngredient : Dict String Process -> Decoder Ingredient decodeIngredient processes = Decode.succeed Ingredient - |> Pipe.required "id" decodeId - |> Pipe.required "name" Decode.string |> Pipe.required "categories" (Decode.list IngredientCategory.decode) |> Pipe.required "default" (linkProcess processes) |> Pipe.required "default_origin" Origin.decode + |> Pipe.required "density" (Decode.float |> Decode.map gramsPerCubicCentimeter) + |> Pipe.optional "ecosystemicServices" EcosystemicServices.decode EcosystemicServices.empty + |> Pipe.required "id" decodeId |> Pipe.required "inedible_part" Split.decodeFloat + |> Pipe.required "name" Decode.string |> Pipe.required "raw_to_cooked_ratio" (Unit.decodeRatio { percentage = False }) - |> Pipe.required "density" (Decode.float |> Decode.map gramsPerCubicCentimeter) |> Pipe.required "transport_cooling" decodeTransportCooling |> Pipe.required "visible" Decode.bool - |> Pipe.optional "ecosystemicServices" EcosystemicServices.decode EcosystemicServices.empty decodeTransportCooling : Decoder TransportCooling @@ -182,18 +182,18 @@ getDefaultOriginTransport planeTransport origin = Transport.default Impact.empty in case origin of - Origin.France -> - default - Origin.EuropeAndMaghreb -> { default | road = Length.kilometers 2500 } + Origin.France -> + default + Origin.OutOfEuropeAndMaghreb -> { default | road = Length.kilometers 2500, sea = Length.kilometers 18000 } Origin.OutOfEuropeAndMaghrebByPlane -> if planeTransport == ByPlane then - { default | road = Length.kilometers 2500, air = Length.kilometers 18000 } + { default | air = Length.kilometers 18000, road = Length.kilometers 2500 } else { default | road = Length.kilometers 2500, sea = Length.kilometers 18000 } diff --git a/src/Data/Food/Ingredient/Category.elm b/src/Data/Food/Ingredient/Category.elm index 7cdf84bbc..10021ceea 100644 --- a/src/Data/Food/Ingredient/Category.elm +++ b/src/Data/Food/Ingredient/Category.elm @@ -10,18 +10,18 @@ import Json.Decode.Extra as DE type Category = AnimalProduct + | BleuBlancCoeur | Conventional | DairyProduct - | GrainRaw | GrainProcessed + | GrainRaw | Misc - | NutOilseedRaw | NutOilseedProcessed + | NutOilseedRaw + | Organic | SpiceCondimentOrAdditive | VegetableFresh | VegetableProcessed - | Organic - | BleuBlancCoeur fromString : String -> Result String Category @@ -76,26 +76,32 @@ toLabel category = AnimalProduct -> "Viandes, œufs, poissons, et dérivés" + BleuBlancCoeur -> + "Bleu-Blanc-Cœur" + Conventional -> "Conventionnel" DairyProduct -> "Lait et ingrédients laitiers" - GrainRaw -> - "Céréales brutes" - GrainProcessed -> "Céréales transformées" + GrainRaw -> + "Céréales brutes" + Misc -> "Divers" + NutOilseedProcessed -> + "Graisses végétales et oléoprotéagineux transformés" + NutOilseedRaw -> "Fruits à coque et oléoprotéagineux bruts" - NutOilseedProcessed -> - "Graisses végétales et oléoprotéagineux transformés" + Organic -> + "Bio" SpiceCondimentOrAdditive -> "Condiments, épices, additifs" @@ -106,12 +112,6 @@ toLabel category = VegetableProcessed -> "Fruits et légumes transformés" - Organic -> - "Bio" - - BleuBlancCoeur -> - "Bleu-Blanc-Cœur" - decode : Decoder Category decode = diff --git a/src/Data/Food/Origin.elm b/src/Data/Food/Origin.elm index 470e1dcbe..745b2dd84 100644 --- a/src/Data/Food/Origin.elm +++ b/src/Data/Food/Origin.elm @@ -9,8 +9,8 @@ import Json.Decode.Extra as DE type Origin - = France - | EuropeAndMaghreb + = EuropeAndMaghreb + | France | OutOfEuropeAndMaghreb | OutOfEuropeAndMaghrebByPlane @@ -24,12 +24,12 @@ decode = fromString : String -> Result String Origin fromString string = case string of - "France" -> - Ok France - "EuropeAndMaghreb" -> Ok EuropeAndMaghreb + "France" -> + Ok France + "OutOfEuropeAndMaghreb" -> Ok OutOfEuropeAndMaghreb @@ -43,12 +43,12 @@ fromString string = toLabel : Origin -> String toLabel origin = case origin of - France -> - "France" - EuropeAndMaghreb -> "Europe et Maghreb" + France -> + "France" + OutOfEuropeAndMaghreb -> "Hors Europe et Maghreb" diff --git a/src/Data/Food/Preparation.elm b/src/Data/Food/Preparation.elm index 750e151aa..19150f5b9 100644 --- a/src/Data/Food/Preparation.elm +++ b/src/Data/Food/Preparation.elm @@ -22,11 +22,11 @@ import Mass exposing (Mass) type alias Preparation = - { id : Id - , name : String + { applyRawToCookedRatio : Bool , elec : ( Energy, Split ) , heat : ( Energy, Split ) - , applyRawToCookedRatio : Bool + , id : Id + , name : String } @@ -37,47 +37,47 @@ type Id all : List Preparation all = -- see https://fabrique-numerique.gitbook.io/ecobalyse/alimentaire/etapes-du-cycles-de-vie/consommation#preparations-de-preparation - [ { id = Id "frying" - , name = "Friture" + [ { applyRawToCookedRatio = True , elec = ( Energy.kilowattHours 0.667, Split.full ) , heat = ( Energy.megajoules 0, Split.zero ) - , applyRawToCookedRatio = True + , id = Id "frying" + , name = "Friture" } - , { id = Id "pan-cooking" - , name = "Cuisson à la poêle" + , { applyRawToCookedRatio = True , elec = ( Energy.kilowattHours 0.44, Split.fourty ) , heat = ( Energy.megajoules 1.584, Split.complement Split.fourty ) - , applyRawToCookedRatio = True + , id = Id "pan-cooking" + , name = "Cuisson à la poêle" } - , { id = Id "pan-warming" - , name = "Réchauffage à la poêle" + , { applyRawToCookedRatio = False , elec = ( Energy.kilowattHours 0.08, Split.fourty ) , heat = ( Energy.megajoules 0.288, Split.complement Split.fourty ) - , applyRawToCookedRatio = False + , id = Id "pan-warming" + , name = "Réchauffage à la poêle" } - , { id = Id "oven" - , name = "Cuisson au four" + , { applyRawToCookedRatio = True , elec = ( Energy.kilowattHours 0.999, Split.full ) , heat = ( Energy.megajoules 0, Split.zero ) - , applyRawToCookedRatio = True + , id = Id "oven" + , name = "Cuisson au four" } - , { id = Id "microwave" - , name = "Cuisson au four micro-ondes" + , { applyRawToCookedRatio = True , elec = ( Energy.kilowattHours 0.128, Split.full ) , heat = ( Energy.megajoules 0, Split.zero ) - , applyRawToCookedRatio = True + , id = Id "microwave" + , name = "Cuisson au four micro-ondes" } - , { id = Id "refrigeration" - , name = "Réfrigération" + , { applyRawToCookedRatio = False , elec = ( Energy.kilowattHours 0.0777, Split.full ) , heat = ( Energy.megajoules 0, Split.zero ) - , applyRawToCookedRatio = False + , id = Id "refrigeration" + , name = "Réfrigération" } - , { id = Id "freezing" - , name = "Congélation" + , { applyRawToCookedRatio = False , elec = ( Energy.kilowattHours 0.294, Split.full ) , heat = ( Energy.megajoules 0, Split.zero ) - , applyRawToCookedRatio = False + , id = Id "freezing" + , name = "Congélation" } ] diff --git a/src/Data/Food/Process.elm b/src/Data/Food/Process.elm index 2f042f930..a2bee15d6 100644 --- a/src/Data/Food/Process.elm +++ b/src/Data/Food/Process.elm @@ -30,16 +30,16 @@ A process is an entry from public/data/food/processes.json. It has impacts and various other data like categories, code, unit... -} type alias Process = - { name : ProcessName - , displayName : Maybe String - , impacts : Impacts - , unit : String - , identifier : Identifier - , category : Category - , systemDescription : String + { category : Category , comment : Maybe String + , displayName : Maybe String , id_ : String + , identifier : Identifier + , impacts : Impacts + , name : ProcessName , source : String + , systemDescription : String + , unit : String } @@ -183,31 +183,31 @@ encodeCategory = decodeProcess : Decoder Impact.Impacts -> Decoder Process decodeProcess impactsDecoder = Decode.succeed Process - |> Pipe.required "name" (Decode.map nameFromString Decode.string) - |> Pipe.optional "displayName" (Decode.maybe Decode.string) Nothing - |> Pipe.required "impacts" impactsDecoder - |> Pipe.required "unit" decodeStringUnit - |> Pipe.required "identifier" decodeIdentifier |> Pipe.required "category" decodeCategory - |> Pipe.required "system_description" Decode.string |> Pipe.optional "comment" (Decode.maybe Decode.string) Nothing + |> Pipe.optional "displayName" (Decode.maybe Decode.string) Nothing |> Pipe.required "id" Decode.string + |> Pipe.required "identifier" decodeIdentifier + |> Pipe.required "impacts" impactsDecoder + |> Pipe.required "name" (Decode.map nameFromString Decode.string) |> Pipe.required "source" Decode.string + |> Pipe.required "system_description" Decode.string + |> Pipe.required "unit" decodeStringUnit encode : Process -> Encode.Value encode process = Encode.object - [ ( "name", Encode.string (nameToString process.name) ) - , ( "displayName", EncodeExtra.maybe Encode.string process.displayName ) - , ( "impacts", Impact.encode process.impacts ) - , ( "unit", encodeStringUnit process.unit ) - , ( "identifier", encodeIdentifier process.identifier ) - , ( "category", encodeCategory process.category ) - , ( "system_description", Encode.string process.systemDescription ) + [ ( "category", encodeCategory process.category ) , ( "comment", EncodeExtra.maybe Encode.string process.comment ) + , ( "displayName", EncodeExtra.maybe Encode.string process.displayName ) , ( "id", Encode.string process.id_ ) + , ( "identifier", encodeIdentifier process.identifier ) + , ( "impacts", Impact.encode process.impacts ) + , ( "name", Encode.string (nameToString process.name) ) , ( "source", Encode.string process.source ) + , ( "system_description", Encode.string process.systemDescription ) + , ( "unit", encodeStringUnit process.unit ) ] diff --git a/src/Data/Food/Query.elm b/src/Data/Food/Query.elm index 4cfa04050..360643e40 100644 --- a/src/Data/Food/Query.elm +++ b/src/Data/Food/Query.elm @@ -38,9 +38,9 @@ import Url.Parser as Parser exposing (Parser) type alias IngredientQuery = - { id : Ingredient.Id + { country : Maybe Country.Code + , id : Ingredient.Id , mass : Mass - , country : Maybe Country.Code , planeTransport : Ingredient.PlaneTransport } @@ -52,11 +52,11 @@ type alias ProcessQuery = type alias Query = - { ingredients : List IngredientQuery - , transform : Maybe ProcessQuery + { distribution : Maybe Retail.Distribution + , ingredients : List IngredientQuery , packaging : List ProcessQuery - , distribution : Maybe Retail.Distribution , preparation : List Preparation.Id + , transform : Maybe ProcessQuery } @@ -102,11 +102,11 @@ buildApiQuery clientUrl query = decode : Decoder Query decode = Decode.succeed Query + |> Pipe.optional "distribution" (Decode.maybe Retail.decode) Nothing |> Pipe.required "ingredients" (Decode.list decodeIngredient) - |> Pipe.optional "transform" (Decode.maybe decodeProcess) Nothing |> Pipe.optional "packaging" (Decode.list decodeProcess) [] - |> Pipe.optional "distribution" (Decode.maybe Retail.decode) Nothing |> Pipe.optional "preparation" (Decode.list Preparation.decodeId) [] + |> Pipe.optional "transform" (Decode.maybe decodeProcess) Nothing decodePlaneTransport : Decoder Ingredient.PlaneTransport @@ -153,9 +153,9 @@ decodeProcess = decodeIngredient : Decoder IngredientQuery decodeIngredient = Decode.succeed IngredientQuery + |> Pipe.optional "country" (Decode.maybe Country.decodeCode) Nothing |> Pipe.required "id" Ingredient.decodeId |> Pipe.required "mass" decodeMassInGrams - |> Pipe.optional "country" (Decode.maybe Country.decodeCode) Nothing |> Pipe.optional "byPlane" decodePlaneTransport Ingredient.PlaneNotApplicable @@ -179,11 +179,11 @@ deleteIngredient id query = empty : Query empty = - { ingredients = [] - , transform = Nothing + { distribution = Nothing + , ingredients = [] , packaging = [] - , distribution = Nothing , preparation = [] + , transform = Nothing } diff --git a/src/Data/Food/Recipe.elm b/src/Data/Food/Recipe.elm index 3c8c0e5a7..ccbcf8e03 100644 --- a/src/Data/Food/Recipe.elm +++ b/src/Data/Food/Recipe.elm @@ -56,33 +56,33 @@ france = type alias Packaging = - { process : Process.Process - , mass : Mass + { mass : Mass + , process : Process.Process } type alias RecipeIngredient = - { ingredient : Ingredient + { country : Maybe Country + , ingredient : Ingredient , mass : Mass - , country : Maybe Country , planeTransport : Ingredient.PlaneTransport } type alias Recipe = - { ingredients : List RecipeIngredient - , transform : Maybe Transform + { distribution : Maybe Retail.Distribution + , ingredients : List RecipeIngredient , packaging : List Packaging - , distribution : Maybe Retail.Distribution , preparation : List Preparation + , transform : Maybe Transform } type alias Results = - { total : Impacts + { distribution : { total : Impacts, transports : Transport } + , packaging : Impacts , perKg : Impacts - , scoring : Scoring - , totalMass : Mass + , preparation : Impacts , preparedMass : Mass , recipe : { total : Impacts @@ -96,19 +96,16 @@ type alias Results = , transports : Transport , transformedMass : Mass } - , packaging : Impacts - , distribution : - { total : Impacts - , transports : Transport - } - , preparation : Impacts + , scoring : Scoring + , total : Impacts + , totalMass : Mass , transports : Transport } type alias Transform = - { process : Process.Process - , mass : Mass + { mass : Mass + , process : Process.Process } @@ -248,55 +245,46 @@ compute db = (Impact.getTotalComplementsImpacts totalComplementsImpactPerKg) in ( recipe - , { total = totalImpacts + , { distribution = { total = distributionImpacts, transports = distributionTransport } + , packaging = packagingImpacts , perKg = impactsPerKg - , scoring = scoring - , totalMass = getMassAtPackaging recipe + , preparation = preparationImpacts , preparedMass = preparedMass , recipe = - { total = addIngredientsComplements recipeImpacts - , initialMass = recipe.ingredients |> List.map .mass |> Quantity.sum - , edibleMass = removeIngredientsInedibleMass recipe.ingredients |> List.map .mass |> Quantity.sum - , ingredientsTotal = addIngredientsComplements ingredientsTotalImpacts + { edibleMass = removeIngredientsInedibleMass recipe.ingredients |> List.map .mass |> Quantity.sum , ingredients = ingredientsImpacts + , ingredientsTotal = addIngredientsComplements ingredientsTotalImpacts + , initialMass = recipe.ingredients |> List.map .mass |> Quantity.sum + , total = addIngredientsComplements recipeImpacts , totalComplementsImpact = totalComplementsImpact , totalComplementsImpactPerKg = totalComplementsImpactPerKg , transform = transformImpacts - , transports = ingredientsTransport , transformedMass = transformedIngredientsMass + , transports = ingredientsTransport } - , packaging = packagingImpacts - , distribution = - { total = distributionImpacts - , transports = distributionTransport - } - , preparation = preparationImpacts - , transports = - Transport.sum - [ ingredientsTransport - , distributionTransport - ] + , scoring = scoring + , total = totalImpacts + , totalMass = getMassAtPackaging recipe + , transports = Transport.sum [ ingredientsTransport, distributionTransport ] } ) ) computeIngredientComplementsImpacts : EcosystemicServices -> Mass -> Impact.ComplementsImpacts -computeIngredientComplementsImpacts { hedges, plotSize, cropDiversity, permanentPasture, livestockDensity } ingredientMass = +computeIngredientComplementsImpacts { cropDiversity, hedges, livestockDensity, permanentPasture, plotSize } ingredientMass = let apply coeff = Quantity.multiplyBy (Mass.inKilograms ingredientMass) >> Quantity.multiplyBy (Unit.ratioToFloat coeff) in - { hedges = apply EcosystemicServices.coefficients.hedges hedges - , plotSize = apply EcosystemicServices.coefficients.plotSize plotSize - , cropDiversity = apply EcosystemicServices.coefficients.cropDiversity cropDiversity - , permanentPasture = apply EcosystemicServices.coefficients.permanentPasture permanentPasture + { cropDiversity = apply EcosystemicServices.coefficients.cropDiversity cropDiversity + , hedges = apply EcosystemicServices.coefficients.hedges hedges , livestockDensity = apply EcosystemicServices.coefficients.livestockDensity livestockDensity - - -- Note: these complements don't apply to ingredients , microfibers = Unit.impact 0 , outOfEuropeEOL = Unit.impact 0 + , permanentPasture = apply EcosystemicServices.coefficients.permanentPasture permanentPasture + , plotSize = apply EcosystemicServices.coefficients.plotSize plotSize } @@ -307,7 +295,7 @@ computeImpact mass _ = >> Unit.impact -computeProcessImpacts : { a | process : Process, mass : Mass } -> Impacts +computeProcessImpacts : { a | mass : Mass, process : Process } -> Impacts computeProcessImpacts item = item.process.impacts |> Impact.mapImpacts (computeImpact item.mass) @@ -333,7 +321,7 @@ computeIngredientsTotalComplements = computeIngredientTransport : Db -> RecipeIngredient -> Transport -computeIngredientTransport db { ingredient, country, mass, planeTransport } = +computeIngredientTransport db { country, ingredient, mass, planeTransport } = let emptyImpacts = Impact.empty @@ -466,11 +454,11 @@ encodeScoring scoring = fromQuery : Db -> Query -> Result String Recipe fromQuery db query = Ok Recipe + |> RE.andMap (Ok query.distribution) |> RE.andMap (ingredientListFromQuery db query) - |> RE.andMap (transformFromQuery db.food query) |> RE.andMap (packagingListFromQuery db.food query) - |> RE.andMap (Ok query.distribution) |> RE.andMap (preparationListFromQuery query) + |> RE.andMap (transformFromQuery db.food query) getMassAtPackaging : Recipe -> Mass @@ -579,14 +567,12 @@ ingredientListFromQuery db = ingredientFromQuery : Db -> BuilderQuery.IngredientQuery -> Result String RecipeIngredient -ingredientFromQuery db { id, mass, country, planeTransport } = +ingredientFromQuery db { country, id, mass, planeTransport } = let ingredientResult = Ingredient.findByID id db.food.ingredients in Ok RecipeIngredient - |> RE.andMap ingredientResult - |> RE.andMap (Ok mass) |> RE.andMap (case Maybe.map (\c -> Country.findByCode c db.countries) country of Just (Ok country_) -> @@ -598,6 +584,8 @@ ingredientFromQuery db { id, mass, country, planeTransport } = Nothing -> Ok Nothing ) + |> RE.andMap ingredientResult + |> RE.andMap (Ok mass) |> RE.andMap (ingredientResult |> Result.andThen (Ingredient.byPlaneAllowed planeTransport) @@ -606,9 +594,9 @@ ingredientFromQuery db { id, mass, country, planeTransport } = ingredientQueryFromIngredient : Ingredient -> BuilderQuery.IngredientQuery ingredientQueryFromIngredient ingredient = - { id = ingredient.id + { country = Nothing + , id = ingredient.id , mass = Mass.grams 100 - , country = Nothing , planeTransport = Ingredient.byPlaneByDefault ingredient } @@ -624,9 +612,9 @@ packagingListFromQuery db query = packagingFromQuery : Food.Db -> BuilderQuery.ProcessQuery -> Result String Packaging packagingFromQuery { processes } { code, mass } = - Result.map2 Packaging - (Process.findByIdentifier code processes) - (Ok mass) + processes + |> Process.findByIdentifier code + |> Result.map (Packaging mass) processQueryFromProcess : Process -> BuilderQuery.ProcessQuery @@ -653,18 +641,18 @@ toStepsImpacts trigram results = Impact.getImpact trigram >> Just in - { materials = getImpact results.recipe.ingredientsTotal - , transform = getImpact results.recipe.transform + { distribution = getImpact results.distribution.total + , endOfLife = Nothing + , materials = getImpact results.recipe.ingredientsTotal , packaging = getImpact results.packaging + , transform = getImpact results.recipe.transform , transports = getImpact results.transports.impacts - , distribution = getImpact results.distribution.total , usage = getImpact results.preparation - , endOfLife = Nothing } toString : Recipe -> String -toString { ingredients, transform, packaging } = +toString { ingredients, packaging, transform } = let formatMass = Mass.inGrams >> round >> String.fromInt @@ -678,7 +666,7 @@ toString { ingredients, transform, packaging } = |> SE.nonEmpty , transform |> Maybe.map - (\{ process, mass } -> + (\{ mass, process } -> Process.getDisplayName process ++ "(" ++ formatMass mass ++ ")" ) , packaging @@ -699,9 +687,8 @@ transformFromQuery { processes } query = query.transform |> Maybe.map (\transform -> - Result.map2 Transform - (Process.findByIdentifier transform.code processes) - (Ok transform.mass) - |> Result.map Just + processes + |> Process.findByIdentifier transform.code + |> Result.map (Transform transform.mass >> Just) ) |> Maybe.withDefault (Ok Nothing) diff --git a/src/Data/Food/Retail.elm b/src/Data/Food/Retail.elm index aebff9c3a..dac3d7a72 100644 --- a/src/Data/Food/Retail.elm +++ b/src/Data/Food/Retail.elm @@ -46,10 +46,10 @@ type Type type alias Needs = --- what it needs to store a product at the retail store - { energy : Quantity Float (Rate Joules CubicMeters) - , cooling : Quantity Float (Rate Joules CubicMeters) - , water : Float + { cooling : Quantity Float (Rate Joules CubicMeters) + , energy : Quantity Float (Rate Joules CubicMeters) , transport : Length + , water : Float } @@ -60,30 +60,30 @@ type alias Needs = ambient : Distribution ambient = Distribution Ambient - { energy = rate (kilowattHours 123.08) (cubicMeters 1) - , cooling = rate (kilowattHours 0) (cubicMeters 1) - , water = ratio (liters 561.5) (cubicMeters 1) + { cooling = rate (kilowattHours 0) (cubicMeters 1) + , energy = rate (kilowattHours 123.08) (cubicMeters 1) , transport = Length.kilometers 600 + , water = ratio (liters 561.5) (cubicMeters 1) } fresh : Distribution fresh = Distribution Fresh - { energy = rate (kilowattHours 46.15) (cubicMeters 1) - , cooling = rate (kilowattHours 219.23) (cubicMeters 1) - , water = ratio (liters 210.6) (cubicMeters 1) + { cooling = rate (kilowattHours 219.23) (cubicMeters 1) + , energy = rate (kilowattHours 46.15) (cubicMeters 1) , transport = Length.kilometers 600 + , water = ratio (liters 210.6) (cubicMeters 1) } frozen : Distribution frozen = Distribution Frozen - { energy = rate (kilowattHours 61.54) (cubicMeters 1) - , cooling = rate (kilowattHours 415.38) (cubicMeters 1) - , water = ratio (liters 280.8) (cubicMeters 1) + { cooling = rate (kilowattHours 415.38) (cubicMeters 1) + , energy = rate (kilowattHours 61.54) (cubicMeters 1) , transport = Length.kilometers 600 + , water = ratio (liters 280.8) (cubicMeters 1) } diff --git a/src/Data/Food/WellKnown.elm b/src/Data/Food/WellKnown.elm index fc2d345b5..64cec58b0 100644 --- a/src/Data/Food/WellKnown.elm +++ b/src/Data/Food/WellKnown.elm @@ -8,14 +8,14 @@ import Result.Extra as RE type alias WellKnown = - { lorryTransport : Process + { boatCoolingTransport : Process , boatTransport : Process - , planeTransport : Process + , domesticGasHeat : Process , lorryCoolingTransport : Process - , boatCoolingTransport : Process - , water : Process + , lorryTransport : Process , lowVoltageElectricity : Process - , domesticGasHeat : Process + , planeTransport : Process + , water : Process } @@ -26,11 +26,11 @@ load processes = RE.andMap (Process.findById processes id_) in Ok WellKnown - |> resolve "lorry" + |> resolve "boat-cooling" |> resolve "boat" - |> resolve "plane" + |> resolve "domestic-gas-heat" |> resolve "lorry-cooling" - |> resolve "boat-cooling" - |> resolve "tap-water" + |> resolve "lorry" |> resolve "low-voltage-electricity" - |> resolve "domestic-gas-heat" + |> resolve "plane" + |> resolve "tap-water" diff --git a/src/Data/Gitbook.elm b/src/Data/Gitbook.elm index 03a521d52..8794c3ad0 100644 --- a/src/Data/Gitbook.elm +++ b/src/Data/Gitbook.elm @@ -9,10 +9,10 @@ import Data.Env as Env type alias Page = - { title : String - , description : Maybe String + { description : Maybe String , markdown : String , path : Path + , title : String } @@ -40,11 +40,11 @@ type Path | TextileFabric -- Tissage/Tricotage textile | TextileFabricWaste -- Taux de perte en tissage/tricotage textile | TextileHeat -- Chaleur textile - | TextileMaterial -- Matière textile | TextileMaking -- Confection textile | TextileMakingComplexity -- Complexité de la confection textile | TextileMakingDeadStock -- Deadstock lors de la confection textile | TextileMakingWaste -- Taux de perte en confection textile + | TextileMaterial -- Matière textile | TextileSpinning -- Filature textile | TextileTransport -- Transport textile | TextileUse -- Utilisation textile @@ -104,12 +104,12 @@ pathToString path = TextileEnnobling -> "textile/etapes-du-cycle-de-vie/ennoblissement" - TextileEnnoblingToxicity -> - "textile/etapes-du-cycle-de-vie/ennoblissement/inventaires-enrichis" - TextileEnnoblingCountriesAquaticPollution -> "textile/etapes-du-cycle-de-vie/ennoblissement/inventaires-enrichis#pays-less-than-greater-than-taux-de-pollution-aquatique" + TextileEnnoblingToxicity -> + "textile/etapes-du-cycle-de-vie/ennoblissement/inventaires-enrichis" + TextileExamples -> "textile/exemples" @@ -122,9 +122,6 @@ pathToString path = TextileHeat -> "textile/parametres-transverses/chaleur" - TextileMaterial -> - "textile/etapes-du-cycle-de-vie/etape-1-matieres" - TextileMaking -> "textile/etapes-du-cycle-de-vie/confection" @@ -137,6 +134,9 @@ pathToString path = TextileMakingWaste -> "textile/parametres-transverses/pertes-et-rebus" + TextileMaterial -> + "textile/etapes-du-cycle-de-vie/etape-1-matieres" + TextileSpinning -> "textile/etapes-du-cycle-de-vie/etape-2-fabrication-du-fil-new" diff --git a/src/Data/Impact.elm b/src/Data/Impact.elm index 1522ecd6a..b78689db3 100644 --- a/src/Data/Impact.elm +++ b/src/Data/Impact.elm @@ -56,30 +56,26 @@ import Url.Parser as Parser exposing (Parser) type alias ComplementsImpacts = -- Note: these are always expressed in ecoscore (ecs) Pts { -- Ecosystemic services impacts - hedges : Unit.Impact - , plotSize : Unit.Impact - , cropDiversity : Unit.Impact - , permanentPasture : Unit.Impact + cropDiversity : Unit.Impact + , hedges : Unit.Impact , livestockDensity : Unit.Impact - - -- Other impacts , microfibers : Unit.Impact , outOfEuropeEOL : Unit.Impact + , permanentPasture : Unit.Impact + , plotSize : Unit.Impact } addComplementsImpacts : ComplementsImpacts -> ComplementsImpacts -> ComplementsImpacts addComplementsImpacts a b = { -- Ecosystemic services impacts - hedges = Quantity.plus a.hedges b.hedges - , plotSize = Quantity.plus a.plotSize b.plotSize - , cropDiversity = Quantity.plus a.cropDiversity b.cropDiversity - , permanentPasture = Quantity.plus a.permanentPasture b.permanentPasture + cropDiversity = Quantity.plus a.cropDiversity b.cropDiversity + , hedges = Quantity.plus a.hedges b.hedges , livestockDensity = Quantity.plus a.livestockDensity b.livestockDensity - - -- Other impacts , microfibers = Quantity.plus a.microfibers b.microfibers , outOfEuropeEOL = Quantity.plus a.outOfEuropeEOL b.outOfEuropeEOL + , permanentPasture = Quantity.plus a.permanentPasture b.permanentPasture + , plotSize = Quantity.plus a.plotSize b.plotSize } @@ -106,41 +102,38 @@ encodeComplementsImpacts complementsImpact = negateComplementsImpacts complementsImpact in Encode.object - -- Ecosystemic services - [ ( "hedges", Unit.encodeImpact negated.hedges ) - , ( "plotSize", Unit.encodeImpact negated.plotSize ) - , ( "cropDiversity", Unit.encodeImpact negated.cropDiversity ) - , ( "permanentPasture", Unit.encodeImpact negated.permanentPasture ) + [ ( "cropDiversity", Unit.encodeImpact negated.cropDiversity ) + , ( "hedges", Unit.encodeImpact negated.hedges ) , ( "livestockDensity", Unit.encodeImpact negated.livestockDensity ) - - -- Textile complements , ( "microfibers", Unit.encodeImpact negated.microfibers ) , ( "outOfEuropeEOL", Unit.encodeImpact negated.outOfEuropeEOL ) + , ( "permanentPasture", Unit.encodeImpact negated.permanentPasture ) + , ( "plotSize", Unit.encodeImpact negated.plotSize ) ] getTotalComplementsImpacts : ComplementsImpacts -> Unit.Impact getTotalComplementsImpacts complementsImpacts = Quantity.sum - [ complementsImpacts.hedges - , complementsImpacts.plotSize - , complementsImpacts.cropDiversity - , complementsImpacts.permanentPasture + [ complementsImpacts.cropDiversity + , complementsImpacts.hedges , complementsImpacts.livestockDensity , complementsImpacts.microfibers , complementsImpacts.outOfEuropeEOL + , complementsImpacts.permanentPasture + , complementsImpacts.plotSize ] mapComplementsImpacts : (Unit.Impact -> Unit.Impact) -> ComplementsImpacts -> ComplementsImpacts mapComplementsImpacts fn ci = - { hedges = fn ci.hedges - , plotSize = fn ci.plotSize - , cropDiversity = fn ci.cropDiversity - , permanentPasture = fn ci.permanentPasture + { cropDiversity = fn ci.cropDiversity + , hedges = fn ci.hedges , livestockDensity = fn ci.livestockDensity , microfibers = fn ci.microfibers , outOfEuropeEOL = fn ci.outOfEuropeEOL + , permanentPasture = fn ci.permanentPasture + , plotSize = fn ci.plotSize } @@ -151,13 +144,13 @@ negateComplementsImpacts = noComplementsImpacts : ComplementsImpacts noComplementsImpacts = - { hedges = Unit.impact 0 - , plotSize = Unit.impact 0 - , cropDiversity = Unit.impact 0 - , permanentPasture = Unit.impact 0 + { cropDiversity = Unit.impact 0 + , hedges = Unit.impact 0 , livestockDensity = Unit.impact 0 , microfibers = Unit.impact 0 , outOfEuropeEOL = Unit.impact 0 + , permanentPasture = Unit.impact 0 + , plotSize = Unit.impact 0 } @@ -179,31 +172,31 @@ impactsWithComplements complementsImpacts impacts = sumEcosystemicImpacts : ComplementsImpacts -> Unit.Impact sumEcosystemicImpacts c = Quantity.sum - [ c.hedges - , c.plotSize - , c.cropDiversity - , c.permanentPasture + [ c.cropDiversity + , c.hedges , c.livestockDensity + , c.permanentPasture + , c.plotSize ] -complementsImpactAsChartEntries : ComplementsImpacts -> List { name : String, value : Float, color : String } +complementsImpactAsChartEntries : ComplementsImpacts -> List { color : String, name : String, value : Float } complementsImpactAsChartEntries c = -- Notes: -- - We want those complements/bonuses to appear as negative values on the chart -- - We want to sum ecosystemic service components impacts to only have a single entry in the charts - [ { name = "Services écosystémiques", value = -(Unit.impactToFloat (sumEcosystemicImpacts c)), color = "#606060" } - , { name = "Complément microfibres", value = -(Unit.impactToFloat c.microfibers), color = "#c0c0c0" } - , { name = "Complément export hors-Europe", value = -(Unit.impactToFloat c.outOfEuropeEOL), color = "#e0e0e0" } + [ { color = "#606060", name = "Services écosystémiques", value = -(Unit.impactToFloat (sumEcosystemicImpacts c)) } + , { color = "#c0c0c0", name = "Complément microfibres", value = -(Unit.impactToFloat c.microfibers) } + , { color = "#e0e0e0", name = "Complément export hors-Europe", value = -(Unit.impactToFloat c.outOfEuropeEOL) } ] -totalComplementsImpactAsChartEntry : ComplementsImpacts -> { name : String, value : Float, color : String } +totalComplementsImpactAsChartEntry : ComplementsImpacts -> { color : String, name : String, value : Float } totalComplementsImpactAsChartEntry complementsImpacts = -- We want bonuses to appear as negative values on the chart, maluses as positive ones - { name = "Compléments" + { color = "#808080" + , name = "Compléments" , value = -(Unit.impactToFloat (getTotalComplementsImpacts complementsImpacts)) - , color = "#808080" } @@ -212,13 +205,13 @@ totalComplementsImpactAsChartEntry complementsImpacts = type alias Steps a = - { materials : a - , transform : a + { distribution : a + , endOfLife : a + , materials : a , packaging : a + , transform : a , transports : a - , distribution : a , usage : a - , endOfLife : a } @@ -228,25 +221,25 @@ type alias StepsImpacts = mapSteps : (a -> a) -> Steps a -> Steps a mapSteps fn steps = - { materials = fn steps.materials - , transform = fn steps.transform + { distribution = fn steps.distribution + , endOfLife = fn steps.endOfLife + , materials = fn steps.materials , packaging = fn steps.packaging + , transform = fn steps.transform , transports = fn steps.transports - , distribution = fn steps.distribution , usage = fn steps.usage - , endOfLife = fn steps.endOfLife } noStepsImpacts : StepsImpacts noStepsImpacts = - { materials = Nothing - , transform = Nothing + { distribution = Nothing + , endOfLife = Nothing + , materials = Nothing , packaging = Nothing + , transform = Nothing , transports = Nothing - , distribution = Nothing , usage = Nothing - , endOfLife = Nothing } @@ -261,17 +254,17 @@ type alias StepsColors = stepsColors : StepsColors stepsColors = - { materials = Color.purple - , transform = Color.pink + { distribution = Color.red + , endOfLife = Color.turquoise + , materials = Color.purple , packaging = Color.blue + , transform = Color.pink , transports = Color.green - , distribution = Color.red , usage = Color.yellow - , endOfLife = Color.turquoise } -stepsImpactsAsChartEntries : StepsImpacts -> List { name : String, value : Float, color : String } +stepsImpactsAsChartEntries : StepsImpacts -> List { color : String, name : String, value : Float } stepsImpactsAsChartEntries stepsImpacts = [ ( "Matières premières", stepsImpacts.materials, stepsColors.materials ) , ( "Transformation", stepsImpacts.transform, stepsColors.transform ) @@ -283,8 +276,8 @@ stepsImpactsAsChartEntries stepsImpacts = ] |> List.map (\( label, maybeValue, color ) -> - { name = label - , color = color + { color = color + , name = label , value = -- All categories MUST be filled in order to allow comparing Food and Textile simulations -- So, when we don't have a value for a given step, we fallback to zero @@ -301,10 +294,10 @@ stepsImpactsAsChartEntries stepsImpacts = type alias ProtectionAreas = -- Protection Areas is basically scientific slang for subscores - { climate : Unit.Impact -- Climat - , biodiversity : Unit.Impact -- Biodiversité - , resources : Unit.Impact -- Ressources + { biodiversity : Unit.Impact -- Biodiversité + , climate : Unit.Impact -- Climat , health : Unit.Impact -- Santé environnementale + , resources : Unit.Impact -- Ressources } @@ -317,11 +310,7 @@ toProtectionAreas definitions (Impacts impactsPerKgWithoutComplements) = |> Impacts |> computeAggregatedScore definitions .ecoscoreData in - { climate = - pick - [ Definition.Cch -- Climate change - ] - , biodiversity = + { biodiversity = pick [ Definition.Acd -- Acidification , Definition.Tre -- Terrestrial eutrophication @@ -330,6 +319,10 @@ toProtectionAreas definitions (Impacts impactsPerKgWithoutComplements) = , Definition.EtfC -- Ecotoxicity: freshwater , Definition.Ldu -- Land use ] + , climate = + pick + [ Definition.Cch -- Climate change + ] , health = pick [ Definition.Ozd -- Ozone depletion @@ -471,12 +464,12 @@ getAggregatedScoreData definitions getter (Impacts impacts) = in case getter def of Just { normalization, weighting, color } -> - { name = def.label + { color = color ++ "bb" -- pastelization through slight transparency + , name = def.label , value = impact |> Unit.impactAggregateScore normalization weighting |> Unit.impactToFloat - , color = color ++ "bb" -- pastelization through slight transparency } :: acc @@ -487,7 +480,7 @@ getAggregatedScoreData definitions getter (Impacts impacts) = impacts -encodeAggregatedScoreChartEntry : { name : String, value : Float, color : String } -> Encode.Value +encodeAggregatedScoreChartEntry : { color : String, name : String, value : Float } -> Encode.Value encodeAggregatedScoreChartEntry entry = -- This is to be easily used with Highcharts.js in a Web Component Encode.object diff --git a/src/Data/Matomo.elm b/src/Data/Matomo.elm index 743962fb5..55945da06 100644 --- a/src/Data/Matomo.elm +++ b/src/Data/Matomo.elm @@ -9,8 +9,8 @@ import Time exposing (Posix) type alias Stat = - { label : String - , hits : Int + { hits : Int + , label : String , time : Posix } @@ -29,18 +29,20 @@ decodeStats key = (Dict.toList >> List.map (\( label, hits ) -> - Iso8601.toTime label - |> Result.map (Stat label hits) + Ok Stat + |> RE.andMap (Ok hits) + |> RE.andMap (Ok label) + |> RE.andMap (Iso8601.toTime label) |> Result.mapError (always ("Format de date invalide: " ++ label)) ) >> RE.combine >> (\res -> case res of - Ok list -> - Decode.succeed list - Err err -> Decode.fail err + + Ok list -> + Decode.succeed list ) ) @@ -49,7 +51,7 @@ encodeStats : List Stat -> String encodeStats stats = stats |> Encode.list - (\{ time, hits } -> + (\{ hits, time } -> -- The format for Highcharts' line chart is [[timestamp, value], …] Encode.list Encode.int [ Time.posixToMillis time, hits ] ) diff --git a/src/Data/Scoring.elm b/src/Data/Scoring.elm index 7bc6e573a..65fd5b866 100644 --- a/src/Data/Scoring.elm +++ b/src/Data/Scoring.elm @@ -13,9 +13,9 @@ import Quantity type alias Scoring = { all : Unit.Impact , allWithoutComplements : Unit.Impact - , complements : Unit.Impact - , climate : Unit.Impact , biodiversity : Unit.Impact + , climate : Unit.Impact + , complements : Unit.Impact , health : Unit.Impact , resources : Unit.Impact } @@ -34,9 +34,9 @@ compute definitions totalComplementsImpactPerKg perKgWithoutComplements = in { all = Quantity.difference ecsPerKgWithoutComplements totalComplementsImpactPerKg , allWithoutComplements = ecsPerKgWithoutComplements - , complements = totalComplementsImpactPerKg - , climate = subScores.climate , biodiversity = subScores.biodiversity + , climate = subScores.climate + , complements = totalComplementsImpactPerKg , health = subScores.health , resources = subScores.resources } @@ -46,9 +46,9 @@ empty : Scoring empty = { all = Unit.impact 0 , allWithoutComplements = Unit.impact 0 - , complements = Unit.impact 0 - , climate = Unit.impact 0 , biodiversity = Unit.impact 0 + , climate = Unit.impact 0 + , complements = Unit.impact 0 , health = Unit.impact 0 , resources = Unit.impact 0 } diff --git a/src/Data/Session.elm b/src/Data/Session.elm index d9dc01f0d..2321245da 100644 --- a/src/Data/Session.elm +++ b/src/Data/Session.elm @@ -39,19 +39,16 @@ import Static.Json as StaticJson exposing (RawJsonProcesses) type alias Session = - { db : Db - , navKey : Nav.Key - , clientUrl : String - , enableFoodSection : Bool - , store : Store + { clientUrl : String , currentVersion : Version + , db : Db + , enableFoodSection : Bool , matomo : { host : String, siteId : String } + , navKey : Nav.Key , notifications : List Notification - , queries : - { food : FoodQuery.Query - , textile : TextileQuery.Query - } + , queries : { food : FoodQuery.Query, textile : TextileQuery.Query } , releases : WebData (List Github.Release) + , store : Store } @@ -188,31 +185,31 @@ selectNoBookmarks = type alias Store = - { comparedSimulations : Set String + { auth : Auth , bookmarks : List Bookmark - , auth : Auth + , comparedSimulations : Set String } type Auth - = NotAuthenticated - | Authenticated User + = Authenticated User + | NotAuthenticated defaultStore : Store defaultStore = - { comparedSimulations = Set.empty + { auth = NotAuthenticated , bookmarks = [] - , auth = NotAuthenticated + , comparedSimulations = Set.empty } decodeStore : Decoder Store decodeStore = Decode.succeed Store - |> JDP.optional "comparedSimulations" (Decode.map Set.fromList (Decode.list Decode.string)) Set.empty - |> JDP.optional "bookmarks" (Decode.list Bookmark.decode) [] |> JDP.optional "auth" decodeAuth NotAuthenticated + |> JDP.optional "bookmarks" (Decode.list Bookmark.decode) [] + |> JDP.optional "comparedSimulations" (Decode.map Set.fromList (Decode.list Decode.string)) Set.empty decodeAuth : Decoder Auth @@ -233,12 +230,12 @@ encodeStore store = encodeAuth : Auth -> Encode.Value encodeAuth auth = case auth of - NotAuthenticated -> - Encode.null - Authenticated user -> Encode.object [ ( "user", User.encode user ) ] + NotAuthenticated -> + Encode.null + getUser : Session -> Maybe User getUser { store } = @@ -285,24 +282,24 @@ updateStore update session = authenticated : User -> RawJsonProcesses -> Session -> Session authenticated user rawDetailedProcessesJson ({ store } as session) = case StaticDb.db rawDetailedProcessesJson of - Ok db -> - { session | db = db, store = { store | auth = Authenticated user } } - Err err -> session |> notifyError "Impossible de recharger la db avec les nouveaux procédés" err + Ok db -> + { session | db = db, store = { store | auth = Authenticated user } } + logout : Session -> Session logout ({ store } as session) = case StaticDb.db StaticJson.rawJsonProcesses of - Ok db -> - { session | store = { store | auth = NotAuthenticated }, db = db } - Err err -> { session | store = { store | auth = NotAuthenticated } } |> notifyError "Impossible de recharger la db avec les procédés par défaut" err + Ok db -> + { session | db = db, store = { store | auth = NotAuthenticated } } + isAuthenticated : Session -> Bool isAuthenticated { store } = diff --git a/src/Data/Split.elm b/src/Data/Split.elm index c1e8865aa..e3285a276 100644 --- a/src/Data/Split.elm +++ b/src/Data/Split.elm @@ -167,11 +167,11 @@ decodeFloat = |> Decode.andThen (\result -> case result of - Ok split -> - Decode.succeed split - Err error -> Decode.fail error + + Ok split -> + Decode.succeed split ) diff --git a/src/Data/Textile/Db.elm b/src/Data/Textile/Db.elm index bbfbe9bbb..0ff783c1c 100644 --- a/src/Data/Textile/Db.elm +++ b/src/Data/Textile/Db.elm @@ -11,12 +11,13 @@ import Data.Textile.Product as Product exposing (Product) import Data.Textile.Query as Query exposing (Query) import Data.Textile.WellKnown as WellKnown exposing (WellKnown) import Json.Decode as Decode +import Result.Extra as RE type alias Db = - { processes : List Process - , examples : List (Example Query) + { examples : List (Example Query) , materials : List Material + , processes : List Process , products : List Product , wellKnown : WellKnown } @@ -29,9 +30,21 @@ buildFromJson exampleProductsJson materialsJson productsJson processesJson = |> Result.mapError Decode.errorToString |> Result.andThen (\processes -> - Result.map4 (Db processes) - (exampleProductsJson |> Example.decodeListFromJsonString Query.decode) - (Decode.decodeString (Material.decodeList processes) materialsJson |> Result.mapError Decode.errorToString) - (Decode.decodeString (Product.decodeList processes) productsJson |> Result.mapError Decode.errorToString) - (WellKnown.load processes) + Ok Db + |> 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) ) diff --git a/src/Data/Textile/Economics.elm b/src/Data/Textile/Economics.elm index 1484624b8..e31e49d7b 100644 --- a/src/Data/Textile/Economics.elm +++ b/src/Data/Textile/Economics.elm @@ -45,12 +45,12 @@ type Price type Business - = -- PME/TPE - SmallBusiness - -- Grande entreprise avec service de réparation - | LargeBusinessWithServices + = -- Grande entreprise avec service de réparation + LargeBusinessWithServices -- Grande entreprise sans service de réparation | LargeBusinessWithoutServices + -- PME/TPE + | SmallBusiness businessFromString : String -> Result String Business @@ -72,28 +72,28 @@ businessFromString string = businessToLabel : Business -> String businessToLabel business = case business of - SmallBusiness -> - "PME/TPE" - LargeBusinessWithServices -> "Grande entreprise avec service de réparation" LargeBusinessWithoutServices -> "Grande entreprise sans service de réparation" + SmallBusiness -> + "PME/TPE" + businessToString : Business -> String businessToString business = case business of - SmallBusiness -> - "small-business" - LargeBusinessWithServices -> "large-business-with-services" LargeBusinessWithoutServices -> "large-business-without-services" + SmallBusiness -> + "small-business" + computeNonPhysicalDurabilityIndex : Economics -> Unit.NonPhysicalDurability computeNonPhysicalDurabilityIndex economics = @@ -144,12 +144,12 @@ computeRepairCostIndex business price repairCost = in Unit.ratio <| case business of - LargeBusinessWithoutServices -> - repairabilityIndice * 0.67 - LargeBusinessWithServices -> repairabilityIndice * 0.67 + 0.33 + LargeBusinessWithoutServices -> + repairabilityIndice * 0.67 + SmallBusiness -> repairabilityIndice diff --git a/src/Data/Textile/Fabric.elm b/src/Data/Textile/Fabric.elm index 62a308e80..edebd20db 100644 --- a/src/Data/Textile/Fabric.elm +++ b/src/Data/Textile/Fabric.elm @@ -23,12 +23,12 @@ import Json.Encode as Encode type Fabric - = Weaving - | KnittingCircular + = KnittingCircular | KnittingFullyFashioned - | KnittingMix | KnittingIntegral + | KnittingMix | KnittingStraight + | Weaving decode : Decoder Fabric @@ -61,8 +61,8 @@ fabricProcesses = fromString : String -> Result String Fabric fromString string = case string of - "knitting-mix" -> - Ok KnittingMix + "knitting-circular" -> + Ok KnittingCircular "knitting-fully-fashioned" -> Ok KnittingFullyFashioned @@ -70,8 +70,8 @@ fromString string = "knitting-integral" -> Ok KnittingIntegral - "knitting-circular" -> - Ok KnittingCircular + "knitting-mix" -> + Ok KnittingMix "knitting-straight" -> Ok KnittingStraight @@ -124,8 +124,8 @@ getMakingWaste defaultWaste maybeCustomWaste maybeFabric = getProcess : WellKnown -> Fabric -> Process getProcess wellKnown fabric = case fabric of - KnittingMix -> - wellKnown.knittingMix + KnittingCircular -> + wellKnown.knittingCircular KnittingFullyFashioned -> wellKnown.knittingFullyFashioned @@ -133,8 +133,8 @@ getProcess wellKnown fabric = KnittingIntegral -> wellKnown.knittingSeamless - KnittingCircular -> - wellKnown.knittingCircular + KnittingMix -> + wellKnown.knittingMix KnittingStraight -> wellKnown.knittingStraight @@ -156,8 +156,8 @@ isKnitted fabric = toLabel : Fabric -> String toLabel fabricProcess = case fabricProcess of - KnittingMix -> - "Tricotage moyen (par défaut)" + KnittingCircular -> + "Tricotage Circulaire" KnittingFullyFashioned -> "Tricotage Fully fashioned / Seamless" @@ -165,8 +165,8 @@ toLabel fabricProcess = KnittingIntegral -> "Tricotage Intégral / Whole garment" - KnittingCircular -> - "Tricotage Circulaire" + KnittingMix -> + "Tricotage moyen (par défaut)" KnittingStraight -> "Tricotage Rectiligne" @@ -178,8 +178,8 @@ toLabel fabricProcess = toString : Fabric -> String toString fabricProcess = case fabricProcess of - KnittingMix -> - "knitting-mix" + KnittingCircular -> + "knitting-circular" KnittingFullyFashioned -> "knitting-fully-fashioned" @@ -187,8 +187,8 @@ toString fabricProcess = KnittingIntegral -> "knitting-integral" - KnittingCircular -> - "knitting-circular" + KnittingMix -> + "knitting-mix" KnittingStraight -> "knitting-straight" diff --git a/src/Data/Textile/Formula.elm b/src/Data/Textile/Formula.elm index caa274542..6ae45d96c 100644 --- a/src/Data/Textile/Formula.elm +++ b/src/Data/Textile/Formula.elm @@ -37,26 +37,33 @@ import Quantity import Volume exposing (Volume) +type alias StepValues = + { heat : Energy + , impacts : Impacts + , kwh : Energy + } + + -- Waste {-| Compute source mass needed and waste generated by the operation. -} -genericWaste : Unit.Ratio -> Mass -> { waste : Mass, mass : Mass } +genericWaste : Unit.Ratio -> Mass -> { mass : Mass, waste : Mass } genericWaste processWaste baseMass = let waste = baseMass |> Quantity.multiplyBy (Unit.ratioToFloat processWaste) in - { waste = waste, mass = baseMass |> Quantity.plus waste } + { mass = baseMass |> Quantity.plus waste, waste = waste } {-| Compute source material mass needed and waste generated by the operation, according to material & product waste data. -} -makingWaste : Split -> Mass -> { waste : Mass, mass : Mass } +makingWaste : Split -> Mass -> { mass : Mass, waste : Mass } makingWaste pcrWaste baseMass = let mass = @@ -64,7 +71,7 @@ makingWaste pcrWaste baseMass = baseMass |> Quantity.divideBy (Split.toFloat (Split.complement pcrWaste)) in - { waste = Quantity.minus baseMass mass, mass = mass } + { mass = mass, waste = Quantity.minus baseMass mass } {-| Compute source material mass needed and deadstock generated by the operation, according to @@ -98,10 +105,10 @@ pureMaterialImpacts impacts process mass = recycledMaterialImpacts : Impacts - -> { recycledProcess : Process, nonRecycledProcess : Process, cffData : CFFData } + -> { cffData : CFFData, nonRecycledProcess : Process, recycledProcess : Process } -> Mass -> Impacts -recycledMaterialImpacts impacts { recycledProcess, nonRecycledProcess, cffData } outputMass = +recycledMaterialImpacts impacts { cffData, nonRecycledProcess, recycledProcess } outputMass = let { manufacturerAllocation, recycledQualityRatio } = cffData @@ -126,16 +133,17 @@ recycledMaterialImpacts impacts { recycledProcess, nonRecycledProcess, cffData } spinningImpacts : Impacts - -> { spinningKwh : Energy, countryElecProcess : Process } - -> { kwh : Energy, impacts : Impacts } -spinningImpacts impacts { spinningKwh, countryElecProcess } = - { kwh = spinningKwh + -> { countryElecProcess : Process, spinningKwh : Energy } + -> StepValues +spinningImpacts impacts { countryElecProcess, spinningKwh } = + { heat = Quantity.zero , impacts = impacts |> Impact.mapImpacts (\trigram _ -> spinningKwh |> Unit.forKWh (Process.getImpact trigram countryElecProcess) ) + , kwh = spinningKwh } @@ -145,7 +153,7 @@ dyeingImpacts : -> Process -- Outbound: country heat impact -> Process -- Outbound: country electricity impact -> Mass - -> { heat : Energy, kwh : Energy, impacts : Impacts } + -> StepValues dyeingImpacts impacts dyeingProcess heatProcess elecProcess baseMass = let heatMJ = @@ -159,7 +167,6 @@ dyeingImpacts impacts dyeingProcess heatProcess elecProcess baseMass = |> Energy.megajoules in { heat = heatMJ - , kwh = kwh , impacts = impacts |> Impact.mapImpacts @@ -170,21 +177,22 @@ dyeingImpacts impacts dyeingProcess heatProcess elecProcess baseMass = , kwh |> Unit.forKWh (Process.getImpact trigram elecProcess) ] ) + , kwh = kwh } printingImpacts : Impacts -> - { printingProcess : Process -- Inbound: Printing process + { elecProcess : Process -- Outbound: country electricity impact , heatProcess : Process -- Outbound: country heat impact - , elecProcess : Process -- Outbound: country electricity impact - , surfaceMass : Unit.SurfaceMass + , printingProcess : Process -- Inbound: Printing process , ratio : Split + , surfaceMass : Unit.SurfaceMass } -> Mass - -> { heat : Energy, kwh : Energy, impacts : Impacts } -printingImpacts impacts { printingProcess, heatProcess, elecProcess, surfaceMass, ratio } baseMass = + -> StepValues +printingImpacts impacts { elecProcess, heatProcess, printingProcess, ratio, surfaceMass } baseMass = let surface = Unit.surfaceMassToSurface surfaceMass baseMass @@ -199,7 +207,6 @@ printingImpacts impacts { printingProcess, heatProcess, elecProcess, surfaceMass ) in { heat = heatMJ - , kwh = kwh , impacts = impacts |> Impact.mapImpacts @@ -210,19 +217,20 @@ printingImpacts impacts { printingProcess, heatProcess, elecProcess, surfaceMass , kwh |> Unit.forKWh (Process.getImpact trigram elecProcess) ] ) + , kwh = kwh } finishingImpacts : Impacts -> - { finishingProcess : Process -- Inbound: Printing process + { elecProcess : Process -- Outbound: country electricity impact + , finishingProcess : Process -- Inbound: Printing process , heatProcess : Process -- Outbound: country heat impact - , elecProcess : Process -- Outbound: country electricity impact } -> Mass - -> { heat : Energy, kwh : Energy, impacts : Impacts } -finishingImpacts impacts { finishingProcess, heatProcess, elecProcess } baseMass = + -> StepValues +finishingImpacts impacts { elecProcess, finishingProcess, heatProcess } baseMass = let ( heatMJ, kwh ) = ( Quantity.multiplyBy (Mass.inKilograms baseMass) finishingProcess.heat @@ -230,7 +238,6 @@ finishingImpacts impacts { finishingProcess, heatProcess, elecProcess } baseMass ) in { heat = heatMJ - , kwh = kwh , impacts = impacts |> Impact.mapImpacts @@ -241,6 +248,7 @@ finishingImpacts impacts { finishingProcess, heatProcess, elecProcess } baseMass , kwh |> Unit.forKWh (Process.getImpact trigram elecProcess) ] ) + , kwh = kwh } @@ -264,12 +272,12 @@ getAquaticPollutionRealRatio scenario = bleachingImpacts : Impacts -> - { bleachingProcess : Process -- Inbound: Bleaching process - , aquaticPollutionScenario : Country.AquaticPollutionScenario + { aquaticPollutionScenario : Country.AquaticPollutionScenario + , bleachingProcess : Process -- Inbound: Bleaching process } -> Mass -> Impacts -bleachingImpacts impacts { bleachingProcess, aquaticPollutionScenario } baseMass = +bleachingImpacts impacts { aquaticPollutionScenario, bleachingProcess } baseMass = impacts |> Impact.mapImpacts (\trigram _ -> @@ -282,13 +290,13 @@ bleachingImpacts impacts { bleachingProcess, aquaticPollutionScenario } baseMass materialDyeingToxicityImpacts : Impacts -> - { dyeingToxicityProcess : Process -- Inbound: dyeing process - , aquaticPollutionScenario : Country.AquaticPollutionScenario + { aquaticPollutionScenario : Country.AquaticPollutionScenario + , dyeingToxicityProcess : Process -- Inbound: dyeing process } -> Mass -> Split -> Impacts -materialDyeingToxicityImpacts impacts { dyeingToxicityProcess, aquaticPollutionScenario } baseMass split = +materialDyeingToxicityImpacts impacts { aquaticPollutionScenario, dyeingToxicityProcess } baseMass split = impacts |> Impact.mapImpacts (\trigram _ -> @@ -302,13 +310,13 @@ materialDyeingToxicityImpacts impacts { dyeingToxicityProcess, aquaticPollutionS materialPrintingToxicityImpacts : Impacts -> - { printingToxicityProcess : Process -- Inbound: printing process - , aquaticPollutionScenario : Country.AquaticPollutionScenario + { aquaticPollutionScenario : Country.AquaticPollutionScenario + , printingToxicityProcess : Process -- Inbound: printing process } -> Split -> Mass -> Impacts -materialPrintingToxicityImpacts impacts { printingToxicityProcess, aquaticPollutionScenario } split baseMass = +materialPrintingToxicityImpacts impacts { aquaticPollutionScenario, printingToxicityProcess } split baseMass = impacts |> Impact.mapImpacts (\trigram _ -> @@ -322,14 +330,14 @@ materialPrintingToxicityImpacts impacts { printingToxicityProcess, aquaticPollut makingImpacts : Impacts -> - { makingComplexity : MakingComplexity - , fadingProcess : Maybe Process - , countryElecProcess : Process + { countryElecProcess : Process , countryHeatProcess : Process + , fadingProcess : Maybe Process + , makingComplexity : MakingComplexity } -> Mass - -> { kwh : Energy, heat : Energy, impacts : Impacts } -makingImpacts impacts { makingComplexity, fadingProcess, countryElecProcess, countryHeatProcess } outputMass = + -> StepValues +makingImpacts impacts { countryElecProcess, countryHeatProcess, fadingProcess, makingComplexity } outputMass = -- Note: Fading, when enabled, is applied at the Making step because -- it can only be applied on finished products (using step output mass). -- Also: @@ -354,8 +362,7 @@ makingImpacts impacts { makingComplexity, fadingProcess, countryElecProcess, cou elec = Quantity.multiplyBy (MakingComplexity.toDuration makingComplexity |> Duration.inMinutes) kWhPerMinute in - { kwh = Quantity.sum [ elec, fadingElec ] - , heat = fadingHeat + { heat = fadingHeat , impacts = impacts |> Impact.mapImpacts @@ -378,35 +385,37 @@ makingImpacts impacts { makingComplexity, fadingProcess, countryElecProcess, cou |> Unit.forMJ (Process.getImpact trigram countryHeatProcess) ] ) + , kwh = Quantity.sum [ elec, fadingElec ] } knittingImpacts : Impacts - -> { elec : Energy, countryElecProcess : Process } + -> { countryElecProcess : Process, elec : Energy } -> Mass -> - { kwh : Energy - , threadDensity : Maybe Unit.ThreadDensity + { impacts : Impacts + , kwh : Energy , picking : Maybe Unit.PickPerMeter - , impacts : Impacts + , threadDensity : Maybe Unit.ThreadDensity } -knittingImpacts impacts { elec, countryElecProcess } baseMass = +knittingImpacts impacts { countryElecProcess, elec } baseMass = let electricityKWh = Energy.kilowattHours (Mass.inKilograms baseMass * Energy.inKilowattHours elec) in - { kwh = electricityKWh - , threadDensity = Nothing - , picking = Nothing - , impacts = + -- FIXME: why don't we use threadDensity and picking here? + { impacts = impacts |> Impact.mapImpacts (\trigram _ -> electricityKWh |> Unit.forKWh (Process.getImpact trigram countryElecProcess) ) + , kwh = electricityKWh + , picking = Nothing + , threadDensity = Nothing } @@ -420,10 +429,10 @@ weavingImpacts : , yarnSize : Unit.YarnSize } -> - { kwh : Energy - , threadDensity : Maybe Unit.ThreadDensity + { impacts : Impacts + , kwh : Energy , picking : Maybe Unit.PickPerMeter - , impacts : Impacts + , threadDensity : Maybe Unit.ThreadDensity } weavingImpacts impacts { countryElecProcess, outputMass, pickingElec, surfaceMass, yarnSize } = -- Methodology: https://fabrique-numerique.gitbook.io/ecobalyse/textile/etapes-du-cycle-de-vie/tricotage-tissage @@ -443,16 +452,16 @@ weavingImpacts impacts { countryElecProcess, outputMass, pickingElec, surfaceMas * Unit.pickPerMeterToFloat picking |> Energy.kilowattHours in - { kwh = electricityKWh - , threadDensity = Just threadDensity - , picking = Just picking - , impacts = + { impacts = impacts |> Impact.mapImpacts (\trigram _ -> electricityKWh |> Unit.forKWh (Process.getImpact trigram countryElecProcess) ) + , kwh = electricityKWh + , picking = Just picking + , threadDensity = Just threadDensity } @@ -488,14 +497,14 @@ computePicking threadDensity outputSurface = useImpacts : Impacts -> - { useNbCycles : Int + { countryElecProcess : Process , ironingElec : Energy , nonIroningProcess : Process - , countryElecProcess : Process + , useNbCycles : Int } -> Mass - -> { kwh : Energy, impacts : Impacts } -useImpacts impacts { useNbCycles, ironingElec, nonIroningProcess, countryElecProcess } baseMass = + -> StepValues +useImpacts impacts { countryElecProcess, ironingElec, nonIroningProcess, useNbCycles } baseMass = let totalEnergy = -- Note: Ironing is expressed per-item, non-ironing is mass-depdendent @@ -506,7 +515,7 @@ useImpacts impacts { useNbCycles, ironingElec, nonIroningProcess, countryElecPro |> Quantity.sum |> Quantity.multiplyBy (toFloat useNbCycles) in - { kwh = totalEnergy + { heat = Quantity.zero , impacts = impacts |> Impact.mapImpacts @@ -519,21 +528,22 @@ useImpacts impacts { useNbCycles, ironingElec, nonIroningProcess, countryElecPro |> Quantity.multiplyBy (toFloat useNbCycles) ] ) + , kwh = totalEnergy } endOfLifeImpacts : Impacts -> - { volume : Volume - , passengerCar : Process + { countryElecProcess : Process , endOfLife : Process - , countryElecProcess : Process , heatProcess : Process + , passengerCar : Process + , volume : Volume } -> Mass - -> { kwh : Energy, heat : Energy, impacts : Impacts } -endOfLifeImpacts impacts { volume, passengerCar, endOfLife, countryElecProcess, heatProcess } baseMass = + -> StepValues +endOfLifeImpacts impacts { countryElecProcess, endOfLife, heatProcess, passengerCar, volume } baseMass = -- Notes: -- - passengerCar is expressed per-item -- - endOfLife is mass-dependent @@ -559,8 +569,7 @@ endOfLifeImpacts impacts { volume, passengerCar, endOfLife, countryElecProcess, ] ) in - { kwh = elecEnergy - , heat = heatEnergy + { heat = heatEnergy , impacts = impacts |> Impact.mapImpacts @@ -576,6 +585,7 @@ endOfLifeImpacts impacts { volume, passengerCar, endOfLife, countryElecProcess, |> Unit.forKg (Process.getImpact trigram endOfLife) ] ) + , kwh = elecEnergy } @@ -593,7 +603,7 @@ transportRatio airTransportRatio ({ road, sea, air } as transport) = Split.complement roadRatio in { transport - | road = road |> Quantity.multiplyBy (Split.apply (Split.toFloat roadRatio) (Split.complement airTransportRatio)) + | air = air |> Quantity.multiplyBy (Split.toFloat airTransportRatio) + , road = road |> Quantity.multiplyBy (Split.apply (Split.toFloat roadRatio) (Split.complement airTransportRatio)) , sea = sea |> Quantity.multiplyBy (Split.apply (Split.toFloat seaRatio) (Split.complement airTransportRatio)) - , air = air |> Quantity.multiplyBy (Split.toFloat airTransportRatio) } diff --git a/src/Data/Textile/Inputs.elm b/src/Data/Textile/Inputs.elm index 26cfe9667..c118f6b7f 100644 --- a/src/Data/Textile/Inputs.elm +++ b/src/Data/Textile/Inputs.elm @@ -39,52 +39,52 @@ import Views.Format as Format type alias MaterialInput = - { material : Material + { country : Maybe Country + , material : Material , share : Split , spinning : Maybe Spinning - , country : Maybe Country } type alias Inputs = - { mass : Mass - , materials : List MaterialInput - , product : Product + { airTransportRatio : Maybe Split + , business : Maybe Economics.Business + , countryDistribution : Country + , countryDyeing : Country + , countryEndOfLife : Country + , countryFabric : Country + , countryMaking : Country -- TODO: countryMaterial isn't used anymore, but we still need it because `countryList` uses it, -- which in turn is used to build the lifecycle, which needs a country for each step. , countryMaterial : Country , countrySpinning : Country - , countryFabric : Country - , countryDyeing : Country - , countryMaking : Country - , countryDistribution : Country , countryUse : Country - , countryEndOfLife : Country - , airTransportRatio : Maybe Split - , makingWaste : Maybe Split - , makingDeadStock : Maybe Split - , makingComplexity : Maybe MakingComplexity - , yarnSize : Maybe Unit.YarnSize - , surfaceMass : Maybe Unit.SurfaceMass - , fabricProcess : Maybe Fabric , disabledSteps : List Label - , fading : Maybe Bool , dyeingMedium : Maybe DyeingMedium - , printing : Maybe Printing - , business : Maybe Economics.Business + , fabricProcess : Maybe Fabric + , fading : Maybe Bool + , makingComplexity : Maybe MakingComplexity + , makingDeadStock : Maybe Split + , makingWaste : Maybe Split + , mass : Mass + , materials : List MaterialInput , numberOfReferences : Maybe Int + , physicalDurability : Maybe Unit.PhysicalDurability , price : Maybe Economics.Price + , printing : Maybe Printing + , product : Product + , surfaceMass : Maybe Unit.SurfaceMass , traceability : Maybe Bool , upcycled : Bool - , physicalDurability : Maybe Unit.PhysicalDurability + , yarnSize : Maybe Unit.YarnSize } fromMaterialQuery : List Material -> List Country -> List MaterialQuery -> Result String (List MaterialInput) fromMaterialQuery materials countries = List.map - (\{ id, share, spinning, country } -> + (\{ country, id, share, spinning } -> let countryResult = case country of @@ -97,10 +97,10 @@ fromMaterialQuery materials countries = in Result.map2 (\material_ country_ -> - { material = material_ + { country = country_ + , material = material_ , share = share , spinning = spinning - , country = country_ } ) (Material.findById id materials) @@ -112,11 +112,11 @@ fromMaterialQuery materials countries = toMaterialQuery : List MaterialInput -> List MaterialQuery toMaterialQuery = List.map - (\{ material, share, spinning, country } -> - { id = material.id + (\{ country, material, share, spinning } -> + { country = country |> Maybe.andThen (.code >> toQueryCountryCode) + , id = material.id , share = share , spinning = spinning - , country = country |> Maybe.andThen (.code >> toQueryCountryCode) } ) @@ -166,68 +166,68 @@ fromQuery { countries, textile } query = fallbackResult in Ok Inputs - |> RE.andMap (Ok query.mass) - |> RE.andMap materials_ - |> RE.andMap (textile.products |> Product.findById query.product) + |> RE.andMap (Ok query.airTransportRatio) + |> RE.andMap (Ok query.business) + -- The distribution country is always France + |> RE.andMap franceResult + |> RE.andMap (getCountryResult unknownCountryResult query.countryDyeing) + -- The end of life country is always France + |> RE.andMap franceResult + |> RE.andMap (getCountryResult unknownCountryResult query.countryFabric) + |> RE.andMap (getCountryResult unknownCountryResult query.countryMaking) -- Material country is constrained to be the first material's default country |> RE.andMap mainMaterialCountry -- Spinning country is either provided by query or fallbacks to material's default -- country, making the parameter optional |> RE.andMap (getCountryResult mainMaterialCountry query.countrySpinning) - |> RE.andMap (getCountryResult unknownCountryResult query.countryFabric) - |> RE.andMap (getCountryResult unknownCountryResult query.countryDyeing) - |> RE.andMap (getCountryResult unknownCountryResult query.countryMaking) - -- The distribution country is always France - |> RE.andMap franceResult -- The use country is always France |> RE.andMap franceResult - -- The end of life country is always France - |> RE.andMap franceResult - |> RE.andMap (Ok query.airTransportRatio) - |> RE.andMap (Ok query.makingWaste) - |> RE.andMap (Ok query.makingDeadStock) - |> RE.andMap (Ok query.makingComplexity) - |> RE.andMap (Ok query.yarnSize) - |> RE.andMap (Ok query.surfaceMass) - |> RE.andMap (Ok query.fabricProcess) |> RE.andMap (Ok query.disabledSteps) - |> RE.andMap (Ok query.fading) |> RE.andMap (Ok query.dyeingMedium) - |> RE.andMap (Ok query.printing) - |> RE.andMap (Ok query.business) + |> RE.andMap (Ok query.fabricProcess) + |> RE.andMap (Ok query.fading) + |> RE.andMap (Ok query.makingComplexity) + |> RE.andMap (Ok query.makingDeadStock) + |> RE.andMap (Ok query.makingWaste) + |> RE.andMap (Ok query.mass) + |> RE.andMap materials_ |> RE.andMap (Ok query.numberOfReferences) + |> RE.andMap (Ok query.physicalDurability) |> RE.andMap (Ok query.price) + |> RE.andMap (Ok query.printing) + |> RE.andMap (textile.products |> Product.findById query.product) + |> RE.andMap (Ok query.surfaceMass) |> RE.andMap (Ok query.traceability) |> RE.andMap (Ok query.upcycled) - |> RE.andMap (Ok query.physicalDurability) + |> RE.andMap (Ok query.yarnSize) toQuery : Inputs -> Query toQuery inputs = - { mass = inputs.mass - , materials = toMaterialQuery inputs.materials - , product = inputs.product.id - , countrySpinning = toQueryCountryCode inputs.countrySpinning.code - , countryFabric = toQueryCountryCode inputs.countryFabric.code + { airTransportRatio = inputs.airTransportRatio + , business = inputs.business , countryDyeing = toQueryCountryCode inputs.countryDyeing.code + , countryFabric = toQueryCountryCode inputs.countryFabric.code , countryMaking = toQueryCountryCode inputs.countryMaking.code - , airTransportRatio = inputs.airTransportRatio - , makingWaste = inputs.makingWaste - , makingDeadStock = inputs.makingDeadStock - , makingComplexity = inputs.makingComplexity - , yarnSize = inputs.yarnSize - , surfaceMass = inputs.surfaceMass - , fabricProcess = inputs.fabricProcess + , countrySpinning = toQueryCountryCode inputs.countrySpinning.code , disabledSteps = inputs.disabledSteps - , fading = inputs.fading , dyeingMedium = inputs.dyeingMedium - , printing = inputs.printing - , business = inputs.business + , fabricProcess = inputs.fabricProcess + , fading = inputs.fading + , makingComplexity = inputs.makingComplexity + , makingDeadStock = inputs.makingDeadStock + , makingWaste = inputs.makingWaste + , mass = inputs.mass + , materials = toMaterialQuery inputs.materials , numberOfReferences = inputs.numberOfReferences + , physicalDurability = inputs.physicalDurability , price = inputs.price + , printing = inputs.printing + , product = inputs.product.id + , surfaceMass = inputs.surfaceMass , traceability = inputs.traceability , upcycled = inputs.upcycled - , physicalDurability = inputs.physicalDurability + , yarnSize = inputs.yarnSize } @@ -330,7 +330,7 @@ materialsToString materials = materials |> List.filter (\{ share } -> Split.toFloat share > 0) |> List.map - (\{ material, share, country } -> + (\{ country, material, share } -> let countryName = country @@ -347,7 +347,7 @@ materialsToString materials = makingOptionsToString : Inputs -> String -makingOptionsToString { makingWaste, makingDeadStock, makingComplexity, airTransportRatio, fading } = +makingOptionsToString { airTransportRatio, fading, makingComplexity, makingDeadStock, makingWaste } = [ makingWaste |> Maybe.map (Split.toPercentString 0 >> (\s -> s ++ "\u{202F}% de perte")) , makingDeadStock @@ -459,7 +459,7 @@ getOutOfEuropeEOLComplement { mass, materials } = computeMaterialTransport : Distances -> Country.Code -> MaterialInput -> Transport -computeMaterialTransport distances nextCountryCode { material, country, share } = +computeMaterialTransport distances nextCountryCode { country, material, share } = if share /= Split.zero then let emptyImpacts = @@ -485,37 +485,37 @@ isFabricOfType fabric { fabricProcess, product } = encode : Inputs -> Encode.Value encode inputs = Encode.object - [ ( "mass", Encode.float (Mass.inKilograms inputs.mass) ) - , ( "materials", Encode.list encodeMaterialInput inputs.materials ) - , ( "product", Product.encode inputs.product ) - , ( "countryFabric", Country.encode inputs.countryFabric ) + [ ( "airTransportRatio", inputs.airTransportRatio |> Maybe.map Split.encodeFloat |> Maybe.withDefault Encode.null ) + , ( "business", inputs.business |> Maybe.map Economics.encodeBusiness |> Maybe.withDefault Encode.null ) , ( "countryDyeing", Country.encode inputs.countryDyeing ) + , ( "countryFabric", Country.encode inputs.countryFabric ) , ( "countryMaking", Country.encode inputs.countryMaking ) - , ( "airTransportRatio", inputs.airTransportRatio |> Maybe.map Split.encodeFloat |> Maybe.withDefault Encode.null ) - , ( "makingWaste", inputs.makingWaste |> Maybe.map Split.encodeFloat |> Maybe.withDefault Encode.null ) - , ( "makingDeadStock", inputs.makingDeadStock |> Maybe.map Split.encodeFloat |> Maybe.withDefault Encode.null ) - , ( "makingComplexity", inputs.makingComplexity |> Maybe.map (MakingComplexity.toString >> Encode.string) |> Maybe.withDefault Encode.null ) - , ( "yarnSize", inputs.yarnSize |> Maybe.map Unit.encodeYarnSize |> Maybe.withDefault Encode.null ) - , ( "surfaceMass", inputs.surfaceMass |> Maybe.map Unit.encodeSurfaceMass |> Maybe.withDefault Encode.null ) - , ( "fabricProcess", inputs.fabricProcess |> Maybe.map Fabric.encode |> Maybe.withDefault Encode.null ) , ( "disabledSteps", Encode.list Label.encode inputs.disabledSteps ) - , ( "fading", inputs.fading |> Maybe.map Encode.bool |> Maybe.withDefault Encode.null ) , ( "dyeingMedium", inputs.dyeingMedium |> Maybe.map DyeingMedium.encode |> Maybe.withDefault Encode.null ) - , ( "printing", inputs.printing |> Maybe.map Printing.encode |> Maybe.withDefault Encode.null ) - , ( "business", inputs.business |> Maybe.map Economics.encodeBusiness |> Maybe.withDefault Encode.null ) + , ( "fabricProcess", inputs.fabricProcess |> Maybe.map Fabric.encode |> Maybe.withDefault Encode.null ) + , ( "fading", inputs.fading |> Maybe.map Encode.bool |> Maybe.withDefault Encode.null ) + , ( "makingComplexity", inputs.makingComplexity |> Maybe.map (MakingComplexity.toString >> Encode.string) |> Maybe.withDefault Encode.null ) + , ( "makingDeadStock", inputs.makingDeadStock |> Maybe.map Split.encodeFloat |> Maybe.withDefault Encode.null ) + , ( "makingWaste", inputs.makingWaste |> Maybe.map Split.encodeFloat |> Maybe.withDefault Encode.null ) + , ( "mass", Encode.float (Mass.inKilograms inputs.mass) ) + , ( "materials", Encode.list encodeMaterialInput inputs.materials ) , ( "numberOfReferences", inputs.numberOfReferences |> Maybe.map Encode.int |> Maybe.withDefault Encode.null ) , ( "price", inputs.price |> Maybe.map Economics.encodePrice |> Maybe.withDefault Encode.null ) + , ( "printing", inputs.printing |> Maybe.map Printing.encode |> Maybe.withDefault Encode.null ) + , ( "product", Product.encode inputs.product ) + , ( "surfaceMass", inputs.surfaceMass |> Maybe.map Unit.encodeSurfaceMass |> Maybe.withDefault Encode.null ) , ( "traceability", inputs.traceability |> Maybe.map Encode.bool |> Maybe.withDefault Encode.null ) , ( "upcycled", Encode.bool inputs.upcycled ) + , ( "yarnSize", inputs.yarnSize |> Maybe.map Unit.encodeYarnSize |> Maybe.withDefault Encode.null ) ] encodeMaterialInput : MaterialInput -> Encode.Value encodeMaterialInput v = - [ ( "material", Material.encode v.material |> Just ) + [ ( "country", v.country |> Maybe.map (.code >> Country.encodeCode) ) + , ( "material", Material.encode v.material |> Just ) , ( "share", Split.encodeFloat v.share |> Just ) , ( "spinning", v.spinning |> Maybe.map Spinning.encode ) - , ( "country", v.country |> Maybe.map (.code >> Country.encodeCode) ) ] |> List.filterMap (\( key, maybeVal ) -> maybeVal |> Maybe.map (\val -> ( key, val ))) |> Encode.object diff --git a/src/Data/Textile/LifeCycle.elm b/src/Data/Textile/LifeCycle.elm index adcf0d0c9..a2edabdf5 100644 --- a/src/Data/Textile/LifeCycle.elm +++ b/src/Data/Textile/LifeCycle.elm @@ -55,9 +55,7 @@ computeTotalTransportImpacts = Array.foldl (\{ transport } acc -> { acc - | road = acc.road |> Quantity.plus transport.road - , sea = acc.sea |> Quantity.plus transport.sea - , air = acc.air |> Quantity.plus transport.air + | air = acc.air |> Quantity.plus transport.air , impacts = acc.impacts |> Impact.mapImpacts @@ -67,6 +65,8 @@ computeTotalTransportImpacts = , Impact.getImpact trigram transport.impacts ] ) + , road = acc.road |> Quantity.plus transport.road + , sea = acc.sea |> Quantity.plus transport.sea } ) (Transport.default Impact.empty) @@ -130,10 +130,10 @@ init { textile } inputs = |> List.map2 (\( label, editable ) country -> Step.create - { label = label + { country = country , editable = editable - , country = country , enabled = not (List.member label inputs.disabledSteps) + , label = label } ) [ ( Label.Material, False ) diff --git a/src/Data/Textile/MakingComplexity.elm b/src/Data/Textile/MakingComplexity.elm index 3639693b3..d125378da 100644 --- a/src/Data/Textile/MakingComplexity.elm +++ b/src/Data/Textile/MakingComplexity.elm @@ -13,101 +13,101 @@ import Json.Decode.Extra as DE type MakingComplexity - = VeryHigh - | High - | Medium + = High | Low - | VeryLow + | Medium | NotApplicable + | VeryHigh + | VeryLow toDuration : MakingComplexity -> Duration toDuration makingComplexity = case makingComplexity of - VeryHigh -> - Duration.minutes 120 - High -> Duration.minutes 60 - Medium -> - Duration.minutes 30 - Low -> Duration.minutes 15 - VeryLow -> - Duration.minutes 5 + Medium -> + Duration.minutes 30 NotApplicable -> Duration.minutes 0 + VeryHigh -> + Duration.minutes 120 + + VeryLow -> + Duration.minutes 5 + toLabel : MakingComplexity -> String toLabel makingComplexity = case makingComplexity of - VeryHigh -> - "Très élevée" - High -> "Elevée" - Medium -> - "Moyenne" - Low -> "Faible" - VeryLow -> - "Très faible" + Medium -> + "Moyenne" NotApplicable -> "Non applicable" + VeryHigh -> + "Très élevée" + + VeryLow -> + "Très faible" + toString : MakingComplexity -> String toString makingComplexity = case makingComplexity of - VeryHigh -> - "very-high" - High -> "high" - Medium -> - "medium" - Low -> "low" - VeryLow -> - "very-low" + Medium -> + "medium" NotApplicable -> "non-applicable" + VeryHigh -> + "very-high" + + VeryLow -> + "very-low" + fromString : String -> Result String MakingComplexity fromString str = case str of - "very-high" -> - Ok VeryHigh - "high" -> Ok High - "medium" -> - Ok Medium - "low" -> Ok Low - "very-low" -> - Ok VeryLow + "medium" -> + Ok Medium "not-applicable" -> Ok NotApplicable + "very-high" -> + Ok VeryHigh + + "very-low" -> + Ok VeryLow + _ -> Err ("Type de complexité de fabrication inconnu\u{00A0}: " ++ str) diff --git a/src/Data/Textile/Material.elm b/src/Data/Textile/Material.elm index 094f26c01..8e5e5d169 100644 --- a/src/Data/Textile/Material.elm +++ b/src/Data/Textile/Material.elm @@ -20,16 +20,16 @@ import Json.Encode as Encode type alias Material = - { id : Id + { cffData : Maybe CFFData + , defaultCountry : Country.Code -- Default country for Material and Spinning steps + , geographicOrigin : String -- A textual information about the geographic origin of the material + , id : Id + , materialProcess : Process , name : String - , shortName : String , origin : Origin - , materialProcess : Process - , recycledProcess : Maybe Process , recycledFrom : Maybe Id - , geographicOrigin : String -- A textual information about the geographic origin of the material - , defaultCountry : Country.Code -- Default country for Material and Spinning steps - , cffData : Maybe CFFData + , recycledProcess : Maybe Process + , shortName : String } @@ -76,16 +76,16 @@ findById id = decode : List Process -> Decoder Material decode processes = Decode.succeed Material + |> JDP.required "cff" (Decode.maybe decodeCFFData) + |> JDP.required "defaultCountry" (Decode.string |> Decode.map Country.codeFromString) + |> JDP.required "geographicOrigin" Decode.string |> JDP.required "id" (Decode.map Id Decode.string) + |> JDP.required "materialProcessUuid" (Process.decodeFromUuid processes) |> JDP.required "name" Decode.string - |> JDP.required "shortName" Decode.string |> JDP.required "origin" Origin.decode - |> JDP.required "materialProcessUuid" (Process.decodeFromUuid processes) - |> JDP.required "recycledProcessUuid" (Decode.maybe (Process.decodeFromUuid processes)) |> JDP.required "recycledFrom" (Decode.maybe (Decode.map Id Decode.string)) - |> JDP.required "geographicOrigin" Decode.string - |> JDP.required "defaultCountry" (Decode.string |> Decode.map Country.codeFromString) - |> JDP.required "cff" (Decode.maybe decodeCFFData) + |> JDP.required "recycledProcessUuid" (Decode.maybe (Process.decodeFromUuid processes)) + |> JDP.required "shortName" Decode.string decodeCFFData : Decoder CFFData diff --git a/src/Data/Textile/Material/Spinning.elm b/src/Data/Textile/Material/Spinning.elm index f64e4498d..05709bd1c 100644 --- a/src/Data/Textile/Material/Spinning.elm +++ b/src/Data/Textile/Material/Spinning.elm @@ -22,8 +22,8 @@ import Mass exposing (Mass) type Spinning = Conventional - | Unconventional | Synthetic + | Unconventional type alias ProcessData = @@ -36,12 +36,12 @@ fromString string = "ConventionalSpinning" -> Ok Conventional - "UnconventionalSpinning" -> - Ok Unconventional - "SyntheticSpinning" -> Ok Synthetic + "UnconventionalSpinning" -> + Ok Unconventional + other -> Err <| "Le procédé de filature ou filage " ++ other ++ " n'est pas valide" @@ -52,12 +52,12 @@ toString spinning = Conventional -> "ConventionalSpinning" - Unconventional -> - "UnconventionalSpinning" - Synthetic -> "SyntheticSpinning" + Unconventional -> + "UnconventionalSpinning" + toLabel : Spinning -> String toLabel spinning = @@ -65,12 +65,12 @@ toLabel spinning = Conventional -> "Filature conventionnelle" - Unconventional -> - "Filature non conventionnelle" - Synthetic -> "Filage" + Unconventional -> + "Filature non conventionnelle" + decode : Decoder Spinning decode = @@ -83,13 +83,13 @@ encode = toString >> Encode.string -processesData : { conventional : ProcessData, unconventional : ProcessData, synthetic : ProcessData } +processesData : { conventional : ProcessData, synthetic : ProcessData, unconventional : ProcessData } processesData = -- See https://fabrique-numerique.gitbook.io/ecobalyse/textile/etapes-du-cycle-de-vie/etape-2-fabrication-du-fil-new-draft#consommation-delectricite -- and https://fabrique-numerique.gitbook.io/ecobalyse/textile/etapes-du-cycle-de-vie/etape-2-fabrication-du-fil-new-draft#taux-de-pertes { conventional = { normalization = 4, waste = Split.fromPercent 12 |> Result.withDefault Split.zero } - , unconventional = { normalization = 2, waste = Split.fromPercent 12 |> Result.withDefault Split.zero } , synthetic = { normalization = 1.5, waste = Split.fromPercent 3 |> Result.withDefault Split.zero } + , unconventional = { normalization = 2, waste = Split.fromPercent 12 |> Result.withDefault Split.zero } } @@ -123,12 +123,12 @@ normalization spinning = Conventional -> processesData.conventional.normalization - Unconventional -> - processesData.unconventional.normalization - Synthetic -> processesData.synthetic.normalization + Unconventional -> + processesData.unconventional.normalization + waste : Spinning -> Split waste spinning = @@ -136,12 +136,12 @@ waste spinning = Conventional -> processesData.conventional.waste - Unconventional -> - processesData.unconventional.waste - Synthetic -> processesData.synthetic.waste + Unconventional -> + processesData.unconventional.waste + getElec : Mass -> Unit.YarnSize -> Spinning -> Float getElec mass yarnSize spinning = diff --git a/src/Data/Textile/Process.elm b/src/Data/Textile/Process.elm index 5f63bd98e..5fdb6c592 100644 --- a/src/Data/Textile/Process.elm +++ b/src/Data/Textile/Process.elm @@ -25,20 +25,20 @@ import Json.Encode.Extra as EncodeExtra type alias Process = - { name : String + { alias : Maybe Alias + , correctif : String , displayName : Maybe String + , elec : Energy -- MJ per kg of material to process + , elec_pppm : Float -- kWh/(pick,m) per kg of material to process + , heat : Energy -- MJ per kg of material to process + , impacts : Impacts , info : String - , unit : String + , name : String , source : String - , correctif : String , stepUsage : String + , unit : String , uuid : Uuid - , impacts : Impacts - , heat : Energy -- MJ per kg of material to process - , elec_pppm : Float -- kWh/(pick,m) per kg of material to process - , elec : Energy -- MJ per kg of material to process , waste : Unit.Ratio -- share of raw material wasted when initially processed - , alias : Maybe Alias } @@ -93,20 +93,20 @@ decodeFromUuid processes = decode : Decoder Impact.Impacts -> Decoder Process decode impactsDecoder = Decode.succeed Process - |> Pipe.required "name" Decode.string + |> Pipe.required "alias" (Decode.maybe decodeAlias) + |> Pipe.required "correctif" Decode.string |> Pipe.optional "displayName" (Decode.maybe Decode.string) Nothing + |> Pipe.required "elec_MJ" (Decode.map Energy.megajoules Decode.float) + |> Pipe.required "elec_pppm" Decode.float + |> Pipe.required "heat_MJ" (Decode.map Energy.megajoules Decode.float) + |> Pipe.required "impacts" impactsDecoder |> Pipe.required "info" Decode.string - |> Pipe.required "unit" Decode.string + |> Pipe.required "name" Decode.string |> Pipe.required "source" Decode.string - |> Pipe.required "correctif" Decode.string |> Pipe.required "step_usage" Decode.string + |> Pipe.required "unit" Decode.string |> Pipe.required "uuid" decodeUuid - |> Pipe.required "impacts" impactsDecoder - |> Pipe.required "heat_MJ" (Decode.map Energy.megajoules Decode.float) - |> Pipe.required "elec_pppm" Decode.float - |> Pipe.required "elec_MJ" (Decode.map Energy.megajoules Decode.float) |> Pipe.required "waste" (Unit.decodeRatio { percentage = False }) - |> Pipe.required "alias" (Decode.maybe decodeAlias) getDisplayName : Process -> String diff --git a/src/Data/Textile/Product.elm b/src/Data/Textile/Product.elm index 891b02907..d420463cd 100644 --- a/src/Data/Textile/Product.elm +++ b/src/Data/Textile/Product.elm @@ -31,20 +31,20 @@ type alias DyeingOptions = type alias MakingOptions = - { pcrWaste : Split -- PCR product waste ratio - , complexity : MakingComplexity -- How complex is this making + { complexity : MakingComplexity -- How complex is this making + , pcrWaste : Split -- PCR product waste ratio } type alias UseOptions = - { ironingElec : Energy -- Quantitié d'éléctricité mobilisée pour repasser une pièce - , nonIroningProcess : Process -- Procédé composite d'utilisation hors-repassage - , wearsPerCycle : Int -- Nombre de jours porté par cycle d'entretien + { daysOfWear : Duration -- Nombre de jour d'utilisation du vêtement (not used in computations) , defaultNbCycles : Int -- Nombre par défaut de cycles d'entretien (not used in computations) + , ironingElec : Energy -- Quantitié d'éléctricité mobilisée pour repasser une pièce + , nonIroningProcess : Process -- Procédé composite d'utilisation hors-repassage , ratioDryer : Split -- Ratio de séchage électrique (not used in computations) , ratioIroning : Split -- Ratio de repassage (not used in computations) , timeIroning : Duration -- Temps de repassage (not used in computations) - , daysOfWear : Duration -- Nombre de jour d'utilisation du vêtement (not used in computations) + , wearsPerCycle : Int -- Nombre de jours porté par cycle d'entretien } @@ -54,16 +54,16 @@ type alias EndOfLifeOptions = type alias Product = - { id : Id - , name : String - , surfaceMass : Unit.SurfaceMass - , yarnSize : Unit.YarnSize - , fabric : Fabric + { dyeing : DyeingOptions , economics : Economics - , dyeing : DyeingOptions + , endOfLife : EndOfLifeOptions + , fabric : Fabric + , id : Id , making : MakingOptions + , name : String + , surfaceMass : Unit.SurfaceMass , use : UseOptions - , endOfLife : EndOfLifeOptions + , yarnSize : Unit.YarnSize } @@ -99,21 +99,21 @@ decodeDyeingOptions = decodeMakingOptions : Decoder MakingOptions decodeMakingOptions = Decode.succeed MakingOptions - |> Pipe.required "pcrWaste" Split.decodeFloat |> Pipe.required "complexity" MakingComplexity.decode + |> Pipe.required "pcrWaste" Split.decodeFloat decodeUseOptions : List Process -> Decoder UseOptions decodeUseOptions processes = Decode.succeed UseOptions + |> Pipe.required "daysOfWear" (Decode.map Duration.days Decode.float) + |> Pipe.required "defaultNbCycles" Decode.int |> Pipe.required "ironingElecInMJ" (Decode.map Energy.megajoules Decode.float) |> Pipe.required "nonIroningProcessUuid" (Process.decodeFromUuid processes) - |> Pipe.required "wearsPerCycle" Decode.int - |> Pipe.required "defaultNbCycles" Decode.int |> Pipe.required "ratioDryer" Split.decodeFloat |> Pipe.required "ratioIroning" Split.decodeFloat |> Pipe.required "timeIroning" (Decode.map Duration.hours Decode.float) - |> Pipe.required "daysOfWear" (Decode.map Duration.days Decode.float) + |> Pipe.required "wearsPerCycle" Decode.int decodeEndOfLifeOptions : Decoder EndOfLifeOptions @@ -125,16 +125,16 @@ decodeEndOfLifeOptions = decode : List Process -> Decoder Product decode processes = Decode.succeed Product + |> Pipe.required "dyeing" decodeDyeingOptions + |> Pipe.required "economics" Economics.decode + |> Pipe.required "endOfLife" decodeEndOfLifeOptions + |> Pipe.required "fabric" Fabric.decode |> Pipe.required "id" (Decode.map Id Decode.string) + |> Pipe.required "making" decodeMakingOptions |> Pipe.required "name" Decode.string |> Pipe.required "surfaceMass" Unit.decodeSurfaceMass - |> Pipe.required "yarnSize" Unit.decodeYarnSize - |> Pipe.required "fabric" Fabric.decode - |> Pipe.required "economics" Economics.decode - |> Pipe.required "dyeing" decodeDyeingOptions - |> Pipe.required "making" decodeMakingOptions |> Pipe.required "use" (decodeUseOptions processes) - |> Pipe.required "endOfLife" decodeEndOfLifeOptions + |> Pipe.required "yarnSize" Unit.decodeYarnSize decodeList : List Process -> Decoder (List Product) diff --git a/src/Data/Textile/Query.elm b/src/Data/Textile/Query.elm index 8e2776ceb..6ce1d82c5 100644 --- a/src/Data/Textile/Query.elm +++ b/src/Data/Textile/Query.elm @@ -84,10 +84,10 @@ addMaterial : Material -> Query -> Query addMaterial material query = let materialQuery = - { id = material.id + { country = Nothing + , id = material.id , share = Split.zero , spinning = Nothing - , country = Nothing } in { query @@ -348,14 +348,14 @@ updateStepCountry label code query = Label.Making -> { query - | countryMaking = maybeCode - , airTransportRatio = + | airTransportRatio = if query.countryMaking /= maybeCode then -- reset custom value as we just switched country Nothing else query.airTransportRatio + , countryMaking = maybeCode } Label.Spinning -> @@ -409,10 +409,10 @@ default = , makingWaste = Nothing , mass = Mass.kilograms 0.17 , materials = - [ { id = Material.Id "ei-coton" + [ { country = Nothing + , id = Material.Id "ei-coton" , share = Split.full , spinning = Nothing - , country = Nothing } ] , numberOfReferences = Nothing diff --git a/src/Data/Textile/Simulator.elm b/src/Data/Textile/Simulator.elm index b6089ce65..9b435d95a 100644 --- a/src/Data/Textile/Simulator.elm +++ b/src/Data/Textile/Simulator.elm @@ -36,13 +36,13 @@ import Static.Db exposing (Db) type alias Simulator = - { inputs : Inputs - , lifeCycle : LifeCycle - , impacts : Impacts - , complementsImpacts : Impact.ComplementsImpacts + { complementsImpacts : Impact.ComplementsImpacts + , daysOfWear : Duration , durability : Unit.HolisticDurability + , impacts : Impacts + , inputs : Inputs + , lifeCycle : LifeCycle , transport : Transport - , daysOfWear : Duration , useNbCycles : Int } @@ -50,13 +50,13 @@ type alias Simulator = encode : Simulator -> Encode.Value encode v = Encode.object - [ ( "inputs", Inputs.encode v.inputs ) - , ( "lifeCycle", LifeCycle.encode v.lifeCycle ) + [ ( "complementsImpacts", Impact.encodeComplementsImpacts v.complementsImpacts ) + , ( "daysOfWear", v.daysOfWear |> Duration.inDays |> round |> Encode.int ) + , ( "durability", v.durability |> Unit.floatDurabilityFromHolistic |> Encode.float ) , ( "impacts", Impact.encode v.impacts ) - , ( "complementsImpacts", Impact.encodeComplementsImpacts v.complementsImpacts ) + , ( "inputs", Inputs.encode v.inputs ) + , ( "lifeCycle", LifeCycle.encode v.lifeCycle ) , ( "transport", Transport.encode v.transport ) - , ( "durability", v.durability |> Unit.floatDurabilityFromHolistic |> Encode.float ) - , ( "daysOfWear", v.daysOfWear |> Duration.inDays |> round |> Encode.int ) , ( "useNbCycles", Encode.int v.useNbCycles ) ] @@ -70,16 +70,16 @@ init db = inputs |> LifeCycle.init db |> (\lifeCycle -> - { inputs = inputs - , lifeCycle = lifeCycle - , impacts = Impact.empty - , complementsImpacts = Impact.noComplementsImpacts + { complementsImpacts = Impact.noComplementsImpacts + , daysOfWear = inputs.product.use.daysOfWear , durability = { nonPhysical = Unit.standardDurability Unit.NonPhysicalDurability , physical = inputs.physicalDurability |> Maybe.withDefault (Unit.maxDurability Unit.PhysicalDurability) } + , impacts = Impact.empty + , inputs = inputs + , lifeCycle = lifeCycle , transport = Transport.default Impact.empty - , daysOfWear = inputs.product.use.daysOfWear , useNbCycles = Product.customDaysOfWear product.use } ) @@ -196,12 +196,15 @@ computeDurability ({ inputs } as simulator) = } newDurability = - { physical = simulator.durability.physical, nonPhysical = nonPhysicalDurability } + { nonPhysical = nonPhysicalDurability + , physical = simulator.durability.physical + } in { simulator - | durability = newDurability - , daysOfWear = - simulator.daysOfWear |> Quantity.multiplyBy (Unit.floatDurabilityFromHolistic newDurability) + | daysOfWear = + simulator.daysOfWear + |> Quantity.multiplyBy (Unit.floatDurabilityFromHolistic newDurability) + , durability = newDurability , useNbCycles = round (toFloat simulator.useNbCycles * Unit.floatDurabilityFromHolistic newDurability) } @@ -213,20 +216,20 @@ computeEndOfLifeImpacts { textile } simulator = |> updateLifeCycleStep Label.EndOfLife (\({ country } as step) -> let - { kwh, heat, impacts } = + { heat, impacts, kwh } = step.outputMass |> Formula.endOfLifeImpacts step.impacts - { volume = simulator.inputs.product.endOfLife.volume - , passengerCar = textile.wellKnown.passengerCar + { countryElecProcess = country.electricityProcess , endOfLife = textile.wellKnown.endOfLife - , countryElecProcess = country.electricityProcess , heatProcess = country.heatProcess + , passengerCar = textile.wellKnown.passengerCar + , volume = simulator.inputs.product.endOfLife.volume } in { step - | impacts = impacts + | heat = heat + , impacts = impacts , kwh = kwh - , heat = heat } ) @@ -237,13 +240,13 @@ computeUseImpacts ({ inputs, useNbCycles } as simulator) = |> updateLifeCycleStep Label.Use (\({ country } as step) -> let - { kwh, impacts } = + { impacts, kwh } = step.outputMass |> Formula.useImpacts step.impacts - { useNbCycles = useNbCycles + { countryElecProcess = country.electricityProcess , ironingElec = inputs.product.use.ironingElec , nonIroningProcess = inputs.product.use.nonIroningProcess - , countryElecProcess = country.electricityProcess + , useNbCycles = useNbCycles } in { step | impacts = impacts, kwh = kwh } @@ -256,12 +259,11 @@ computeMakingImpacts { textile } ({ inputs } as simulator) = |> updateLifeCycleStep Label.Making (\({ country } as step) -> let - { kwh, heat, impacts } = + { heat, impacts, kwh } = step.outputMass |> Formula.makingImpacts step.impacts - { makingComplexity = - inputs.fabricProcess - |> Fabric.getMakingComplexity inputs.product.making.complexity inputs.makingComplexity + { countryElecProcess = country.electricityProcess + , countryHeatProcess = country.heatProcess , fadingProcess = -- Note: in the future, we may have distinct fading processes per countries if inputs.fading == Just True then @@ -269,11 +271,12 @@ computeMakingImpacts { textile } ({ inputs } as simulator) = else Nothing - , countryElecProcess = country.electricityProcess - , countryHeatProcess = country.heatProcess + , makingComplexity = + inputs.fabricProcess + |> Fabric.getMakingComplexity inputs.product.making.complexity inputs.makingComplexity } in - { step | impacts = impacts, kwh = kwh, heat = heat } + { step | heat = heat, impacts = impacts, kwh = kwh } ) @@ -299,20 +302,20 @@ computeDyeingImpacts { textile } ({ inputs } as simulator) = |> List.map (\{ material, share } -> Formula.materialDyeingToxicityImpacts step.impacts - { dyeingToxicityProcess = + { aquaticPollutionScenario = step.country.aquaticPollutionScenario + , dyeingToxicityProcess = if Origin.isSynthetic material.origin then textile.wellKnown.dyeingSynthetic else textile.wellKnown.dyeingCellulosic - , aquaticPollutionScenario = step.country.aquaticPollutionScenario } step.outputMass share ) |> Impact.sumImpacts - { heat, kwh, impacts } = + { heat, impacts, kwh } = step.outputMass |> Formula.dyeingImpacts step.impacts dyeingProcess @@ -321,8 +324,8 @@ computeDyeingImpacts { textile } ({ inputs } as simulator) = in { step | heat = step.heat |> Quantity.plus heat - , kwh = step.kwh |> Quantity.plus kwh , impacts = Impact.sumImpacts [ step.impacts, impacts, dyeingToxicity ] + , kwh = step.kwh |> Quantity.plus kwh } ) @@ -338,29 +341,29 @@ computePrintingImpacts { textile } ({ inputs } as simulator) = { printingProcess, printingToxicityProcess } = WellKnown.getPrintingProcess kind textile.wellKnown - { heat, kwh, impacts } = + { heat, impacts, kwh } = step.outputMass |> Formula.printingImpacts step.impacts - { printingProcess = printingProcess + { elecProcess = country.electricityProcess , heatProcess = WellKnown.getEnnoblingHeatProcess textile.wellKnown country - , elecProcess = country.electricityProcess - , surfaceMass = Maybe.withDefault inputs.product.surfaceMass inputs.surfaceMass + , printingProcess = printingProcess , ratio = ratio + , surfaceMass = Maybe.withDefault inputs.product.surfaceMass inputs.surfaceMass } printingToxicity = step.outputMass |> Formula.materialPrintingToxicityImpacts step.impacts - { printingToxicityProcess = printingToxicityProcess - , aquaticPollutionScenario = step.country.aquaticPollutionScenario + { aquaticPollutionScenario = step.country.aquaticPollutionScenario + , printingToxicityProcess = printingToxicityProcess } ratio in { step | heat = step.heat |> Quantity.plus heat - , kwh = step.kwh |> Quantity.plus kwh , impacts = Impact.sumImpacts [ step.impacts, impacts, printingToxicity ] + , kwh = step.kwh |> Quantity.plus kwh } Nothing -> @@ -374,18 +377,18 @@ computeFinishingImpacts { textile } simulator = |> updateLifeCycleStep Label.Ennobling (\({ country } as step) -> let - { heat, kwh, impacts } = + { heat, impacts, kwh } = step.outputMass |> Formula.finishingImpacts step.impacts - { finishingProcess = textile.wellKnown.finishing + { elecProcess = country.electricityProcess + , finishingProcess = textile.wellKnown.finishing , heatProcess = WellKnown.getEnnoblingHeatProcess textile.wellKnown country - , elecProcess = country.electricityProcess } in { step | heat = step.heat |> Quantity.plus heat - , kwh = step.kwh |> Quantity.plus kwh , impacts = Impact.sumImpacts [ step.impacts, impacts ] + , kwh = step.kwh |> Quantity.plus kwh } ) @@ -399,8 +402,8 @@ computeBleachingImpacts { textile } simulator = impacts = step.outputMass |> Formula.bleachingImpacts step.impacts - { bleachingProcess = textile.wellKnown.bleaching - , aquaticPollutionScenario = step.country.aquaticPollutionScenario + { aquaticPollutionScenario = step.country.aquaticPollutionScenario + , bleachingProcess = textile.wellKnown.bleaching } in { step @@ -412,20 +415,20 @@ computeBleachingImpacts { textile } simulator = stepMaterialImpacts : Db -> Material -> Step -> Impacts stepMaterialImpacts { textile } material step = case Material.getRecyclingData material textile.materials of - -- Non-recycled Material - Nothing -> - step.outputMass - |> Formula.pureMaterialImpacts step.impacts material.materialProcess - -- Recycled material: apply CFF Just ( sourceMaterial, cffData ) -> step.outputMass |> Formula.recycledMaterialImpacts step.impacts - { recycledProcess = material.materialProcess + { cffData = cffData , nonRecycledProcess = sourceMaterial.materialProcess - , cffData = cffData + , recycledProcess = material.materialProcess } + -- Non-recycled Material + Nothing -> + step.outputMass + |> Formula.pureMaterialImpacts step.impacts material.materialProcess + computeMaterialImpacts : Db -> Simulator -> Simulator computeMaterialImpacts db ({ inputs } as simulator) = @@ -446,7 +449,7 @@ computeMaterialImpacts db ({ inputs } as simulator) = ) -stepSpinningImpacts : Material -> Maybe Spinning -> Product -> Step -> { impacts : Impacts, kwh : Energy } +stepSpinningImpacts : Material -> Maybe Spinning -> Product -> Step -> { heat : Energy, impacts : Impacts, kwh : Energy } stepSpinningImpacts material maybeSpinning product step = let yarnSize = @@ -463,8 +466,8 @@ stepSpinningImpacts material maybeSpinning product step = |> Energy.kilowattHours in Formula.spinningImpacts step.impacts - { spinningKwh = kwh - , countryElecProcess = step.country.electricityProcess + { countryElecProcess = step.country.electricityProcess + , spinningKwh = kwh } @@ -474,26 +477,26 @@ computeSpinningImpacts ({ inputs } as simulator) = |> updateLifeCycleStep Label.Spinning (\step -> { step - | kwh = + | impacts = inputs.materials |> List.map (\{ material, share, spinning } -> step |> stepSpinningImpacts material spinning inputs.product - |> .kwh - |> Quantity.multiplyBy (Split.toFloat share) + |> .impacts + |> Impact.mapImpacts (\_ -> Quantity.multiplyBy (Split.toFloat share)) ) - |> List.foldl Quantity.plus Quantity.zero - , impacts = + |> Impact.sumImpacts + , kwh = inputs.materials |> List.map (\{ material, share, spinning } -> step |> stepSpinningImpacts material spinning inputs.product - |> .impacts - |> Impact.mapImpacts (\_ -> Quantity.multiplyBy (Split.toFloat share)) + |> .kwh + |> Quantity.multiplyBy (Split.toFloat share) ) - |> Impact.sumImpacts + |> List.foldl Quantity.plus Quantity.zero } ) @@ -514,17 +517,17 @@ computeFabricImpacts { textile } ({ inputs, lifeCycle } as simulator) = |> Maybe.withDefault inputs.product.fabric |> Fabric.getProcess textile.wellKnown - { kwh, threadDensity, picking, impacts } = + { impacts, kwh, picking, threadDensity } = if inputs.fabricProcess |> Maybe.withDefault inputs.product.fabric |> Fabric.isKnitted then - Formula.knittingImpacts step.impacts - { elec = process.elec - , countryElecProcess = country.electricityProcess - } - step.outputMass + step.outputMass + |> Formula.knittingImpacts step.impacts + { countryElecProcess = country.electricityProcess + , elec = process.elec + } else let @@ -540,14 +543,19 @@ computeFabricImpacts { textile } ({ inputs, lifeCycle } as simulator) = , yarnSize = inputs.yarnSize |> Maybe.withDefault inputs.product.yarnSize } in - { step | impacts = impacts, threadDensity = threadDensity, kwh = kwh, picking = picking } + { step + | impacts = impacts + , kwh = kwh + , picking = picking + , threadDensity = threadDensity + } ) computeMakingStepWaste : Simulator -> Simulator computeMakingStepWaste ({ inputs } as simulator) = let - { product, makingWaste, fabricProcess } = + { fabricProcess, makingWaste, product } = inputs { mass, waste } = @@ -565,7 +573,7 @@ computeMakingStepWaste ({ inputs } as simulator) = computeMakingStepDeadStock : Simulator -> Simulator computeMakingStepDeadStock ({ inputs, lifeCycle } as simulator) = let - { mass, deadstock } = + { deadstock, mass } = lifeCycle |> LifeCycle.getStepProp Label.Making .inputMass Quantity.zero |> Formula.makingDeadStock (Maybe.withDefault Env.defaultDeadStock inputs.makingDeadStock) @@ -651,8 +659,8 @@ computeSpinningStepWaste ({ inputs, lifeCycle } as simulator) = |> Split.divideBy (Mass.inKilograms outputMaterialMass) |> Mass.kilograms in - { waste = Quantity.difference inputMaterialMass outputMaterialMass - , mass = inputMaterialMass + { mass = inputMaterialMass + , waste = Quantity.difference inputMaterialMass outputMaterialMass } ) |> List.foldl @@ -752,10 +760,16 @@ toStepsImpacts trigram simulator = else identity in - { materials = + { distribution = Nothing + , endOfLife = + getImpacts Label.EndOfLife + |> getImpact + |> applyComplement simulator.complementsImpacts.outOfEuropeEOL + , materials = getImpacts Label.Material |> getImpact |> applyComplement simulator.complementsImpacts.microfibers + , packaging = Nothing , transform = [ getImpacts Label.Spinning , getImpacts Label.Fabric @@ -764,13 +778,7 @@ toStepsImpacts trigram simulator = ] |> Impact.sumImpacts |> getImpact - , packaging = Nothing , transports = getImpact simulator.transport.impacts - , distribution = Nothing , usage = getImpacts Label.Use |> getImpact - , endOfLife = - getImpacts Label.EndOfLife - |> getImpact - |> applyComplement simulator.complementsImpacts.outOfEuropeEOL } |> Impact.divideStepsImpactsBy (Unit.floatDurabilityFromHolistic simulator.durability) diff --git a/src/Data/Textile/Step.elm b/src/Data/Textile/Step.elm index df0e64f14..cc58a0bb5 100644 --- a/src/Data/Textile/Step.elm +++ b/src/Data/Textile/Step.elm @@ -45,106 +45,106 @@ import Views.Format as Format type alias Step = - { label : Label - , enabled : Bool + { airTransportRatio : Split + , complementsImpacts : Impact.ComplementsImpacts , country : Country - , editable : Bool - , inputMass : Mass - , outputMass : Mass - , waste : Mass , deadstock : Mass - , transport : Transport - , impacts : Impacts - , complementsImpacts : Impact.ComplementsImpacts + , durability : Unit.NonPhysicalDurability + , dyeingMedium : Maybe DyeingMedium + , editable : Bool + , enabled : Bool , heat : Energy + , impacts : Impacts + , inputMass : Mass , kwh : Energy - , processInfo : ProcessInfo - , airTransportRatio : Split -- FIXME: why not Maybe? - , durability : Unit.NonPhysicalDurability + , label : Label , makingComplexity : Maybe MakingComplexity - , makingWaste : Maybe Split , makingDeadStock : Maybe Split + , makingWaste : Maybe Split + , outputMass : Mass , picking : Maybe Unit.PickPerMeter + , printing : Maybe Printing + , processInfo : ProcessInfo + , surfaceMass : Maybe Unit.SurfaceMass , threadDensity : Maybe Unit.ThreadDensity + , transport : Transport + , waste : Mass , yarnSize : Maybe Unit.YarnSize - , surfaceMass : Maybe Unit.SurfaceMass - , dyeingMedium : Maybe DyeingMedium - , printing : Maybe Printing } type alias ProcessInfo = - { countryElec : Maybe String - , countryHeat : Maybe String + { airTransport : Maybe String , airTransportRatio : Maybe String - , airTransport : Maybe String - , seaTransport : Maybe String - , roadTransport : Maybe String - , useIroning : Maybe String - , useNonIroning : Maybe String - , passengerCar : Maybe String + , countryElec : Maybe String + , countryHeat : Maybe String + , distribution : Maybe String + , dyeing : Maybe String , endOfLife : Maybe String , fabric : Maybe String - , dyeing : Maybe String - , making : Maybe String - , distribution : Maybe String , fading : Maybe String + , making : Maybe String + , passengerCar : Maybe String , printing : Maybe String + , roadTransport : Maybe String + , seaTransport : Maybe String + , useIroning : Maybe String + , useNonIroning : Maybe String } -create : { label : Label, editable : Bool, country : Country, enabled : Bool } -> Step -create { label, editable, country, enabled } = +create : { country : Country, editable : Bool, enabled : Bool, label : Label } -> Step +create { country, editable, enabled, label } = let defaultImpacts = Impact.empty in - { label = label - , enabled = enabled + { airTransportRatio = Split.zero -- Note: this depends on next step country, so we can't set an accurate default value initially + , complementsImpacts = Impact.noComplementsImpacts , country = country - , editable = editable - , inputMass = Quantity.zero - , outputMass = Quantity.zero - , waste = Quantity.zero , deadstock = Quantity.zero - , transport = Transport.default defaultImpacts - , impacts = defaultImpacts - , complementsImpacts = Impact.noComplementsImpacts + , durability = Unit.standardDurability Unit.NonPhysicalDurability + , dyeingMedium = Nothing + , editable = editable + , enabled = enabled , heat = Quantity.zero + , impacts = defaultImpacts + , inputMass = Quantity.zero , kwh = Quantity.zero - , processInfo = defaultProcessInfo - , airTransportRatio = Split.zero -- Note: this depends on next step country, so we can't set an accurate default value initially - , durability = Unit.standardDurability Unit.NonPhysicalDurability + , label = label , makingComplexity = Nothing - , makingWaste = Nothing , makingDeadStock = Nothing + , makingWaste = Nothing + , outputMass = Quantity.zero , picking = Nothing + , printing = Nothing + , processInfo = defaultProcessInfo + , surfaceMass = Nothing , threadDensity = Nothing + , transport = Transport.default defaultImpacts + , waste = Quantity.zero , yarnSize = Nothing - , surfaceMass = Nothing - , dyeingMedium = Nothing - , printing = Nothing } defaultProcessInfo : ProcessInfo defaultProcessInfo = - { countryElec = Nothing - , countryHeat = Nothing + { airTransport = Nothing , airTransportRatio = Nothing - , airTransport = Nothing - , seaTransport = Nothing - , roadTransport = Nothing - , useIroning = Nothing - , useNonIroning = Nothing - , passengerCar = Nothing + , countryElec = Nothing + , countryHeat = Nothing + , distribution = Nothing + , dyeing = Nothing , endOfLife = Nothing , fabric = Nothing - , dyeing = Nothing - , making = Nothing - , distribution = Nothing , fading = Nothing + , making = Nothing + , passengerCar = Nothing , printing = Nothing + , roadTransport = Nothing + , seaTransport = Nothing + , useIroning = Nothing + , useNonIroning = Nothing } @@ -187,21 +187,17 @@ computeTransports db inputs next ({ processInfo } as current) = { current | processInfo = { processInfo - | roadTransport = Just db.textile.wellKnown.roadTransport.name + | airTransport = Just db.textile.wellKnown.airTransport.name + , roadTransport = Just db.textile.wellKnown.roadTransport.name , seaTransport = Just db.textile.wellKnown.seaTransport.name - , airTransport = Just db.textile.wellKnown.airTransport.name } , transport = transport } computeTransportImpacts : Impacts -> WellKnown -> Process -> Mass -> Transport -> Transport -computeTransportImpacts impacts { seaTransport, airTransport } roadProcess mass { road, sea, air } = - { road = road - , roadCooled = Quantity.zero - , sea = sea - , seaCooled = Quantity.zero - , air = air +computeTransportImpacts impacts { airTransport, seaTransport } roadProcess mass { air, road, sea } = + { air = air , impacts = impacts |> Impact.mapImpacts @@ -215,6 +211,10 @@ computeTransportImpacts impacts { seaTransport, airTransport } roadProcess mass in Quantity.sum [ roadImpact, seaImpact, airImpact ] ) + , road = road + , roadCooled = Quantity.zero + , sea = sea + , seaCooled = Quantity.zero } @@ -227,6 +227,14 @@ computeTransportSummary step transport = ) in case step.label of + Label.Distribution -> + -- Product Distribution leverages no transports + noTransports + + Label.EndOfLife -> + -- End of life leverages no transports + noTransports + Label.Making -> -- Air transport only applies between the Making and the Distribution steps transport @@ -236,18 +244,10 @@ computeTransportSummary step transport = -- Also ensure we don't add unnecessary air transport |> Transport.add { defaultInland | air = Quantity.zero } - Label.Distribution -> - -- Product Distribution leverages no transports - noTransports - Label.Use -> -- Product Use leverages no transports noTransports - Label.EndOfLife -> - -- End of life leverages no transports - noTransports - _ -> -- All other steps don't use air transport, force a 0 split transport @@ -281,40 +281,28 @@ getTransportedMass inputs { label, outputMass } = updateFromInputs : Textile.Db -> Inputs -> Step -> Step updateFromInputs { wellKnown } inputs ({ label, country, complementsImpacts } as step) = let - { airTransportRatio, makingComplexity, makingWaste, makingDeadStock, yarnSize, surfaceMass, dyeingMedium, printing } = + { airTransportRatio, dyeingMedium, makingComplexity, makingDeadStock, makingWaste, printing, surfaceMass, yarnSize } = inputs in case label of - Label.Material -> + Label.Distribution -> { step - | complementsImpacts = - { complementsImpacts - -- Note: no other steps than the Material one generate microfibers pollution - | microfibers = Inputs.getTotalMicrofibersComplement inputs - } + | processInfo = + { defaultProcessInfo | distribution = Just wellKnown.distribution.name } } - Label.Spinning -> + Label.EndOfLife -> { step - | yarnSize = yarnSize - , processInfo = - { defaultProcessInfo - | countryElec = Just country.electricityProcess.name + | complementsImpacts = + { complementsImpacts + | outOfEuropeEOL = Inputs.getOutOfEuropeEOLComplement inputs } - } - - Label.Fabric -> - { step - | yarnSize = yarnSize - , surfaceMass = surfaceMass , processInfo = { defaultProcessInfo | countryElec = Just country.electricityProcess.name - , fabric = - inputs.product.fabric - |> Fabric.getProcess wellKnown - |> .name - |> Just + , countryHeat = Just country.heatProcess.name + , endOfLife = Just wellKnown.endOfLife.name + , passengerCar = Just "Transport en voiture vers point de collecte (1km)" } } @@ -324,8 +312,8 @@ updateFromInputs { wellKnown } inputs ({ label, country, complementsImpacts } as , printing = printing , processInfo = { defaultProcessInfo - | countryHeat = Just country.heatProcess.name - , countryElec = Just country.electricityProcess.name + | countryElec = Just country.electricityProcess.name + , countryHeat = Just country.heatProcess.name , dyeing = wellKnown |> WellKnown.getDyeingProcess @@ -343,28 +331,52 @@ updateFromInputs { wellKnown } inputs ({ label, country, complementsImpacts } as } } + Label.Fabric -> + { step + | processInfo = + { defaultProcessInfo + | countryElec = Just country.electricityProcess.name + , fabric = + inputs.product.fabric + |> Fabric.getProcess wellKnown + |> .name + |> Just + } + , surfaceMass = surfaceMass + , yarnSize = yarnSize + } + Label.Making -> { step | airTransportRatio = airTransportRatio |> Maybe.withDefault country.airTransportRatio - , makingWaste = makingWaste - , makingDeadStock = makingDeadStock , makingComplexity = makingComplexity + , makingDeadStock = makingDeadStock + , makingWaste = makingWaste , processInfo = { defaultProcessInfo - | countryElec = Just country.electricityProcess.name - , fading = Just wellKnown.fading.name - , airTransportRatio = + | airTransportRatio = country.airTransportRatio |> airTransportRatioToString |> Just + , countryElec = Just country.electricityProcess.name + , fading = Just wellKnown.fading.name } } - Label.Distribution -> + Label.Material -> { step - | processInfo = - { defaultProcessInfo | distribution = Just wellKnown.distribution.name } + | complementsImpacts = + { complementsImpacts + -- Note: no other steps than the Material one generate microfibers pollution + | microfibers = Inputs.getTotalMicrofibersComplement inputs + } + } + + Label.Spinning -> + { step + | processInfo = { defaultProcessInfo | countryElec = Just country.electricityProcess.name } + , yarnSize = yarnSize } Label.Use -> @@ -383,21 +395,6 @@ updateFromInputs { wellKnown } inputs ({ label, country, complementsImpacts } as } } - Label.EndOfLife -> - { step - | complementsImpacts = - { complementsImpacts - | outOfEuropeEOL = Inputs.getOutOfEuropeEOLComplement inputs - } - , processInfo = - { defaultProcessInfo - | passengerCar = Just "Transport en voiture vers point de collecte (1km)" - , countryElec = Just country.electricityProcess.name - , countryHeat = Just country.heatProcess.name - , endOfLife = Just wellKnown.endOfLife.name - } - } - initMass : Mass -> Step -> Step initMass mass step = @@ -410,9 +407,9 @@ initMass mass step = updateWasteAndMasses : Mass -> Mass -> Step -> Step updateWasteAndMasses waste mass step = { step - | waste = waste - , inputMass = mass + | inputMass = mass , outputMass = Quantity.difference mass waste + , waste = waste } @@ -426,7 +423,7 @@ updateDeadStock deadstock mass step = airTransportDisabled : Step -> Bool -airTransportDisabled { enabled, label, country } = +airTransportDisabled { country, enabled, label } = not enabled || -- Note: disallow air transport from France to France at the Making step (label == Label.Making && country.code == Country.codeFromString "FR") @@ -478,32 +475,28 @@ yarnSizeToDtexString yarnSize = encode : Step -> Encode.Value encode v = Encode.object - [ ( "label", Encode.string (Label.toString v.label) ) - , ( "enabled", Encode.bool v.enabled ) + [ ( "airTransportRatio", Split.encodeFloat v.airTransportRatio ) + , ( "complementsImpacts", Impact.encodeComplementsImpacts v.complementsImpacts ) , ( "country", Country.encode v.country ) - , ( "editable", Encode.bool v.editable ) - , ( "inputMass", Encode.float (Mass.inKilograms v.inputMass) ) - , ( "outputMass", Encode.float (Mass.inKilograms v.outputMass) ) - , ( "waste", Encode.float (Mass.inKilograms v.waste) ) , ( "deadstock", Encode.float (Mass.inKilograms v.deadstock) ) - , ( "transport", Transport.encode v.transport ) - , ( "impacts" - , v.impacts - |> Impact.applyComplements (Impact.getTotalComplementsImpacts v.complementsImpacts) - |> Impact.encode - ) - , ( "complementsImpacts", Impact.encodeComplementsImpacts v.complementsImpacts ) - , ( "heat_MJ", Encode.float (Energy.inMegajoules v.heat) ) - , ( "elec_kWh", Encode.float (Energy.inKilowattHours v.kwh) ) - , ( "processInfo", encodeProcessInfo v.processInfo ) - , ( "airTransportRatio", Split.encodeFloat v.airTransportRatio ) , ( "durability", Unit.encodeNonPhysicalDurability v.durability ) - , ( "makingWaste", v.makingWaste |> Maybe.map Split.encodeFloat |> Maybe.withDefault Encode.null ) + , ( "editable", Encode.bool v.editable ) + , ( "elec_kWh", Encode.float (Energy.inKilowattHours v.kwh) ) + , ( "enabled", Encode.bool v.enabled ) + , ( "heat_MJ", Encode.float (Energy.inMegajoules v.heat) ) + , ( "impacts", v.impacts |> Impact.applyComplements (Impact.getTotalComplementsImpacts v.complementsImpacts) |> Impact.encode ) + , ( "inputMass", Encode.float (Mass.inKilograms v.inputMass) ) + , ( "label", Encode.string (Label.toString v.label) ) , ( "makingDeadStock", v.makingDeadStock |> Maybe.map Split.encodeFloat |> Maybe.withDefault Encode.null ) + , ( "makingWaste", v.makingWaste |> Maybe.map Split.encodeFloat |> Maybe.withDefault Encode.null ) + , ( "outputMass", Encode.float (Mass.inKilograms v.outputMass) ) , ( "picking", v.picking |> Maybe.map Unit.encodePickPerMeter |> Maybe.withDefault Encode.null ) + , ( "processInfo", encodeProcessInfo v.processInfo ) + , ( "surfaceMass", v.surfaceMass |> Maybe.map Unit.encodeSurfaceMass |> Maybe.withDefault Encode.null ) , ( "threadDensity", v.threadDensity |> Maybe.map Unit.encodeThreadDensity |> Maybe.withDefault Encode.null ) + , ( "transport", Transport.encode v.transport ) + , ( "waste", Encode.float (Mass.inKilograms v.waste) ) , ( "yarnSize", v.yarnSize |> Maybe.map Unit.encodeYarnSize |> Maybe.withDefault Encode.null ) - , ( "surfaceMass", v.surfaceMass |> Maybe.map Unit.encodeSurfaceMass |> Maybe.withDefault Encode.null ) ] @@ -514,20 +507,20 @@ encodeProcessInfo v = Maybe.map Encode.string >> Maybe.withDefault Encode.null in Encode.object - [ ( "countryElec", encodeMaybeString v.countryElec ) - , ( "countryHeat", encodeMaybeString v.countryHeat ) + [ ( "airTransport", encodeMaybeString v.airTransport ) , ( "airTransportRatio", encodeMaybeString v.airTransportRatio ) - , ( "airTransport", encodeMaybeString v.airTransport ) - , ( "seaTransport", encodeMaybeString v.seaTransport ) - , ( "roadTransport", encodeMaybeString v.roadTransport ) - , ( "useIroning", encodeMaybeString v.useIroning ) - , ( "useNonIroning", encodeMaybeString v.useNonIroning ) - , ( "passengerCar", encodeMaybeString v.passengerCar ) + , ( "countryElec", encodeMaybeString v.countryElec ) + , ( "countryHeat", encodeMaybeString v.countryHeat ) + , ( "distribution", encodeMaybeString v.distribution ) , ( "endOfLife", encodeMaybeString v.endOfLife ) , ( "fabric", encodeMaybeString v.fabric ) - , ( "making", encodeMaybeString v.making ) - , ( "distribution", encodeMaybeString v.distribution ) , ( "fading", encodeMaybeString v.fading ) + , ( "making", encodeMaybeString v.making ) + , ( "passengerCar", encodeMaybeString v.passengerCar ) + , ( "roadTransport", encodeMaybeString v.roadTransport ) + , ( "seaTransport", encodeMaybeString v.seaTransport ) + , ( "useIroning", encodeMaybeString v.useIroning ) + , ( "useNonIroning", encodeMaybeString v.useNonIroning ) ] diff --git a/src/Data/Textile/Step/Label.elm b/src/Data/Textile/Step/Label.elm index 5a9ba2edd..51e1333a3 100644 --- a/src/Data/Textile/Step/Label.elm +++ b/src/Data/Textile/Step/Label.elm @@ -20,14 +20,14 @@ import Json.Encode as Encode type Label - = Material -- Matière - | Spinning -- Filature - | Fabric -- Tissage ou Tricotage + = Distribution -- Distribution + | EndOfLife -- Fin de vie | Ennobling -- Ennoblissement + | Fabric -- Tissage ou Tricotage | Making -- Confection - | Distribution -- Distribution + | Material -- Matière + | Spinning -- Filature | Use -- Utilisation - | EndOfLife -- Fin de vie all : List Label @@ -55,10 +55,13 @@ upcyclables = toColor : Label -> String toColor label = case label of - Material -> - Impact.stepsColors.materials + Distribution -> + Impact.stepsColors.distribution - Spinning -> + EndOfLife -> + Impact.stepsColors.endOfLife + + Ennobling -> Impact.stepsColors.transform Fabric -> @@ -67,29 +70,27 @@ toColor label = Making -> Impact.stepsColors.transform - Ennobling -> - Impact.stepsColors.transform + Material -> + Impact.stepsColors.materials - Distribution -> - Impact.stepsColors.distribution + Spinning -> + Impact.stepsColors.transform Use -> Impact.stepsColors.usage - EndOfLife -> - Impact.stepsColors.endOfLife - toId : Label -> String toId label = case label of - Material -> - "materials-step" + Distribution -> + "distribution-step" - Spinning -> - -- We only want a single "transform-step" id, as it's used for the Html `id` attribute - -- and they are meant to be unique throughout the page. - "transform-step" + EndOfLife -> + "end-of-life-step" + + Ennobling -> + "transform-step-ennobling" Fabric -> "transform-step-fabric" @@ -97,24 +98,23 @@ toId label = Making -> "transform-step-making" - Ennobling -> - "transform-step-ennobling" + Material -> + "materials-step" - Distribution -> - "distribution-step" + Spinning -> + -- We only want a single "transform-step" id, as it's used for the Html `id` attribute + -- and they are meant to be unique throughout the page. + "transform-step" Use -> "usage-step" - EndOfLife -> - "end-of-life-step" - toName : Label -> String toName label = case label of - Spinning -> - "Transformation\u{00A0}- Filature" + Ennobling -> + "Transformation\u{00A0}- Ennoblissement" Fabric -> "Transformation\u{00A0}- Tissage / Tricotage" @@ -122,8 +122,8 @@ toName label = Making -> "Transformation\u{00A0}- Confection" - Ennobling -> - "Transformation\u{00A0}- Ennoblissement" + Spinning -> + "Transformation\u{00A0}- Filature" _ -> toString label @@ -132,11 +132,14 @@ toName label = toString : Label -> String toString label = case label of - Material -> - "Matières premières" + Distribution -> + "Distribution" - Spinning -> - "Filature" + EndOfLife -> + "Fin de vie" + + Ennobling -> + "Ennoblissement" Fabric -> "Tissage & Tricotage" @@ -144,27 +147,27 @@ toString label = Making -> "Confection" - Ennobling -> - "Ennoblissement" + Material -> + "Matières premières" - Distribution -> - "Distribution" + Spinning -> + "Filature" Use -> "Utilisation" - EndOfLife -> - "Fin de vie" - fromCodeString : String -> Result String Label fromCodeString code = case code of - "material" -> - Ok Material + "distribution" -> + Ok Distribution - "spinning" -> - Ok Spinning + "ennobling" -> + Ok Ennobling + + "eol" -> + Ok EndOfLife "fabric" -> Ok Fabric @@ -172,18 +175,15 @@ fromCodeString code = "making" -> Ok Making - "ennobling" -> - Ok Ennobling + "material" -> + Ok Material - "distribution" -> - Ok Distribution + "spinning" -> + Ok Spinning "use" -> Ok Use - "eol" -> - Ok EndOfLife - _ -> Err ("Code étape inconnu: " ++ code) @@ -191,11 +191,14 @@ fromCodeString code = toCodeString : Label -> String toCodeString label = case label of - Material -> - "material" + Distribution -> + "distribution" - Spinning -> - "spinning" + EndOfLife -> + "eol" + + Ennobling -> + "ennobling" Fabric -> "fabric" @@ -203,46 +206,43 @@ toCodeString label = Making -> "making" - Ennobling -> - "ennobling" + Material -> + "material" - Distribution -> - "distribution" + Spinning -> + "spinning" Use -> "use" - EndOfLife -> - "eol" - toGitbookPath : Label -> Gitbook.Path toGitbookPath label = case label of - Material -> - Gitbook.TextileMaterial - - Spinning -> - Gitbook.TextileSpinning + Distribution -> + Gitbook.TextileDistribution - Fabric -> - Gitbook.TextileFabric + EndOfLife -> + Gitbook.TextileEndOfLife Ennobling -> Gitbook.TextileEnnobling + Fabric -> + Gitbook.TextileFabric + Making -> Gitbook.TextileMaking - Distribution -> - Gitbook.TextileDistribution + Material -> + Gitbook.TextileMaterial + + Spinning -> + Gitbook.TextileSpinning Use -> Gitbook.TextileUse - EndOfLife -> - Gitbook.TextileEndOfLife - decodeFromCode : Decoder Label decodeFromCode = diff --git a/src/Data/Textile/WellKnown.elm b/src/Data/Textile/WellKnown.elm index 5fe5f5c47..f8fd5255c 100644 --- a/src/Data/Textile/WellKnown.elm +++ b/src/Data/Textile/WellKnown.elm @@ -17,30 +17,30 @@ import Result.Extra as RE type alias WellKnown = { airTransport : Process , bleaching : Process - , seaTransport : Process - , roadTransport : Process - , trainTransport : Process , distribution : Process - , dyeingYarn : Process - , dyeingFabric : Process , dyeingArticle : Process - , dyeingSynthetic : Process , dyeingCellulosic : Process - , knittingMix : Process + , dyeingFabric : Process + , dyeingSynthetic : Process + , dyeingYarn : Process + , endOfLife : Process + , fading : Process + , finishing : Process + , heatEurope : Process + , heatRoW : Process + , knittingCircular : Process , knittingFullyFashioned : Process + , knittingMix : Process , knittingSeamless : Process - , knittingCircular : Process , knittingStraight : Process + , passengerCar : Process + , printingDyes : Process + , printingPaste : Process , printingPigment : Process , printingSubstantive : Process - , printingPaste : Process - , printingDyes : Process - , finishing : Process - , passengerCar : Process - , endOfLife : Process - , fading : Process - , heatEurope : Process - , heatRoW : Process + , roadTransport : Process + , seaTransport : Process + , trainTransport : Process , weaving : Process } @@ -71,7 +71,7 @@ getEnnoblingHeatProcess wk country = getPrintingProcess : Printing.Kind -> WellKnown -> { printingProcess : Process, printingToxicityProcess : Process } -getPrintingProcess medium { printingPigment, printingSubstantive, printingDyes, printingPaste } = +getPrintingProcess medium { printingDyes, printingPaste, printingPigment, printingSubstantive } = case medium of Printing.Pigment -> { printingProcess = printingPigment, printingToxicityProcess = printingPaste } @@ -83,64 +83,34 @@ getPrintingProcess medium { printingPigment, printingSubstantive, printingDyes, load : List Process -> Result String WellKnown load processes = let - mapping = - { airTransport = "air-transport" - , bleaching = "bleaching" - , seaTransport = "sea-transport" - , roadTransport = "road-transport" - , trainTransport = "train-transport" - , distribution = "road-transport" - , dyeingYarn = "dyeing-yarn" - , dyeingFabric = "dyeing-fabric" - , dyeingArticle = "dyeing-article" - , dyeingSynthetic = "dyeing-synthetic-fiber" - , dyeingCellulosic = "dyeing-cellulosic-fiber" - , printingPigment = "printing-pigment" - , printingSubstantive = "printing-substantive" - , printingPaste = "printing-paste" - , printingDyes = "printing-dyes" - , finishing = "finishing" - , passengerCar = "passenger-car" - , endOfLife = "end-of-life" - , fading = "fading" - , heatEurope = "heat-europe" - , heatRoW = "heat-row" - , knittingMix = "knitting-mix" - , knittingFullyFashioned = "knitting-fully-fashioned" - , knittingSeamless = "knitting-seamless" - , knittingCircular = "knitting-circular" - , knittingStraight = "knitting-straight" - , weaving = "weaving" - } - - find get = - RE.andMap (Process.findByAlias (Alias <| get mapping) processes) + fromAlias key = + RE.andMap (Process.findByAlias (Alias key) processes) in Ok WellKnown - |> find .airTransport - |> find .bleaching - |> find .seaTransport - |> find .roadTransport - |> find .trainTransport - |> find .distribution - |> find .dyeingYarn - |> find .dyeingFabric - |> find .dyeingArticle - |> find .dyeingSynthetic - |> find .dyeingCellulosic - |> find .knittingMix - |> find .knittingFullyFashioned - |> find .knittingSeamless - |> find .knittingCircular - |> find .knittingStraight - |> find .printingPigment - |> find .printingSubstantive - |> find .printingPaste - |> find .printingDyes - |> find .finishing - |> find .passengerCar - |> find .endOfLife - |> find .fading - |> find .heatEurope - |> find .heatRoW - |> find .weaving + |> fromAlias "air-transport" + |> fromAlias "bleaching" + |> fromAlias "road-transport" + |> fromAlias "dyeing-article" + |> fromAlias "dyeing-cellulosic-fiber" + |> fromAlias "dyeing-fabric" + |> fromAlias "dyeing-synthetic-fiber" + |> fromAlias "dyeing-yarn" + |> fromAlias "end-of-life" + |> fromAlias "fading" + |> fromAlias "finishing" + |> fromAlias "heat-europe" + |> fromAlias "heat-row" + |> fromAlias "knitting-circular" + |> fromAlias "knitting-fully-fashioned" + |> fromAlias "knitting-mix" + |> fromAlias "knitting-seamless" + |> fromAlias "knitting-straight" + |> fromAlias "passenger-car" + |> fromAlias "printing-dyes" + |> fromAlias "printing-paste" + |> fromAlias "printing-pigment" + |> fromAlias "printing-substantive" + |> fromAlias "road-transport" + |> fromAlias "sea-transport" + |> fromAlias "train-transport" + |> fromAlias "weaving" diff --git a/src/Data/Transport.elm b/src/Data/Transport.elm index 6547fb4c2..0270fcc36 100644 --- a/src/Data/Transport.elm +++ b/src/Data/Transport.elm @@ -36,23 +36,23 @@ type alias Distances = type alias Transport = - { road : Length + { air : Length + , impacts : Impacts + , road : Length , roadCooled : Length , sea : Length , seaCooled : Length - , air : Length - , impacts : Impacts } default : Impacts -> Transport default impacts = - { road = Quantity.zero + { air = Quantity.zero + , impacts = impacts + , road = Quantity.zero , roadCooled = Quantity.zero , sea = Quantity.zero , seaCooled = Quantity.zero - , air = Quantity.zero - , impacts = impacts } @@ -60,21 +60,21 @@ erroneous : Impacts -> Transport erroneous impacts = -- FIXME temporary data to display something weird instead of zero, until -- we use of a Result String Transport in the transport computations - { road = Quantity.infinity + { air = Quantity.infinity + , impacts = impacts + , road = Quantity.infinity , roadCooled = Quantity.infinity , sea = Quantity.infinity , seaCooled = Quantity.infinity - , air = Quantity.infinity - , impacts = impacts } add : Transport -> Transport -> Transport add a b = { b - | road = b.road |> Quantity.plus a.road + | air = b.air |> Quantity.plus a.air + , road = b.road |> Quantity.plus a.road , sea = b.sea |> Quantity.plus a.sea - , air = b.air |> Quantity.plus a.air } @@ -116,21 +116,21 @@ computeImpacts { wellKnown } mass transport = sum : List Transport -> Transport sum = List.foldl - (\{ road, roadCooled, sea, seaCooled, air, impacts } acc -> + (\{ air, impacts, road, roadCooled, sea, seaCooled } acc -> { acc - | road = acc.road |> Quantity.plus road + | air = acc.air |> Quantity.plus air + , impacts = Impact.sumImpacts [ acc.impacts, impacts ] + , road = acc.road |> Quantity.plus road , roadCooled = acc.roadCooled |> Quantity.plus roadCooled , sea = acc.sea |> Quantity.plus sea , seaCooled = acc.seaCooled |> Quantity.plus seaCooled - , air = acc.air |> Quantity.plus air - , impacts = Impact.sumImpacts [ acc.impacts, impacts ] } ) (default Impact.empty) totalKm : Transport -> Float -totalKm { road, sea, air } = +totalKm { air, road, sea } = Quantity.sum [ road, sea, air ] |> Length.inKilometers @@ -205,25 +205,25 @@ encodeKm = decode : Decoder Transport decode = Decode.map6 Transport + (Decode.field "air" decodeKm) + (Decode.succeed Impact.empty) (Decode.field "road" decodeKm) -- roadCooled (Decode.succeed Quantity.zero) (Decode.field "sea" decodeKm) -- seaCooled (Decode.succeed Quantity.zero) - (Decode.field "air" decodeKm) - (Decode.succeed Impact.empty) encode : Transport -> Encode.Value encode v = Encode.object - [ ( "road", encodeKm v.road ) + [ ( "air", encodeKm v.air ) + , ( "impacts", Impact.encode v.impacts ) + , ( "road", encodeKm v.road ) , ( "roadCooled", encodeKm v.roadCooled ) , ( "sea", encodeKm v.sea ) , ( "seaCooled", encodeKm v.seaCooled ) - , ( "air", encodeKm v.air ) - , ( "impacts", Impact.encode v.impacts ) ] diff --git a/src/Data/Unit.elm b/src/Data/Unit.elm index df02365ce..df235f046 100644 --- a/src/Data/Unit.elm +++ b/src/Data/Unit.elm @@ -124,8 +124,8 @@ type NonPhysicalDurability type alias HolisticDurability = - { physical : PhysicalDurability - , nonPhysical : NonPhysicalDurability + { nonPhysical : NonPhysicalDurability + , physical : PhysicalDurability } @@ -155,7 +155,7 @@ nonPhysicalDurability value = floatDurabilityFromHolistic : HolisticDurability -> Float -floatDurabilityFromHolistic { physical, nonPhysical } = +floatDurabilityFromHolistic { nonPhysical, physical } = min (physicalDurabilityToFloat physical) (nonPhysicalDurabilityToFloat nonPhysical) diff --git a/src/Data/User.elm b/src/Data/User.elm index 60411ceb8..56a06d4bc 100644 --- a/src/Data/User.elm +++ b/src/Data/User.elm @@ -12,11 +12,11 @@ import Json.Encode as Encode type alias User = - { email : String + { cgu : Bool + , company : String + , email : String , firstname : String , lastname : String - , company : String - , cgu : Bool , token : String } @@ -28,11 +28,11 @@ type alias Form a = decode : Decoder User decode = Decode.succeed User + |> Pipe.required "terms_of_use" Decode.bool + |> Pipe.optional "organization" Decode.string "" |> Pipe.required "email" Decode.string |> Pipe.required "first_name" Decode.string |> Pipe.required "last_name" Decode.string - |> Pipe.optional "organization" Decode.string "" - |> Pipe.required "terms_of_use" Decode.bool |> Pipe.required "token" Decode.string @@ -62,11 +62,11 @@ encodeForm user = form : User -> Form User form user = - { email = user.email + { cgu = user.cgu + , company = user.company + , email = user.email , firstname = user.firstname , lastname = user.lastname - , company = user.company - , cgu = user.cgu - , token = "" , next = "/#/auth/authenticated" + , token = "" } diff --git a/src/Main.elm b/src/Main.elm index f93931abf..887d2be41 100644 --- a/src/Main.elm +++ b/src/Main.elm @@ -43,28 +43,28 @@ type alias Flags = type Page = ApiPage Api.Model | AuthPage Auth.Model - | LoadingPage | ChangelogPage Changelog.Model | EditorialPage Editorial.Model | ExplorePage Explore.Model | FoodBuilderPage FoodBuilder.Model | HomePage Home.Model + | LoadingPage | NotFoundPage | StatsPage Stats.Model | TextileSimulatorPage TextileSimulator.Model type State - = Loaded Session Page - | Errored String + = Errored String + | Loaded Session Page type alias Model = - { state : State - , mobileNavigationOpened : Bool + { mobileNavigationOpened : Bool -- Duplicate the nav key in the model so Parcel's hot module reloading finds it always in the same place. , navKey : Nav.Key + , state : State , url : Url } @@ -98,14 +98,23 @@ init : Flags -> Url -> Nav.Key -> ( Model, Cmd Msg ) init flags requestedUrl navKey = setRoute requestedUrl <| case StaticDb.db StaticJson.rawJsonProcesses of + Err err -> + ( { mobileNavigationOpened = False + , navKey = navKey + , state = Errored err + , url = requestedUrl + } + , Cmd.none + ) + Ok db -> let session = setupSession navKey flags db in - ( { state = Loaded session LoadingPage - , mobileNavigationOpened = False + ( { mobileNavigationOpened = False , navKey = navKey + , state = Loaded session LoadingPage , url = requestedUrl } , Cmd.batch @@ -121,15 +130,6 @@ init flags requestedUrl navKey = ] ) - Err err -> - ( { state = Errored err - , mobileNavigationOpened = False - , navKey = navKey - , url = requestedUrl - } - , Cmd.none - ) - setupSession : Nav.Key -> Flags -> Db -> Session setupSession navKey flags db = @@ -137,13 +137,12 @@ setupSession navKey flags db = store = Session.deserializeStore flags.rawStore in - { db = db - , clientUrl = flags.clientUrl - , enableFoodSection = flags.enableFoodSection - , navKey = navKey - , store = store + { clientUrl = flags.clientUrl , currentVersion = Request.Version.Unknown + , db = db + , enableFoodSection = flags.enableFoodSection , matomo = flags.matomo + , navKey = navKey , notifications = [] , queries = { food = FoodQuery.empty @@ -154,12 +153,17 @@ setupSession navKey flags db = |> Result.withDefault TextileQuery.default } , releases = RemoteData.NotAsked + , store = store } setRoute : Url -> ( Model, Cmd Msg ) -> ( Model, Cmd Msg ) setRoute url ( { state } as model, cmds ) = case state of + Errored _ -> + -- FIXME: Static database decoding error, highly unlikely to ever happen + ( model, cmds ) + Loaded session _ -> let -- TODO: factor this with `update` internal `toPage` @@ -181,25 +185,10 @@ setRoute url ( { state } as model, cmds ) = ) in case Route.fromUrl url of - Nothing -> - ( { model | state = Loaded session NotFoundPage }, Cmd.none ) - - Just Route.Home -> - Home.init session - |> toPage HomePage HomeMsg - - Just Route.Api -> - Api.init session - |> toPage ApiPage ApiMsg - Just (Route.Auth data) -> Auth.init session data |> toPage AuthPage AuthMsg - Just Route.Changelog -> - Changelog.init session - |> toPage ChangelogPage ChangelogMsg - Just (Route.Editorial slug) -> Editorial.init slug session |> toPage EditorialPage EditorialMsg @@ -208,10 +197,6 @@ setRoute url ( { state } as model, cmds ) = Explore.init scope dataset session |> toPage ExplorePage ExploreMsg - Just Route.FoodBuilderHome -> - FoodBuilder.init session Impact.default Nothing - |> toPage FoodBuilderPage FoodBuilderMsg - Just (Route.FoodBuilder trigram maybeQuery) -> FoodBuilder.init session trigram maybeQuery |> toPage FoodBuilderPage FoodBuilderMsg @@ -220,14 +205,6 @@ setRoute url ( { state } as model, cmds ) = FoodBuilder.initFromExample session uuid |> toPage FoodBuilderPage FoodBuilderMsg - Just Route.Stats -> - Stats.init session - |> toPage StatsPage StatsMsg - - Just Route.TextileSimulatorHome -> - TextileSimulator.init Impact.default Nothing session - |> toPage TextileSimulatorPage TextileSimulatorMsg - Just (Route.TextileSimulator trigram maybeQuery) -> TextileSimulator.init trigram maybeQuery session |> toPage TextileSimulatorPage TextileSimulatorMsg @@ -236,9 +213,32 @@ setRoute url ( { state } as model, cmds ) = TextileSimulator.initFromExample session uuid |> toPage TextileSimulatorPage TextileSimulatorMsg - Errored _ -> - -- FIXME: Static database decoding error, highly unlikely to ever happen - ( model, cmds ) + Just Route.Api -> + Api.init session + |> toPage ApiPage ApiMsg + + Just Route.Changelog -> + Changelog.init session + |> toPage ChangelogPage ChangelogMsg + + Just Route.FoodBuilderHome -> + FoodBuilder.init session Impact.default Nothing + |> toPage FoodBuilderPage FoodBuilderMsg + + Just Route.Home -> + Home.init session + |> toPage HomePage HomeMsg + + Just Route.Stats -> + Stats.init session + |> toPage StatsPage StatsMsg + + Just Route.TextileSimulatorHome -> + TextileSimulator.init Impact.default Nothing session + |> toPage TextileSimulatorPage TextileSimulatorMsg + + Nothing -> + ( { model | state = Loaded session NotFoundPage }, Cmd.none ) update : Msg -> Model -> ( Model, Cmd Msg ) @@ -281,13 +281,13 @@ update rawMsg ({ state } as model) = ( DetailedProcessesReceived url (Ok rawDetailedProcessesJson), currentPage ) -> -- When detailed processes are received, rebuild the entire static db using them case StaticDb.db rawDetailedProcessesJson of + Err error -> + ( { model | state = Errored error }, Cmd.none ) + Ok detailedDb -> { model | state = currentPage |> Loaded { session | db = detailedDb } } |> update (UrlChanged url) - Err error -> - ( { model | state = Errored error }, Cmd.none ) - ( DetailedProcessesReceived _ (Err httpError), _ ) -> ( { model | state = Errored (Request.Common.errorToString httpError) } , Cmd.none @@ -430,14 +430,14 @@ subscriptions { state } = view : Model -> Document Msg -view { state, mobileNavigationOpened } = +view { mobileNavigationOpened, state } = case state of Errored error -> - { title = "Erreur lors du chargement…" - , body = + { body = [ Html.p [] [ Html.text <| "Database couldn't be parsed: " ] , Html.pre [] [ Html.text error ] ] + , title = "Erreur lors du chargement…" } Loaded session page -> @@ -456,11 +456,6 @@ view { state, mobileNavigationOpened } = ( title, content |> List.map (Html.map msg) ) in case page of - HomePage homeModel -> - Home.view session homeModel - |> mapMsg HomeMsg - |> Page.frame (pageConfig Page.Home) - ApiPage examplesModel -> Api.view session examplesModel |> mapMsg ApiMsg @@ -491,32 +486,37 @@ view { state, mobileNavigationOpened } = |> mapMsg FoodBuilderMsg |> Page.frame (pageConfig Page.FoodBuilder) - TextileSimulatorPage simulatorModel -> - TextileSimulator.view session simulatorModel - |> mapMsg TextileSimulatorMsg - |> Page.frame (pageConfig Page.TextileSimulator) + HomePage homeModel -> + Home.view session homeModel + |> mapMsg HomeMsg + |> Page.frame (pageConfig Page.Home) - StatsPage statsModel -> - Stats.view session statsModel - |> mapMsg StatsMsg - |> Page.frame (pageConfig Page.Stats) + LoadingPage -> + ( "Chargement…", [ Page.loading ] ) + |> Page.frame (pageConfig Page.Other) NotFoundPage -> ( "Page manquante", [ Page.notFound ] ) |> Page.frame (pageConfig Page.Other) - LoadingPage -> - ( "Chargement…", [ Page.loading ] ) - |> Page.frame (pageConfig Page.Other) + StatsPage statsModel -> + Stats.view session statsModel + |> mapMsg StatsMsg + |> Page.frame (pageConfig Page.Stats) + + TextileSimulatorPage simulatorModel -> + TextileSimulator.view session simulatorModel + |> mapMsg TextileSimulatorMsg + |> Page.frame (pageConfig Page.TextileSimulator) main : Program Flags Model Msg main = Browser.application { init = init - , view = view - , update = update - , subscriptions = subscriptions , onUrlChange = UrlChanged , onUrlRequest = UrlRequested + , subscriptions = subscriptions + , update = update + , view = view } diff --git a/src/Page/Auth.elm b/src/Page/Auth.elm index 36b7de455..35e6fb6a7 100644 --- a/src/Page/Auth.elm +++ b/src/Page/Auth.elm @@ -44,8 +44,8 @@ type Msg type Tab - = RegistrationTab - | AuthenticationTab + = AuthenticationTab + | RegistrationTab init : Session -> { authenticated : Bool } -> ( Model, Session, Cmd Msg ) @@ -306,11 +306,11 @@ viewLoginRegisterForm model = ] , div [ class "card-body" ] [ case model.currentTab of - RegistrationTab -> - viewRegistrationForm model - AuthenticationTab -> viewLoginForm model + + RegistrationTab -> + viewRegistrationForm model ] ] diff --git a/src/Page/Changelog.elm b/src/Page/Changelog.elm index be2c39881..0b80eea06 100644 --- a/src/Page/Changelog.elm +++ b/src/Page/Changelog.elm @@ -57,14 +57,14 @@ view _ model = , [ Container.centered [ class "pb-5" ] [ h1 [ class "mb-3" ] [ text "Changelog" ] , case model.releases of + RemoteData.Failure error -> + Alert.httpError error + RemoteData.Success releases -> releases |> List.map (.markdown >> Markdown.simple []) |> div [] - RemoteData.Failure error -> - Alert.httpError error - _ -> SpinnerView.view ] diff --git a/src/Page/Editorial.elm b/src/Page/Editorial.elm index 354847195..d74ff45f1 100644 --- a/src/Page/Editorial.elm +++ b/src/Page/Editorial.elm @@ -55,6 +55,15 @@ update session msg model = view : Session -> Model -> ( String, List (Html Msg) ) view _ model = case model.content of + RemoteData.Failure httpError -> + ( "Erreur de chargement", [ Alert.httpError httpError ] ) + + RemoteData.Loading -> + ( "Chargement…", [ Spinner.view ] ) + + RemoteData.NotAsked -> + ( "", [] ) + RemoteData.Success content -> ( content |> String.split "\n" @@ -66,12 +75,3 @@ view _ model = ] ] ) - - RemoteData.Loading -> - ( "Chargement…", [ Spinner.view ] ) - - RemoteData.Failure httpError -> - ( "Erreur de chargement", [ Alert.httpError httpError ] ) - - RemoteData.NotAsked -> - ( "", [] ) diff --git a/src/Page/Explore.elm b/src/Page/Explore.elm index 4f55af514..e1b156734 100644 --- a/src/Page/Explore.elm +++ b/src/Page/Explore.elm @@ -60,8 +60,8 @@ type alias Model = type Msg - = NoOp - | CloseModal + = CloseModal + | NoOp | OpenDetail Route | ScopeChange Scope | SetTableState SortableTable.State @@ -75,9 +75,6 @@ init scope dataset session = Dataset.Countries _ -> "Nom" - Dataset.Impacts _ -> - "Code" - Dataset.FoodExamples _ -> "Coût Environnemental" @@ -87,17 +84,20 @@ init scope dataset session = Dataset.FoodProcesses _ -> "Nom" + Dataset.Impacts _ -> + "Code" + Dataset.TextileExamples _ -> "Coût Environnemental" - Dataset.TextileProducts _ -> - "Identifiant" - Dataset.TextileMaterials _ -> "Identifiant" Dataset.TextileProcesses _ -> "Nom" + + Dataset.TextileProducts _ -> + "Identifiant" in ( { dataset = dataset , scope = scope @@ -111,9 +111,6 @@ init scope dataset session = update : Session -> Msg -> Model -> ( Model, Session, Cmd Msg ) update session msg model = case msg of - NoOp -> - ( model, session, Cmd.none ) - CloseModal -> ( model , session @@ -124,6 +121,9 @@ update session msg model = |> Nav.pushUrl session.navKey ) + NoOp -> + ( model, session, Cmd.none ) + OpenDetail route -> ( model , session @@ -248,12 +248,12 @@ countriesExplorer { distances, countries } tableConfig tableState scope maybeCod Just code -> detailsModal (case Country.findByCode code countries of + Err error -> + alert error + Ok country -> country |> Table.viewDetails scope (ExploreCountries.table distances countries) - - Err error -> - alert error ) Nothing -> @@ -321,6 +321,9 @@ foodExamplesExplorer db tableConfig tableState maybeId = Just id -> detailsModal (case Example.findByUuid id db.food.examples of + Err error -> + alert error + Ok example -> Table.viewDetails Scope.Food (FoodExamples.table max) @@ -329,9 +332,6 @@ foodExamplesExplorer db tableConfig tableState maybeId = , per100g = getFoodScorePer100g db example } ) - - Err error -> - alert error ) Nothing -> @@ -353,11 +353,11 @@ foodIngredientsExplorer { food } tableConfig tableState maybeId = Just id -> detailsModal (case Ingredient.findByID id food.ingredients of - Ok ingredient -> - foodIngredientDetails food ingredient - Err error -> alert error + + Ok ingredient -> + foodIngredientDetails food ingredient ) Nothing -> @@ -384,12 +384,12 @@ foodProcessesExplorer { food } tableConfig tableState maybeId = Just id -> detailsModal (case FoodProcess.findByIdentifier id food.processes of + Err error -> + alert error + Ok process -> process |> Table.viewDetails Scope.Food (FoodProcesses.table food) - - Err error -> - alert error ) Nothing -> @@ -437,6 +437,9 @@ textileExamplesExplorer db tableConfig tableState maybeId = Just id -> detailsModal (case Example.findByUuid id db.textile.examples of + Err error -> + alert error + Ok example -> Table.viewDetails Scope.Textile (TextileExamples.table max) @@ -445,9 +448,6 @@ textileExamplesExplorer db tableConfig tableState maybeId = , per100g = getTextileScorePer100g db example } ) - - Err error -> - alert error ) Nothing -> @@ -468,11 +468,11 @@ textileProductsExplorer db tableConfig tableState maybeId = Just id -> detailsModal (case Product.findById id db.textile.products of - Ok product -> - Table.viewDetails Scope.Textile (TextileProducts.table db) product - Err error -> alert error + + Ok product -> + Table.viewDetails Scope.Textile (TextileProducts.table db) product ) Nothing -> @@ -493,11 +493,11 @@ textileMaterialsExplorer db tableConfig tableState maybeId = Just id -> detailsModal (case Material.findById id db.textile.materials of - Ok material -> - textileMaterialDetails db material - Err error -> alert error + + Ok material -> + textileMaterialDetails db material ) Nothing -> @@ -523,12 +523,12 @@ textileProcessesExplorer { textile } tableConfig tableState maybeId = Just id -> detailsModal (case Process.findByUuid id textile.processes of + Err error -> + alert error + Ok process -> process |> Table.viewDetails Scope.Textile TextileProcesses.table - - Err error -> - alert error ) Nothing -> @@ -603,9 +603,6 @@ explore { db } { scope, dataset, tableState } = Dataset.Countries maybeCode -> countriesExplorer db tableConfig tableState scope maybeCode - Dataset.Impacts maybeTrigram -> - impactsExplorer db.definitions tableConfig tableState scope maybeTrigram - Dataset.FoodExamples maybeId -> foodExamplesExplorer db tableConfig tableState maybeId @@ -615,18 +612,21 @@ explore { db } { scope, dataset, tableState } = Dataset.FoodProcesses maybeId -> foodProcessesExplorer db tableConfig tableState maybeId + Dataset.Impacts maybeTrigram -> + impactsExplorer db.definitions tableConfig tableState scope maybeTrigram + Dataset.TextileExamples maybeId -> textileExamplesExplorer db tableConfig tableState maybeId Dataset.TextileMaterials maybeId -> textileMaterialsExplorer db tableConfig tableState maybeId - Dataset.TextileProducts maybeId -> - textileProductsExplorer db tableConfig tableState maybeId - Dataset.TextileProcesses maybeId -> textileProcessesExplorer db tableConfig tableState maybeId + Dataset.TextileProducts maybeId -> + textileProductsExplorer db tableConfig tableState maybeId + view : Session -> Model -> ( String, List (Html Msg) ) view session model = diff --git a/src/Page/Explore/Table.elm b/src/Page/Explore/Table.elm index bcba51983..0edf2cabd 100644 --- a/src/Page/Explore/Table.elm +++ b/src/Page/Explore/Table.elm @@ -37,8 +37,8 @@ type alias Column data comparable msg = type Value comparable data = FloatValue (data -> Float) | IntValue (data -> Int) - | StringValue (data -> String) | NoValue + | StringValue (data -> String) type alias Config data msg = @@ -169,8 +169,8 @@ valueToString item toValue = IntValue getInt -> getInt item |> String.fromInt - StringValue getString -> - getString item - NoValue -> "N/A" + + StringValue getString -> + getString item diff --git a/src/Page/Explore/TextileMaterials.elm b/src/Page/Explore/TextileMaterials.elm index fa2ab7ea9..0ac362b89 100644 --- a/src/Page/Explore/TextileMaterials.elm +++ b/src/Page/Explore/TextileMaterials.elm @@ -94,9 +94,6 @@ table db { detailed, scope } = , toCell = \material -> case Country.findByCode material.defaultCountry db.countries of - Ok country -> - text country.name - Err error -> Alert.simple { level = Alert.Danger @@ -104,6 +101,9 @@ table db { detailed, scope } = , title = Nothing , content = [ text error ] } + + Ok country -> + text country.name } , { label = "CFF: Coefficient d'allocation" , toValue = diff --git a/src/Page/Food.elm b/src/Page/Food.elm index 46c15e42e..248c82440 100644 --- a/src/Page/Food.elm +++ b/src/Page/Food.elm @@ -78,19 +78,19 @@ type alias Model = type Modal - = NoModal - | AddIngredientModal (Maybe Recipe.RecipeIngredient) (Autocomplete Ingredient) + = AddIngredientModal (Maybe Recipe.RecipeIngredient) (Autocomplete Ingredient) | ComparatorModal | ExplorerDetailsModal Ingredient + | NoModal | SelectExampleModal (Autocomplete Query) type Msg - = AddIngredient Ingredient + = AddDistribution + | AddIngredient Ingredient | AddPackaging | AddPreparation | AddTransform - | AddDistribution | CopyToClipBoard String | DeleteBookmark Bookmark | DeleteIngredient Ingredient.Id @@ -104,8 +104,8 @@ type Msg | OnStepClick String | OpenComparator | Reset - | ResetTransform | ResetDistribution + | ResetTransform | SaveBookmark | SaveBookmarkWithTime String Bookmark.Query Posix | SelectAllBookmarks @@ -117,11 +117,11 @@ type Msg | SwitchImpactsTab ImpactTabs.Tab | ToggleComparedSimulation Bookmark Bool | UpdateBookmarkName String + | UpdateDistribution String | UpdateIngredient Query.IngredientQuery Query.IngredientQuery | UpdatePackaging Process.Identifier Query.ProcessQuery | UpdatePreparation Preparation.Id Preparation.Id | UpdateTransform Query.ProcessQuery - | UpdateDistribution String init : Session -> Definition.Trigram -> Maybe Query -> ( Model, Session, Cmd Msg ) @@ -149,11 +149,11 @@ init session trigram maybeQuery = } , session |> Session.updateFoodQuery query , case maybeQuery of - Nothing -> - Ports.scrollTo { x = 0, y = 0 } - Just _ -> Cmd.none + + Nothing -> + Ports.scrollTo { x = 0, y = 0 } ) @@ -195,6 +195,10 @@ update ({ db, queries } as session) msg model = |> Maybe.withDefault ( model, session, Cmd.none ) in case msg of + AddDistribution -> + ( model, session, Cmd.none ) + |> updateQuery (Query.setDistribution Retail.ambient query) + AddIngredient ingredient -> update session (SetModal NoModal) model |> updateQuery (query |> Query.addIngredient (Recipe.ingredientQueryFromIngredient ingredient)) @@ -239,10 +243,6 @@ update ({ db, queries } as session) msg model = firstTransform |> maybeUpdateQuery (\transform -> Query.setTransform transform query) - AddDistribution -> - ( model, session, Cmd.none ) - |> updateQuery (Query.setDistribution Retail.ambient query) - CopyToClipBoard shareableLink -> ( model, session, Ports.copyToClipboard shareableLink ) @@ -365,12 +365,6 @@ update ({ db, queries } as session) msg model = SelectNoBookmarks -> ( model, Session.selectNoBookmarks session, Cmd.none ) - SetModal NoModal -> - ( { model | modal = NoModal } - , session - , commandsForNoModal model.modal - ) - SetModal ComparatorModal -> ( { model | modal = ComparatorModal } , session @@ -393,6 +387,12 @@ update ({ db, queries } as session) msg model = ] ) + SetModal NoModal -> + ( { model | modal = NoModal } + , session + , commandsForNoModal model.modal + ) + SetModal (SelectExampleModal autocomplete) -> ( { model | modal = SelectExampleModal autocomplete } , session @@ -1349,21 +1349,21 @@ mainView ({ db } as session) model = } } , case computed of - Ok ( recipe, results ) -> - stepListView db session model recipe results - Err error -> errorView error + + Ok ( recipe, results ) -> + stepListView db session model recipe results , session.queries.food |> debugQueryView db ] , div [ class "col-lg-4 d-flex flex-column gap-3" ] [ case computed of - Ok ( _, results ) -> - sidebarView session model results - Err error -> errorView error + + Ok ( _, results ) -> + sidebarView session model results ] ] @@ -1514,8 +1514,23 @@ view session model = , [ Container.centered [ class "pb-3" ] [ mainView session model , case model.modal of - NoModal -> - text "" + AddIngredientModal _ autocompleteState -> + AutocompleteSelectorView.view + { autocompleteState = autocompleteState + , closeModal = SetModal NoModal + , footer = [] + , noOp = NoOp + , onAutocomplete = OnAutocompleteIngredient + , onAutocompleteSelect = OnAutocompleteSelect + , placeholderText = "tapez ici le nom de la matière première pour la rechercher" + , title = "Sélectionnez un ingrédient" + , toLabel = .name + , toCategory = + .categories + >> List.head + >> Maybe.map IngredientCategory.toLabel + >> Maybe.withDefault "" + } ComparatorModal -> ModalView.view @@ -1551,23 +1566,8 @@ view session model = , footer = [] } - AddIngredientModal _ autocompleteState -> - AutocompleteSelectorView.view - { autocompleteState = autocompleteState - , closeModal = SetModal NoModal - , footer = [] - , noOp = NoOp - , onAutocomplete = OnAutocompleteIngredient - , onAutocompleteSelect = OnAutocompleteSelect - , placeholderText = "tapez ici le nom de la matière première pour la rechercher" - , title = "Sélectionnez un ingrédient" - , toLabel = .name - , toCategory = - .categories - >> List.head - >> Maybe.map IngredientCategory.toLabel - >> Maybe.withDefault "" - } + NoModal -> + text "" SelectExampleModal autocompleteState -> AutocompleteSelectorView.view diff --git a/src/Page/Home.elm b/src/Page/Home.elm index 500b8820e..b2272ffe1 100644 --- a/src/Page/Home.elm +++ b/src/Page/Home.elm @@ -37,8 +37,8 @@ type Msg type Modal = CalculatorPickerModal - | PresentationVideoModal | NoModal + | PresentationVideoModal init : Session -> ( Model, Session, Cmd Msg ) @@ -101,9 +101,6 @@ viewHero { enableFoodSection } modal = ] ] , case modal of - NoModal -> - text "" - CalculatorPickerModal -> ModalView.view { size = ModalView.Large @@ -116,6 +113,9 @@ viewHero { enableFoodSection } modal = , footer = [] } + NoModal -> + text "" + PresentationVideoModal -> ModalView.view { size = ModalView.ExtraLarge diff --git a/src/Page/Stats.elm b/src/Page/Stats.elm index ec3f7bd64..5fcfdaacd 100644 --- a/src/Page/Stats.elm +++ b/src/Page/Stats.elm @@ -28,8 +28,8 @@ type alias Model = type Msg = ApiStats (WebData (List Matomo.Stat)) - | WebStats (WebData (List Matomo.Stat)) | ToggleMode Mode + | WebStats (WebData (List Matomo.Stat)) type Mode @@ -58,24 +58,24 @@ update session msg model = ApiStats apiStats -> ( { model | apiStats = apiStats }, session, Cmd.none ) - WebStats webStats -> - ( { model | webStats = webStats }, session, Cmd.none ) - ToggleMode mode -> ( { model | mode = mode }, session, Cmd.none ) + WebStats webStats -> + ( { model | webStats = webStats }, session, Cmd.none ) + viewStats : { heading : String, unit : String } -> WebData (List Matomo.Stat) -> Html Msg viewStats { heading, unit } webData = case webData of - RemoteData.NotAsked -> - text "" + RemoteData.Failure err -> + Alert.httpError err RemoteData.Loading -> Spinner.view - RemoteData.Failure err -> - Alert.httpError err + RemoteData.NotAsked -> + text "" RemoteData.Success stats -> node "chart-stats" @@ -112,14 +112,6 @@ view { matomo } { mode, apiStats, webStats } = ] , div [ class "border border-top-0 rounded p-2" ] [ case mode of - Simple -> - div [] - [ webStats - |> viewStats { heading = "Fréquentation", unit = "visite" } - , apiStats - |> viewStats { heading = "Traffic sur l'API", unit = "requête" } - ] - Advanced -> let matomoBaseUrl = @@ -190,6 +182,14 @@ view { matomo } { mode, apiStats, webStats } = [] ] ] + + Simple -> + div [] + [ webStats + |> viewStats { heading = "Fréquentation", unit = "visite" } + , apiStats + |> viewStats { heading = "Traffic sur l'API", unit = "requête" } + ] ] ] ] diff --git a/src/Page/Textile.elm b/src/Page/Textile.elm index ae47267dc..fd0835cf1 100644 --- a/src/Page/Textile.elm +++ b/src/Page/Textile.elm @@ -85,11 +85,11 @@ type alias Model = type Modal - = NoModal - | AddMaterialModal (Maybe Inputs.MaterialInput) (Autocomplete Material) + = AddMaterialModal (Maybe Inputs.MaterialInput) (Autocomplete Material) | ComparatorModal | ConfirmSwitchToRegulatoryModal | ExplorerDetailsTab Material + | NoModal | SelectExampleModal (Autocomplete Query) | SelectProductModal (Autocomplete Product) @@ -131,14 +131,14 @@ type Msg | UpdateBusiness (Result String Economics.Business) | UpdateDyeingMedium DyeingMedium | UpdateFabricProcess Fabric - | UpdatePhysicalDurability (Maybe Unit.PhysicalDurability) | UpdateMakingComplexity MakingComplexity - | UpdateMakingWaste (Maybe Split) | UpdateMakingDeadStock (Maybe Split) + | UpdateMakingWaste (Maybe Split) | UpdateMassInput String | UpdateMaterial MaterialQuery MaterialQuery | UpdateMaterialSpinning Material Spinning | UpdateNumberOfReferences (Maybe Int) + | UpdatePhysicalDurability (Maybe Unit.PhysicalDurability) | UpdatePrice (Maybe Economics.Price) | UpdatePrinting (Maybe Printing) | UpdateStepCountry Label Country.Code @@ -191,15 +191,15 @@ init trigram maybeUrlQuery session = identity ) , case maybeUrlQuery of - -- If we don't have an URL query, we may be coming from another app page, so we should - -- reposition the viewport at the top. - Nothing -> - Ports.scrollTo { x = 0, y = 0 } - -- If we do have an URL query, we either come from a bookmark, a saved simulation click or -- we're tweaking params for the current simulation: we shouldn't reposition the viewport. Just _ -> Cmd.none + + -- If we don't have an URL query, we may be coming from another app page, so we should + -- reposition the viewport at the top. + Nothing -> + Ports.scrollTo { x = 0, y = 0 } ) @@ -1147,12 +1147,18 @@ view session model = ( "Simulateur" , [ Container.centered [ class "Simulator pb-3" ] (case model.simulator of + Err error -> + [ Alert.simple + { level = Alert.Danger + , close = Nothing + , title = Just "Erreur" + , content = [ text error ] + } + ] + Ok simulator -> [ simulatorView session model simulator , case model.modal of - NoModal -> - text "" - AddMaterialModal _ autocompleteState -> AutocompleteSelector.view { autocompleteState = autocompleteState @@ -1230,6 +1236,9 @@ view session model = , footer = [] } + NoModal -> + text "" + SelectExampleModal autocompleteState -> AutocompleteSelector.view { autocompleteState = autocompleteState @@ -1267,15 +1276,6 @@ view session model = , toCategory = always "" } ] - - Err error -> - [ Alert.simple - { level = Alert.Danger - , close = Nothing - , title = Just "Erreur" - , content = [ text error ] - } - ] ) ] ) diff --git a/src/Request/Auth.elm b/src/Request/Auth.elm index df77a5265..4a0651cac 100644 --- a/src/Request/Auth.elm +++ b/src/Request/Auth.elm @@ -23,8 +23,8 @@ type alias Errors = type AuthResponse - = SuccessResponse String - | ErrorResponse String Errors + = ErrorResponse String Errors + | SuccessResponse String decodeAuthResponse : Decoder AuthResponse @@ -46,46 +46,46 @@ decodeAuthResponse = login : (Result Http.Error AuthResponse -> msg) -> String -> Cmd msg login event email = Http.post - { url = "/accounts/login/" - , body = Http.jsonBody (Encode.object [ ( "email", Encode.string email ) ]) + { body = Http.jsonBody (Encode.object [ ( "email", Encode.string email ) ]) , expect = Http.expectJson event decodeAuthResponse + , url = "/accounts/login/" } logout : msg -> Cmd msg logout event = Http.post - { url = "/accounts/logout/" - , body = Http.emptyBody + { body = Http.emptyBody , expect = Http.expectWhatever (always event) + , url = "/accounts/logout/" } processes : (Result Http.Error RawJsonProcesses -> msg) -> String -> Cmd msg processes event token = Http.request - { method = "GET" - , url = "processes/processes.json" - , headers = [ Http.header "token" token ] - , body = Http.emptyBody + { body = Http.emptyBody , expect = Http.expectJson event Db.decodeRawJsonProcesses + , headers = [ Http.header "token" token ] + , method = "GET" , timeout = Nothing , tracker = Nothing + , url = "processes/processes.json" } profile : (Result Http.Error User -> msg) -> Cmd msg profile event = Http.get - { url = "/accounts/profile/" - , expect = Http.expectJson event User.decode + { expect = Http.expectJson event User.decode + , url = "/accounts/profile/" } register : (Result Http.Error AuthResponse -> msg) -> Encode.Value -> Cmd msg register event userForm = Http.post - { url = "/accounts/register/" - , body = Http.jsonBody userForm + { body = Http.jsonBody userForm , expect = Http.expectJson event decodeAuthResponse + , url = "/accounts/register/" } diff --git a/src/Request/Common.elm b/src/Request/Common.elm index 79bd9d8f0..7658e756d 100644 --- a/src/Request/Common.elm +++ b/src/Request/Common.elm @@ -6,17 +6,17 @@ import Http errorToString : Http.Error -> String errorToString error = case error of + Http.BadBody body -> + "Échec de l'interprétation de la réponse HTTP: " ++ body + + Http.BadStatus status_code -> + "Erreur HTTP " ++ String.fromInt status_code + Http.BadUrl url -> "URL invalide: " ++ url - Http.Timeout -> - "Délai dépassé." - Http.NetworkError -> "Erreur de communication réseau. Êtes-vous connecté ?" - Http.BadStatus status_code -> - "Erreur HTTP " ++ String.fromInt status_code - - Http.BadBody body -> - "Échec de l'interprétation de la réponse HTTP: " ++ body + Http.Timeout -> + "Délai dépassé." diff --git a/src/Request/Matomo.elm b/src/Request/Matomo.elm index 6c8fc4181..4f69605a5 100644 --- a/src/Request/Matomo.elm +++ b/src/Request/Matomo.elm @@ -9,10 +9,10 @@ import RemoteData exposing (WebData) getStats : String -> String -> String -> (WebData (List Matomo.Stat) -> msg) -> Cmd msg getStats host jsonKey qs event = Http.get - { url = "https://" ++ host ++ "/" ++ qs - , expect = + { expect = Matomo.decodeStats jsonKey |> Http.expectJson (RemoteData.fromResult >> event) + , url = "https://" ++ host ++ "/" ++ qs } diff --git a/src/Request/Version.elm b/src/Request/Version.elm index b09cdd36b..207120e01 100644 --- a/src/Request/Version.elm +++ b/src/Request/Version.elm @@ -33,6 +33,9 @@ updateVersion currentVersion webData = case webData of RemoteData.Success v -> case currentVersion of + NewerVersion -> + currentVersion + Version currentV -> if currentV.hash /= v.hash || currentV.tag /= v.tag then NewerVersion @@ -40,9 +43,6 @@ updateVersion currentVersion webData = else currentVersion - NewerVersion -> - currentVersion - _ -> Version v diff --git a/src/Route.elm b/src/Route.elm index fd2a76568..46965d0b6 100644 --- a/src/Route.elm +++ b/src/Route.elm @@ -20,19 +20,19 @@ import Url.Parser as Parser exposing ((), Parser) type Route - = Home - | Api + = Api | Auth { authenticated : Bool } | Changelog | Editorial String | Explore Scope Dataset | FoodBuilder Definition.Trigram (Maybe FoodQuery.Query) - | FoodBuilderHome | FoodBuilderExample Uuid - | TextileSimulatorHome + | FoodBuilderHome + | Home + | Stats | TextileSimulator Definition.Trigram (Maybe TextileQuery.Query) | TextileSimulatorExample Uuid - | Stats + | TextileSimulatorHome parser : Parser (Route -> a) a @@ -172,9 +172,6 @@ toString route = let pieces = case route of - Home -> - [] - Api -> [ "api" ] @@ -202,9 +199,6 @@ toString route = Explore scope dataset -> "explore" :: Scope.toString scope :: Dataset.toRoutePath dataset - FoodBuilderHome -> - [ "food" ] - FoodBuilder trigram Nothing -> [ "food", Definition.toString trigram ] @@ -214,8 +208,14 @@ toString route = FoodBuilderExample uuid -> [ "food", "edit-example", Uuid.toString uuid ] - TextileSimulatorHome -> - [ "textile", "simulator" ] + FoodBuilderHome -> + [ "food" ] + + Home -> + [] + + Stats -> + [ "stats" ] TextileSimulator trigram (Just query) -> [ "textile" @@ -233,7 +233,7 @@ toString route = TextileSimulatorExample uuid -> [ "textile", "edit-example", Uuid.toString uuid ] - Stats -> - [ "stats" ] + TextileSimulatorHome -> + [ "textile", "simulator" ] in "#/" ++ String.join "/" pieces diff --git a/src/Server.elm b/src/Server.elm index 1fa68104f..8de961528 100644 --- a/src/Server.elm +++ b/src/Server.elm @@ -47,7 +47,7 @@ apiDocUrl = sendResponse : Int -> Request -> Encode.Value -> Cmd Msg -sendResponse httpStatus { method, url, jsResponseHandler } body = +sendResponse httpStatus { jsResponseHandler, method, url } body = Encode.object [ ( "status", Encode.int httpStatus ) , ( "method", Encode.string method ) @@ -69,15 +69,15 @@ encodeStringError error = toResponse : Result String Encode.Value -> JsonResponse toResponse encodedResult = case encodedResult of - Ok encoded -> - ( 200, encoded ) - Err error -> ( 400, encodeStringError error ) + Ok encoded -> + ( 200, encoded ) + toAllImpactsSimple : Simulator -> Encode.Value -toAllImpactsSimple { inputs, impacts } = +toAllImpactsSimple { impacts, inputs } = Encode.object [ ( "webUrl", serverRootUrl ++ toTextileWebUrl Nothing inputs |> Encode.string ) , ( "impacts", Impact.encode impacts ) @@ -101,7 +101,7 @@ toTextileWebUrl maybeTrigram textileQuery = toSingleImpactSimple : Definition.Trigram -> Simulator -> Encode.Value -toSingleImpactSimple trigram { inputs, impacts } = +toSingleImpactSimple trigram { impacts, inputs } = Encode.object [ ( "webUrl", serverRootUrl ++ toTextileWebUrl (Just trigram) inputs |> Encode.string ) , ( "impacts" @@ -205,96 +205,96 @@ handleRequest : Db -> Request -> JsonResponse handleRequest db request = case Route.endpoint db request of -- GET routes - Just Route.GetFoodCountryList -> + Just Route.FoodGetCountryList -> db.countries |> Scope.only Scope.Food |> Encode.list encodeCountry |> respondWith 200 - Just Route.GetFoodIngredientList -> + Just Route.FoodGetIngredientList -> db.food.ingredients |> encodeIngredients |> respondWith 200 - Just Route.GetFoodPackagingList -> + Just Route.FoodGetPackagingList -> db.food.processes |> List.filter (.category >> (==) FoodProcess.Packaging) |> encodeFoodProcessList |> respondWith 200 - Just Route.GetFoodTransformList -> + Just Route.FoodGetTransformList -> db.food.processes |> List.filter (.category >> (==) FoodProcess.Transform) |> encodeFoodProcessList |> respondWith 200 - Just (Route.GetFoodRecipe (Ok query)) -> + Just (Route.FoodGetRecipe (Ok query)) -> query |> executeFoodQuery db (toFoodResults query) - Just (Route.GetFoodRecipe (Err errors)) -> + Just (Route.FoodGetRecipe (Err errors)) -> Query.encodeErrors errors |> respondWith 400 - Just Route.GetTextileCountryList -> + Just Route.TextileGetCountryList -> db.countries |> Scope.only Scope.Textile |> Encode.list encodeCountry |> respondWith 200 - Just Route.GetTextileMaterialList -> + Just Route.TextileGetMaterialList -> db.textile.materials |> Encode.list encodeMaterial |> respondWith 200 - Just Route.GetTextileProductList -> + Just Route.TextileGetProductList -> db.textile.products |> Encode.list encodeProduct |> respondWith 200 - Just (Route.GetTextileSimulator (Ok query)) -> + Just (Route.TextileGetSimulator (Ok query)) -> query |> executeTextileQuery db toAllImpactsSimple - Just (Route.GetTextileSimulator (Err errors)) -> + Just (Route.TextileGetSimulator (Err errors)) -> Query.encodeErrors errors |> respondWith 400 - Just (Route.GetTextileSimulatorDetailed (Ok query)) -> + Just (Route.TextileGetSimulatorDetailed (Ok query)) -> query |> executeTextileQuery db Simulator.encode - Just (Route.GetTextileSimulatorDetailed (Err errors)) -> + Just (Route.TextileGetSimulatorDetailed (Err errors)) -> Query.encodeErrors errors |> respondWith 400 - Just (Route.GetTextileSimulatorSingle trigram (Ok query)) -> + Just (Route.TextileGetSimulatorSingle trigram (Ok query)) -> query |> executeTextileQuery db (toSingleImpactSimple trigram) - Just (Route.GetTextileSimulatorSingle _ (Err errors)) -> + Just (Route.TextileGetSimulatorSingle _ (Err errors)) -> Query.encodeErrors errors |> respondWith 400 -- POST routes - Just Route.PostFoodRecipe -> + Just Route.FoodPostRecipe -> request.body |> handleDecodeBody BuilderQuery.decode (\query -> executeFoodQuery db (toFoodResults query) query ) - Just Route.PostTextileSimulator -> + Just Route.TextilePostSimulator -> request.body |> handleDecodeBody TextileQuery.decode (executeTextileQuery db toAllImpactsSimple) - Just Route.PostTextileSimulatorDetailed -> + Just Route.TextilePostSimulatorDetailed -> request.body |> handleDecodeBody TextileQuery.decode (executeTextileQuery db Simulator.encode) - Just (Route.PostTextileSimulatorSingle trigram) -> + Just (Route.TextilePostSimulatorSingle trigram) -> request.body |> handleDecodeBody TextileQuery.decode (executeTextileQuery db (toSingleImpactSimple trigram)) @@ -307,33 +307,32 @@ handleRequest db request = handleDecodeBody : Decode.Decoder a -> (a -> JsonResponse) -> Encode.Value -> JsonResponse handleDecodeBody decoder mapper jsonBody = case Decode.decodeValue decoder jsonBody of - Ok x -> - mapper x - Err error -> ( 400, Encode.string (Decode.errorToString error) ) + Ok x -> + mapper x + update : Msg -> Cmd Msg update msg = case msg of Received request -> case db request.processes of - Ok db -> - cmdRequest db request - Err error -> encodeStringError error |> sendResponse 503 request + Ok db -> + cmdRequest db request + main : Program () () Msg main = + -- Note: The Api server being stateless, there's no need for a model Platform.worker { init = always ( (), Cmd.none ) - - -- The Api server being stateless, there's no need of a model - , update = \msg _ -> ( (), update msg ) , subscriptions = always (input Received) + , update = \msg _ -> ( (), update msg ) } diff --git a/src/Server/Query.elm b/src/Server/Query.elm index fe991ce63..393c61677 100644 --- a/src/Server/Query.elm +++ b/src/Server/Query.elm @@ -66,11 +66,11 @@ succeed = parseFoodQuery : List Country -> Food.Db -> Parser (Result Errors BuilderQuery.Query) parseFoodQuery countries food = succeed (Ok BuilderQuery.Query) + |> apply (distributionParser "distribution") |> apply (ingredientListParser "ingredients" countries food) - |> apply (maybeTransformParser "transform" food.processes) |> apply (packagingListParser "packaging" food.processes) - |> apply (distributionParser "distribution") |> apply (preparationListParser "preparation") + |> apply (maybeTransformParser "transform" food.processes) ingredientListParser : String -> List Country -> Food.Db -> Parser (ParseResult (List BuilderQuery.IngredientQuery)) @@ -98,9 +98,9 @@ ingredientParser countries food string = |> Ingredient.findByID (Ingredient.idFromString id) in Ok BuilderQuery.IngredientQuery + |> RE.andMap (Ok Nothing) |> RE.andMap (Result.map .id ingredient) |> RE.andMap (validateMassInGrams mass) - |> RE.andMap (Ok Nothing) |> RE.andMap (Result.map Ingredient.byPlaneByDefault ingredient) [ id, mass, countryCode ] -> @@ -110,9 +110,9 @@ ingredientParser countries food string = |> Ingredient.findByID (Ingredient.idFromString id) in Ok BuilderQuery.IngredientQuery + |> RE.andMap (countryParser countries Scope.Food countryCode) |> RE.andMap (Result.map .id ingredient) |> RE.andMap (validateMassInGrams mass) - |> RE.andMap (countryParser countries Scope.Food countryCode) |> RE.andMap (Result.map Ingredient.byPlaneByDefault ingredient) [ id, mass, countryCode, byPlane ] -> @@ -122,9 +122,9 @@ ingredientParser countries food string = |> Ingredient.findByID (Ingredient.idFromString id) in Ok BuilderQuery.IngredientQuery + |> RE.andMap (countryParser countries Scope.Food countryCode) |> RE.andMap (Result.map .id ingredient) |> RE.andMap (validateMassInGrams mass) - |> RE.andMap (countryParser countries Scope.Food countryCode) |> RE.andMap (ingredient |> Result.andThen (byPlaneParser byPlane)) [ "" ] -> @@ -608,11 +608,11 @@ maybeDyeingMedium key = (Maybe.map (\str -> case DyeingMedium.fromString str of - Ok dyeingMedium -> - Ok (Just dyeingMedium) - Err err -> Err ( key, err ) + + Ok dyeingMedium -> + Ok (Just dyeingMedium) ) >> Maybe.withDefault (Ok Nothing) ) @@ -625,11 +625,11 @@ maybePrinting key = (Maybe.map (\str -> case Printing.fromStringParam str of - Ok printing -> - Ok (Just printing) - Err err -> Err ( key, err ) + + Ok printing -> + Ok (Just printing) ) >> Maybe.withDefault (Ok Nothing) ) @@ -658,11 +658,11 @@ maybeMakingComplexityParser key = (Maybe.map (\str -> case MakingComplexity.fromString str of - Ok printing -> - Ok (Just printing) - Err err -> Err ( key, err ) + + Ok printing -> + Ok (Just printing) ) >> Maybe.withDefault (Ok Nothing) ) @@ -823,11 +823,11 @@ maybeFabricParser key = (Maybe.map (\str -> case Fabric.fromString str of - Ok fabric -> - Ok (Just fabric) - Err err -> Err ( key, err ) + + Ok fabric -> + Ok (Just fabric) ) >> Maybe.withDefault (Ok Nothing) ) diff --git a/src/Server/Request.elm b/src/Server/Request.elm index 64b735532..e501f017e 100644 --- a/src/Server/Request.elm +++ b/src/Server/Request.elm @@ -10,9 +10,9 @@ type alias Request = -- - `url` is ExpressJS' request `url` string -- - `body` is the JSON body; if no JSON body exist in the request, fallbacks to `{}` -- - `jsResponseHandler` is an ExpressJS response callback function - { method : String - , url : String - , body : Encode.Value - , processes : StaticJson.RawJsonProcesses + { body : Encode.Value , jsResponseHandler : Encode.Value + , method : String + , processes : StaticJson.RawJsonProcesses + , url : String } diff --git a/src/Server/Route.elm b/src/Server/Route.elm index 4485f7799..34f519a64 100644 --- a/src/Server/Route.elm +++ b/src/Server/Route.elm @@ -27,63 +27,63 @@ type Route = -- Food Routes -- GET -- Food country list - GetFoodCountryList + FoodGetCountryList -- Food ingredient list - | GetFoodIngredientList + | FoodGetIngredientList -- Food packaging list - | GetFoodPackagingList - -- Food transforms list - | GetFoodTransformList + | FoodGetPackagingList -- Food recipe builder (GET, query string) - | GetFoodRecipe (Result Query.Errors BuilderQuery.Query) + | FoodGetRecipe (Result Query.Errors BuilderQuery.Query) + -- Food transforms list + | FoodGetTransformList -- POST -- Food recipe builder (POST, JSON body) - | PostFoodRecipe + | FoodPostRecipe -- -- Textile Routes -- GET -- Textile country list - | GetTextileCountryList + | TextileGetCountryList -- Textile Material list - | GetTextileMaterialList + | TextileGetMaterialList -- Textile Product list - | GetTextileProductList + | TextileGetProductList -- Textile Simple version of all impacts (GET, query string) - | GetTextileSimulator (Result Query.Errors TextileQuery.Query) + | TextileGetSimulator (Result Query.Errors TextileQuery.Query) -- Textile Detailed version for all impacts (GET, query string) - | GetTextileSimulatorDetailed (Result Query.Errors TextileQuery.Query) + | TextileGetSimulatorDetailed (Result Query.Errors TextileQuery.Query) -- Textile Simple version for one specific impact (GET, query string) - | GetTextileSimulatorSingle Definition.Trigram (Result Query.Errors TextileQuery.Query) + | TextileGetSimulatorSingle Definition.Trigram (Result Query.Errors TextileQuery.Query) -- POST -- Textile Simple version of all impacts (POST, JSON body) - | PostTextileSimulator + | TextilePostSimulator -- Textile Detailed version for all impacts (POST, JSON body) - | PostTextileSimulatorDetailed + | TextilePostSimulatorDetailed -- Textile Simple version for one specific impact (POST, JSON bosy) - | PostTextileSimulatorSingle Definition.Trigram + | TextilePostSimulatorSingle Definition.Trigram parser : Food.Db -> Textile.Db -> List Country -> Parser (Route -> a) a parser foodDb textile countries = Parser.oneOf [ -- Food - Parser.map GetFoodCountryList (s "GET" s "food" s "countries") - , Parser.map GetFoodIngredientList (s "GET" s "food" s "ingredients") - , Parser.map GetFoodTransformList (s "GET" s "food" s "transforms") - , Parser.map GetFoodPackagingList (s "GET" s "food" s "packagings") - , Parser.map GetFoodRecipe (s "GET" s "food" Query.parseFoodQuery countries foodDb) - , Parser.map PostFoodRecipe (s "POST" s "food") + Parser.map FoodGetCountryList (s "GET" s "food" s "countries") + , Parser.map FoodGetIngredientList (s "GET" s "food" s "ingredients") + , Parser.map FoodGetTransformList (s "GET" s "food" s "transforms") + , Parser.map FoodGetPackagingList (s "GET" s "food" s "packagings") + , Parser.map FoodGetRecipe (s "GET" s "food" Query.parseFoodQuery countries foodDb) + , Parser.map FoodPostRecipe (s "POST" s "food") -- Textile - , Parser.map GetTextileCountryList (s "GET" s "textile" s "countries") - , Parser.map GetTextileMaterialList (s "GET" s "textile" s "materials") - , Parser.map GetTextileProductList (s "GET" s "textile" s "products") - , Parser.map GetTextileSimulator (s "GET" s "textile" s "simulator" Query.parseTextileQuery countries textile) - , Parser.map GetTextileSimulatorDetailed (s "GET" s "textile" s "simulator" s "detailed" Query.parseTextileQuery countries textile) - , Parser.map GetTextileSimulatorSingle (s "GET" s "textile" s "simulator" Impact.parseTrigram Query.parseTextileQuery countries textile) - , Parser.map PostTextileSimulator (s "POST" s "textile" s "simulator") - , Parser.map PostTextileSimulatorDetailed (s "POST" s "textile" s "simulator" s "detailed") - , Parser.map PostTextileSimulatorSingle (s "POST" s "textile" s "simulator" Impact.parseTrigram) + , Parser.map TextileGetCountryList (s "GET" s "textile" s "countries") + , Parser.map TextileGetMaterialList (s "GET" s "textile" s "materials") + , Parser.map TextileGetProductList (s "GET" s "textile" s "products") + , Parser.map TextileGetSimulator (s "GET" s "textile" s "simulator" Query.parseTextileQuery countries textile) + , Parser.map TextileGetSimulatorDetailed (s "GET" s "textile" s "simulator" s "detailed" Query.parseTextileQuery countries textile) + , Parser.map TextileGetSimulatorSingle (s "GET" s "textile" s "simulator" Impact.parseTrigram Query.parseTextileQuery countries textile) + , Parser.map TextilePostSimulator (s "POST" s "textile" s "simulator") + , Parser.map TextilePostSimulatorDetailed (s "POST" s "textile" s "simulator" s "detailed") + , Parser.map TextilePostSimulatorSingle (s "POST" s "textile" s "simulator" Impact.parseTrigram) ] diff --git a/src/Static/Db.elm b/src/Static/Db.elm index f7c88b292..ae14df170 100644 --- a/src/Static/Db.elm +++ b/src/Static/Db.elm @@ -12,15 +12,16 @@ 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 Result.Extra as RE import Static.Json as StaticJson exposing (RawJsonProcesses) type alias Db = - { definitions : Definitions - , textile : TextileDb.Db - , food : FoodDb.Db - , countries : List Country + { countries : List Country + , definitions : Definitions , distances : Distances + , food : FoodDb.Db + , textile : TextileDb.Db } @@ -29,13 +30,12 @@ db procs = StaticJson.db procs |> Result.andThen (\{ foodDb, textileDb } -> - Result.map3 - (\okImpactDefinitions okCountries okDistances -> - Db okImpactDefinitions textileDb foodDb okCountries okDistances - ) - impactDefinitions - (countries textileDb) - distances + Ok Db + |> RE.andMap (countries textileDb) + |> RE.andMap impactDefinitions + |> RE.andMap distances + |> RE.andMap (Ok foodDb) + |> RE.andMap (Ok textileDb) ) diff --git a/src/Views/Alert.elm b/src/Views/Alert.elm index a05df48ea..cd028cf12 100644 --- a/src/Views/Alert.elm +++ b/src/Views/Alert.elm @@ -15,10 +15,10 @@ import Views.Icon as Icon type alias Config msg = - { level : Level - , close : Maybe msg - , title : Maybe String + { close : Maybe msg , content : List (Html msg) + , level : Level + , title : Maybe String } @@ -44,9 +44,7 @@ icon level = httpError : Http.Error -> Html msg httpError error = simple - { title = Just "Erreur de chargement des données" - , close = Nothing - , level = Info + { close = Nothing , content = case error |> HttpCommon.errorToString |> String.lines of [] -> @@ -76,6 +74,8 @@ httpError error = [ text "Envoyer un rapport d'incident" ] ] ] + , level = Info + , title = Just "Erreur de chargement des données" } @@ -85,7 +85,7 @@ preformatted config = simple : Config msg -> Html msg -simple { level, content, title, close } = +simple { close, content, level, title } = div [ class <| "alert alert-" ++ levelToClass level , classList [ ( "alert-dismissible", close /= Nothing ) ] diff --git a/src/Views/AutocompleteSelector.elm b/src/Views/AutocompleteSelector.elm index 76f77aa09..48d2fce53 100644 --- a/src/Views/AutocompleteSelector.elm +++ b/src/Views/AutocompleteSelector.elm @@ -16,29 +16,24 @@ type alias Config element msg = , onAutocompleteSelect : msg , placeholderText : String , title : String - , toLabel : element -> String , toCategory : element -> String + , toLabel : element -> String } view : Config element msg -> Html msg view ({ autocompleteState, closeModal, footer, noOp, onAutocomplete, onAutocompleteSelect, placeholderText, title } as config) = ModalView.view - { size = ModalView.Large - , close = closeModal - , noOp = noOp - , title = title - , subTitle = Nothing - , formAction = Nothing + { close = closeModal , content = let - { query, choices, selectedIndex } = + { choices, query, selectedIndex } = Autocomplete.viewState autocompleteState - { inputEvents, choiceEvents } = + { choiceEvents, inputEvents } = AutocompleteView.events - { onSelect = onAutocompleteSelect - , mapHtml = onAutocomplete + { mapHtml = onAutocomplete + , onSelect = onAutocompleteSelect } in [ input @@ -60,11 +55,16 @@ view ({ autocompleteState, closeModal, footer, noOp, onAutocomplete, onAutocompl |> div [ class "ElementAutocomplete", id "element-autocomplete-choices" ] ] , footer = footer + , formAction = Nothing + , noOp = noOp + , size = ModalView.Large + , subTitle = Nothing + , title = title } renderChoice : Config element msg -> (Int -> List (Attribute msg)) -> Maybe Int -> Int -> element -> Html msg -renderChoice { toLabel, toCategory } events selectedIndex_ index element = +renderChoice { toCategory, toLabel } events selectedIndex_ index element = let selected = Autocomplete.isSelected selectedIndex_ index diff --git a/src/Views/BaseElement.elm b/src/Views/BaseElement.elm index 4292598aa..f75ad6059 100644 --- a/src/Views/BaseElement.elm +++ b/src/Views/BaseElement.elm @@ -13,16 +13,16 @@ import Views.Icon as Icon type alias BaseElement element quantity = - { element : element + { country : Maybe Country + , element : element , quantity : quantity - , country : Maybe Country } type alias Db element = - { elements : List element - , countries : List Country + { countries : List Country , definitions : Definitions + , elements : List element } @@ -34,12 +34,10 @@ type alias Config element quantity msg = , delete : element -> msg , excluded : List element , impact : Impacts - - -- TODO: introduce complementsView , openExplorerDetails : element -> msg , quantityView : { quantity : quantity, onChange : Maybe quantity -> msg } -> Html msg - , selectedImpact : Definition , selectElement : element -> Autocomplete element -> msg + , selectedImpact : Definition , toId : element -> String , toString : element -> String , toTooltip : element -> String @@ -64,8 +62,7 @@ view ({ baseElement, db, impact } as config) = in [ span [ class "QuantityInputWrapper" ] [ config.quantityView - { quantity = baseElement.quantity - , onChange = + { onChange = \maybeQuantity -> case maybeQuantity of Just quantity -> @@ -73,6 +70,7 @@ view ({ baseElement, db, impact } as config) = _ -> updateEvent baseElement + , quantity = baseElement.quantity } ] , autocompleteState @@ -133,7 +131,7 @@ deleteItemButton { disabled } event = selectorView : Config element quantity msg -> msg -> Html msg -selectorView { baseElement, openExplorerDetails, toId, toTooltip, toString } selectElement = +selectorView { baseElement, openExplorerDetails, toId, toString, toTooltip } selectElement = let { element } = baseElement diff --git a/src/Views/Bookmark.elm b/src/Views/Bookmark.elm index 33aa2c2bd..697ba5691 100644 --- a/src/Views/Bookmark.elm +++ b/src/Views/Bookmark.elm @@ -16,19 +16,17 @@ import Views.Icon as Icon type alias ManagerConfig msg = - { session : Session - , activeTab : ActiveTab + { activeTab : ActiveTab , bookmarkName : String - , impact : Definition - , scope : Scope - - -- Messages - , copyToClipBoard : String -> msg , compare : msg + , copyToClipBoard : String -> msg , delete : Bookmark -> msg + , impact : Definition , save : msg - , update : String -> msg + , scope : Scope + , session : Session , switchTab : ActiveTab -> msg + , update : String -> msg } @@ -41,30 +39,30 @@ view : ManagerConfig msg -> Html msg view cfg = CardTabs.view { attrs = [] + , content = + [ case cfg.activeTab of + SaveTab -> + managerView cfg + + ShareTab -> + shareTabView cfg + ] , tabs = [ ( SaveTab, text "Sauvegarder" ) , ( ShareTab, text "Partager" ) ] |> List.map (\( tab, label ) -> - { label = label + { active = cfg.activeTab == tab + , label = label , onTabClick = cfg.switchTab tab - , active = cfg.activeTab == tab } ) - , content = - [ case cfg.activeTab of - ShareTab -> - shareTabView cfg - - SaveTab -> - managerView cfg - ] } shareTabView : ManagerConfig msg -> Html msg -shareTabView { session, impact, copyToClipBoard, scope } = +shareTabView { copyToClipBoard, impact, scope, session } = let ( shareableLink, apiCall, jsonParams ) = case scope of diff --git a/src/Views/CardTabs.elm b/src/Views/CardTabs.elm index ef317fc78..3ae451c0c 100644 --- a/src/Views/CardTabs.elm +++ b/src/Views/CardTabs.elm @@ -6,26 +6,26 @@ import Html.Events exposing (..) type alias Tab msg = - { label : Html msg - , active : Bool + { active : Bool + , label : Html msg , onTabClick : msg } type alias Config msg = { attrs : List (Attribute msg) - , tabs : List (Tab msg) , content : List (Html msg) + , tabs : List (Tab msg) } view : Config msg -> Html msg -view { attrs, tabs, content } = +view { attrs, content, tabs } = div [ class "CardTabs card shadow-sm" ] (div (class "card-header px-0 pb-0 border-bottom-0 bg-white" :: attrs) [ tabs |> List.map - (\{ label, onTabClick, active } -> + (\{ active, label, onTabClick } -> li [ class "TabsTab nav-item", classList [ ( "active", active ) ] ] [ button [ class "nav-link no-outline border-top-0" diff --git a/src/Views/Comparator.elm b/src/Views/Comparator.elm index f74123593..9f61a0c1f 100644 --- a/src/Views/Comparator.elm +++ b/src/Views/Comparator.elm @@ -40,9 +40,9 @@ type ComparisonType type alias ChartsData = - { label : String + { complementsImpact : Impact.ComplementsImpacts , impacts : Impact.Impacts - , complementsImpact : Impact.ComplementsImpacts + , label : String , stepsImpacts : Impact.StepsImpacts } @@ -60,7 +60,7 @@ view config = sidebarView : Config msg -> List (Html msg) -sidebarView { session, toggle, selectAll, selectNone } = +sidebarView { selectAll, selectNone, session, toggle } = [ div [ class "p-2 ps-3 mb-0 text-muted" ] [ text "Sélectionnez des simulations pour les comparer" ] @@ -112,9 +112,9 @@ addToComparison session label query = |> Recipe.compute session.db |> Result.map (\( _, { recipe, total } as results ) -> - { label = label + { complementsImpact = recipe.totalComplementsImpact , impacts = total - , complementsImpact = recipe.totalComplementsImpact + , label = label , stepsImpacts = results |> Recipe.toStepsImpacts Definition.Ecs @@ -126,9 +126,9 @@ addToComparison session label query = |> Simulator.compute session.db |> Result.map (\simulator -> - { label = label + { complementsImpact = simulator.complementsImpacts , impacts = simulator.impacts - , complementsImpact = simulator.complementsImpacts + , label = label , stepsImpacts = simulator |> Simulator.toStepsImpacts Definition.Ecs @@ -176,6 +176,14 @@ comparatorView config = ) |> ul [ class "Tabs nav nav-tabs nav-fill justify-content-end gap-3 mt-2 px-2" ] , case charts of + Err error -> + Alert.simple + { close = Nothing + , content = [ text error ] + , level = Alert.Danger + , title = Just "Erreur" + } + Ok [] -> p [ class "d-flex h-100 justify-content-center align-items-center pb-5" ] [ text "Sélectionnez une ou plusieurs simulations pour les comparer" ] @@ -187,12 +195,12 @@ comparatorView config = IndividualImpacts -> dataForIndividualImpacts config.session.db.definitions chartsData - Subscores -> - dataForSubscoresImpacts config.session.db.definitions chartsData - Steps -> dataForSteps chartsData + Subscores -> + dataForSubscoresImpacts config.session.db.definitions chartsData + Total -> dataForTotalImpacts chartsData in @@ -203,12 +211,12 @@ comparatorView config = IndividualImpacts -> "individual-impacts" - Subscores -> - "grouped-impacts" - Steps -> "steps-impacts" + Subscores -> + "grouped-impacts" + Total -> "total-impacts" ) @@ -217,14 +225,6 @@ comparatorView config = [ attribute "data" data ] [] ] - - Err error -> - Alert.simple - { level = Alert.Danger - , close = Nothing - , title = Just "Erreur" - , content = [ text error ] - } ] @@ -277,7 +277,7 @@ dataForIndividualImpacts definitions chartsData = in chartsData |> List.map - (\{ label, impacts, complementsImpact } -> + (\{ complementsImpact, impacts, label } -> let complementImpacts = Impact.complementsImpactAsChartEntries complementsImpact @@ -305,7 +305,7 @@ dataForSubscoresImpacts : Definitions -> List ChartsData -> String dataForSubscoresImpacts definitions chartsData = chartsData |> List.map - (\{ label, impacts, complementsImpact } -> + (\{ complementsImpact, impacts, label } -> let complementImpacts = Impact.totalComplementsImpactAsChartEntry complementsImpact @@ -313,13 +313,13 @@ dataForSubscoresImpacts definitions chartsData = entries = impacts |> Impact.toProtectionAreas definitions - |> (\{ climate, biodiversity, health, resources } -> + |> (\{ biodiversity, climate, health, resources } -> List.reverse [ complementImpacts - , { name = "Climat", color = "#9025be", value = Unit.impactToFloat climate } - , { name = "Biodiversité", color = "#00b050", value = Unit.impactToFloat biodiversity } - , { name = "Santé environnementale", color = "#ffc000", value = Unit.impactToFloat health } - , { name = "Ressource", color = "#0070c0", value = Unit.impactToFloat resources } + , { color = "#9025be", name = "Climat", value = Unit.impactToFloat climate } + , { color = "#00b050", name = "Biodiversité", value = Unit.impactToFloat biodiversity } + , { color = "#ffc000", name = "Santé environnementale", value = Unit.impactToFloat health } + , { color = "#0070c0", name = "Ressource", value = Unit.impactToFloat resources } ] ) in @@ -355,13 +355,13 @@ dataForTotalImpacts : List ChartsData -> String dataForTotalImpacts chartsData = chartsData |> List.map - (\{ label, impacts } -> + (\{ impacts, label } -> Encode.object [ ( "label", Encode.string label ) , ( "data" , Encode.list Impact.encodeAggregatedScoreChartEntry - [ { name = "Impact total" - , color = "#333333" + [ { color = "#333333" + , name = "Impact total" , value = impacts |> Impact.getImpact Definition.Ecs diff --git a/src/Views/Component/SplitInput.elm b/src/Views/Component/SplitInput.elm index f8ac5d716..2fd32c4c6 100644 --- a/src/Views/Component/SplitInput.elm +++ b/src/Views/Component/SplitInput.elm @@ -8,13 +8,13 @@ import Html.Events exposing (..) type alias Config msg = { disabled : Bool - , share : Split , onChange : Maybe Split -> msg + , share : Split } view : Config msg -> Html msg -view { disabled, share, onChange } = +view { disabled, onChange, share } = div [ class "input-group" ] [ input [ class "form-control text-end incdec-arrows-left" diff --git a/src/Views/Format.elm b/src/Views/Format.elm index 010e5ecc9..b3f7f8655 100644 --- a/src/Views/Format.elm +++ b/src/Views/Format.elm @@ -44,13 +44,13 @@ import Quantity import Volume exposing (Volume) -formatImpactFloat : { a | unit : String, decimals : Int } -> Float -> Html msg -formatImpactFloat { unit, decimals } = +formatImpactFloat : { a | decimals : Int, unit : String } -> Float -> Html msg +formatImpactFloat { decimals, unit } = formatRichFloat decimals unit formatImpact : Definition -> Impacts -> Html msg -formatImpact { trigram, unit, decimals } = +formatImpact { decimals, trigram, unit } = Impact.getImpact trigram >> Unit.impactToFloat >> formatRichFloat decimals unit diff --git a/src/Views/ImpactTabs.elm b/src/Views/ImpactTabs.elm index acedf6c54..85a05cde5 100644 --- a/src/Views/ImpactTabs.elm +++ b/src/Views/ImpactTabs.elm @@ -34,55 +34,38 @@ type alias Config msg = , impactDefinition : Definition , onStepClick : String -> msg , scoring : Scoring + , session : Session , stepsImpacts : Impact.StepsImpacts , switchImpactsTab : Tab -> msg , total : Impacts - , session : Session } view : Definitions -> Config msg -> Html msg -view definitions { activeImpactsTab, impactDefinition, switchImpactsTab, total, complementsImpact, onStepClick, scoring, stepsImpacts, session } = +view definitions { activeImpactsTab, complementsImpact, impactDefinition, onStepClick, scoring, session, stepsImpacts, switchImpactsTab, total } = CardTabs.view { attrs = [] - , tabs = - (if impactDefinition.trigram == Definition.Ecs && Session.isAuthenticated session then - [ ( StepImpactsTab, text "Étapes" ) - , ( SubscoresTab, text "Sous-scores" ) - , ( DetailedImpactsTab, text "Impacts" ) - ] - - else - [ ( StepImpactsTab, text "Étapes" ) ] - ) - |> List.map - (\( tab, label ) -> - { label = label - , onTabClick = switchImpactsTab tab - , active = activeImpactsTab == tab - } - ) , content = [ case activeImpactsTab of DetailedImpactsTab -> total |> Impact.getAggregatedScoreData definitions .ecoscoreData - |> List.map (\{ name, value } -> { name = name, value = value, entryAttributes = [] }) + |> List.map (\{ name, value } -> { entryAttributes = [], name = name, value = value }) |> (++) [ -- Food ecosystemic services - { name = "Services écosystémiques" + { entryAttributes = [] + , name = "Services écosystémiques" , value = -(Unit.impactToFloat (Impact.sumEcosystemicImpacts complementsImpact)) - , entryAttributes = [] } -- Textile complements - , { name = "Complément export hors-Europe" + , { entryAttributes = [] + , name = "Complément export hors-Europe" , value = -(Unit.impactToFloat complementsImpact.outOfEuropeEOL) - , entryAttributes = [] } - , { name = "Complément microfibres" + , { entryAttributes = [] + , name = "Complément microfibres" , value = -(Unit.impactToFloat complementsImpact.microfibers) - , entryAttributes = [] } ] |> List.sortBy .value @@ -90,77 +73,94 @@ view definitions { activeImpactsTab, impactDefinition, switchImpactsTab, total, |> Table.percentageTable impactDefinition StepImpactsTab -> - [ { name = "Matières premières" - , value = stepsImpacts.materials - , entryAttributes = + [ { entryAttributes = [ StepsBorder.style Impact.stepsColors.materials , onClick <| onStepClick "materials-step" ] + , name = "Matières premières" + , value = stepsImpacts.materials } - , { name = "Transformation" - , value = stepsImpacts.transform - , entryAttributes = + , { entryAttributes = [ StepsBorder.style Impact.stepsColors.transform , onClick <| onStepClick "transform-step" ] + , name = "Transformation" + , value = stepsImpacts.transform } - , { name = "Emballage" - , value = stepsImpacts.packaging - , entryAttributes = + , { entryAttributes = [ StepsBorder.style Impact.stepsColors.packaging , onClick <| onStepClick "packaging-step" ] + , name = "Emballage" + , value = stepsImpacts.packaging } - , { name = "Transports" - , value = stepsImpacts.transports - , entryAttributes = + , { entryAttributes = [ StepsBorder.style Impact.stepsColors.transports , onClick <| onStepClick "transport-step" ] + , name = "Transports" + , value = stepsImpacts.transports } - , { name = "Distribution" - , value = stepsImpacts.distribution - , entryAttributes = + , { entryAttributes = [ StepsBorder.style Impact.stepsColors.distribution , onClick <| onStepClick "distribution-step" ] + , name = "Distribution" + , value = stepsImpacts.distribution } - , { name = "Utilisation" - , value = stepsImpacts.usage - , entryAttributes = + , { entryAttributes = [ StepsBorder.style Impact.stepsColors.usage , onClick <| onStepClick "usage-step" ] + , name = "Utilisation" + , value = stepsImpacts.usage } - , { name = "Fin de vie" - , value = stepsImpacts.endOfLife - , entryAttributes = + , { entryAttributes = [ StepsBorder.style Impact.stepsColors.endOfLife , onClick <| onStepClick "end-of-life-step" ] + , name = "Fin de vie" + , value = stepsImpacts.endOfLife } ] |> List.map - (\{ name, value, entryAttributes } -> - { name = name + (\{ entryAttributes, name, value } -> + { entryAttributes = style "cursor" "pointer" :: entryAttributes + , name = name , value = value |> Maybe.map Unit.impactToFloat |> Maybe.withDefault 0 - , entryAttributes = style "cursor" "pointer" :: entryAttributes } ) |> Table.percentageTable impactDefinition SubscoresTab -> Table.percentageTable impactDefinition - [ { name = "Climat", value = Unit.impactToFloat scoring.climate, entryAttributes = [] } - , { name = "Biodiversité", value = Unit.impactToFloat scoring.biodiversity, entryAttributes = [] } - , { name = "Santé environnementale", value = Unit.impactToFloat scoring.health, entryAttributes = [] } - , { name = "Ressource", value = Unit.impactToFloat scoring.resources, entryAttributes = [] } - , { name = "Compléments", value = -(Unit.impactToFloat scoring.complements), entryAttributes = [] } + [ { entryAttributes = [], name = "Climat", value = Unit.impactToFloat scoring.climate } + , { entryAttributes = [], name = "Biodiversité", value = Unit.impactToFloat scoring.biodiversity } + , { entryAttributes = [], name = "Santé environnementale", value = Unit.impactToFloat scoring.health } + , { entryAttributes = [], name = "Ressource", value = Unit.impactToFloat scoring.resources } + , { entryAttributes = [], name = "Compléments", value = -(Unit.impactToFloat scoring.complements) } ] ] + , tabs = + (if impactDefinition.trigram == Definition.Ecs && Session.isAuthenticated session then + [ ( StepImpactsTab, text "Étapes" ) + , ( SubscoresTab, text "Sous-scores" ) + , ( DetailedImpactsTab, text "Impacts" ) + ] + + else + [ ( StepImpactsTab, text "Étapes" ) ] + ) + |> List.map + (\( tab, label ) -> + { active = activeImpactsTab == tab + , label = label + , onTabClick = switchImpactsTab tab + } + ) } @@ -171,20 +171,20 @@ createConfig session impactDefinition activeImpactsTab onStepClick switchImpacts , impactDefinition = impactDefinition , onStepClick = onStepClick , scoring = Scoring.empty + , session = session , stepsImpacts = Impact.noStepsImpacts , switchImpactsTab = switchImpactsTab , total = Impact.empty - , session = session } forFood : Recipe.Results -> Config msg -> Config msg forFood results config = { config - | total = results.total - , complementsImpact = results.recipe.totalComplementsImpact + | complementsImpact = results.recipe.totalComplementsImpact , scoring = results.scoring , stepsImpacts = Recipe.toStepsImpacts config.impactDefinition.trigram results + , total = results.total } @@ -195,12 +195,12 @@ forTextile definitions simulator config = Simulator.getTotalImpactsWithoutComplements simulator in { config - | total = totalImpactsWithoutComplements - , complementsImpact = simulator.complementsImpacts + | complementsImpact = simulator.complementsImpacts , scoring = totalImpactsWithoutComplements |> Scoring.compute definitions (Impact.getTotalComplementsImpacts simulator.complementsImpacts) , stepsImpacts = simulator |> Simulator.toStepsImpacts config.impactDefinition.trigram + , total = totalImpactsWithoutComplements } diff --git a/src/Views/Markdown.elm b/src/Views/Markdown.elm index a8a0239d1..e8387b3f7 100644 --- a/src/Views/Markdown.elm +++ b/src/Views/Markdown.elm @@ -15,9 +15,11 @@ import Views.Icon as Icon import Views.Link as Link -type ContentType - = Simple String - | Gitbook Gitbook.Page +type + ContentType + -- FIXME: remove the Gitbook type as this is not used anymore + = Gitbook Gitbook.Page + | Simple String siteUrl : String @@ -33,9 +35,7 @@ clean = renderer : Maybe Gitbook.Path -> Renderer (Html msg) renderer maybePath = { defaultHtmlRenderer - | link = renderLink maybePath - , image = renderImage - , html = + | html = MdHtml.oneOf [ MdHtml.tag "hint" renderHint |> MdHtml.withAttribute "level" @@ -44,13 +44,15 @@ renderer maybePath = -- NOTE: sometimes gitbook exposes raw HTML in markdown , MdHtml.tag "a" - (\href title -> renderLink maybePath { title = title, destination = href }) + (\href title -> renderLink maybePath { destination = href, title = title }) |> MdHtml.withAttribute "href" |> MdHtml.withOptionalAttribute "title" , MdHtml.tag "code" (code []) , MdHtml.tag "em" (em []) , MdHtml.tag "p" (p [ class "mb-1" ]) ] + , image = renderImage + , link = renderLink maybePath } @@ -88,8 +90,8 @@ renderMark style_ = ] -renderLink : Maybe Gitbook.Path -> { title : Maybe String, destination : String } -> List (Html msg) -> Html msg -renderLink maybePath { title, destination } = +renderLink : Maybe Gitbook.Path -> { destination : String, title : Maybe String } -> List (Html msg) -> Html msg +renderLink maybePath { destination, title } = let destination_ = Gitbook.handleMarkdownGitbookLink maybePath destination @@ -109,8 +111,8 @@ renderLink maybePath { title, destination } = Link.internal (Attr.href destination_ :: baseAttrs) -renderImage : { title : Maybe String, alt : String, src : String } -> Html msg -renderImage { title, src, alt } = +renderImage : { alt : String, src : String, title : Maybe String } -> Html msg +renderImage { alt, src, title } = Html.img (List.filterMap identity [ Maybe.map Attr.title title @@ -134,28 +136,28 @@ simple attrs markdown = view : List (Attribute msg) -> ContentType -> Html msg view attrs content = case parse content of - Ok rendered -> - div (class "Markdown bottomed-paragraphs" :: attrs) rendered - Err errors -> Alert.preformatted - { title = Just "Des erreurs ont été rencontrées" - , close = Nothing - , level = Alert.Danger + { close = Nothing , content = [ text errors ] + , level = Alert.Danger + , title = Just "Des erreurs ont été rencontrées" } + Ok rendered -> + div (class "Markdown bottomed-paragraphs" :: attrs) rendered + parse : ContentType -> Result String (List (Html msg)) parse content = let ( markdown, path ) = case content of - Simple string -> - ( string, Nothing ) - Gitbook page -> ( page.markdown, Just page.path ) + + Simple string -> + ( string, Nothing ) in clean markdown |> Parser.parse diff --git a/src/Views/Modal.elm b/src/Views/Modal.elm index 04dc243a0..5f9153b95 100644 --- a/src/Views/Modal.elm +++ b/src/Views/Modal.elm @@ -7,14 +7,14 @@ import Json.Decode as Decode type alias Config msg = - { size : Size - , close : msg - , noOp : msg - , title : String - , subTitle : Maybe String + { close : msg , content : List (Html msg) , footer : List (Html msg) , formAction : Maybe msg + , noOp : msg + , size : Size + , subTitle : Maybe String + , title : String } @@ -33,8 +33,8 @@ view config = , custom "mouseup" (Decode.succeed { message = config.noOp - , stopPropagation = True , preventDefault = True + , stopPropagation = True } ) ] @@ -56,8 +56,8 @@ view config = , custom "mouseup" (Decode.succeed { message = config.close - , stopPropagation = True , preventDefault = True + , stopPropagation = True } ) ] diff --git a/src/Views/Page.elm b/src/Views/Page.elm index 62feee1b9..2ad6cc4e6 100644 --- a/src/Views/Page.elm +++ b/src/Views/Page.elm @@ -39,8 +39,8 @@ type ActivePage type MenuLink - = Internal String Route.Route ActivePage - | External String String + = External String String + | Internal String Route.Route ActivePage | MailTo String String @@ -191,14 +191,14 @@ pageFooter session = let makeLink link = case link of - Internal label route _ -> - Link.internal [ class "text-decoration-none", Route.href route ] - [ text label ] - External label url -> Link.external [ class "text-decoration-none", href url ] [ text label ] + Internal label route _ -> + Link.internal [ class "text-decoration-none", Route.href route ] + [ text label ] + MailTo label email -> a [ class "text-decoration-none", href <| "mailto:" ++ email ] [ text label ] @@ -373,6 +373,10 @@ pageHeader { session, activePage, openMobileNavigation, loadUrl, switchVersion } viewNavigationLink : ActivePage -> MenuLink -> Html msg viewNavigationLink activePage link = case link of + External label url -> + Link.external [ class "nav-link link-external-muted", href url ] + [ text label ] + Internal label route page -> Link.internal (class "nav-link" @@ -387,10 +391,6 @@ viewNavigationLink activePage link = ) [ text label ] - External label url -> - Link.external [ class "nav-link link-external-muted", href url ] - [ text label ] - MailTo label email -> a [ class "nav-link", href <| "mailto:" ++ email ] [ text label ] diff --git a/src/Views/RangeSlider.elm b/src/Views/RangeSlider.elm index 18712e535..1caac70c3 100644 --- a/src/Views/RangeSlider.elm +++ b/src/Views/RangeSlider.elm @@ -13,22 +13,20 @@ import Html.Events exposing (..) type alias PercentConfig msg = - { id : String + { disabled : Bool + , id : String + , max : Int + , min : Int + , toString : Split -> String , update : Maybe Split -> msg , value : Split - , toString : Split -> String - , disabled : Bool - , min : Int - , max : Int } percent : PercentConfig msg -> Html msg percent config = narrowLayout - { id = config.id - , label = config.toString config.value - , attributes = + { attributes = [ onInput (String.toFloat >> Maybe.andThen (Split.fromPercent >> Result.toMaybe) >> config.update) , Attr.min (String.fromInt config.min) , Attr.max (String.fromInt config.max) @@ -39,24 +37,24 @@ percent config = , value (String.fromFloat (Split.toPercent config.value)) , Attr.disabled config.disabled ] + , id = config.id + , label = config.toString config.value } type alias SurfaceMassConfig msg = - { id : String + { disabled : Bool + , id : String + , toString : Unit.SurfaceMass -> String , update : Maybe Unit.SurfaceMass -> msg , value : Unit.SurfaceMass - , toString : Unit.SurfaceMass -> String - , disabled : Bool } surfaceMass : SurfaceMassConfig msg -> Html msg surfaceMass config = narrowLayout - { id = config.id - , label = config.toString config.value - , attributes = + { attributes = [ onInput (String.toInt >> Maybe.map Unit.gramsPerSquareMeter >> config.update) , Attr.min (String.fromInt (Unit.surfaceMassInGramsPerSquareMeters Unit.minSurfaceMass)) , Attr.max (String.fromInt (Unit.surfaceMassInGramsPerSquareMeters Unit.maxSurfaceMass)) @@ -67,24 +65,24 @@ surfaceMass config = , value (String.fromInt (Unit.surfaceMassInGramsPerSquareMeters config.value)) , Attr.disabled config.disabled ] + , id = config.id + , label = config.toString config.value } type alias YarnSizeConfig msg = - { id : String + { disabled : Bool + , id : String + , toString : Unit.YarnSize -> String , update : Maybe Unit.YarnSize -> msg , value : Unit.YarnSize - , toString : Unit.YarnSize -> String - , disabled : Bool } yarnSize : YarnSizeConfig msg -> Html msg yarnSize config = narrowLayout - { id = config.id - , label = config.toString config.value - , attributes = + { attributes = [ onInput (String.toFloat >> Maybe.map Unit.yarnSizeKilometersPerKg >> config.update) , Attr.min (String.fromFloat (Unit.yarnSizeInKilometers Unit.minYarnSize)) , Attr.max (String.fromFloat (Unit.yarnSizeInKilometers Unit.maxYarnSize)) @@ -95,24 +93,24 @@ yarnSize config = , value (String.fromFloat (Unit.yarnSizeInKilometers config.value)) , Attr.disabled config.disabled ] + , id = config.id + , label = config.toString config.value } type alias PhysicalDurabilityConfig msg = - { id : String + { disabled : Bool + , id : String + , toString : Unit.PhysicalDurability -> String , update : Maybe Unit.PhysicalDurability -> msg , value : Unit.PhysicalDurability - , toString : Unit.PhysicalDurability -> String - , disabled : Bool } physicalDurability : PhysicalDurabilityConfig msg -> Html msg physicalDurability config = wideLayout - { id = config.id - , label = config.toString config.value - , attributes = + { attributes = [ onInput (String.toFloat >> Maybe.map Unit.physicalDurability >> config.update) , Attr.min (String.fromFloat (Unit.physicalDurabilityToFloat (Unit.minDurability Unit.PhysicalDurability))) , Attr.max (String.fromFloat (Unit.physicalDurabilityToFloat (Unit.maxDurability Unit.PhysicalDurability))) @@ -123,11 +121,13 @@ physicalDurability config = , value (String.fromFloat (Unit.physicalDurabilityToFloat config.value)) , Attr.disabled config.disabled ] + , id = config.id + , label = config.toString config.value } -narrowLayout : { id : String, label : String, attributes : List (Attribute msg) } -> Html msg -narrowLayout { id, label, attributes } = +narrowLayout : { attributes : List (Attribute msg), id : String, label : String } -> Html msg +narrowLayout { attributes, id, label } = div [ class "RangeSlider row" ] [ div [ class "col-xxl-6" ] [ Html.label [ for id, class "form-label text-nowrap fs-7 mb-0" ] @@ -138,8 +138,8 @@ narrowLayout { id, label, attributes } = ] -wideLayout : { id : String, label : String, attributes : List (Attribute msg) } -> Html msg -wideLayout { id, label, attributes } = +wideLayout : { attributes : List (Attribute msg), id : String, label : String } -> Html msg +wideLayout { attributes, id, label } = div [ class "RangeSlider row", style "flex-grow" "1" ] [ div [ class "col-xxl-2" ] [ Html.label [ for id, class "form-label text-nowrap mb-0" ] diff --git a/src/Views/Score.elm b/src/Views/Score.elm index 67721a965..cc6da3fd3 100644 --- a/src/Views/Score.elm +++ b/src/Views/Score.elm @@ -9,8 +9,8 @@ import Views.Format as Format type alias Config msg = - { impactDefinition : Definition - , customInfo : Maybe (Html msg) + { customInfo : Maybe (Html msg) + , impactDefinition : Definition , mass : Mass , score : Impacts } diff --git a/src/Views/Sidebar.elm b/src/Views/Sidebar.elm index 4fcc8cc17..6ce0b459a 100644 --- a/src/Views/Sidebar.elm +++ b/src/Views/Sidebar.elm @@ -15,30 +15,22 @@ import Views.Score as ScoreView type alias Config msg = - { session : Session - , scope : Scope - - -- Impact selector - , selectedImpact : Definition - , switchImpact : Result String Trigram -> msg - - -- Score - , customScoreInfo : Maybe (Html msg) - , productMass : Mass - , totalImpacts : Impacts - - -- Impacts tabs - , impactTabsConfig : ImpactTabs.Config msg - - -- Bookmarks - , activeBookmarkTab : BookmarkView.ActiveTab + { activeBookmarkTab : BookmarkView.ActiveTab , bookmarkName : String - , copyToClipBoard : String -> msg , compareBookmarks : msg + , copyToClipBoard : String -> msg + , customScoreInfo : Maybe (Html msg) , deleteBookmark : Bookmark -> msg + , impactTabsConfig : ImpactTabs.Config msg + , productMass : Mass , saveBookmark : msg - , updateBookmarkName : String -> msg + , scope : Scope + , selectedImpact : Definition + , session : Session , switchBookmarkTab : BookmarkView.ActiveTab -> msg + , switchImpact : Result String Trigram -> msg + , totalImpacts : Impacts + , updateBookmarkName : String -> msg } @@ -64,22 +56,22 @@ view config = , ScoreView.view { customInfo = config.customScoreInfo , impactDefinition = config.selectedImpact - , score = config.totalImpacts , mass = config.productMass + , score = config.totalImpacts } , config.impactTabsConfig |> ImpactTabs.view db.definitions , BookmarkView.view - { session = config.session - , activeTab = config.activeBookmarkTab + { activeTab = config.activeBookmarkTab , bookmarkName = config.bookmarkName - , impact = config.selectedImpact - , scope = config.scope - , copyToClipBoard = config.copyToClipBoard , compare = config.compareBookmarks + , copyToClipBoard = config.copyToClipBoard , delete = config.deleteBookmark + , impact = config.selectedImpact , save = config.saveBookmark - , update = config.updateBookmarkName + , scope = config.scope + , session = config.session , switchTab = config.switchBookmarkTab + , update = config.updateBookmarkName } ] diff --git a/src/Views/Table.elm b/src/Views/Table.elm index c73ca23fe..8dc375f31 100644 --- a/src/Views/Table.elm +++ b/src/Views/Table.elm @@ -10,9 +10,9 @@ import Views.Format as Format type alias DataPoint msg = - { name : String + { entryAttributes : List (Attribute msg) + , name : String , value : Float - , entryAttributes : List (Attribute msg) } @@ -47,9 +47,10 @@ percentageTable impactDefinition data = [ table [ class "table table-hover w-100 m-0" ] [ data |> List.map - (\{ name, value, entryAttributes } -> - { name = name + (\{ entryAttributes, name, value } -> + { entryAttributes = entryAttributes , impact = value + , name = name , percent = value / total * 100 , width = if value < 0 then @@ -57,11 +58,10 @@ percentageTable impactDefinition data = else value / maximum * 100 - , entryAttributes = entryAttributes } ) |> List.map - (\{ name, impact, percent, width, entryAttributes } -> + (\{ entryAttributes, impact, name, percent, width } -> let entryTitle = name diff --git a/src/Views/Textile/Step.elm b/src/Views/Textile/Step.elm index ec7524063..9f27fd579 100644 --- a/src/Views/Textile/Step.elm +++ b/src/Views/Textile/Step.elm @@ -49,11 +49,10 @@ import Views.Transport as TransportView type alias Config msg modal = - { db : Db - , addMaterialModal : Maybe Inputs.MaterialInput -> Autocomplete Material -> modal + { addMaterialModal : Maybe Inputs.MaterialInput -> Autocomplete Material -> modal , current : Step , daysOfWear : Duration - , useNbCycles : Int + , db : Db , deleteMaterial : Material -> msg , index : Int , inputs : Inputs @@ -76,6 +75,7 @@ type alias Config msg modal = , updatePrinting : Maybe Printing -> msg , updateSurfaceMass : Maybe Unit.SurfaceMass -> msg , updateYarnSize : Maybe Unit.YarnSize -> msg + , useNbCycles : Int } @@ -115,13 +115,13 @@ airTransportRatioField : Config msg modal -> Html msg airTransportRatioField { current, updateAirTransportRatio } = span [ title "Part de transport aérien pour le transport entre la confection et l'entrepôt en France." ] [ RangeSlider.percent - { id = "airTransportRatio" + { disabled = Step.airTransportDisabled current + , id = "airTransportRatio" + , max = 100 + , min = 0 + , toString = Step.airTransportRatioToString , update = updateAirTransportRatio , value = current.airTransportRatio - , toString = Step.airTransportRatioToString - , disabled = Step.airTransportDisabled current - , min = 0 - , max = 100 } ] @@ -254,6 +254,12 @@ printingFields { current, inputs, updatePrinting } = (\str -> updatePrinting (case Printing.fromString str of + Err _ -> + -- Note: we've most likely received the "Aucune" string value from + -- when the user picked this choice, so it's fair to reset any + -- previously selected printing process. + Nothing + Ok kind -> case inputs.printing of Just printing -> @@ -261,12 +267,6 @@ printingFields { current, inputs, updatePrinting } = Nothing -> Just { kind = kind, ratio = Printing.defaultRatio } - - Err _ -> - -- Note: we've most likely received the "Aucune" string value from - -- when the user picked this choice, so it's fair to reset any - -- previously selected printing process. - Nothing ) ) ] @@ -378,62 +378,60 @@ makingWasteField : Config msg modal -> Html msg makingWasteField { current, inputs, updateMakingWaste } = span [ title "Taux moyen de pertes en confection" ] [ RangeSlider.percent - { id = "makingWaste" + { disabled = not current.enabled + , id = "makingWaste" + , max = Env.maxMakingWasteRatio |> Split.toPercent |> round + , min = Env.minMakingWasteRatio |> Split.toPercent |> round + , toString = Step.makingWasteToString , update = updateMakingWaste , value = inputs.fabricProcess |> Fabric.getMakingWaste inputs.product.making.pcrWaste inputs.makingWaste - , toString = Step.makingWasteToString - , disabled = not current.enabled - , min = Env.minMakingWasteRatio |> Split.toPercent |> round - , max = Env.maxMakingWasteRatio |> Split.toPercent |> round } ] makingDeadStockField : Config msg modal -> Html msg -makingDeadStockField { current, updateMakingDeadStock, showAdvancedFields } = +makingDeadStockField { current, showAdvancedFields, updateMakingDeadStock } = showIf showAdvancedFields <| span [ title "Taux moyen de stocks dormants (vêtements non vendus + produits semi-finis non utilisés) sur l’ensemble de la chaîne de valeur" ] [ RangeSlider.percent - { id = "makingDeadStock" + { disabled = not current.enabled + , id = "makingDeadStock" + , max = Env.maxMakingDeadStockRatio |> Split.toPercent |> round + , min = Env.minMakingDeadStockRatio |> Split.toPercent |> round + , toString = Step.makingDeadStockToString , update = updateMakingDeadStock , value = Maybe.withDefault Env.defaultDeadStock current.makingDeadStock - , toString = Step.makingDeadStockToString - , disabled = not current.enabled - , min = Env.minMakingDeadStockRatio |> Split.toPercent |> round - , max = Env.maxMakingDeadStockRatio |> Split.toPercent |> round } ] surfaceMassField : Config msg modal -> Html msg -surfaceMassField { current, updateSurfaceMass, inputs } = +surfaceMassField { current, inputs, updateSurfaceMass } = div [ class "mt-2" , title "Le grammage de l'étoffe, exprimé en g/m², représente sa masse surfacique." ] [ RangeSlider.surfaceMass - { id = "surface-density" + { disabled = not current.enabled + , id = "surface-density" + , toString = Step.surfaceMassToString , update = updateSurfaceMass , value = current.surfaceMass |> Maybe.withDefault inputs.product.surfaceMass - , toString = Step.surfaceMassToString - - -- Note: hide for knitted products as surface mass doesn't have any impact on them - , disabled = not current.enabled } ] yarnSizeField : Config msg modal -> Html msg -yarnSizeField { current, updateYarnSize, inputs } = +yarnSizeField { current, inputs, updateYarnSize } = span [ title "Le titrage indique la grosseur d’un fil textile" ] [ RangeSlider.yarnSize - { id = "yarnSize" + { disabled = not current.enabled + , id = "yarnSize" + , toString = Step.yarnSizeToString , update = updateYarnSize , value = current.yarnSize |> Maybe.withDefault inputs.product.yarnSize - , toString = Step.yarnSizeToString - , disabled = not current.enabled } ] @@ -485,7 +483,7 @@ isStepUpcycled upcycled label = viewStepImpacts : Definition -> Step -> Html msg -viewStepImpacts selectedImpact { impacts, complementsImpacts } = +viewStepImpacts selectedImpact { complementsImpacts, impacts } = showIf (Quantity.greaterThanZero (Impact.getImpact selectedImpact.trigram impacts)) <| let stepComplementsImpact = @@ -535,12 +533,12 @@ viewMaterials config = , [ span [ class "text-muted d-flex fs-7 gap-3 justify-content-left ElementTransportDistances" ] (transport |> TransportView.viewDetails - { fullWidth = False + { airTransportLabel = Nothing + , fullWidth = False , hideNoLength = True , onlyIcons = False - , airTransportLabel = Nothing - , seaTransportLabel = Nothing , roadTransportLabel = Nothing + , seaTransportLabel = Nothing } ) , span @@ -656,16 +654,16 @@ createElementSelectorConfig cfg materialInput = let materialQuery : MaterialQuery materialQuery = - { id = materialInput.material.id + { country = materialInput.country |> Maybe.map .code + , id = materialInput.material.id , share = materialInput.share , spinning = materialInput.spinning - , country = materialInput.country |> Maybe.map .code } baseElement = - { element = materialInput.material + { country = materialInput.country + , element = materialInput.material , quantity = materialInput.share - , country = materialInput.country } excluded = @@ -680,12 +678,12 @@ createElementSelectorConfig cfg materialInput = { allowEmptyList = False , baseElement = baseElement , db = - { elements = cfg.db.textile.materials - , countries = + { countries = cfg.db.countries |> Scope.only Scope.Textile |> List.sortBy .name , definitions = cfg.db.definitions + , elements = cfg.db.textile.materials } , defaultCountry = materialInput.material.geographicOrigin , delete = cfg.deleteMaterial @@ -693,14 +691,14 @@ createElementSelectorConfig cfg materialInput = , impact = impacts , openExplorerDetails = cfg.openExplorerDetails , quantityView = - \{ quantity, onChange } -> + \{ onChange, quantity } -> SplitInput.view { disabled = False - , share = quantity , onChange = onChange + , share = quantity } - , selectedImpact = cfg.selectedImpact , selectElement = \_ autocompleteState -> cfg.setModal (cfg.addMaterialModal (Just materialInput) autocompleteState) + , selectedImpact = cfg.selectedImpact , toId = .id >> Material.idToString , toString = .shortName , toTooltip = .materialProcess >> .name @@ -709,9 +707,9 @@ createElementSelectorConfig cfg materialInput = cfg.updateMaterial materialQuery { materialQuery - | id = newElement.element.id + | country = newElement.country |> Maybe.map .code + , id = newElement.element.id , share = newElement.quantity - , country = newElement.country |> Maybe.map .code } } @@ -728,12 +726,12 @@ viewTransport ({ selectedImpact, current, inputs } as config) = [ div [ class "d-flex justify-content-between gap-3 flex-column flex-md-row" ] (current.transport |> TransportView.viewDetails - { fullWidth = False + { airTransportLabel = Nothing + , fullWidth = False , hideNoLength = True , onlyIcons = False - , airTransportLabel = Nothing - , seaTransportLabel = Nothing , roadTransportLabel = Nothing + , seaTransportLabel = Nothing } ) , span [] @@ -823,8 +821,8 @@ ennoblingToxicityView db ({ selectedImpact, inputs } as config) current = bleachingToxicity = current.outputMass |> Formula.bleachingImpacts current.impacts - { bleachingProcess = db.textile.wellKnown.bleaching - , aquaticPollutionScenario = current.country.aquaticPollutionScenario + { aquaticPollutionScenario = current.country.aquaticPollutionScenario + , bleachingProcess = db.textile.wellKnown.bleaching } dyeingToxicity = @@ -832,13 +830,13 @@ ennoblingToxicityView db ({ selectedImpact, inputs } as config) current = |> List.map (\{ material, share } -> Formula.materialDyeingToxicityImpacts current.impacts - { dyeingToxicityProcess = + { aquaticPollutionScenario = current.country.aquaticPollutionScenario + , dyeingToxicityProcess = if Origin.isSynthetic material.origin then db.textile.wellKnown.dyeingSynthetic else db.textile.wellKnown.dyeingCellulosic - , aquaticPollutionScenario = current.country.aquaticPollutionScenario } current.outputMass share @@ -853,10 +851,9 @@ ennoblingToxicityView db ({ selectedImpact, inputs } as config) current = WellKnown.getPrintingProcess kind db.textile.wellKnown in current.outputMass - |> Formula.materialPrintingToxicityImpacts - current.impacts - { printingToxicityProcess = printingToxicityProcess - , aquaticPollutionScenario = current.country.aquaticPollutionScenario + |> Formula.materialPrintingToxicityImpacts current.impacts + { aquaticPollutionScenario = current.country.aquaticPollutionScenario + , printingToxicityProcess = printingToxicityProcess } ratio @@ -1078,13 +1075,6 @@ advancedStepView ({ db, inputs, selectedImpact, current } as config) = (List.map (\line -> li [ class "list-group-item fs-7" ] [ line ]) (case current.label of - Label.Spinning -> - [ yarnSizeField config - ] - - Label.Fabric -> - [ surfaceMassField config ] - Label.Ennobling -> [ div [ class "mb-2" ] [ text "Pré-traitement\u{00A0}: non applicable" ] @@ -1093,6 +1083,9 @@ advancedStepView ({ db, inputs, selectedImpact, current } as config) = [ text "Finition\u{00A0}: apprêt chimique" ] ] + Label.Fabric -> + [ surfaceMassField config ] + Label.Making -> [ makingWasteField config , makingDeadStockField config @@ -1100,6 +1093,10 @@ advancedStepView ({ db, inputs, selectedImpact, current } as config) = , fadingField config ] + Label.Spinning -> + [ yarnSizeField config + ] + Label.Use -> [ daysOfWearInfo config ] @@ -1164,8 +1161,7 @@ advancedStepView ({ db, inputs, selectedImpact, current } as config) = view : Config msg modal -> ViewWithTransport msg view config = - { transport = viewTransport config - , step = + { step = stepView config (if config.current.label == Label.Material then viewMaterials config @@ -1176,4 +1172,5 @@ view config = else regulatoryStepView config ) + , transport = viewTransport config } diff --git a/src/Views/Transport.elm b/src/Views/Transport.elm index e0fe0b730..a5740f4ea 100644 --- a/src/Views/Transport.elm +++ b/src/Views/Transport.elm @@ -9,17 +9,17 @@ import Views.Icon as Icon type alias Config = - { fullWidth : Bool - , onlyIcons : Bool + { airTransportLabel : Maybe String + , fullWidth : Bool , hideNoLength : Bool - , airTransportLabel : Maybe String - , seaTransportLabel : Maybe String + , onlyIcons : Bool , roadTransportLabel : Maybe String + , seaTransportLabel : Maybe String } viewDetails : Config -> Transport -> List (Html msg) -viewDetails { onlyIcons, hideNoLength, airTransportLabel, seaTransportLabel, roadTransportLabel } { air, sea, seaCooled, road, roadCooled } = +viewDetails { airTransportLabel, hideNoLength, onlyIcons, roadTransportLabel, seaTransportLabel } { air, road, roadCooled, sea, seaCooled } = [ { distance = air, icon = Icon.plane, label = Maybe.withDefault "Transport aérien" airTransportLabel } , { distance = sea, icon = Icon.boat, label = Maybe.withDefault "Transport maritime" seaTransportLabel } , { distance = seaCooled, icon = Icon.boatCooled, label = "Transport maritime réfrigéré" } @@ -32,20 +32,20 @@ viewDetails { onlyIcons, hideNoLength, airTransportLabel, seaTransportLabel, roa Nothing else - Just <| entry { onlyIcons = onlyIcons, distance = distance, icon = icon, label = label } + Just <| entry { distance = distance, icon = icon, label = label, onlyIcons = onlyIcons } ) type alias EntryConfig msg = - { onlyIcons : Bool - , distance : Length + { distance : Length , icon : Html msg , label : String + , onlyIcons : Bool } entry : EntryConfig msg -> Html msg -entry { onlyIcons, distance, icon, label } = +entry { distance, icon, label, onlyIcons } = span [ class "d-flex align-items-center gap-1", title label ] [ span [ style "cursor" "help" ] [ icon ] diff --git a/tests/Server/RouteTest.elm b/tests/Server/RouteTest.elm index 59e116c93..9ea98dab0 100644 --- a/tests/Server/RouteTest.elm +++ b/tests/Server/RouteTest.elm @@ -42,13 +42,13 @@ foodEndpoints : StaticDb.Db -> List Test foodEndpoints db = [ describe "GET endpoints" [ testEndpoint db "GET" Encode.null "/food/ingredients" - |> Expect.equal (Just Route.GetFoodIngredientList) + |> Expect.equal (Just Route.FoodGetIngredientList) |> asTest "should map the /food/ingredients endpoint" , testEndpoint db "GET" Encode.null "/food/transforms" - |> Expect.equal (Just Route.GetFoodTransformList) + |> Expect.equal (Just Route.FoodGetTransformList) |> asTest "should map the /food/transforms endpoint" , testEndpoint db "GET" Encode.null "/food/packagings" - |> Expect.equal (Just Route.GetFoodPackagingList) + |> Expect.equal (Just Route.FoodGetPackagingList) |> asTest "should map the /food/packagings endpoint" , [ "/food?" , "ingredients[]=flour;97" @@ -69,17 +69,17 @@ foodEndpoints db = ] |> String.join "&" |> testEndpoint db "GET" Encode.null - |> Expect.equal (Just <| Route.GetFoodRecipe (Ok Fixtures.royalPizza)) + |> Expect.equal (Just <| Route.FoodGetRecipe (Ok Fixtures.royalPizza)) |> asTest "should map the /food endpoint" ] , describe "POST endpoints" [ "/food" |> testEndpoint db "POST" (FoodQuery.encode FoodQuery.empty) - |> Expect.equal (Just Route.PostFoodRecipe) + |> Expect.equal (Just Route.FoodPostRecipe) |> asTest "should map the POST /food endpoint" , "/food" |> testEndpoint db "POST" Encode.null - |> Expect.equal (Just Route.PostFoodRecipe) + |> Expect.equal (Just Route.FoodPostRecipe) |> asTest "should map the POST /food endpoint whatever the request body is" ] , describe "validation" @@ -164,7 +164,7 @@ textileEndpoints db = , "countryMaking=FR" ] |> testEndpoint db "GET" Encode.null - |> Expect.equal (Just <| Route.GetTextileSimulator (Ok sampleQuery)) + |> Expect.equal (Just <| Route.TextileGetSimulator (Ok sampleQuery)) |> asTest "should map the /textile/simulator endpoint" , [ "/textile/simulator?mass=0.17" , "product=tshirt" @@ -178,7 +178,7 @@ textileEndpoints db = |> testEndpoint db "GET" Encode.null |> Expect.equal (Just <| - Route.GetTextileSimulator <| + Route.TextileGetSimulator <| Ok { sampleQuery | disabledSteps = [ Label.Making, Label.Ennobling ] } ) |> asTest "should map the /textile/simulator endpoint with the disabledSteps parameter set" @@ -193,7 +193,7 @@ textileEndpoints db = |> testEndpoint db "GET" Encode.null |> Expect.equal (Just <| - Route.GetTextileSimulatorSingle Definition.Fwe <| + Route.TextileGetSimulatorSingle Definition.Fwe <| Ok sampleQuery ) |> asTest "should map the /textile/simulator/{impact} endpoint" @@ -208,7 +208,7 @@ textileEndpoints db = |> testEndpoint db "GET" Encode.null |> Expect.equal (Just <| - Route.GetTextileSimulatorDetailed <| + Route.TextileGetSimulatorDetailed <| Ok sampleQuery ) |> asTest "should map the /textile/simulator/detailed endpoint" @@ -216,11 +216,11 @@ textileEndpoints db = , describe "POST endpoints" [ "/textile/simulator" |> testEndpoint db "POST" (Query.encode tShirtCotonFrance) - |> Expect.equal (Just Route.PostTextileSimulator) + |> Expect.equal (Just Route.TextilePostSimulator) |> asTest "should map the POST /textile/simulator endpoint" , "/textile/simulator" |> testEndpoint db "POST" Encode.null - |> Expect.equal (Just Route.PostTextileSimulator) + |> Expect.equal (Just Route.TextilePostSimulator) |> asTest "should map the POST /textile/simulator endpoint whatever the request body is" ] , describe "materials param checks" @@ -402,7 +402,7 @@ testEndpoint dbs method body url = extractQuery : Route.Route -> Maybe Query extractQuery route = case route of - Route.GetTextileSimulator (Ok query) -> + Route.TextileGetSimulator (Ok query) -> Just query _ -> @@ -412,7 +412,7 @@ extractQuery route = extractFoodErrors : Route.Route -> Maybe (Dict String String) extractFoodErrors route = case route of - Route.GetFoodRecipe (Err errors) -> + Route.FoodGetRecipe (Err errors) -> Just errors _ -> @@ -422,7 +422,7 @@ extractFoodErrors route = extractTextileErrors : Route.Route -> Maybe (Dict String String) extractTextileErrors route = case route of - Route.GetTextileSimulator (Err errors) -> + Route.TextileGetSimulator (Err errors) -> Just errors _ -> From de121377c868f5ffd5625b9d03d9281cc1e6dd43 Mon Sep 17 00:00:00 2001 From: Nicolas Perriault Date: Thu, 12 Sep 2024 13:44:18 +0200 Subject: [PATCH 09/26] refactor: remove obsolete gitbook markdown parsing code. (#744) --------- Co-authored-by: Vincent Jousse --- package-lock.json | 2 +- review/src/ReviewConfig.elm | 1 + src/Data/Gitbook.elm | 51 +----------- src/Views/Icon.elm | 5 -- src/Views/Markdown.elm | 147 +++-------------------------------- tests/Data/GitbookTest.elm | 30 ------- tests/Views/MarkdownTest.elm | 21 ----- 7 files changed, 13 insertions(+), 244 deletions(-) delete mode 100644 tests/Data/GitbookTest.elm diff --git a/package-lock.json b/package-lock.json index 9abbf4525..0c3177078 100644 --- a/package-lock.json +++ b/package-lock.json @@ -44,7 +44,7 @@ "supertest": "^7.0.0" }, "engines": { - "node": ">=20.0.0" + "node": "20.17" } }, "node_modules/@ampproject/remapping": { diff --git a/review/src/ReviewConfig.elm b/review/src/ReviewConfig.elm index 1d4d9c029..c94bc70e3 100644 --- a/review/src/ReviewConfig.elm +++ b/review/src/ReviewConfig.elm @@ -53,6 +53,7 @@ config = , NoUnsortedConstructors.rule |> Rule.ignoreErrorsForDirectories [ "tests/" ] |> Rule.ignoreErrorsForFiles [ "src/Data/Impact/Definition.elm" ] + |> Rule.ignoreErrorsForFiles [ "src/Data/Impact/Definition.elm" ] -- NoUnused , NoUnused.CustomTypeConstructors.rule [] |> Rule.ignoreErrorsForFiles [ "src/Views/Modal.elm" ] diff --git a/src/Data/Gitbook.elm b/src/Data/Gitbook.elm index 8794c3ad0..496470af3 100644 --- a/src/Data/Gitbook.elm +++ b/src/Data/Gitbook.elm @@ -1,21 +1,11 @@ module Data.Gitbook exposing - ( Page - , Path(..) - , handleMarkdownGitbookLink + ( Path(..) , publicUrlFromPath ) import Data.Env as Env -type alias Page = - { description : Maybe String - , markdown : String - , path : Path - , title : String - } - - type Path = FoodComplements -- Bonus et compléments hors-ACV | FoodDistribution -- Distribution @@ -147,11 +137,6 @@ pathToString path = "textile/etapes-du-cycle-de-vie/etape-6-utilisation" -pathPrefixes : List String -pathPrefixes = - [ "faq", "glossaire", "methodologie" ] - - publicUrlFromPath : Path -> String publicUrlFromPath = pathToString >> publicUrlFromString @@ -160,37 +145,3 @@ publicUrlFromPath = publicUrlFromString : String -> String publicUrlFromString path = Env.gitbookUrl ++ "/" ++ path - - -handleMarkdownGitbookLink : Maybe Path -> String -> String -handleMarkdownGitbookLink maybePath link = - if List.any (\x -> String.startsWith x link) pathPrefixes then - publicUrlFromString link - - else if String.endsWith ".md" link then - case maybePath of - Just path -> - -- check for current folder, eg. "filature.md", "../faq.md", "methodologie/transport.md" - (extractLinkFolder path ++ [ String.replace ".md" "" link ]) - |> String.join "/" - |> publicUrlFromString - - Nothing -> - publicUrlFromString link - - else - link - - -extractLinkFolder : Path -> List String -extractLinkFolder path = - case String.split "/" (pathToString path) of - folder :: _ -> - if folder == ".." then - [] - - else - [ folder ] - - _ -> - [] diff --git a/src/Views/Icon.elm b/src/Views/Icon.elm index ec93487b9..04d9385a8 100644 --- a/src/Views/Icon.elm +++ b/src/Views/Icon.elm @@ -46,11 +46,6 @@ clipboard = icon "clipboard" -exclamation : Html msg -exclamation = - icon "exclamation" - - ham : Html msg ham = icon "ham" diff --git a/src/Views/Markdown.elm b/src/Views/Markdown.elm index e8387b3f7..3b1d84434 100644 --- a/src/Views/Markdown.elm +++ b/src/Views/Markdown.elm @@ -1,30 +1,13 @@ module Views.Markdown exposing - ( ContentType(..) - , parse + ( parse , simple ) -import Data.Gitbook as Gitbook import Html exposing (..) -import Html.Attributes as Attr exposing (..) -import Markdown.Html as MdHtml +import Html.Attributes exposing (..) import Markdown.Parser as Parser -import Markdown.Renderer exposing (Renderer, defaultHtmlRenderer) +import Markdown.Renderer exposing (defaultHtmlRenderer) import Views.Alert as Alert -import Views.Icon as Icon -import Views.Link as Link - - -type - ContentType - -- FIXME: remove the Gitbook type as this is not used anymore - = Gitbook Gitbook.Page - | Simple String - - -siteUrl : String -siteUrl = - "https://ecobalyse.beta.gouv.fr" clean : String -> String @@ -32,109 +15,8 @@ clean = String.split "\n\n" >> List.map String.trim >> String.join "\n\n" -renderer : Maybe Gitbook.Path -> Renderer (Html msg) -renderer maybePath = - { defaultHtmlRenderer - | html = - MdHtml.oneOf - [ MdHtml.tag "hint" renderHint - |> MdHtml.withAttribute "level" - , MdHtml.tag "mark" renderMark - |> MdHtml.withAttribute "style" - - -- NOTE: sometimes gitbook exposes raw HTML in markdown - , MdHtml.tag "a" - (\href title -> renderLink maybePath { destination = href, title = title }) - |> MdHtml.withAttribute "href" - |> MdHtml.withOptionalAttribute "title" - , MdHtml.tag "code" (code []) - , MdHtml.tag "em" (em []) - , MdHtml.tag "p" (p [ class "mb-1" ]) - ] - , image = renderImage - , link = renderLink maybePath - } - - -renderHint : String -> List (Html msg) -> Html msg -renderHint level content = - let - makeIcon icon = - span [ class "fs-4", style "opacity" ".8", style "line-height" "0" ] [ icon ] - in - div - [ class <| "d-flex justify-content-between align-items-start gap-2 alert alert-" ++ level - ] - [ case level of - "danger" -> - makeIcon Icon.exclamation - - "info" -> - makeIcon Icon.info - - "warning" -> - makeIcon Icon.warning - - _ -> - text "" - , div [ class "flex-fill" ] content - ] - - -renderMark : String -> List (Html msg) -> Html msg -renderMark style_ = - span - [ class "mark" - , attribute "style" style_ - , style "background-color" "transparent" - ] - - -renderLink : Maybe Gitbook.Path -> { destination : String, title : Maybe String } -> List (Html msg) -> Html msg -renderLink maybePath { destination, title } = - let - destination_ = - Gitbook.handleMarkdownGitbookLink maybePath destination - - baseAttrs = - List.filterMap identity - [ Maybe.map Attr.title title - ] - in - if String.startsWith siteUrl destination_ then - Link.internal (Attr.href (String.replace siteUrl "" destination_) :: baseAttrs) - - else if String.startsWith "http" destination_ then - Link.external (Attr.href destination_ :: baseAttrs) - - else - Link.internal (Attr.href destination_ :: baseAttrs) - - -renderImage : { alt : String, src : String, title : Maybe String } -> Html msg -renderImage { alt, src, title } = - Html.img - (List.filterMap identity - [ Maybe.map Attr.title title - , src - |> String.replace "../.gitbook/assets/" - "https://raw.githubusercontent.com/MTES-MCT/ecobalyse/docs/.gitbook/assets/" - |> Attr.src - |> Just - , Just <| Attr.alt alt - , Just <| attribute "crossorigin" "anonymous" - ] - ) - [] - - simple : List (Attribute msg) -> String -> Html msg -simple attrs markdown = - view attrs (Simple markdown) - - -view : List (Attribute msg) -> ContentType -> Html msg -view attrs content = +simple attrs content = case parse content of Err errors -> Alert.preformatted @@ -148,18 +30,9 @@ view attrs content = div (class "Markdown bottomed-paragraphs" :: attrs) rendered -parse : ContentType -> Result String (List (Html msg)) -parse content = - let - ( markdown, path ) = - case content of - Gitbook page -> - ( page.markdown, Just page.path ) - - Simple string -> - ( string, Nothing ) - in - clean markdown - |> Parser.parse - |> Result.mapError (List.map Parser.deadEndToString >> String.join "\n") - |> Result.andThen (Markdown.Renderer.render (renderer path)) +parse : String -> Result String (List (Html msg)) +parse = + clean + >> Parser.parse + >> Result.mapError (List.map Parser.deadEndToString >> String.join "\n") + >> Result.andThen (Markdown.Renderer.render defaultHtmlRenderer) diff --git a/tests/Data/GitbookTest.elm b/tests/Data/GitbookTest.elm deleted file mode 100644 index 255d7d141..000000000 --- a/tests/Data/GitbookTest.elm +++ /dev/null @@ -1,30 +0,0 @@ -module Data.GitbookTest exposing (..) - -import Data.Env as Env -import Data.Gitbook as Gitbook -import Expect -import Test exposing (..) -import TestUtils exposing (asTest) - - -suite : Test -suite = - describe "Data.Gitbook" - [ describe "handleMarkdownLink" - [ Gitbook.handleMarkdownGitbookLink Nothing "http://google.com" - |> Expect.equal "http://google.com" - |> asTest "should resolve an external link" - , Gitbook.handleMarkdownGitbookLink (Just Gitbook.TextileSpinning) "http://google.com" - |> Expect.equal "http://google.com" - |> asTest "should resolve an external link even with a path provided" - , Gitbook.handleMarkdownGitbookLink (Just Gitbook.TextileSpinning) "filature.md" - |> Expect.equal (Env.gitbookUrl ++ "/textile/filature") - |> asTest "should resolve an internal link from current page path" - , Gitbook.handleMarkdownGitbookLink (Just Gitbook.TextileSpinning) "../faq.md" - |> Expect.equal (Env.gitbookUrl ++ "/textile/../faq") - |> asTest "should resolve an internal link from current page path down a folder level" - , Gitbook.handleMarkdownGitbookLink (Just Gitbook.TextileSpinning) "foo/bar.md" - |> Expect.equal (Env.gitbookUrl ++ "/textile/foo/bar") - |> asTest "should resolve an internal link from current page path up a folder level" - ] - ] diff --git a/tests/Views/MarkdownTest.elm b/tests/Views/MarkdownTest.elm index 920cc3fea..b77c4588e 100644 --- a/tests/Views/MarkdownTest.elm +++ b/tests/Views/MarkdownTest.elm @@ -1,6 +1,5 @@ module Views.MarkdownTest exposing (..) -import Data.Gitbook as Gitbook import Expect import Html exposing (..) import Test exposing (..) @@ -8,15 +7,6 @@ import TestUtils exposing (asTest) import Views.Markdown as Markdown -gitbookPage : String -> Gitbook.Page -gitbookPage md = - { title = "" - , description = Nothing - , path = Gitbook.TextileUse - , markdown = md - } - - suite : Test suite = describe "Views.Markdown" @@ -25,19 +15,8 @@ suite = -- making it super hard to debug. I couldn't identify any solution to this, -- yet it's important to have this test ensuring the very basics work. [ "plop" - |> Markdown.Simple |> Markdown.parse |> Expect.equal (Ok [ p [] [ text "plop" ] ]) |> asTest "should parse the simplest Markdown string" - , gitbookPage "plop" - |> Markdown.Gitbook - |> Markdown.parse - |> Expect.equal (Ok [ p [] [ text "plop" ] ]) - |> asTest "should parse the simplest Gitbook page Markdown string" - , gitbookPage "Foo & Bar" - |> Markdown.Gitbook - |> Markdown.parse - |> Expect.equal (Ok [ p [] [ text "Foo & Bar" ] ]) - |> asTest "should handle Gitbook page HTML entities in Markdown string" ] ] From efe88f57d4f61e74e84f62c5334a89d36fd767ee Mon Sep 17 00:00:00 2001 From: Nicolas Perriault Date: Thu, 12 Sep 2024 14:47:49 +0200 Subject: [PATCH 10/26] feat: add link to changelog in app footer. (#748) --- src/Views/Page.elm | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/Views/Page.elm b/src/Views/Page.elm index 2ad6cc4e6..b0c93cebc 100644 --- a/src/Views/Page.elm +++ b/src/Views/Page.elm @@ -147,7 +147,7 @@ mainMenuLinks { enableFoodSection } = secondaryMenuLinks : List MenuLink secondaryMenuLinks = - [ Internal "Changelog" Route.Changelog Changelog + [ Internal "Versions" Route.Changelog Changelog , Internal "Statistiques" Route.Stats Stats , External "Documentation" Env.gitbookUrl , External "Communauté" Env.communityUrl @@ -263,7 +263,12 @@ pageFooter session = |> List.map (List.singleton >> li []) |> List.intersperse (li [ attribute "aria-hidden" "true", class "text-muted" ] [ text "|" ]) |> ul [ class "FooterLegal d-flex justify-content-start flex-wrap gap-2 list-unstyled mt-3 pt-2 border-top" ] - , versionLink session.currentVersion + , div [ class "d-flex align-items-center gap-1 fs-9 mb-2" ] + [ versionLink session.currentVersion + , text "(" + , Link.internal [ Route.href Route.Changelog ] [ text "changelog" ] + , text ")" + ] ] ] @@ -274,13 +279,11 @@ versionLink version = Version versionData -> let displayLink url linkText = - p [ class "fs-9 text-muted" ] - [ Link.external - [ class "text-decoration-none" - , href url - ] - [ text <| "Version\u{00A0}: " ++ linkText ] + Link.external + [ class "text-decoration-none" + , href url ] + [ text <| "Version\u{00A0}: " ++ linkText ] in case ( versionData.hash, versionData.tag ) of -- If we have a tag provided, display it by default From f6750b8aea6dc0a4500a23465bfdbc0f0b627743 Mon Sep 17 00:00:00 2001 From: Nicolas Perriault Date: Fri, 13 Sep 2024 10:18:09 +0200 Subject: [PATCH 11/26] fix: encode physicalDurability parameter. (#751) --- src/Data/Textile/Inputs.elm | 8 ++++++++ src/Data/Textile/Query.elm | 1 + src/Data/Unit.elm | 6 ++++++ 3 files changed, 15 insertions(+) diff --git a/src/Data/Textile/Inputs.elm b/src/Data/Textile/Inputs.elm index c118f6b7f..bb667d593 100644 --- a/src/Data/Textile/Inputs.elm +++ b/src/Data/Textile/Inputs.elm @@ -257,6 +257,13 @@ stepsToStrings inputs = else "" ) + ++ (case inputs.physicalDurability of + Just physicalDurability -> + ", durabilité physique " ++ String.fromFloat (Unit.physicalDurabilityToFloat physicalDurability) + + Nothing -> + "" + ) , Format.kgToString inputs.mass ] , ifStepEnabled Label.Material @@ -500,6 +507,7 @@ encode inputs = , ( "mass", Encode.float (Mass.inKilograms inputs.mass) ) , ( "materials", Encode.list encodeMaterialInput inputs.materials ) , ( "numberOfReferences", inputs.numberOfReferences |> Maybe.map Encode.int |> Maybe.withDefault Encode.null ) + , ( "physicalDurability", inputs.physicalDurability |> Maybe.map Unit.encodePhysicalDurability |> Maybe.withDefault Encode.null ) , ( "price", inputs.price |> Maybe.map Economics.encodePrice |> Maybe.withDefault Encode.null ) , ( "printing", inputs.printing |> Maybe.map Printing.encode |> Maybe.withDefault Encode.null ) , ( "product", Product.encode inputs.product ) diff --git a/src/Data/Textile/Query.elm b/src/Data/Textile/Query.elm index 6ce1d82c5..ba4a9863e 100644 --- a/src/Data/Textile/Query.elm +++ b/src/Data/Textile/Query.elm @@ -170,6 +170,7 @@ encode query = , ( "mass", query.mass |> Mass.inKilograms |> Encode.float |> Just ) , ( "materials", query.materials |> Encode.list encodeMaterialQuery |> Just ) , ( "numberOfReferences", query.numberOfReferences |> Maybe.map Encode.int ) + , ( "physicalDurability", query.physicalDurability |> Maybe.map Unit.encodePhysicalDurability ) , ( "price", query.price |> Maybe.map Economics.encodePrice ) , ( "printing", query.printing |> Maybe.map Printing.encode ) , ( "product", query.product |> Product.idToString |> Encode.string |> Just ) diff --git a/src/Data/Unit.elm b/src/Data/Unit.elm index df235f046..c8e33edf9 100644 --- a/src/Data/Unit.elm +++ b/src/Data/Unit.elm @@ -16,6 +16,7 @@ module Data.Unit exposing , decodeYarnSize , encodeImpact , encodeNonPhysicalDurability + , encodePhysicalDurability , encodePickPerMeter , encodeRatio , encodeSurfaceMass @@ -191,6 +192,11 @@ decodePhysicalDurability = |> Decode.map physicalDurability +encodePhysicalDurability : PhysicalDurability -> Encode.Value +encodePhysicalDurability (PhysicalDurability float) = + Encode.float float + + encodeNonPhysicalDurability : NonPhysicalDurability -> Encode.Value encodeNonPhysicalDurability (NonPhysicalDurability float) = Encode.float float From 0e7f1c18e9d879e66f5724aed2fec4baeef2f865 Mon Sep 17 00:00:00 2001 From: Christophe Combelles Date: Mon, 16 Sep 2024 14:27:55 +0200 Subject: [PATCH 12/26] chore(data): removed recycled viscose (unsafe) (#750) [story](https://www.notion.so/Supprimer-le-statut-de-la-viscose-recycl-e-bf537a5bb10e4120a1726c61c4cdec60) removed recycled viscose (unsafe data) ecobalyse-private: viscose_recyclee --- public/data/textile/materials.json | 17 ------------- public/data/textile/processes.json | 38 ------------------------------ 2 files changed, 55 deletions(-) diff --git a/public/data/textile/materials.json b/public/data/textile/materials.json index d4eb32ee2..ca6a86770 100644 --- a/public/data/textile/materials.json +++ b/public/data/textile/materials.json @@ -231,22 +231,5 @@ "defaultCountry": "CN", "priority": 0, "cff": null - }, - { - "id": "ei-viscose-r", - "materialProcessUuid": "7e2fdd1e285fd01e8852977a584a9ae3", - "recycledProcessUuid": null, - "recycledFrom": "ei-viscose", - "name": "Viscose recyclée", - "shortName": "Viscose recyclée", - "origin": "ArtificialFromOrganic", - "primary": true, - "geographicOrigin": "Asie - Pacifique", - "defaultCountry": "CN", - "priority": 0, - "cff": { - "manufacturerAllocation": 0.8, - "recycledQualityRatio": 1 - } } ] diff --git a/public/data/textile/processes.json b/public/data/textile/processes.json index 4a59fee01..416c085e8 100644 --- a/public/data/textile/processes.json +++ b/public/data/textile/processes.json @@ -949,44 +949,6 @@ "waste": 0.0319569, "alias": null }, - { - "name": "viscose fibre, recycled, post-consumer, production [GLO]", - "displayName": "Production de fibres de viscose recyclées", - "info": "Textile > Matières > Matières recyclées", - "unit": "kg", - "source": "Ecoinvent 3.9.1", - "correctif": "", - "step_usage": "Matières", - "uuid": "7e2fdd1e285fd01e8852977a584a9ae3", - "impacts": { - "acd": 0, - "cch": 0, - "etf": 0, - "etf-c": 0, - "fru": 0, - "fwe": 0, - "htc": 0, - "htc-c": 0, - "htn": 0, - "htn-c": 0, - "ior": 0, - "ldu": 0, - "mru": 0, - "ozd": 0, - "pco": 0, - "pma": 0, - "swe": 0, - "tre": 0, - "wtu": 0, - "ecs": 61.70583196748168, - "pef": 66.11022949135275 - }, - "heat_MJ": 0, - "elec_pppm": 0, - "elec_MJ": 0, - "waste": 0.137851, - "alias": null - }, { "name": "fibre production, flax, retting [RoW]", "displayName": "Production de fibres de lin, rouissage", From 54b6577e974b589b78938761c336243d6149d3df Mon Sep 17 00:00:00 2001 From: Nicolas Perriault Date: Mon, 16 Sep 2024 16:20:32 +0200 Subject: [PATCH 13/26] chore: upgrade deps (2024-09-12) (#746) Closes #739 Closes #740 Closes #741 Closes #742 Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 93 ++++++++++++++++++++++++++--------------------- package.json | 2 +- 2 files changed, 53 insertions(+), 42 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0c3177078..bde2c5768 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,7 @@ "@sentry/tracing": "^7.114.0", "cors": "^2.8.5", "dotenv": "^16.4.5", - "express": "^4.19.2", + "express": "^4.21.0", "express-rate-limit": "^7.4.0", "helmet": "^7.1.0", "js-yaml": "^4.1.0", @@ -5123,9 +5123,9 @@ } }, "node_modules/body-parser": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", - "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", "dependencies": { "bytes": "3.1.2", "content-type": "~1.0.5", @@ -5135,7 +5135,7 @@ "http-errors": "2.0.0", "iconv-lite": "0.4.24", "on-finished": "2.4.1", - "qs": "6.11.0", + "qs": "6.13.0", "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" @@ -6553,9 +6553,9 @@ "dev": true }, "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "engines": { "node": ">= 0.8" } @@ -6714,36 +6714,36 @@ } }, "node_modules/express": { - "version": "4.19.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", - "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", + "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.2", + "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", "cookie": "0.6.0", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "1.2.0", + "finalhandler": "1.3.1", "fresh": "0.5.2", "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", + "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", + "path-to-regexp": "0.1.10", "proxy-addr": "~2.0.7", - "qs": "6.11.0", + "qs": "6.13.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", + "send": "0.19.0", + "serve-static": "1.16.2", "setprototypeof": "1.2.0", "statuses": "2.0.1", "type-is": "~1.6.18", @@ -6868,12 +6868,12 @@ } }, "node_modules/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", "dependencies": { "debug": "2.6.9", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "on-finished": "2.4.1", "parseurl": "~1.3.3", @@ -8931,9 +8931,12 @@ } }, "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/merge-stream": { "version": "2.0.0", @@ -9806,9 +9809,9 @@ "license": "ISC" }, "node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", + "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==" }, "node_modules/path-type": { "version": "4.0.0", @@ -10124,11 +10127,11 @@ ] }, "node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", "dependencies": { - "side-channel": "^1.0.4" + "side-channel": "^1.0.6" }, "engines": { "node": ">=0.6" @@ -10667,9 +10670,9 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", "dependencies": { "debug": "2.6.9", "depd": "2.0.0", @@ -10689,20 +10692,28 @@ "node": ">= 0.8.0" } }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/send/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", "dependencies": { - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.18.0" + "send": "0.19.0" }, "engines": { "node": ">= 0.8.0" diff --git a/package.json b/package.json index 77c351eca..667961b4d 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "@sentry/tracing": "^7.114.0", "cors": "^2.8.5", "dotenv": "^16.4.5", - "express": "^4.19.2", + "express": "^4.21.0", "express-rate-limit": "^7.4.0", "helmet": "^7.1.0", "js-yaml": "^4.1.0", From a57dbf9b676b8a58b1ba10f98d1454cda6138f42 Mon Sep 17 00:00:00 2001 From: Nicolas Perriault Date: Tue, 17 Sep 2024 10:44:08 +0200 Subject: [PATCH 14/26] chore: render food api docs conditionally from env. (#755) While we introduced the `ENABLE_FOOD_SECTION` env var to hide or show the Food section, associated menus and links, we hide the API docs for Food completely and there's no current way to access these API docs. This patch checks for the value of the `ENABLE_FOOD_SECTION` var and filter the openapi docs appropriately. --- README.md | 1 + openapi.yaml | 308 +++++++++++++++++++++++++-------------------------- server.js | 32 +++++- 3 files changed, 183 insertions(+), 158 deletions(-) diff --git a/README.md b/README.md index 72b5df20f..4856674eb 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,7 @@ Les variables d'environnement suivantes doivent être définies : - `EMAIL_HOST` : le host SMTP pour envoyer les mail liés à l'authentification - `EMAIL_HOST_USER`: l'utilisateur du compte SMTP - `EMAIL_HOST_PASSWORD` : le mot de passe du compte SMTP pour envoyer les mail liés à l'authentification +- `ENABLE_FOOD_SECTION` : affichage ou non de la section expérimentale dédiée à l'alimentaire (valeur `True` ou `False`, par défault `False`) - `MATOMO_HOST`: le domaine de l'instance Matomo permettant le suivi d'audience du produit (typiquement `stats.beta.gouv.fr`). - `MATOMO_SITE_ID`: l'identifiant du site Ecobalyse sur l'instance Matomo permettant le suivi d'audience du produit. - `MATOMO_TOKEN`: le token Matomo permettant le suivi d'audience du produit. diff --git a/openapi.yaml b/openapi.yaml index aa15caad2..194b32c4d 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -295,160 +295,160 @@ paths: application/json: schema: $ref: "#/components/schemas/InvalidParametersError" - # /food/countries: - # get: - # tags: - # - Alimentaire - # summary: Liste des pays utilisables pour les simulations alimentairess. - # responses: - # 200: - # description: Opération réussie - # content: - # application/json: - # schema: - # $ref: "#/components/schemas/CountryListResponse" - # /food/ingredients: - # get: - # tags: - # - Alimentaire - # summary: Liste des ingrédients disponibles pour élaborer une recette - # responses: - # 200: - # description: Opération réussie - # content: - # application/json: - # schema: - # $ref: "#/components/schemas/IngredientListResponse" - # /food/transforms: - # get: - # tags: - # - Alimentaire - # summary: Liste des procédés de transformation alimentaire - # responses: - # 200: - # description: Opération réussie - # content: - # application/json: - # schema: - # $ref: "#/components/schemas/TransformListResponse" - # /food/packagings: - # get: - # tags: - # - Alimentaire - # summary: Liste des emballages disponibles pour conditionner une recette - # responses: - # 200: - # description: Opération réussie - # content: - # application/json: - # schema: - # $ref: "#/components/schemas/PackagingListResponse" - # /food: - # get: - # tags: - # - Alimentaire - # summary: Calcul des impacts environnementaux d'une recette alimentaire - # parameters: - # - $ref: "#/components/parameters/ingredientsParam" - # - $ref: "#/components/parameters/transformParam" - # - $ref: "#/components/parameters/packagingParam" - # - $ref: "#/components/parameters/distributionParam" - # - $ref: "#/components/parameters/preparationParam" - # responses: - # 200: - # description: Opération réussie - # content: - # application/json: - # schema: - # $ref: "#/components/schemas/RecipeResultsResponse" - # 400: - # description: Paramètres invalides - # content: - # application/json: - # schema: - # $ref: "#/components/schemas/InvalidParametersError" - # post: - # tags: - # - Alimentaire - # summary: Calcul des impacts environnementaux d'une recette alimentaire - # requestBody: - # description: Requête modélisant les éléments de la recette à évaluer - # required: true - # content: - # application/json: - # schema: - # $ref: "#/components/schemas/FoodQuery" - # examples: - # CarrotCake: - # summary: "Carrot cake conventionnel" - # value: - # ingredients: - # - id: egg - # mass: 120 - # - id: wheat - # mass: 140 - # - id: milk - # mass: 60 - # - id: carrot - # mass: 225 - # transform: - # code: AGRIBALU000000003103966 - # mass: 545 - # packaging: - # - code: AGRIBALU000000003104019 - # mass: 105 - # distribution: ambient - # preparation: - # - refrigeration - # SpanishOrganicCarrotCake: - # summary: "Carrot cake bio, origine Espagne" - # value: - # ingredients: - # - id: egg-organic - # mass: 120 - # country: ES - # - id: wheat-organic - # mass: 140 - # country: ES - # - id: milk-organic - # mass: 60 - # country: ES - # - id: carrot-organic - # mass: 225 - # country: ES - # transform: - # code: AGRIBALU000000003103966 - # mass: 545 - # packaging: - # - code: AGRIBALU000000003104019 - # mass: 105 - # distribution: ambient - # preparation: - # - refrigeration - # BrazilianMango: - # summary: "Mangue du Brésil" - # value: - # ingredients: - # - id: mango - # mass: 500 - # country: BR - # transform: null - # packaging: [] - # distribution: ambient - # preparation: [] - # responses: - # 200: - # description: Opération réussie - # content: - # application/json: - # schema: - # $ref: "#/components/schemas/RecipeResultsResponse" - # 400: - # description: Paramètres invalides - # content: - # application/json: - # schema: - # $ref: "#/components/schemas/InvalidParametersError" + /food/countries: + get: + tags: + - Alimentaire + summary: Liste des pays utilisables pour les simulations alimentairess. + responses: + 200: + description: Opération réussie + content: + application/json: + schema: + $ref: "#/components/schemas/CountryListResponse" + /food/ingredients: + get: + tags: + - Alimentaire + summary: Liste des ingrédients disponibles pour élaborer une recette + responses: + 200: + description: Opération réussie + content: + application/json: + schema: + $ref: "#/components/schemas/IngredientListResponse" + /food/transforms: + get: + tags: + - Alimentaire + summary: Liste des procédés de transformation alimentaire + responses: + 200: + description: Opération réussie + content: + application/json: + schema: + $ref: "#/components/schemas/TransformListResponse" + /food/packagings: + get: + tags: + - Alimentaire + summary: Liste des emballages disponibles pour conditionner une recette + responses: + 200: + description: Opération réussie + content: + application/json: + schema: + $ref: "#/components/schemas/PackagingListResponse" + /food: + get: + tags: + - Alimentaire + summary: Calcul des impacts environnementaux d'une recette alimentaire + parameters: + - $ref: "#/components/parameters/ingredientsParam" + - $ref: "#/components/parameters/transformParam" + - $ref: "#/components/parameters/packagingParam" + - $ref: "#/components/parameters/distributionParam" + - $ref: "#/components/parameters/preparationParam" + responses: + 200: + description: Opération réussie + content: + application/json: + schema: + $ref: "#/components/schemas/RecipeResultsResponse" + 400: + description: Paramètres invalides + content: + application/json: + schema: + $ref: "#/components/schemas/InvalidParametersError" + post: + tags: + - Alimentaire + summary: Calcul des impacts environnementaux d'une recette alimentaire + requestBody: + description: Requête modélisant les éléments de la recette à évaluer + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/FoodQuery" + examples: + CarrotCake: + summary: "Carrot cake conventionnel" + value: + ingredients: + - id: egg + mass: 120 + - id: wheat + mass: 140 + - id: milk + mass: 60 + - id: carrot + mass: 225 + transform: + code: AGRIBALU000000003103966 + mass: 545 + packaging: + - code: AGRIBALU000000003104019 + mass: 105 + distribution: ambient + preparation: + - refrigeration + SpanishOrganicCarrotCake: + summary: "Carrot cake bio, origine Espagne" + value: + ingredients: + - id: egg-organic + mass: 120 + country: ES + - id: wheat-organic + mass: 140 + country: ES + - id: milk-organic + mass: 60 + country: ES + - id: carrot-organic + mass: 225 + country: ES + transform: + code: AGRIBALU000000003103966 + mass: 545 + packaging: + - code: AGRIBALU000000003104019 + mass: 105 + distribution: ambient + preparation: + - refrigeration + BrazilianMango: + summary: "Mangue du Brésil" + value: + ingredients: + - id: mango + mass: 500 + country: BR + transform: null + packaging: [] + distribution: ambient + preparation: [] + responses: + 200: + description: Opération réussie + content: + application/json: + schema: + $ref: "#/components/schemas/RecipeResultsResponse" + 400: + description: Paramètres invalides + content: + application/json: + schema: + $ref: "#/components/schemas/InvalidParametersError" components: securitySchemes: token: diff --git a/server.js b/server.js index a33311666..979f0e9fb 100644 --- a/server.js +++ b/server.js @@ -21,7 +21,14 @@ const djangoPort = 8002; const version = express(); // version app // Env vars -const { ECOBALYSE_DATA_DIR, MATOMO_HOST, MATOMO_SITE_ID, MATOMO_TOKEN, NODE_ENV } = process.env; +const { + ECOBALYSE_DATA_DIR, + ENABLE_FOOD_SECTION, + MATOMO_HOST, + MATOMO_SITE_ID, + MATOMO_TOKEN, + NODE_ENV, +} = process.env; var rateLimiter = rateLimit({ windowMs: 1000, // 1 second @@ -155,8 +162,10 @@ if (fs.existsSync(versionsDir)) { // API -const openApiContents = yaml.load(fs.readFileSync("openapi.yaml")); -openApiContents.version = require("./package.json").version; +const openApiContents = processOpenApi( + yaml.load(fs.readFileSync("openapi.yaml")), + require("./package.json").version, +); // Matomo const apiTracker = lib.setupTracker(openApiContents); @@ -186,6 +195,18 @@ const getProcesses = async (token, customProcessesImpacts, customProcesses) => { } }; +function processOpenApi(contents, versionNumber) { + // Add app version info to openapi docs + contents.version = versionNumber; + // Remove food api docs if disabled from env + if (ENABLE_FOOD_SECTION !== "True") { + contents.paths = Object.fromEntries( + Object.entries(contents.paths).filter(([path, _]) => !path.startsWith("/food")), + ); + } + return contents; +} + app.get("/processes/processes.json", async (req, res) => { return res.status(200).send(await getProcesses(req.headers.token)); }); @@ -243,7 +264,10 @@ version.use("/:versionNumber", checkVersionAndPath, (req, res, next) => { }); version.get("/:versionNumber/api", checkVersionAndPath, (req, res) => { - const openApiContents = yaml.load(fs.readFileSync(path.join(req.staticDir, "openapi.yaml"))); + const openApiContents = processOpenApi( + yaml.load(fs.readFileSync(path.join(req.staticDir, "openapi.yaml"))), + req.params.versionNumber, + ); res.status(200).send(openApiContents); }); From 5b41ef6a6e1d4e799ed2abe7f21a321a87f9ae83 Mon Sep 17 00:00:00 2001 From: Nicolas Perriault Date: Tue, 17 Sep 2024 15:26:38 +0200 Subject: [PATCH 15/26] fix: check db integrity after building it (#753) We didn't check if the static Elm database built was valid, allowing deploying to production a broken app. Let's introduce a script to check the build database integrity. --- .github/workflows/node.js.yml | 3 --- .gitignore | 1 + check-db.js | 30 +++++++++++++++++++++++ compute-aggregated.js | 2 +- package.json | 5 ++-- src/CheckDb.elm | 45 +++++++++++++++++++++++++++++++++++ 6 files changed, 80 insertions(+), 6 deletions(-) create mode 100644 check-db.js create mode 100644 src/CheckDb.elm diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index dfe8e28bb..d758a4031 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -91,9 +91,6 @@ jobs: - name: Build app run: npm run build --if-present - - name: Build Elm static Db - run: npm run db:build - - name: Run prettier, openapi & ruff formatting check run: npm run lint:all diff --git a/.gitignore b/.gitignore index c958f2513..855912abb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ /.env /dist elm-stuff +/check-db-app.js /compute-aggregated-app.js /node_modules /public/app.js diff --git a/check-db.js b/check-db.js new file mode 100644 index 000000000..7250f4e27 --- /dev/null +++ b/check-db.js @@ -0,0 +1,30 @@ +require("dotenv").config(); +const fs = require("fs"); +const { Elm } = require("./check-db-app"); +const lib = require("./lib"); + +const { ECOBALYSE_DATA_DIR } = process.env; + +let dataFiles; +try { + dataFiles = lib.getDataFiles(ECOBALYSE_DATA_DIR); +} catch (err) { + console.error(`🚨 ERROR: ${err.message}`); + process.exit(1); +} + +const elmApp = Elm.CheckDb.init({ + flags: { + textileProcesses: fs.readFileSync(dataFiles.textileDetailed, "utf-8"), + foodProcesses: fs.readFileSync(dataFiles.foodDetailed, "utf-8"), + }, +}); + +elmApp.ports.logAndExit.subscribe(({ message, status }) => { + if (status > 0) { + console.error(`🚨 ERROR: ${message}`); + } else { + console.info(message); + } + process.exit(status); +}); diff --git a/compute-aggregated.js b/compute-aggregated.js index a309ac219..3c487657d 100644 --- a/compute-aggregated.js +++ b/compute-aggregated.js @@ -41,7 +41,7 @@ elmApp.ports.export.subscribe( exportJson(dataFiles.foodNoDetails, foodProcessesOnlyAggregated); console.log(` 4 files exported to: -x" + - ${dataFiles.textileDetailed} - ${dataFiles.foodDetailed} - ${dataFiles.textileNoDetails} diff --git a/package.json b/package.json index 667961b4d..87cafce18 100644 --- a/package.json +++ b/package.json @@ -16,10 +16,10 @@ "build": "npm run server:build && rimraf dist && npm run build:init && parcel build index.html --public-url ./", "build:init": "./bin/update-version.sh && mkdir -p dist && cp -r public/* dist/ && npm run db:build", "build:standalone-app": "npm run build && npm run server:build && cp server-app.js dist/ && cp openapi.yaml dist/", - "processes:build": "elm make src/ComputeAggregated.elm --output=compute-aggregated-app.js && node compute-aggregated.js", "decrypt": "./bin/decrypt", "encrypt": "./bin/encrypt", - "db:build": "./bin/build-db", + "db:build": "./bin/build-db && npm run db:check", + "db:check": "elm make src/CheckDb.elm --optimize --output=check-db-app.js 1> /dev/null && node check-db.js", "lint:openapi": "npx swagger-cli validate openapi.yaml", "lint:prettier": "prettier --config .prettierrc --check", "lint:prettier:all": "npm run lint:prettier -- .", @@ -34,6 +34,7 @@ "fix:ruff:format": "pipenv run ruff format --force-exclude", "fix:all": "npm run fix:ruff:all && npm run fix:prettier:all", "format:json": "npx prettier@3.0.3 --write . && pipenv run ruff check --select I --fix && pipenv run ruff format", + "processes:build": "elm make src/ComputeAggregated.elm --output=compute-aggregated-app.js 1> /dev/null && node compute-aggregated.js", "server:build": "npm run db:build && elm make src/Server.elm --optimize --output=server-app.js", "server:dev": "npm run server:build && nodemon server.js --config nodemon.json", "server:debug": "elm make src/Server.elm --output=server-app.js && nodemon server.js --config nodemon.json", diff --git a/src/CheckDb.elm b/src/CheckDb.elm new file mode 100644 index 000000000..2d98b455d --- /dev/null +++ b/src/CheckDb.elm @@ -0,0 +1,45 @@ +port module CheckDb exposing (main) + +import Static.Db as StaticDb exposing (Db) +import Static.Json as StaticJson + + +type alias Flags = + { foodProcesses : String + , textileProcesses : String + } + + +init : Flags -> ( (), Cmd () ) +init flags = + ( () + , case checkStaticDatabases flags of + Err error -> + logAndExit { message = error, status = 1 } + + Ok _ -> + logAndExit { message = "Dbs look fine", status = 0 } + ) + + +checkStaticDatabases : Flags -> Result String ( Db, Db ) +checkStaticDatabases detailedRawJsonProcesses = + Result.map2 Tuple.pair + (StaticDb.db StaticJson.rawJsonProcesses + |> Result.mapError (\err -> "Non-detailed Db is invalid: " ++ err) + ) + (StaticDb.db detailedRawJsonProcesses + |> Result.mapError (\err -> "Detailed Db is invalid: " ++ err) + ) + + +main : Program Flags () () +main = + Platform.worker + { init = init + , subscriptions = always Sub.none + , update = \_ _ -> ( (), Cmd.none ) + } + + +port logAndExit : { message : String, status : Int } -> Cmd msg From 59d810b2a97557e0f110540d5305d7d4eb4a2317 Mon Sep 17 00:00:00 2001 From: Vincent Jousse Date: Wed, 18 Sep 2024 17:17:08 +0200 Subject: [PATCH 16/26] chore: filter sentry errors by env (#752) ## :wrench: Problem Production and review app errors are mixed and matched in Sentry. [Notion card](https://www.notion.so/Ne-loguer-les-v-nements-Sentry-qu-en-production-43f188ca8a624f08b6b7c7fd8e31254d) ## :cake: Solution Use a different environment in Sentry for production and review-apps errors. ## :rotating_light: Points to watch / comments I've introduced a new file `scalingo.json` used to configure review apps https://doc.scalingo.com/platform/app/review-apps#run-a-task-after-each-deployment-of-a-review-app I have CORS issue with the envelope call on the review-app without knowing why. Could be a server config/version issue cf. https://github.com/getsentry/sentry/issues/24637 ## :desert_island: How to test Clicking on the footer should throw an error (the code will be removed before merging). --- .env.sample | 2 +- README.md | 2 +- bin/build-specific-app-version.sh | 2 +- index.js | 3 +++ lib/instrument.js | 10 +++++++--- scalingo.json | 7 +++++++ 6 files changed, 20 insertions(+), 6 deletions(-) create mode 100644 scalingo.json diff --git a/.env.sample b/.env.sample index 469807bdd..da250d0cb 100644 --- a/.env.sample +++ b/.env.sample @@ -11,6 +11,6 @@ ENABLE_FOOD_SECTION=True MATOMO_HOST=stats.beta.gouv.fr MATOMO_SITE_ID=57 MATOMO_TOKEN=xxx -NODE_ENV=production +NODE_ENV=development SCALINGO_POSTGRESQL_URL=please-change-this SENTRY_DSN=please-change-this diff --git a/README.md b/README.md index 4856674eb..c6f6a127c 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ Les variables d'environnement suivantes doivent être définies : - `MATOMO_HOST`: le domaine de l'instance Matomo permettant le suivi d'audience du produit (typiquement `stats.beta.gouv.fr`). - `MATOMO_SITE_ID`: l'identifiant du site Ecobalyse sur l'instance Matomo permettant le suivi d'audience du produit. - `MATOMO_TOKEN`: le token Matomo permettant le suivi d'audience du produit. -- `NODE_ENV`: l'environnement d'exécution nodejs (par défaut, `production`) +- `NODE_ENV`: l'environnement d'exécution nodejs (par défaut, `development`) - `SCALINGO_POSTGRESQL_URL` : l'uri pour accéder à Postgresl (définie automatiquement par Scalingo). Si non défini sqlite3 est utilisé. - `SENTRY_DSN`: le DSN [Sentry](https://sentry.io) à utiliser pour les rapports d'erreur. diff --git a/bin/build-specific-app-version.sh b/bin/build-specific-app-version.sh index 3b3cd6229..76617e220 100755 --- a/bin/build-specific-app-version.sh +++ b/bin/build-specific-app-version.sh @@ -158,7 +158,7 @@ cd $PUBLIC_GIT_CLONE_DIR # Installing node stuff # We need to specify dev as the env to avoid errors with needed dev packages at build time like # old husky prerequesite -NODE_ENV=dev npm ci +NODE_ENV=development npm ci # We want a production build export NODE_ENV=production diff --git a/index.js b/index.js index 29d3e7bfa..274c6e86f 100644 --- a/index.js +++ b/index.js @@ -11,11 +11,14 @@ if (process.env.SENTRY_DSN) { allowUrls: [ /^https:\/\/ecobalyse\.beta\.gouv\.fr/, /^https:\/\/staging-ecobalyse\.incubateur\.net/, + // Review apps + /^https:\/\/ecobalyse-pr.*\.osc-fr1\.scalingo\.io/, ], ignoreErrors: [ // Most often due to DOM-aggressive browser extensions /_VirtualDom_applyPatch/, ], + environment: process.env.IS_REVIEW_APP ? "review-app" : process.env.NODE_ENV || "development", }); } diff --git a/lib/instrument.js b/lib/instrument.js index e1bbb5aaf..7dbae099d 100644 --- a/lib/instrument.js +++ b/lib/instrument.js @@ -1,19 +1,23 @@ const Sentry = require("@sentry/node"); const { nodeProfilingIntegration } = require("@sentry/profiling-node"); -const { SENTRY_DSN } = process.env; +const { SENTRY_DSN, IS_REVIEW_APP, NODE_ENV } = process.env; -if (SENTRY_DSN) { +const shouldInitSentry = SENTRY_DSN && NODE_ENV === "production"; + +if (shouldInitSentry) { Sentry.init({ dsn: SENTRY_DSN, integrations: [nodeProfilingIntegration()], tracesSampleRate: 1.0, profilesSampleRate: 1.0, + // IS_REVIEW_APP is set by `scalingo.json` only on review apps + environment: IS_REVIEW_APP ? "review-app" : NODE_ENV, }); } function monitorExpressApp(app) { - if (SENTRY_DSN) { + if (shouldInitSentry) { Sentry.setupExpressErrorHandler(app); } } diff --git a/scalingo.json b/scalingo.json new file mode 100644 index 000000000..2f8b9b2ae --- /dev/null +++ b/scalingo.json @@ -0,0 +1,7 @@ +{ + "env": { + "IS_REVIEW_APP": { + "value": "true" + } + } +} From 757d5a6b50363995011e23bd5719adedb44d296f Mon Sep 17 00:00:00 2001 From: Christophe Combelles Date: Thu, 19 Sep 2024 09:27:36 +0200 Subject: [PATCH 17/26] fix: in brightway explorer: improve display of compartment categories, if any (#754) --- data/notebooks/explore.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/data/notebooks/explore.py b/data/notebooks/explore.py index 1d190f5b0..e3c7df4e2 100644 --- a/data/notebooks/explore.py +++ b/data/notebooks/explore.py @@ -112,12 +112,14 @@ def w_csv_button(contents, columns): def display_results(database, search, limit): """display the list of search results in the w_results widget""" results = list(bw2data.Database(database).search(search, limit=limit)) + for a in results: + a["categories"] = ", ".join(a.get("categories", [])) w_results.clear_output() w_details.clear_output() w_activity.options = [("", "")] + [ ( - str(i) - + f" {a.get('name', '')} {'(' if a.get('categories') else ''}{', '.join(a.get('categories', []))}{')' if a.get('categories') else ''}", + str(i) + f" {a.get('name', '')} " + f"{('(in ' + a.get('categories', []) + ')') if a.get('categories') else ''}", a, ) for i, a in enumerate(results) @@ -128,7 +130,7 @@ def display_results(database, search, limit): display( Markdown(f"## {('+' if len(results)==LIMIT else '')}{len(results)} results") ) - columns = ["name", "code", "location"] + columns = ["name", "categories", "code", "location"] html = pandas.io.formats.style.Styler( pandas.DataFrame(results, columns=columns) ) @@ -651,7 +653,7 @@ def display_main_data(method, impact_category, activity): f"
  • Name: {input_.get('name', 'N/A')}
  • " f"
  • Code: {input_.get('code', 'N/A')}
  • " f"
  • Type: {input_.get('type', 'N/A')}
  • " - f"
  • Categories: {', '.join(input_.get('categories', 'N/A'))}
  • " + f"
  • Categories: {', '.join(input_.get('categories', []))}
  • " f"
  • CAS number: {str(input_.get('CAS number'))}
  • " f"
  • Unit: {input_.get('unit', 'N/A')}
  • " f"
  • Id: {input_.get('id', 'N/A')}
  • " @@ -720,7 +722,8 @@ def display_main_data(method, impact_category, activity): w_details.clear_output() display( Markdown( - f"# 1 {activity.get('unit', '')} of {activity.get('name', '')} ({', '.join(activity.get('categories', 'N/A'))})" + f"# 1 {activity.get('unit', '')} of {activity.get('name', '')} " + f"{('(in ' + ', '.join(activity.get('categories', [])) + ')') if activity.get('categories') else ''}" ) ) display( From 5ad9b8cfef5dcbb9f5a23291e3580f96b985e125 Mon Sep 17 00:00:00 2001 From: paulboosz Date: Thu, 19 Sep 2024 09:32:39 +0200 Subject: [PATCH 18/26] chore: fix npm error when make_export_food (#758) Bug : ![image](https://github.com/user-attachments/assets/75098475-311d-4bf6-b88c-769e30f4dc90) Fix : - The previous chown command in the Dockerfile is setting ownership to user ID 1000, but the error message suggests the container is running as user ID 1001. -> use the actual jovyan user ID in the container - clear the npm cache before running any npm commands --- data/Makefile | 3 +-- data/docker/entrypoint.sh | 9 +++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/data/Makefile b/data/Makefile index b713cb3de..521ccbf39 100644 --- a/data/Makefile +++ b/data/Makefile @@ -47,7 +47,7 @@ compare_food: @$(call DOCKER,python3 export.py compare) format: - npm run format:json + npm run fix:all python: echo Running Python inside the container... @@ -94,4 +94,3 @@ clean_image: docker image rm $(NAME) clean: clean_data clean_image - diff --git a/data/docker/entrypoint.sh b/data/docker/entrypoint.sh index a21d800db..1544c574f 100755 --- a/data/docker/entrypoint.sh +++ b/data/docker/entrypoint.sh @@ -8,6 +8,11 @@ if [ $ECOBALYSE_ID -ne $JOVYAN_ID ]; then usermod -u $ECOBALYSE_ID jovyan fi -chown -R 1000:100 "/home/jovyan/.npm" +# Ensure .npm directory is owned by jovyan +mkdir -p /home/jovyan/.npm +chown -R jovyan:100 "/home/jovyan/.npm" -gosu jovyan "$@" +# Clear npm cache +su jovyan -c "npm cache clean --force" + +exec gosu jovyan "$@" From ea2cd9ff566129081ccf37caef25377717933c9d Mon Sep 17 00:00:00 2001 From: Nicolas Perriault Date: Thu, 19 Sep 2024 10:12:25 +0200 Subject: [PATCH 19/26] fix: fix github CI python build setup. (#762) --- .github/workflows/node.js.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index d758a4031..db35ece7c 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -53,9 +53,7 @@ jobs: uses: actions/cache@v4 with: path: ~/.local/share/virtualenvs - key: ${{ runner.os }}-${{ matrix.python-version }}-pipenv-${{ hashFiles('Pipfile.lock') }} - restore-keys: | - ${{ runner.os }}-pipenv- + key: ${{ runner.os }}-python-${{ steps.setup-python.outputs.python-version }}-pipenv-${{ hashFiles('Pipfile.lock') }} - name: Install Node dependencies run: npm ci --prefer-offline --no-audit From 43d03f755b1b3c9865c195e8410bd54e86914abf Mon Sep 17 00:00:00 2001 From: Christophe Combelles Date: Wed, 4 Sep 2024 09:50:09 +0200 Subject: [PATCH 20/26] added agribalyse 3.2 --- data/common/import_.py | 18 ++++++++++++------ data/import_agribalyse.py | 33 ++++++++++++++++++++++++++++++--- 2 files changed, 42 insertions(+), 9 deletions(-) diff --git a/data/common/import_.py b/data/common/import_.py index 7aac80c45..5afe7b86b 100644 --- a/data/common/import_.py +++ b/data/common/import_.py @@ -175,6 +175,7 @@ def import_simapro_csv( dbname, biosphere=BIOSPHERE, migrations=[], + first_strategies=[], excluded_strategies=[], other_strategies=[], source=None, @@ -189,7 +190,7 @@ def import_simapro_csv( zf.extractall() unzipped = datapath[0:-4] - if "AGB3.1.1" in datapath: + if "AGB" in datapath: print("### Patching Agribalyse...") # `yield` is used as a variable in some Simapro parameters. bw2parameters cannot handle it: # (sed is faster than Python) @@ -221,11 +222,16 @@ def import_simapro_csv( print("### Applying strategies...") # exclude strategies/migrations - database.strategies = [ - s - for s in database.strategies - if not any([e in repr(s) for e in excluded_strategies]) - ] + other_strategies + database.strategies = ( + first_strategies + + [ + s + for s in database.strategies + if not any([e in repr(s) for e in excluded_strategies]) + ] + + other_strategies + ) + database.apply_strategies() database.statistics() # try to link remaining unlinked technosphere activities diff --git a/data/import_agribalyse.py b/data/import_agribalyse.py index 5f83f99c9..680f0d2fd 100755 --- a/data/import_agribalyse.py +++ b/data/import_agribalyse.py @@ -15,7 +15,8 @@ ) PROJECT = "default" -AGRIBALYSE = "AGB3.1.1.20230306.CSV.zip" # Agribalyse +AGRIBALYSE31 = "AGB3.1.1.20230306.CSV.zip" # Agribalyse 3.1 +AGRIBALYSE32 = "AGB32beta_08082024.CSV.zip" # Agribalyse 3.2 GINKO = "CSV_369p_et_298chapeaux_final.csv.zip" # additional organic processes PASTOECO = [ "CONVEN~1.CSV.zip", @@ -178,6 +179,19 @@ def remove_negative_land_use_on_tomato(db): return new_db +def remove_some_processes(db): + """Some processes make the whole import fail + due to inability to parse the Input and Calculated parameters""" + new_db = [] + for ds in db: + new_ds = copy.deepcopy(ds) + if ds.get("simapro metadata", {}).get("Process identifier") not in ( + "EI3CQUNI000025017103662", + ): + new_db.append(new_ds) + return new_db + + GINKO_STRATEGIES = [ remove_negative_land_use_on_tomato, remove_azadirachtine, @@ -205,12 +219,25 @@ def remove_negative_land_use_on_tomato(db): bw2io.bw2setup() add_missing_substances(PROJECT, BIOSPHERE) - # AGRIBALYSE + # AGRIBALYSE 3.1.1 if (db := "Agribalyse 3.1.1") not in bw2data.databases: import_simapro_csv( - AGRIBALYSE, + AGRIBALYSE31, + db, + migrations=AGRIBALYSE_MIGRATIONS, + excluded_strategies=EXCLUDED, + other_strategies=AGB_STRATEGIES, + ) + else: + print(f"{db} already imported") + + # AGRIBALYSE 3.2 + if (db := "Agribalyse 3.2 beta 08/08/2024") not in bw2data.databases: + import_simapro_csv( + AGRIBALYSE32, db, migrations=AGRIBALYSE_MIGRATIONS, + first_strategies=[remove_some_processes], excluded_strategies=EXCLUDED, other_strategies=AGB_STRATEGIES, ) From 15bd83d3bc7f1297155d0ee782ae6c5e07f1cbb6 Mon Sep 17 00:00:00 2001 From: Christophe Combelles Date: Wed, 4 Sep 2024 09:52:34 +0200 Subject: [PATCH 21/26] added ecoinvent 3.10 --- data/common/import_.py | 2 +- data/import_ecoinvent.py | 13 +++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/data/common/import_.py b/data/common/import_.py index 5afe7b86b..f905435d7 100644 --- a/data/common/import_.py +++ b/data/common/import_.py @@ -187,7 +187,7 @@ def import_simapro_csv( # unzip with ZipFile(datapath) as zf: print("### Extracting the zip file...") - zf.extractall() + zf.extractall(path=os.path.dirname(datapath)) unzipped = datapath[0:-4] if "AGB" in datapath: diff --git a/data/import_ecoinvent.py b/data/import_ecoinvent.py index 8b735e9d8..7a28dfdcb 100755 --- a/data/import_ecoinvent.py +++ b/data/import_ecoinvent.py @@ -1,13 +1,16 @@ #!/usr/bin/env python3 +from os.path import join + import bw2data import bw2io from bw2data.project import projects from common.import_ import add_missing_substances, import_simapro_csv # Ecoinvent -DATAPATH = "./Ecoinvent3.9.1.CSV.zip" +EI391 = "Ecoinvent3.9.1.CSV.zip" +EI310 = "Ecoinvent3.10.CSV.zip" BIOSPHERE = "biosphere3" PROJECT = "default" @@ -20,7 +23,13 @@ def main(): add_missing_substances(PROJECT, BIOSPHERE) if (db := "Ecoinvent 3.9.1") not in bw2data.databases: - import_simapro_csv(DATAPATH, db) + import_simapro_csv(join("databases", EI391), db) + else: + print(f"{db} already imported") + + if (db := "Ecoinvent 3.10") not in bw2data.databases: + import_simapro_csv(join("databases", EI310), db) + else: print(f"{db} already imported") From d480cc776ab3ab37c2c02dd1298547179c6722bb Mon Sep 17 00:00:00 2001 From: Christophe Combelles Date: Thu, 5 Sep 2024 18:17:25 +0200 Subject: [PATCH 22/26] fixex path --- data/import_ecoinvent.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/data/import_ecoinvent.py b/data/import_ecoinvent.py index 7a28dfdcb..bf554c24a 100755 --- a/data/import_ecoinvent.py +++ b/data/import_ecoinvent.py @@ -1,16 +1,14 @@ #!/usr/bin/env python3 -from os.path import join - import bw2data import bw2io from bw2data.project import projects from common.import_ import add_missing_substances, import_simapro_csv # Ecoinvent -EI391 = "Ecoinvent3.9.1.CSV.zip" -EI310 = "Ecoinvent3.10.CSV.zip" +EI391 = "./Ecoinvent3.9.1.CSV.zip" +EI310 = "./Ecoinvent3.10.CSV.zip" BIOSPHERE = "biosphere3" PROJECT = "default" @@ -23,12 +21,12 @@ def main(): add_missing_substances(PROJECT, BIOSPHERE) if (db := "Ecoinvent 3.9.1") not in bw2data.databases: - import_simapro_csv(join("databases", EI391), db) + import_simapro_csv(EI391, db) else: print(f"{db} already imported") if (db := "Ecoinvent 3.10") not in bw2data.databases: - import_simapro_csv(join("databases", EI310), db) + import_simapro_csv(EI310, db) else: print(f"{db} already imported") From bd98cd78995af1d51fddc1465e2176ee5637908b Mon Sep 17 00:00:00 2001 From: Christophe Combelles Date: Thu, 19 Sep 2024 10:51:15 +0200 Subject: [PATCH 23/26] move db files into a subfolder --- data/Makefile | 6 +++--- data/README.md | 14 +++++++------- data/import_ecoinvent.py | 6 ++++-- data/{import_agribalyse.py => import_food.py} | 13 +++++++------ data/import_method.py | 2 +- 5 files changed, 22 insertions(+), 19 deletions(-) rename data/{import_agribalyse.py => import_food.py} (95%) diff --git a/data/Makefile b/data/Makefile index 521ccbf39..b39bd23a0 100644 --- a/data/Makefile +++ b/data/Makefile @@ -16,14 +16,14 @@ else \ endef all: import export -import : image import_agribalyse import_ecoinvent import_method sync_datapackages +import : image import_food import_ecoinvent import_method sync_datapackages export: export_food format image: docker build -t $(NAME) docker -import_agribalyse: - @$(call DOCKER,python3 import_agribalyse.py --recreate-activities) +import_food: + @$(call DOCKER,python3 import_food.py --recreate-activities) import_method: @$(call DOCKER,python3 import_method.py) diff --git a/data/README.md b/data/README.md index 8f2060bcc..2c3c8a2a6 100644 --- a/data/README.md +++ b/data/README.md @@ -6,8 +6,8 @@ Comment générer les données json utilisées par le frontal elm : - Si vous êtes sur Mac avec architecture ARM, affectez 6Go de RAM à Docker dans Docker Desktop : Settings → Ressources → Advanced → Memory = 6G - Préparez les bases de données à importer, elle ne font pas partie du dépôt : - - Agribalyse : compressé dans un fichier `AGB3.1.1.20230306.CSV.zip` dans ce dossier data/ - - Autres bases alimentaire : consultez les noms de fichier dans `import_agribalyse.py` + - Agribalyse : compressé dans un fichier `AGB3.1.1.20230306.CSV.zip` dans le dossier `data/dbfiles/` + - Autres bases alimentaire : consultez les noms de fichier dans `import_food.py` - Ecoinvent : décompressé dans un dossier `ECOINVENT3.9.1` dans ce même dossier - Lancez **`make`** ce qui va successivement : - construire l'image docker ; @@ -20,12 +20,12 @@ d'abord un `make clean_data` (qui supprime le volume docker). ## Autres commandes : - `make image` : pour construire l'image docker choisie -- `make import_agribalyse` : pour importer les bases de données alimentaire dans Brightway. - Assurez-vous d'avoir les bon fichiers de données dans `data/` -- `make import_ecoinvent` : pour importer Ecoinvent 3.9.1. dans Brightway. Assurez-vous - d'avoir le bon dossier de données dans `data/` +- `make import_food` : pour importer les bases de données alimentaire dans Brightway. + Assurez-vous d'avoir les bon fichiers de données dans `data/dbfiles` +- `make import_ecoinvent` : pour importer Ecoinvent 3.9.1. dans Brightway. + Assurez-vous d'avoir le bon dossier de données dans `data/dbfiles` - `make import_method` : pour importer EF 3.1 adapted dans Brightway. - Assurez-vous d'avoir le bon fichier de données dans `data/` + Assurez-vous d'avoir le bon fichier de données dans `data/dbfiles` - `make export_food` : pour exporter les json pour le builder alimentaire - `make delete_database DB=` : pour supprimer une base de données (Ex avec espace: make delete_database DB="Ecoinvent\ 3.9.1") - `make delete_method` : pour supprimer la méthode EF3.1 diff --git a/data/import_ecoinvent.py b/data/import_ecoinvent.py index bf554c24a..dee96e388 100755 --- a/data/import_ecoinvent.py +++ b/data/import_ecoinvent.py @@ -1,6 +1,8 @@ #!/usr/bin/env python3 +from os.path import join + import bw2data import bw2io from bw2data.project import projects @@ -21,12 +23,12 @@ def main(): add_missing_substances(PROJECT, BIOSPHERE) if (db := "Ecoinvent 3.9.1") not in bw2data.databases: - import_simapro_csv(EI391, db) + import_simapro_csv(join("dbfiles", EI391), db) else: print(f"{db} already imported") if (db := "Ecoinvent 3.10") not in bw2data.databases: - import_simapro_csv(EI310, db) + import_simapro_csv(join("dbfiles", EI310), db) else: print(f"{db} already imported") diff --git a/data/import_agribalyse.py b/data/import_food.py similarity index 95% rename from data/import_agribalyse.py rename to data/import_food.py index 680f0d2fd..2cf28f8d5 100755 --- a/data/import_agribalyse.py +++ b/data/import_food.py @@ -3,6 +3,7 @@ import argparse import copy import functools +from os.path import join import bw2data import bw2io @@ -222,7 +223,7 @@ def remove_some_processes(db): # AGRIBALYSE 3.1.1 if (db := "Agribalyse 3.1.1") not in bw2data.databases: import_simapro_csv( - AGRIBALYSE31, + join("dbfiles", AGRIBALYSE31), db, migrations=AGRIBALYSE_MIGRATIONS, excluded_strategies=EXCLUDED, @@ -234,7 +235,7 @@ def remove_some_processes(db): # AGRIBALYSE 3.2 if (db := "Agribalyse 3.2 beta 08/08/2024") not in bw2data.databases: import_simapro_csv( - AGRIBALYSE32, + join("dbfiles", AGRIBALYSE32), db, migrations=AGRIBALYSE_MIGRATIONS, first_strategies=[remove_some_processes], @@ -247,14 +248,14 @@ def remove_some_processes(db): # PASTO ECO if (db := "PastoEco") not in bw2data.databases: for p in PASTOECO: - import_simapro_csv(p, db, excluded_strategies=EXCLUDED) + import_simapro_csv(join("dbfiles", p), db, excluded_strategies=EXCLUDED) else: print(f"{db} already imported") # GINKO if (db := "Ginko") not in bw2data.databases: import_simapro_csv( - GINKO, + join("dbfiles", GINKO), db, excluded_strategies=EXCLUDED, other_strategies=GINKO_STRATEGIES, @@ -265,13 +266,13 @@ def remove_some_processes(db): # CTCPA if (db := "CTCPA") not in bw2data.databases: - import_simapro_csv(CTCPA, db, excluded_strategies=EXCLUDED) + import_simapro_csv(join("dbfiles", CTCPA), db, excluded_strategies=EXCLUDED) else: print(f"{db} already imported") # WFLDB if (db := "WFLDB") not in bw2data.databases: - import_simapro_csv(WFLDB, db, excluded_strategies=EXCLUDED) + import_simapro_csv(join("dbfiles", WFLDB), db, excluded_strategies=EXCLUDED) else: print(f"{db} already imported") diff --git a/data/import_method.py b/data/import_method.py index 885bf9129..5ad33e7c6 100755 --- a/data/import_method.py +++ b/data/import_method.py @@ -24,7 +24,7 @@ # Agribalyse BIOSPHERE = "biosphere3" METHODNAME = "Environmental Footprint 3.1 (adapted) patch wtu" # defined inside the csv -METHODPATH = METHODNAME + ".CSV.zip" +METHODPATH = os.path.join("dbfiles", METHODNAME + ".CSV.zip") # excluded strategies and migrations EXCLUDED_FOOD = [ From be8df9b9e6790dbd631fd5f4e75c6284dd95269c Mon Sep 17 00:00:00 2001 From: Christophe Combelles Date: Thu, 19 Sep 2024 11:21:51 +0200 Subject: [PATCH 24/26] cache cleaning should be unnecessary --- data/docker/entrypoint.sh | 3 --- 1 file changed, 3 deletions(-) diff --git a/data/docker/entrypoint.sh b/data/docker/entrypoint.sh index 1544c574f..b0b91b5c8 100755 --- a/data/docker/entrypoint.sh +++ b/data/docker/entrypoint.sh @@ -12,7 +12,4 @@ fi mkdir -p /home/jovyan/.npm chown -R jovyan:100 "/home/jovyan/.npm" -# Clear npm cache -su jovyan -c "npm cache clean --force" - exec gosu jovyan "$@" From 23eaea03d6e3c545774809a641d1bc5f2718544f Mon Sep 17 00:00:00 2001 From: Christophe Combelles Date: Thu, 19 Sep 2024 14:00:06 +0200 Subject: [PATCH 25/26] move the db files above the repo --- data/README.md | 8 ++++---- data/import_ecoinvent.py | 4 ++-- data/import_food.py | 18 ++++++++++++------ data/import_method.py | 2 +- 4 files changed, 19 insertions(+), 13 deletions(-) diff --git a/data/README.md b/data/README.md index 2c3c8a2a6..403e2c3f4 100644 --- a/data/README.md +++ b/data/README.md @@ -6,7 +6,7 @@ Comment générer les données json utilisées par le frontal elm : - Si vous êtes sur Mac avec architecture ARM, affectez 6Go de RAM à Docker dans Docker Desktop : Settings → Ressources → Advanced → Memory = 6G - Préparez les bases de données à importer, elle ne font pas partie du dépôt : - - Agribalyse : compressé dans un fichier `AGB3.1.1.20230306.CSV.zip` dans le dossier `data/dbfiles/` + - Agribalyse : compressé dans un fichier `AGB3.1.1.20230306.CSV.zip` dans un dossier `dbfiles/` au dessus du dépôt - Autres bases alimentaire : consultez les noms de fichier dans `import_food.py` - Ecoinvent : décompressé dans un dossier `ECOINVENT3.9.1` dans ce même dossier - Lancez **`make`** ce qui va successivement : @@ -21,11 +21,11 @@ d'abord un `make clean_data` (qui supprime le volume docker). - `make image` : pour construire l'image docker choisie - `make import_food` : pour importer les bases de données alimentaire dans Brightway. - Assurez-vous d'avoir les bon fichiers de données dans `data/dbfiles` + Assurez-vous d'avoir les bon fichiers de données dans `dbfiles/` au dessus du dépôt - `make import_ecoinvent` : pour importer Ecoinvent 3.9.1. dans Brightway. - Assurez-vous d'avoir le bon dossier de données dans `data/dbfiles` + Assurez-vous d'avoir le bon dossier de données dans `dbfiles/` au dessus du dépôt - `make import_method` : pour importer EF 3.1 adapted dans Brightway. - Assurez-vous d'avoir le bon fichier de données dans `data/dbfiles` + Assurez-vous d'avoir le bon fichier de données dans `dbfiles/` au dessus du dépôt - `make export_food` : pour exporter les json pour le builder alimentaire - `make delete_database DB=` : pour supprimer une base de données (Ex avec espace: make delete_database DB="Ecoinvent\ 3.9.1") - `make delete_method` : pour supprimer la méthode EF3.1 diff --git a/data/import_ecoinvent.py b/data/import_ecoinvent.py index dee96e388..df70ba327 100755 --- a/data/import_ecoinvent.py +++ b/data/import_ecoinvent.py @@ -23,12 +23,12 @@ def main(): add_missing_substances(PROJECT, BIOSPHERE) if (db := "Ecoinvent 3.9.1") not in bw2data.databases: - import_simapro_csv(join("dbfiles", EI391), db) + import_simapro_csv(join("..", "..", "dbfiles", EI391), db) else: print(f"{db} already imported") if (db := "Ecoinvent 3.10") not in bw2data.databases: - import_simapro_csv(join("dbfiles", EI310), db) + import_simapro_csv(join("..", "..", "dbfiles", EI310), db) else: print(f"{db} already imported") diff --git a/data/import_food.py b/data/import_food.py index 2cf28f8d5..55cb32781 100755 --- a/data/import_food.py +++ b/data/import_food.py @@ -223,7 +223,7 @@ def remove_some_processes(db): # AGRIBALYSE 3.1.1 if (db := "Agribalyse 3.1.1") not in bw2data.databases: import_simapro_csv( - join("dbfiles", AGRIBALYSE31), + join("..", "..", "dbfiles", AGRIBALYSE31), db, migrations=AGRIBALYSE_MIGRATIONS, excluded_strategies=EXCLUDED, @@ -235,7 +235,7 @@ def remove_some_processes(db): # AGRIBALYSE 3.2 if (db := "Agribalyse 3.2 beta 08/08/2024") not in bw2data.databases: import_simapro_csv( - join("dbfiles", AGRIBALYSE32), + join("..", "..", "dbfiles", AGRIBALYSE32), db, migrations=AGRIBALYSE_MIGRATIONS, first_strategies=[remove_some_processes], @@ -248,14 +248,16 @@ def remove_some_processes(db): # PASTO ECO if (db := "PastoEco") not in bw2data.databases: for p in PASTOECO: - import_simapro_csv(join("dbfiles", p), db, excluded_strategies=EXCLUDED) + import_simapro_csv( + join("..", "..", "dbfiles", p), db, excluded_strategies=EXCLUDED + ) else: print(f"{db} already imported") # GINKO if (db := "Ginko") not in bw2data.databases: import_simapro_csv( - join("dbfiles", GINKO), + join("..", "..", "dbfiles", GINKO), db, excluded_strategies=EXCLUDED, other_strategies=GINKO_STRATEGIES, @@ -266,13 +268,17 @@ def remove_some_processes(db): # CTCPA if (db := "CTCPA") not in bw2data.databases: - import_simapro_csv(join("dbfiles", CTCPA), db, excluded_strategies=EXCLUDED) + import_simapro_csv( + join("..", "..", "dbfiles", CTCPA), db, excluded_strategies=EXCLUDED + ) else: print(f"{db} already imported") # WFLDB if (db := "WFLDB") not in bw2data.databases: - import_simapro_csv(join("dbfiles", WFLDB), db, excluded_strategies=EXCLUDED) + import_simapro_csv( + join("..", "..", "dbfiles", WFLDB), db, excluded_strategies=EXCLUDED + ) else: print(f"{db} already imported") diff --git a/data/import_method.py b/data/import_method.py index 5ad33e7c6..5e0bf2f86 100755 --- a/data/import_method.py +++ b/data/import_method.py @@ -24,7 +24,7 @@ # Agribalyse BIOSPHERE = "biosphere3" METHODNAME = "Environmental Footprint 3.1 (adapted) patch wtu" # defined inside the csv -METHODPATH = os.path.join("dbfiles", METHODNAME + ".CSV.zip") +METHODPATH = os.path.join("..", "..", "dbfiles", METHODNAME + ".CSV.zip") # excluded strategies and migrations EXCLUDED_FOOD = [ From bb770bee9182c610d32ff9f1ce1af4120cf185b5 Mon Sep 17 00:00:00 2001 From: Christophe Combelles Date: Thu, 19 Sep 2024 17:37:24 +0200 Subject: [PATCH 26/26] add a dbfiles bind volume --- data/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/Makefile b/data/Makefile index b39bd23a0..2007dfe98 100644 --- a/data/Makefile +++ b/data/Makefile @@ -12,7 +12,7 @@ env | grep ECOBALYSE_DATA_DIR || exit docker exec -u jovyan -it -e ECOBALYSE_DATA_DIR=/home/jovyan/ecobalyse-private/ -w /home/jovyan/ecobalyse/data $(NAME) $(1);\ else \ echo "(Creating a new container)" &&\ - docker run --rm -it -v $(NAME):/home/jovyan -v $$PWD/../:/home/jovyan/ecobalyse -v $(ECOBALYSE_DATA_DIR):/home/jovyan/ecobalyse-private -e ECOBALYSE_DATA_DIR=/home/jovyan/ecobalyse-private/ -w /home/jovyan/ecobalyse/data $(NAME) $(1); fi + docker run --rm -it -v $(NAME):/home/jovyan -v $$PWD/../:/home/jovyan/ecobalyse -v $$PWD/../../dbfiles/:/home/jovyan/dbfiles -v $(ECOBALYSE_DATA_DIR):/home/jovyan/ecobalyse-private -e ECOBALYSE_DATA_DIR=/home/jovyan/ecobalyse-private/ -w /home/jovyan/ecobalyse/data $(NAME) $(1); fi endef all: import export