openepd 6.13.2__tar.gz → 7.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {openepd-6.13.2 → openepd-7.0.1}/PKG-INFO +2 -2
- {openepd-6.13.2 → openepd-7.0.1}/pyproject.toml +7 -2
- openepd-7.0.1/src/openepd/__version__.py +16 -0
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/api/average_dataset/generic_estimate_sync_api.py +11 -10
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/api/average_dataset/industry_epd_sync_api.py +9 -8
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/api/base_sync_client.py +53 -9
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/api/category/sync_api.py +1 -1
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/api/dto/base.py +4 -4
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/api/dto/common.py +24 -16
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/api/dto/meta.py +15 -11
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/api/dto/mf.py +9 -8
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/api/epd/dto.py +43 -33
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/api/epd/sync_api.py +9 -9
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/api/pcr/sync_api.py +2 -2
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/bundle/model.py +11 -10
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/bundle/reader.py +12 -5
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/bundle/writer.py +17 -6
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/base.py +61 -44
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/category.py +13 -10
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/common.py +107 -59
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/declaration.py +93 -64
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/epd.py +51 -43
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/generic_estimate.py +28 -13
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/industry_epd.py +15 -9
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/lcia.py +161 -136
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/org.py +70 -37
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/pcr.py +38 -32
- openepd-7.0.1/src/openepd/model/specs/asphalt.py +95 -0
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/base.py +14 -11
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/concrete.py +60 -39
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/range/aggregates.py +9 -9
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/range/aluminium.py +7 -7
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/range/asphalt.py +22 -19
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/range/cladding.py +16 -16
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/range/cmu.py +10 -9
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/range/concrete.py +36 -27
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/range/conveying_equipment.py +16 -15
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/range/electrical.py +24 -22
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/range/finishes.py +109 -104
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/range/fire_and_smoke_protection.py +7 -7
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/range/furnishings.py +16 -12
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/range/manufacturing_inputs.py +16 -16
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/range/masonry.py +16 -16
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/range/mechanical.py +47 -47
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/range/mechanical_insulation.py +7 -7
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/range/network_infrastructure.py +54 -46
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/range/openings.py +36 -31
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/range/plumbing.py +15 -13
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/range/precast_concrete.py +20 -16
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/range/sheathing.py +18 -18
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/range/steel.py +27 -25
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/range/thermal_moisture_protection.py +20 -20
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/range/utility_piping.py +9 -9
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/range/wood.py +19 -19
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/range/wood_joists.py +8 -8
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/singular/__init__.py +9 -5
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/singular/aggregates.py +22 -15
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/singular/aluminium.py +20 -5
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/singular/asphalt.py +44 -20
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/singular/cladding.py +38 -23
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/singular/cmu.py +26 -11
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/singular/common.py +3 -2
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/singular/concrete.py +85 -48
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/singular/conveying_equipment.py +30 -17
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/singular/deprecated/__init__.py +3 -2
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/singular/deprecated/concrete.py +68 -33
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/singular/deprecated/steel.py +28 -15
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/singular/electrical.py +69 -41
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/singular/finishes.py +250 -140
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/singular/fire_and_smoke_protection.py +9 -6
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/singular/furnishings.py +16 -14
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/singular/manufacturing_inputs.py +23 -14
- openepd-7.0.1/src/openepd/model/specs/singular/masonry.py +126 -0
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/singular/mechanical.py +48 -47
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/singular/mechanical_insulation.py +7 -6
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/singular/mixins/conduit_mixin.py +13 -10
- openepd-7.0.1/src/openepd/model/specs/singular/network_infrastructure.py +252 -0
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/singular/openings.py +127 -95
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/singular/plumbing.py +15 -12
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/singular/precast_concrete.py +68 -54
- openepd-7.0.1/src/openepd/model/specs/singular/sheathing.py +107 -0
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/singular/steel.py +69 -45
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/singular/thermal_moisture_protection.py +36 -20
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/singular/utility_piping.py +11 -8
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/singular/wood.py +48 -24
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/singular/wood_joists.py +19 -6
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/standard.py +15 -8
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/validation/common.py +9 -3
- openepd-7.0.1/src/openepd/model/validation/numbers.py +15 -0
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/validation/quantity.py +88 -55
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/versioning.py +9 -6
- openepd-6.13.2/src/openepd/__init__.py +0 -21
- openepd-6.13.2/src/openepd/__version__.py +0 -16
- openepd-6.13.2/src/openepd/compat/compat_functional_validators.py +0 -25
- openepd-6.13.2/src/openepd/compat/pydantic.py +0 -30
- openepd-6.13.2/src/openepd/model/specs/asphalt.py +0 -86
- openepd-6.13.2/src/openepd/model/specs/singular/masonry.py +0 -81
- openepd-6.13.2/src/openepd/model/specs/singular/network_infrastructure.py +0 -193
- openepd-6.13.2/src/openepd/model/specs/singular/sheathing.py +0 -87
- openepd-6.13.2/src/openepd/model/validation/numbers.py +0 -28
- openepd-6.13.2/src/openepd/patch_pydantic.py +0 -108
- {openepd-6.13.2 → openepd-7.0.1}/LICENSE +0 -0
- {openepd-6.13.2 → openepd-7.0.1}/README.md +0 -0
- {openepd-6.13.2/src/openepd/api → openepd-7.0.1/src/openepd}/__init__.py +0 -0
- {openepd-6.13.2/src/openepd/api/average_dataset → openepd-7.0.1/src/openepd/api}/__init__.py +0 -0
- {openepd-6.13.2/src/openepd/api/category → openepd-7.0.1/src/openepd/api/average_dataset}/__init__.py +0 -0
- {openepd-6.13.2/src/openepd/api/dto → openepd-7.0.1/src/openepd/api/category}/__init__.py +0 -0
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/api/category/dto.py +0 -0
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/api/common.py +0 -0
- {openepd-6.13.2/src/openepd/api/epd → openepd-7.0.1/src/openepd/api/dto}/__init__.py +0 -0
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/api/dto/params.py +0 -0
- {openepd-6.13.2/src/openepd/api/pcr → openepd-7.0.1/src/openepd/api/epd}/__init__.py +0 -0
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/api/errors.py +0 -0
- {openepd-6.13.2/src/openepd/api/test → openepd-7.0.1/src/openepd/api/pcr}/__init__.py +0 -0
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/api/sync_client.py +0 -0
- {openepd-6.13.2/src/openepd/bundle → openepd-7.0.1/src/openepd/api/test}/__init__.py +0 -0
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/api/utils.py +0 -0
- {openepd-6.13.2/src/openepd/compat → openepd-7.0.1/src/openepd/bundle}/__init__.py +0 -0
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/bundle/base.py +0 -0
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/m49/__init__.py +0 -0
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/m49/const.py +0 -0
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/m49/utils.py +0 -0
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/__init__.py +0 -0
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/factory.py +0 -0
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/geography.py +0 -0
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/README.md +0 -0
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/__init__.py +0 -0
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/enums.py +0 -0
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/range/__init__.py +0 -0
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/range/accessories.py +0 -0
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/range/bulk_materials.py +0 -0
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/range/cast_decks_and_underlayment.py +0 -0
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/range/electrical_transmission_and_distribution_equipment.py +0 -0
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/range/electricity.py +0 -0
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/range/grouting.py +0 -0
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/range/material_handling.py +0 -0
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/range/other_electrical_equipment.py +0 -0
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/range/other_materials.py +0 -0
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/singular/accessories.py +0 -0
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/singular/bulk_materials.py +0 -0
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/singular/cast_decks_and_underlayment.py +0 -0
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/singular/electrical_transmission_and_distribution_equipment.py +0 -0
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/singular/electricity.py +0 -0
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/singular/grouting.py +0 -0
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/singular/material_handling.py +0 -0
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/singular/mixins/__init__.py +0 -0
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/singular/other_electrical_equipment.py +0 -0
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/specs/singular/other_materials.py +0 -0
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/validation/__init__.py +0 -0
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/model/validation/enum.py +0 -0
- {openepd-6.13.2 → openepd-7.0.1}/src/openepd/py.typed +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: openepd
|
3
|
-
Version:
|
3
|
+
Version: 7.0.1
|
4
4
|
Summary: Python library to work with OpenEPD format
|
5
5
|
Home-page: https://github.com/cchangelabs/openepd
|
6
6
|
License: Apache-2.0
|
@@ -21,7 +21,7 @@ Requires-Dist: email-validator (>=1.3.1)
|
|
21
21
|
Requires-Dist: idna (>=3.7)
|
22
22
|
Requires-Dist: open-xpd-uuid (>=0.2.1,<0.3.0)
|
23
23
|
Requires-Dist: openlocationcode (>=1.0.1)
|
24
|
-
Requires-Dist: pydantic (>=
|
24
|
+
Requires-Dist: pydantic (>=2.0,<3)
|
25
25
|
Requires-Dist: requests (>=2.0) ; extra == "api-client"
|
26
26
|
Project-URL: Repository, https://github.com/cchangelabs/openepd
|
27
27
|
Description-Content-Type: text/markdown
|
@@ -1,6 +1,11 @@
|
|
1
|
+
[tool.ruff]
|
2
|
+
line-length = 120
|
3
|
+
target-version = "py311"
|
4
|
+
exclude = [".*pyi"]
|
5
|
+
|
1
6
|
[tool.poetry]
|
2
7
|
name = "openepd"
|
3
|
-
version = "
|
8
|
+
version = "7.0.1"
|
4
9
|
license = "Apache-2.0"
|
5
10
|
description = "Python library to work with OpenEPD format"
|
6
11
|
authors = ["C-Change Labs <support@c-change-labs.com>"]
|
@@ -21,7 +26,7 @@ exclude = ["**/test_*.py", "**/tests/**"]
|
|
21
26
|
|
22
27
|
[tool.poetry.dependencies]
|
23
28
|
python = "^3.11"
|
24
|
-
pydantic = ">=
|
29
|
+
pydantic = ">=2.0,<3"
|
25
30
|
email-validator = ">=1.3.1"
|
26
31
|
requests = { version = ">=2.0", optional = true }
|
27
32
|
idna = ">=3.7"
|
@@ -0,0 +1,16 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2025 by C Change Labs Inc. www.c-change-labs.com
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
#
|
16
|
+
VERSION = "7.0.1"
|
{openepd-6.13.2 → openepd-7.0.1}/src/openepd/api/average_dataset/generic_estimate_sync_api.py
RENAMED
@@ -52,8 +52,8 @@ class GenericEstimateApi(BaseApiMethodGroup):
|
|
52
52
|
"""
|
53
53
|
response = self._client.do_request("get", f"/generic_estimates/{uuid}")
|
54
54
|
if with_response:
|
55
|
-
return GenericEstimate.
|
56
|
-
return GenericEstimate.
|
55
|
+
return GenericEstimate.model_validate(response.json()), response
|
56
|
+
return GenericEstimate.model_validate(response.json())
|
57
57
|
|
58
58
|
@overload
|
59
59
|
def post_with_refs(
|
@@ -88,8 +88,8 @@ class GenericEstimateApi(BaseApiMethodGroup):
|
|
88
88
|
)
|
89
89
|
content = response.json()
|
90
90
|
if with_response:
|
91
|
-
return GenericEstimate.
|
92
|
-
return GenericEstimate.
|
91
|
+
return GenericEstimate.model_validate(content), response
|
92
|
+
return GenericEstimate.model_validate(content)
|
93
93
|
|
94
94
|
@overload
|
95
95
|
def create(self, ge: GenericEstimate, with_response: Literal[True]) -> tuple[GenericEstimateRef, Response]: ...
|
@@ -114,8 +114,8 @@ class GenericEstimateApi(BaseApiMethodGroup):
|
|
114
114
|
)
|
115
115
|
content = response.json()
|
116
116
|
if with_response:
|
117
|
-
return GenericEstimateRef.
|
118
|
-
return GenericEstimateRef.
|
117
|
+
return GenericEstimateRef.model_validate(content), response
|
118
|
+
return GenericEstimateRef.model_validate(content)
|
119
119
|
|
120
120
|
@overload
|
121
121
|
def edit(self, ge: GenericEstimate, with_response: Literal[True]) -> tuple[GenericEstimateRef, Response]: ...
|
@@ -144,8 +144,8 @@ class GenericEstimateApi(BaseApiMethodGroup):
|
|
144
144
|
)
|
145
145
|
content = response.json()
|
146
146
|
if with_response:
|
147
|
-
return GenericEstimateRef.
|
148
|
-
return GenericEstimateRef.
|
147
|
+
return GenericEstimateRef.model_validate(content), response
|
148
|
+
return GenericEstimateRef.model_validate(content)
|
149
149
|
|
150
150
|
@overload
|
151
151
|
def list_raw(
|
@@ -177,7 +177,7 @@ class GenericEstimateApi(BaseApiMethodGroup):
|
|
177
177
|
page_size=page_size,
|
178
178
|
),
|
179
179
|
)
|
180
|
-
data = [GenericEstimatePreview.
|
180
|
+
data = [GenericEstimatePreview.model_validate(o) for o in response.json()]
|
181
181
|
if with_response:
|
182
182
|
return data, response
|
183
183
|
return data
|
@@ -193,7 +193,8 @@ class GenericEstimateApi(BaseApiMethodGroup):
|
|
193
193
|
def _get_page(p_num: int, p_size: int) -> GenericEstimateListResponse:
|
194
194
|
data_list, response = self.list_raw(page_num=p_num, page_size=p_size, with_response=True)
|
195
195
|
return GenericEstimateListResponse(
|
196
|
-
payload=data_list,
|
196
|
+
payload=data_list,
|
197
|
+
meta=GenericEstimateSearchMeta(paging=paging_meta_from_v1_api(response)),
|
197
198
|
)
|
198
199
|
|
199
200
|
return StreamingListResponse[GenericEstimatePreview](_get_page, page_size=page_size)
|
@@ -45,8 +45,8 @@ class IndustryEpdApi(BaseApiMethodGroup):
|
|
45
45
|
"""
|
46
46
|
response = self._client.do_request("get", f"/industry_epds/{uuid}")
|
47
47
|
if with_response:
|
48
|
-
return IndustryEpd.
|
49
|
-
return IndustryEpd.
|
48
|
+
return IndustryEpd.model_validate(response.json()), response
|
49
|
+
return IndustryEpd.model_validate(response.json())
|
50
50
|
|
51
51
|
@overload
|
52
52
|
def create(self, iepd: IndustryEpd, with_response: Literal[True]) -> tuple[IndustryEpdRef, Response]: ...
|
@@ -71,8 +71,8 @@ class IndustryEpdApi(BaseApiMethodGroup):
|
|
71
71
|
)
|
72
72
|
content = response.json()
|
73
73
|
if with_response:
|
74
|
-
return IndustryEpdRef.
|
75
|
-
return IndustryEpdRef.
|
74
|
+
return IndustryEpdRef.model_validate(content), response
|
75
|
+
return IndustryEpdRef.model_validate(content)
|
76
76
|
|
77
77
|
@overload
|
78
78
|
def edit(self, iepd: IndustryEpd, with_response: Literal[True]) -> tuple[IndustryEpdRef, Response]: ...
|
@@ -99,8 +99,8 @@ class IndustryEpdApi(BaseApiMethodGroup):
|
|
99
99
|
)
|
100
100
|
content = response.json()
|
101
101
|
if with_response:
|
102
|
-
return IndustryEpdRef.
|
103
|
-
return IndustryEpdRef.
|
102
|
+
return IndustryEpdRef.model_validate(content), response
|
103
|
+
return IndustryEpdRef.model_validate(content)
|
104
104
|
|
105
105
|
@overload
|
106
106
|
def list_raw(
|
@@ -131,7 +131,7 @@ class IndustryEpdApi(BaseApiMethodGroup):
|
|
131
131
|
page_size=page_size,
|
132
132
|
),
|
133
133
|
)
|
134
|
-
data = [IndustryEpdPreview.
|
134
|
+
data = [IndustryEpdPreview.model_validate(o) for o in response.json()]
|
135
135
|
if with_response:
|
136
136
|
return data, response
|
137
137
|
return data
|
@@ -147,7 +147,8 @@ class IndustryEpdApi(BaseApiMethodGroup):
|
|
147
147
|
def _get_page(p_num: int, p_size: int) -> IndustryEpdListResponse:
|
148
148
|
data_list, response = self.list_raw(page_num=p_num, page_size=p_size, with_response=True)
|
149
149
|
return IndustryEpdListResponse(
|
150
|
-
payload=data_list,
|
150
|
+
payload=data_list,
|
151
|
+
meta=IndustryEpdSearchMeta(paging=paging_meta_from_v1_api(response)),
|
151
152
|
)
|
152
153
|
|
153
154
|
return StreamingListResponse[IndustryEpdPreview](_get_page, page_size=page_size)
|
@@ -238,7 +238,12 @@ class SyncHttpClient:
|
|
238
238
|
return content
|
239
239
|
|
240
240
|
def read_url_write_to_stream(
|
241
|
-
self,
|
241
|
+
self,
|
242
|
+
url: str,
|
243
|
+
target_stream: IO[bytes],
|
244
|
+
method: str = "get",
|
245
|
+
chunk_size: int = 1024,
|
246
|
+
**kwargs,
|
242
247
|
) -> int:
|
243
248
|
"""
|
244
249
|
Perform query to the given endpoint and writes response body to the given stream.
|
@@ -327,7 +332,12 @@ class SyncHttpClient:
|
|
327
332
|
)
|
328
333
|
if timeout > left_time:
|
329
334
|
return resp
|
330
|
-
logger.info(
|
335
|
+
logger.info(
|
336
|
+
"`%s %s` has been throttled for %s second(s)",
|
337
|
+
method,
|
338
|
+
url,
|
339
|
+
timeout,
|
340
|
+
)
|
331
341
|
time.sleep(timeout)
|
332
342
|
left_time -= timeout
|
333
343
|
if left_time > 0:
|
@@ -374,7 +384,13 @@ class SyncHttpClient:
|
|
374
384
|
method,
|
375
385
|
url,
|
376
386
|
self._retry_count,
|
377
|
-
partial(
|
387
|
+
partial(
|
388
|
+
self._run_throttled_request,
|
389
|
+
method,
|
390
|
+
url,
|
391
|
+
request_kwargs,
|
392
|
+
session=session,
|
393
|
+
),
|
378
394
|
)
|
379
395
|
|
380
396
|
response = do_request()
|
@@ -447,13 +463,21 @@ class SyncHttpClient:
|
|
447
463
|
exception = None
|
448
464
|
try:
|
449
465
|
response = func(*args, **kwargs)
|
450
|
-
except (
|
466
|
+
except (
|
467
|
+
requests.exceptions.ConnectionError,
|
468
|
+
ConnectionError,
|
469
|
+
Timeout,
|
470
|
+
) as e:
|
451
471
|
exception = e
|
452
472
|
|
453
473
|
if exception or response.status_code == requests_codes.service_unavailable:
|
454
474
|
secs = random.randint(60, 60 * 5)
|
455
475
|
logger.warning(
|
456
|
-
"%s %s is unavailable. Attempts left: %s. Waiting %s seconds...",
|
476
|
+
"%s %s is unavailable. Attempts left: %s. Waiting %s seconds...",
|
477
|
+
method,
|
478
|
+
url,
|
479
|
+
attempts,
|
480
|
+
secs,
|
457
481
|
)
|
458
482
|
|
459
483
|
# wait random number of seconds and request again
|
@@ -509,28 +533,48 @@ class DefaultOpenApiErrorHandlers:
|
|
509
533
|
def handle_not_found(response: Response, raise_for_status: bool) -> Response | None:
|
510
534
|
if raise_for_status:
|
511
535
|
error = DefaultOpenApiErrorHandlers._parse_error_response(response)
|
512
|
-
raise errors.ObjectNotFound(
|
536
|
+
raise errors.ObjectNotFound(
|
537
|
+
response.status_code,
|
538
|
+
error.summary,
|
539
|
+
response,
|
540
|
+
error_code=error.error_code,
|
541
|
+
)
|
513
542
|
return response
|
514
543
|
|
515
544
|
@staticmethod
|
516
545
|
def handle_unauthorized(response: Response, raise_for_status: bool) -> Response | None:
|
517
546
|
if raise_for_status:
|
518
547
|
error = DefaultOpenApiErrorHandlers._parse_error_response(response)
|
519
|
-
raise errors.NotAuthorizedError(
|
548
|
+
raise errors.NotAuthorizedError(
|
549
|
+
response.status_code,
|
550
|
+
error.summary,
|
551
|
+
response,
|
552
|
+
error_code=error.error_code,
|
553
|
+
)
|
520
554
|
return response
|
521
555
|
|
522
556
|
@staticmethod
|
523
557
|
def handle_access_denied(response: Response, raise_for_status: bool) -> Response | None:
|
524
558
|
if raise_for_status:
|
525
559
|
error = DefaultOpenApiErrorHandlers._parse_error_response(response)
|
526
|
-
raise errors.AccessDeniedError(
|
560
|
+
raise errors.AccessDeniedError(
|
561
|
+
response.status_code,
|
562
|
+
error.summary,
|
563
|
+
response,
|
564
|
+
error_code=error.error_code,
|
565
|
+
)
|
527
566
|
return response
|
528
567
|
|
529
568
|
@staticmethod
|
530
569
|
def handle_server_error(response: Response, raise_for_status: bool) -> Response | None:
|
531
570
|
if raise_for_status:
|
532
571
|
error = DefaultOpenApiErrorHandlers._parse_error_response(response)
|
533
|
-
raise errors.ServerError(
|
572
|
+
raise errors.ServerError(
|
573
|
+
response.status_code,
|
574
|
+
error.summary,
|
575
|
+
response,
|
576
|
+
error_code=error.error_code,
|
577
|
+
)
|
534
578
|
return response
|
535
579
|
|
536
580
|
|
@@ -28,7 +28,7 @@ class CategoryApi(BaseApiMethodGroup):
|
|
28
28
|
:return: categories tree wrapped in OpenEpdApiResponse
|
29
29
|
"""
|
30
30
|
response = self._client.do_request("get", "/v2/categories/tree")
|
31
|
-
return CategoryTreeResponse.
|
31
|
+
return CategoryTreeResponse.model_validate_json(response.content)
|
32
32
|
|
33
33
|
def get_tree(self) -> Category:
|
34
34
|
"""
|
@@ -15,14 +15,14 @@
|
|
15
15
|
#
|
16
16
|
import abc
|
17
17
|
|
18
|
-
|
18
|
+
import pydantic
|
19
|
+
from pydantic import ConfigDict
|
19
20
|
|
20
21
|
|
21
|
-
class BaseOpenEpdApiModel(
|
22
|
+
class BaseOpenEpdApiModel(pydantic.BaseModel):
|
22
23
|
"""Base class for OpenEPD API DTOs."""
|
23
24
|
|
24
|
-
|
25
|
-
extra = pyd.Extra.ignore
|
25
|
+
model_config = ConfigDict(extra="ignore")
|
26
26
|
|
27
27
|
|
28
28
|
class BaseMetaDto(BaseOpenEpdApiModel, metaclass=abc.ABCMeta):
|
@@ -15,12 +15,14 @@
|
|
15
15
|
#
|
16
16
|
import abc
|
17
17
|
import datetime
|
18
|
-
from typing import Final, Generic, TypeAlias, TypeVar
|
18
|
+
from typing import ClassVar, Final, Generic, TypeAlias, TypeVar
|
19
|
+
|
20
|
+
import pydantic
|
21
|
+
from pydantic import ConfigDict
|
19
22
|
|
20
23
|
from openepd.api.dto.base import BaseMetaDto, BaseOpenEpdApiModel, MetaExtensionBase
|
21
24
|
from openepd.api.dto.meta import PerformanceMetaMixin
|
22
|
-
from openepd.
|
23
|
-
from openepd.model.base import AnySerializable, BaseOpenEpdSchema
|
25
|
+
from openepd.model.base import AnySerializable
|
24
26
|
|
25
27
|
DEFAULT_PAGE_SIZE: Final[int] = 100
|
26
28
|
MAX_PAGE_SIZE: Final[int] = 250
|
@@ -29,25 +31,25 @@ MAX_PAGE_SIZE: Final[int] = 250
|
|
29
31
|
class AuditableDto(BaseOpenEpdApiModel, metaclass=abc.ABCMeta):
|
30
32
|
"""Base class for all DTOs that hold audit information."""
|
31
33
|
|
32
|
-
created_by: str =
|
34
|
+
created_by: str = pydantic.Field(
|
33
35
|
title="Created By",
|
34
|
-
|
36
|
+
examples=["johnsmith@cqd.io"],
|
35
37
|
description="User's email or script name that created this list.",
|
36
38
|
)
|
37
|
-
updated_by: str =
|
39
|
+
updated_by: str = pydantic.Field(
|
38
40
|
title="Updated By",
|
39
|
-
|
41
|
+
examples=["bobbuilder@buildingtransparency.org"],
|
40
42
|
description="User's email or script name that updated this list last time.",
|
41
43
|
)
|
42
|
-
created_on: datetime.datetime =
|
44
|
+
created_on: datetime.datetime = pydantic.Field(
|
43
45
|
title="Created On",
|
44
|
-
|
46
|
+
examples=["2019-06-13T13:17:09+00:00"],
|
45
47
|
description="A timestamp when this object has been created "
|
46
48
|
"in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format.",
|
47
49
|
)
|
48
|
-
updated_on: datetime.datetime =
|
50
|
+
updated_on: datetime.datetime = pydantic.Field(
|
49
51
|
title="Updated On",
|
50
|
-
|
52
|
+
examples=["2020-07-13T13:17:09+00:00"],
|
51
53
|
description="A timestamp when this object has been updated last time "
|
52
54
|
"in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format.",
|
53
55
|
)
|
@@ -59,7 +61,7 @@ TMetaDto = TypeVar("TMetaDto", bound=BaseMetaDto)
|
|
59
61
|
TMetaExtension = TypeVar("TMetaExtension", bound=MetaExtensionBase)
|
60
62
|
|
61
63
|
|
62
|
-
class MetaCollectionDto(BaseOpenEpdApiModel,
|
64
|
+
class MetaCollectionDto(BaseOpenEpdApiModel, Generic[TMetaExtension]):
|
63
65
|
"""
|
64
66
|
This class is intended to be used as a container for different meta objects.
|
65
67
|
|
@@ -86,13 +88,19 @@ class MetaCollectionDto(BaseOpenEpdApiModel, pyd_generics.GenericModel, Generic[
|
|
86
88
|
|
87
89
|
ext: TMetaExtension | None = None
|
88
90
|
|
89
|
-
|
90
|
-
|
91
|
+
model_config: ClassVar[ConfigDict] = ConfigDict(
|
92
|
+
json_schema_extra={
|
91
93
|
"description": "Base structure of the response meta section",
|
92
94
|
}
|
95
|
+
)
|
93
96
|
|
94
97
|
|
95
|
-
class BaseMeta(
|
98
|
+
class BaseMeta(
|
99
|
+
PerformanceMetaMixin,
|
100
|
+
MetaCollectionDto[TMetaExtension],
|
101
|
+
Generic[TMetaExtension],
|
102
|
+
metaclass=abc.ABCMeta,
|
103
|
+
):
|
96
104
|
"""Base class for creating meta objects specific to a controller."""
|
97
105
|
|
98
106
|
pass
|
@@ -101,7 +109,7 @@ class BaseMeta(PerformanceMetaMixin, MetaCollectionDto[TMetaExtension], Generic[
|
|
101
109
|
TMeta = TypeVar("TMeta", bound=MetaCollectionDto, covariant=True)
|
102
110
|
|
103
111
|
|
104
|
-
class OpenEpdApiResponse(
|
112
|
+
class OpenEpdApiResponse(BaseOpenEpdApiModel, Generic[TPayload, TMeta]):
|
105
113
|
"""Standard DTO representing response from OpenEPD API server."""
|
106
114
|
|
107
115
|
payload: TPayload
|
@@ -13,8 +13,9 @@
|
|
13
13
|
# See the License for the specific language governing permissions and
|
14
14
|
# limitations under the License.
|
15
15
|
#
|
16
|
+
import pydantic
|
17
|
+
|
16
18
|
from openepd.api.dto.base import BaseMetaDto, BaseOpenEpdApiModel
|
17
|
-
from openepd.compat.pydantic import pyd
|
18
19
|
|
19
20
|
|
20
21
|
class PerformanceMeta(BaseMetaDto):
|
@@ -32,30 +33,33 @@ class PerformanceMetaMixin(BaseMetaDto):
|
|
32
33
|
class PagingMeta(BaseMetaDto):
|
33
34
|
"""Meta for paging information."""
|
34
35
|
|
35
|
-
total_count: int =
|
36
|
-
title="Total results",
|
36
|
+
total_count: int = pydantic.Field(
|
37
|
+
title="Total results",
|
38
|
+
examples=[1233],
|
39
|
+
description="Total number of records for the search",
|
37
40
|
)
|
38
|
-
total_pages: int =
|
39
|
-
page_size: int =
|
41
|
+
total_pages: int = pydantic.Field(title="Total pages", examples=[20], description="Total pages available")
|
42
|
+
page_size: int = pydantic.Field(title="Page size", examples=[150], description="Number of records in page")
|
40
43
|
|
41
44
|
|
42
45
|
class PagingMetaMixin(BaseOpenEpdApiModel):
|
43
46
|
"""Mixin for adding paging meta to MetaCollection."""
|
44
47
|
|
45
|
-
paging: PagingMeta | None
|
48
|
+
paging: PagingMeta | None = pydantic.Field(default=None, description="Paging information")
|
46
49
|
|
47
50
|
|
48
51
|
class WarningMessageDto(BaseOpenEpdApiModel):
|
49
52
|
"""DTO for warning messages."""
|
50
53
|
|
51
|
-
message: str =
|
52
|
-
title="Warning message",
|
54
|
+
message: str = pydantic.Field(
|
55
|
+
title="Warning message",
|
56
|
+
examples=["Categories limited during search, see effective_omf in meta"],
|
53
57
|
)
|
54
|
-
code: str =
|
55
|
-
field: str | None =
|
58
|
+
code: str = pydantic.Field(title="Warning code", examples=["CATEGORIES_LIMITED"])
|
59
|
+
field: str | None = pydantic.Field(
|
56
60
|
title="Field",
|
57
61
|
description="Field to which the warning relates",
|
58
|
-
|
62
|
+
examples=["subcategories"],
|
59
63
|
)
|
60
64
|
|
61
65
|
|
@@ -13,8 +13,9 @@
|
|
13
13
|
# See the License for the specific language governing permissions and
|
14
14
|
# limitations under the License.
|
15
15
|
#
|
16
|
+
import pydantic
|
17
|
+
|
16
18
|
from openepd.api.dto.base import BaseOpenEpdApiModel
|
17
|
-
from openepd.compat.pydantic import pyd
|
18
19
|
|
19
20
|
|
20
21
|
class MaterialFilterDefinition(BaseOpenEpdApiModel):
|
@@ -24,14 +25,14 @@ class MaterialFilterDefinition(BaseOpenEpdApiModel):
|
|
24
25
|
This object includes material filter itself as well as its hash.
|
25
26
|
"""
|
26
27
|
|
27
|
-
mf: str =
|
28
|
+
mf: str = pydantic.Field(
|
28
29
|
title="MaterialFilter",
|
29
|
-
|
30
|
+
examples=['!EC3 search("AluminiumBillets") !pragma oMF("1.0/1")'],
|
30
31
|
description="MaterialFilter in string format",
|
31
32
|
)
|
32
|
-
mf_hash: str =
|
33
|
+
mf_hash: str = pydantic.Field(
|
33
34
|
title="MaterialFilter hash",
|
34
|
-
|
35
|
+
examples=["22bf5b78cee5b79e1c76e818873d521c3972688b"],
|
35
36
|
description="MaterialFilter hash. Can be used to compare filters for equality, put to cache etc.",
|
36
37
|
)
|
37
38
|
|
@@ -39,12 +40,12 @@ class MaterialFilterDefinition(BaseOpenEpdApiModel):
|
|
39
40
|
class MaterialFilterMeta(BaseOpenEpdApiModel):
|
40
41
|
"""Meta holding supplementary information about OMF query execution."""
|
41
42
|
|
42
|
-
excluded_fields: list[str] | None =
|
43
|
-
|
43
|
+
excluded_fields: list[str] | None = pydantic.Field(
|
44
|
+
examples=[["building_jurisdiction", "jurisdiction"]],
|
44
45
|
description="list of fields excluded by server process for any reason",
|
45
46
|
default=None,
|
46
47
|
)
|
47
|
-
effective_omf: MaterialFilterDefinition =
|
48
|
+
effective_omf: MaterialFilterDefinition = pydantic.Field(
|
48
49
|
description="Effective OpenMaterialFilter as applied to search, after transformations if any"
|
49
50
|
)
|
50
51
|
|
@@ -15,11 +15,12 @@
|
|
15
15
|
#
|
16
16
|
from typing import TypeAlias
|
17
17
|
|
18
|
+
import pydantic
|
19
|
+
|
18
20
|
from openepd.api.dto.base import BaseOpenEpdApiModel
|
19
21
|
from openepd.api.dto.common import BaseMeta, OpenEpdApiResponse
|
20
22
|
from openepd.api.dto.meta import PagingMetaMixin, WarningMetaMixin
|
21
23
|
from openepd.api.dto.mf import MaterialFilterMetaMixin
|
22
|
-
from openepd.compat.pydantic import pyd
|
23
24
|
from openepd.model.common import Amount
|
24
25
|
from openepd.model.epd import Epd
|
25
26
|
|
@@ -32,70 +33,79 @@ class StatisticsDto(BaseOpenEpdApiModel):
|
|
32
33
|
"""
|
33
34
|
|
34
35
|
# percentiles
|
35
|
-
pct10_gwp: float =
|
36
|
+
pct10_gwp: float = pydantic.Field(
|
36
37
|
description="10th percentile GWP for this statistics measured in kgCO2e per declared unit"
|
37
38
|
)
|
38
|
-
achievable_target: float =
|
39
|
-
description="Achievable target. 20th percentile of GWP measured in kgCO2e per declared unit",
|
39
|
+
achievable_target: float = pydantic.Field(
|
40
|
+
description="Achievable target. 20th percentile of GWP measured in kgCO2e per declared unit",
|
41
|
+
examples=[445.65],
|
40
42
|
)
|
41
|
-
pct30_gwp: float =
|
43
|
+
pct30_gwp: float = pydantic.Field(
|
42
44
|
description="30th percentile GWP for this statistics measured in kgCO2e per declared unit"
|
43
45
|
)
|
44
|
-
pct40_gwp: float =
|
46
|
+
pct40_gwp: float = pydantic.Field(
|
45
47
|
description="40th percentile GWP for this statistics measured in kgCO2e per declared unit"
|
46
48
|
)
|
47
|
-
pct50_gwp: float =
|
49
|
+
pct50_gwp: float = pydantic.Field(
|
48
50
|
description="50th percentile GWP for this statistics measured in kgCO2e per declared unit"
|
49
51
|
)
|
50
|
-
pct60_gwp: float =
|
52
|
+
pct60_gwp: float = pydantic.Field(
|
51
53
|
description="60th percentile GWP for this statistics measured in kgCO2e per declared unit"
|
52
54
|
)
|
53
|
-
pct70_gwp: float =
|
55
|
+
pct70_gwp: float = pydantic.Field(
|
54
56
|
description="70th percentile GWP for this statistics measured in kgCO2e per declared unit"
|
55
57
|
)
|
56
|
-
conservative_estimate: float =
|
58
|
+
conservative_estimate: float = pydantic.Field(
|
57
59
|
description="Conservative estimate. 80th percentile of GWP per declared unit measured in kgCO2e",
|
58
|
-
|
60
|
+
examples=[640.778],
|
59
61
|
)
|
60
|
-
pct90_gwp: float =
|
62
|
+
pct90_gwp: float = pydantic.Field(
|
61
63
|
description="70th percentile GWP for this statistics measured in kgCO2e per declared unit"
|
62
64
|
)
|
63
65
|
|
64
66
|
# stats
|
65
|
-
average: float =
|
66
|
-
min: float | None =
|
67
|
-
description="Min GWP of returned results measured in kgCO2e per declared unit",
|
67
|
+
average: float = pydantic.Field(description="Average GWP in kgCO2e per declared unit", examples=[554.2])
|
68
|
+
min: float | None = pydantic.Field(
|
69
|
+
description="Min GWP of returned results measured in kgCO2e per declared unit",
|
70
|
+
examples=[998.3],
|
68
71
|
)
|
69
|
-
max: float | None =
|
70
|
-
description="Max GWP of returned results measured in kgCO2e per declared unit",
|
72
|
+
max: float | None = pydantic.Field(
|
73
|
+
description="Max GWP of returned results measured in kgCO2e per declared unit",
|
74
|
+
examples=[120.0],
|
71
75
|
)
|
72
76
|
|
73
77
|
# percentiles w/out burden of doubt
|
74
|
-
pct20_gwp_no_bod: float | None =
|
75
|
-
description="20th percentile of GWP (kgCO2e per declared unit), no burden of doubt",
|
78
|
+
pct20_gwp_no_bod: float | None = pydantic.Field(
|
79
|
+
description="20th percentile of GWP (kgCO2e per declared unit), no burden of doubt",
|
80
|
+
examples=[120],
|
76
81
|
)
|
77
|
-
pct40_gwp_no_bod: float | None =
|
78
|
-
description="40th percentile of GWP (kgCO2e per declared unit), no burden of doubt",
|
82
|
+
pct40_gwp_no_bod: float | None = pydantic.Field(
|
83
|
+
description="40th percentile of GWP (kgCO2e per declared unit), no burden of doubt",
|
84
|
+
examples=[120],
|
79
85
|
)
|
80
|
-
pct60_gwp_no_bod: float | None =
|
81
|
-
description="60th percentile of GWP (kgCO2e per declared unit), no burden of doubt",
|
86
|
+
pct60_gwp_no_bod: float | None = pydantic.Field(
|
87
|
+
description="60th percentile of GWP (kgCO2e per declared unit), no burden of doubt",
|
88
|
+
examples=[120],
|
82
89
|
)
|
83
|
-
pct80_gwp_no_bod: float | None =
|
84
|
-
description="80th percentile of GWP (kgCO2e per declared unit), no burden of doubt",
|
90
|
+
pct80_gwp_no_bod: float | None = pydantic.Field(
|
91
|
+
description="80th percentile of GWP (kgCO2e per declared unit), no burden of doubt",
|
92
|
+
examples=[120],
|
85
93
|
)
|
86
|
-
average_gwp_no_bod: float | None =
|
94
|
+
average_gwp_no_bod: float | None = pydantic.Field(description="Average GWP, no burden of doubt", examples=[120])
|
87
95
|
|
88
96
|
# set parameters
|
89
|
-
standard_deviation: float =
|
90
|
-
epds_count: int =
|
91
|
-
industry_epds_count: int =
|
92
|
-
description="Number of Industry-wide EPDs participated in statistics",
|
97
|
+
standard_deviation: float = pydantic.Field(description="Standard deviation", examples=[87.62])
|
98
|
+
epds_count: int = pydantic.Field(description="Number of EPDs participated in statistics", examples=[55])
|
99
|
+
industry_epds_count: int = pydantic.Field(
|
100
|
+
description="Number of Industry-wide EPDs participated in statistics",
|
101
|
+
examples=[4],
|
93
102
|
)
|
94
|
-
generic_estimates_count: int =
|
95
|
-
description="Number of Generic Estimates participated in statistics",
|
103
|
+
generic_estimates_count: int = pydantic.Field(
|
104
|
+
description="Number of Generic Estimates participated in statistics",
|
105
|
+
examples=[0],
|
96
106
|
)
|
97
107
|
|
98
|
-
declared_unit: Amount =
|
108
|
+
declared_unit: Amount = pydantic.Field(
|
99
109
|
description="Declared unit for the statistics. "
|
100
110
|
"Statistical values - percentiles, averages etc - are based on this unit of product"
|
101
111
|
)
|