openepd 7.0.0__tar.gz → 7.1.0__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-7.0.0 → openepd-7.1.0}/PKG-INFO +4 -2
- {openepd-7.0.0 → openepd-7.1.0}/pyproject.toml +3 -3
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/__version__.py +1 -1
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/api/category/sync_api.py +1 -1
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/base.py +1 -1
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/common.py +8 -5
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/epd.py +4 -4
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/generic_estimate.py +2 -2
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/lcia.py +47 -24
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/org.py +38 -26
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/base.py +3 -2
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/range/furnishings.py +15 -15
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/range/steel.py +3 -1
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/singular/furnishings.py +10 -10
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/validation/quantity.py +37 -32
- openepd-7.0.0/src/openepd/__init__.py +0 -21
- {openepd-7.0.0 → openepd-7.1.0}/LICENSE +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/README.md +0 -0
- {openepd-7.0.0/src/openepd/api → openepd-7.1.0/src/openepd}/__init__.py +0 -0
- {openepd-7.0.0/src/openepd/api/average_dataset → openepd-7.1.0/src/openepd/api}/__init__.py +0 -0
- {openepd-7.0.0/src/openepd/api/category → openepd-7.1.0/src/openepd/api/average_dataset}/__init__.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/api/average_dataset/generic_estimate_sync_api.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/api/average_dataset/industry_epd_sync_api.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/api/base_sync_client.py +0 -0
- {openepd-7.0.0/src/openepd/api/dto → openepd-7.1.0/src/openepd/api/category}/__init__.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/api/category/dto.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/api/common.py +0 -0
- {openepd-7.0.0/src/openepd/api/epd → openepd-7.1.0/src/openepd/api/dto}/__init__.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/api/dto/base.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/api/dto/common.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/api/dto/meta.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/api/dto/mf.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/api/dto/params.py +0 -0
- {openepd-7.0.0/src/openepd/api/pcr → openepd-7.1.0/src/openepd/api/epd}/__init__.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/api/epd/dto.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/api/epd/sync_api.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/api/errors.py +0 -0
- {openepd-7.0.0/src/openepd/api/test → openepd-7.1.0/src/openepd/api/pcr}/__init__.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/api/pcr/sync_api.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/api/sync_client.py +0 -0
- {openepd-7.0.0/src/openepd/bundle → openepd-7.1.0/src/openepd/api/test}/__init__.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/api/utils.py +0 -0
- {openepd-7.0.0/src/openepd/model → openepd-7.1.0/src/openepd/bundle}/__init__.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/bundle/base.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/bundle/model.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/bundle/reader.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/bundle/writer.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/m49/__init__.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/m49/const.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/m49/utils.py +0 -0
- {openepd-7.0.0/src/openepd/model/specs/singular/mixins → openepd-7.1.0/src/openepd/model}/__init__.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/category.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/declaration.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/factory.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/geography.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/industry_epd.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/pcr.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/README.md +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/__init__.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/asphalt.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/concrete.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/enums.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/range/__init__.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/range/accessories.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/range/aggregates.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/range/aluminium.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/range/asphalt.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/range/bulk_materials.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/range/cast_decks_and_underlayment.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/range/cladding.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/range/cmu.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/range/concrete.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/range/conveying_equipment.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/range/electrical.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/range/electrical_transmission_and_distribution_equipment.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/range/electricity.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/range/finishes.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/range/fire_and_smoke_protection.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/range/grouting.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/range/manufacturing_inputs.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/range/masonry.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/range/material_handling.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/range/mechanical.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/range/mechanical_insulation.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/range/network_infrastructure.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/range/openings.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/range/other_electrical_equipment.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/range/other_materials.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/range/plumbing.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/range/precast_concrete.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/range/sheathing.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/range/thermal_moisture_protection.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/range/utility_piping.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/range/wood.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/range/wood_joists.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/singular/__init__.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/singular/accessories.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/singular/aggregates.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/singular/aluminium.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/singular/asphalt.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/singular/bulk_materials.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/singular/cast_decks_and_underlayment.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/singular/cladding.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/singular/cmu.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/singular/common.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/singular/concrete.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/singular/conveying_equipment.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/singular/deprecated/__init__.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/singular/deprecated/concrete.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/singular/deprecated/steel.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/singular/electrical.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/singular/electrical_transmission_and_distribution_equipment.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/singular/electricity.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/singular/finishes.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/singular/fire_and_smoke_protection.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/singular/grouting.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/singular/manufacturing_inputs.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/singular/masonry.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/singular/material_handling.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/singular/mechanical.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/singular/mechanical_insulation.py +0 -0
- {openepd-7.0.0/src/openepd/model/validation → openepd-7.1.0/src/openepd/model/specs/singular/mixins}/__init__.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/singular/mixins/conduit_mixin.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/singular/network_infrastructure.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/singular/openings.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/singular/other_electrical_equipment.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/singular/other_materials.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/singular/plumbing.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/singular/precast_concrete.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/singular/sheathing.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/singular/steel.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/singular/thermal_moisture_protection.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/singular/utility_piping.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/singular/wood.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/specs/singular/wood_joists.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/standard.py +0 -0
- /openepd-7.0.0/src/openepd/model/validation/numbers.py → /openepd-7.1.0/src/openepd/model/validation/__init__.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/validation/common.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/validation/enum.py +0 -0
- /openepd-7.0.0/src/openepd/patch_pydantic.py → /openepd-7.1.0/src/openepd/model/validation/numbers.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/model/versioning.py +0 -0
- {openepd-7.0.0 → openepd-7.1.0}/src/openepd/py.typed +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: openepd
|
3
|
-
Version: 7.
|
3
|
+
Version: 7.1.0
|
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
|
@@ -15,13 +15,15 @@ Classifier: License :: OSI Approved :: Apache Software License
|
|
15
15
|
Classifier: Operating System :: OS Independent
|
16
16
|
Classifier: Programming Language :: Python :: 3
|
17
17
|
Classifier: Programming Language :: Python :: 3.11
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
18
20
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
19
21
|
Provides-Extra: api-client
|
20
22
|
Requires-Dist: email-validator (>=1.3.1)
|
21
23
|
Requires-Dist: idna (>=3.7)
|
22
24
|
Requires-Dist: open-xpd-uuid (>=0.2.1,<0.3.0)
|
23
25
|
Requires-Dist: openlocationcode (>=1.0.1)
|
24
|
-
Requires-Dist: pydantic (>=
|
26
|
+
Requires-Dist: pydantic (>=2.0,<3)
|
25
27
|
Requires-Dist: requests (>=2.0) ; extra == "api-client"
|
26
28
|
Project-URL: Repository, https://github.com/cchangelabs/openepd
|
27
29
|
Description-Content-Type: text/markdown
|
@@ -1,11 +1,11 @@
|
|
1
1
|
[tool.ruff]
|
2
2
|
line-length = 120
|
3
|
-
target-version = "
|
3
|
+
target-version = "py311"
|
4
4
|
exclude = [".*pyi"]
|
5
5
|
|
6
6
|
[tool.poetry]
|
7
7
|
name = "openepd"
|
8
|
-
version = "7.
|
8
|
+
version = "7.1.0"
|
9
9
|
license = "Apache-2.0"
|
10
10
|
description = "Python library to work with OpenEPD format"
|
11
11
|
authors = ["C-Change Labs <support@c-change-labs.com>"]
|
@@ -26,7 +26,7 @@ exclude = ["**/test_*.py", "**/tests/**"]
|
|
26
26
|
|
27
27
|
[tool.poetry.dependencies]
|
28
28
|
python = "^3.11"
|
29
|
-
pydantic = ">=
|
29
|
+
pydantic = ">=2.0,<3"
|
30
30
|
email-validator = ">=1.3.1"
|
31
31
|
requests = { version = ">=2.0", optional = true }
|
32
32
|
idna = ">=3.7"
|
@@ -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
|
"""
|
@@ -234,7 +234,7 @@ class BaseDocumentFactory(Generic[TRootDocument]):
|
|
234
234
|
for x, doc_cls in cls.VERSION_MAP.items():
|
235
235
|
if x.major == version.major:
|
236
236
|
if version.minor <= x.minor:
|
237
|
-
return doc_cls(
|
237
|
+
return doc_cls.model_validate(data)
|
238
238
|
else:
|
239
239
|
raise ValueError(
|
240
240
|
f"Unsupported version: {version}. The highest supported version from branch {x.major}.x is {x}"
|
@@ -14,7 +14,7 @@
|
|
14
14
|
# limitations under the License.
|
15
15
|
#
|
16
16
|
from enum import StrEnum
|
17
|
-
from typing import Annotated, Any
|
17
|
+
from typing import Annotated, Any, Self
|
18
18
|
|
19
19
|
import pydantic
|
20
20
|
import pydantic_core
|
@@ -32,12 +32,15 @@ class Amount(BaseOpenEpdSchema):
|
|
32
32
|
default=None,
|
33
33
|
)
|
34
34
|
|
35
|
-
|
36
|
-
|
35
|
+
model_config = pydantic.ConfigDict(from_attributes=True)
|
36
|
+
|
37
|
+
@pydantic.model_validator(mode="after")
|
38
|
+
def check_qty_or_unit(self) -> Self:
|
37
39
|
"""Ensure that qty or unit is provided."""
|
38
|
-
|
40
|
+
|
41
|
+
if self.qty is None and self.unit is None:
|
39
42
|
raise ValueError("Either qty or unit must be provided.")
|
40
|
-
return
|
43
|
+
return self
|
41
44
|
|
42
45
|
def to_quantity_str(self):
|
43
46
|
"""Return a string representation of the amount."""
|
@@ -282,10 +282,10 @@ class EpdWithDepsV0(EpdV0, title="EPD (with Dependencies)"):
|
|
282
282
|
some required fields in Org (like web_domain), and WithDeps would not.
|
283
283
|
"""
|
284
284
|
|
285
|
-
manufacturer: Org | None = pydantic.Field(description=MANUFACTURER_DESCRIPTION)
|
286
|
-
epd_developer: Org | None = pydantic.Field(description=DEVELOPER_DESCRIPTION, default=None)
|
287
|
-
program_operator: Org | None = pydantic.Field(description=PROGRAM_OPERATOR_DESCRIPTION)
|
288
|
-
third_party_verifier: Org | None = pydantic.Field(description=THIRD_PARTY_VERIFIER_DESCRIPTION)
|
285
|
+
manufacturer: Org | None = pydantic.Field(description=MANUFACTURER_DESCRIPTION, default=None) # type: ignore[assignment]
|
286
|
+
epd_developer: Org | None = pydantic.Field(description=DEVELOPER_DESCRIPTION, default=None) # type: ignore[assignment]
|
287
|
+
program_operator: Org | None = pydantic.Field(description=PROGRAM_OPERATOR_DESCRIPTION, default=None) # type: ignore[assignment]
|
288
|
+
third_party_verifier: Org | None = pydantic.Field(description=THIRD_PARTY_VERIFIER_DESCRIPTION, default=None) # type: ignore[assignment]
|
289
289
|
|
290
290
|
|
291
291
|
EpdWithDeps = EpdWithDepsV0
|
@@ -110,8 +110,8 @@ class GenericEstimateWithDepsV0(GenericEstimateV0, title="Generic Estimate (with
|
|
110
110
|
Contains related entities - orgs - with full fields, to support object matching in implementations.
|
111
111
|
"""
|
112
112
|
|
113
|
-
publisher: Org | None = pydantic.Field(description="Organization that published the LCA results.")
|
114
|
-
reviewer: Org | None = pydantic.Field(description="Org that performed a critical review of the LCA.")
|
113
|
+
publisher: Org | None = pydantic.Field(description="Organization that published the LCA results.", default=None) # type: ignore[assignment]
|
114
|
+
reviewer: Org | None = pydantic.Field(description="Org that performed a critical review of the LCA.", default=None) # type: ignore[assignment]
|
115
115
|
|
116
116
|
|
117
117
|
GenericEstimateWithDeps = GenericEstimateWithDepsV0
|
@@ -18,6 +18,7 @@ from typing import Any, ClassVar
|
|
18
18
|
|
19
19
|
import pydantic
|
20
20
|
from pydantic import ConfigDict
|
21
|
+
from typing_extensions import Self
|
21
22
|
|
22
23
|
from openepd.model.base import BaseOpenEpdSchema
|
23
24
|
from openepd.model.common import Measurement
|
@@ -200,15 +201,18 @@ class ScopeSet(BaseOpenEpdSchema):
|
|
200
201
|
description="Potential net benefits from reuse, recycling, and/or energy recovery beyond the system boundary.",
|
201
202
|
)
|
202
203
|
|
203
|
-
|
204
|
-
|
204
|
+
model_config = pydantic.ConfigDict(from_attributes=True)
|
205
|
+
|
206
|
+
@pydantic.model_validator(mode="after")
|
207
|
+
def _unit_validator(self) -> Self:
|
205
208
|
all_units = set()
|
206
209
|
|
207
|
-
for k
|
210
|
+
for k in self.model_fields:
|
211
|
+
v = getattr(self, k, None)
|
208
212
|
if isinstance(v, Measurement):
|
209
213
|
all_units.add(v.unit)
|
210
214
|
|
211
|
-
if not
|
215
|
+
if not self.allowed_units:
|
212
216
|
# For unknown units - only units should be the same across all measurements (textually)
|
213
217
|
if len(all_units) > 1:
|
214
218
|
raise ValueError("All scopes and measurements should be expressed in the same unit.")
|
@@ -221,9 +225,9 @@ class ScopeSet(BaseOpenEpdSchema):
|
|
221
225
|
ExternalValidationConfig.QUANTITY_VALIDATOR.validate_same_dimensionality(first, unit)
|
222
226
|
|
223
227
|
# can correctly validate unit
|
224
|
-
if
|
228
|
+
if self.allowed_units is not None and len(all_units) == 1 and ExternalValidationConfig.QUANTITY_VALIDATOR:
|
225
229
|
unit = next(iter(all_units))
|
226
|
-
allowed_units =
|
230
|
+
allowed_units = self.allowed_units if isinstance(self.allowed_units, tuple) else (self.allowed_units,)
|
227
231
|
|
228
232
|
matched_unit = False
|
229
233
|
for allowed_unit in allowed_units:
|
@@ -237,7 +241,7 @@ class ScopeSet(BaseOpenEpdSchema):
|
|
237
241
|
f"'{', '.join(allowed_units)}' is only allowed unit for this scopeset. Provided '{unit}'"
|
238
242
|
)
|
239
243
|
|
240
|
-
return
|
244
|
+
return self
|
241
245
|
|
242
246
|
|
243
247
|
class ScopesetByNameBase(BaseOpenEpdSchema, extra="allow"):
|
@@ -279,7 +283,7 @@ class ScopesetByNameBase(BaseOpenEpdSchema, extra="allow"):
|
|
279
283
|
def _extra_scopeset_validator(cls, values: dict[str, Any]) -> dict[str, Any]:
|
280
284
|
for f in values:
|
281
285
|
# only interested in validating the extra fields
|
282
|
-
if f in cls.
|
286
|
+
if f in cls.model_fields:
|
283
287
|
continue
|
284
288
|
|
285
289
|
# extra impact of an unknown type - engage validation of ScopeSet
|
@@ -298,97 +302,103 @@ class ScopesetByNameBase(BaseOpenEpdSchema, extra="allow"):
|
|
298
302
|
class ScopeSetGwp(ScopeSet):
|
299
303
|
"""ScopeSet measured in kgCO2e."""
|
300
304
|
|
301
|
-
allowed_units = "kgCO2e"
|
305
|
+
allowed_units: ClassVar[str | tuple[str, ...] | None] = "kgCO2e"
|
302
306
|
|
303
307
|
|
304
308
|
class ScopeSetOdp(ScopeSet):
|
305
309
|
"""ScopeSet measured in kgCFC11e."""
|
306
310
|
|
307
|
-
allowed_units = "kgCFC11e"
|
311
|
+
allowed_units: ClassVar[str | tuple[str, ...] | None] = "kgCFC11e"
|
308
312
|
|
309
313
|
|
310
314
|
class ScopeSetAp(ScopeSet):
|
311
315
|
"""ScopeSet measured in kgSO2e."""
|
312
316
|
|
313
|
-
allowed_units = ("kgSO2e", "molHe")
|
317
|
+
allowed_units: ClassVar[str | tuple[str, ...] | None] = ("kgSO2e", "molHe")
|
314
318
|
|
315
319
|
|
316
320
|
class ScopeSetEpNe(ScopeSet):
|
317
321
|
"""ScopeSet measured in kgNe."""
|
318
322
|
|
319
|
-
allowed_units = "kgNe"
|
323
|
+
allowed_units: ClassVar[str | tuple[str, ...] | None] = "kgNe"
|
320
324
|
|
321
325
|
|
322
326
|
class ScopeSetPocp(ScopeSet):
|
323
327
|
"""ScopeSet measured in kgO3e."""
|
324
328
|
|
325
|
-
allowed_units = ("kgO3e", "kgNMVOCe")
|
329
|
+
allowed_units: ClassVar[str | tuple[str, ...] | None] = ("kgO3e", "kgNMVOCe")
|
326
330
|
|
327
331
|
|
328
332
|
class ScopeSetEpFresh(ScopeSet):
|
329
333
|
"""ScopeSet measured in kgPO4e."""
|
330
334
|
|
331
|
-
allowed_units = "kgPO4e"
|
335
|
+
allowed_units: ClassVar[str | tuple[str, ...] | None] = "kgPO4e"
|
332
336
|
|
333
337
|
|
334
338
|
class ScopeSetEpTerr(ScopeSet):
|
335
339
|
"""ScopeSet measured in molNe."""
|
336
340
|
|
337
|
-
allowed_units = "molNe"
|
341
|
+
allowed_units: ClassVar[str | tuple[str, ...] | None] = "molNe"
|
338
342
|
|
339
343
|
|
340
344
|
class ScopeSetIrp(ScopeSet):
|
341
345
|
"""ScopeSet measured in kilo Becquerel equivalent of u235."""
|
342
346
|
|
343
|
-
allowed_units = "kBqU235e"
|
347
|
+
allowed_units: ClassVar[str | tuple[str, ...] | None] = "kBqU235e"
|
344
348
|
|
345
349
|
|
346
350
|
class ScopeSetCTUh(ScopeSet):
|
347
351
|
"""ScopeSet measured in CTUh."""
|
348
352
|
|
349
|
-
allowed_units = "CTUh"
|
353
|
+
allowed_units: ClassVar[str | tuple[str, ...] | None] = "CTUh"
|
350
354
|
|
351
355
|
|
352
356
|
class ScopeSetM3Aware(ScopeSet):
|
353
357
|
"""ScopeSet measured in m3AWARE Water consumption by AWARE method."""
|
354
358
|
|
355
|
-
allowed_units = "m3AWARE"
|
359
|
+
allowed_units: ClassVar[str | tuple[str, ...] | None] = "m3AWARE"
|
356
360
|
|
357
361
|
|
358
362
|
class ScopeSetCTUe(ScopeSet):
|
359
363
|
"""ScopeSet measured in CTUe."""
|
360
364
|
|
361
|
-
allowed_units = "CTUe"
|
365
|
+
allowed_units: ClassVar[str | tuple[str, ...] | None] = "CTUe"
|
366
|
+
|
367
|
+
|
368
|
+
class ScopeSetKgSbe(ScopeSet):
|
369
|
+
"""ScopeSet measured in kgSbe."""
|
370
|
+
|
371
|
+
allowed_units: ClassVar[str | tuple[str, ...] | None] = "kgSbe"
|
362
372
|
|
363
373
|
|
364
374
|
class ScopeSetDiseaseIncidence(ScopeSet):
|
365
375
|
"""ScopeSet measuring disease incidence measured in AnnualPerCapita (cases)."""
|
366
376
|
|
367
|
-
allowed_units = "AnnualPerCapita"
|
377
|
+
allowed_units: ClassVar[str | tuple[str, ...] | None] = "AnnualPerCapita"
|
368
378
|
|
369
379
|
|
370
380
|
class ScopeSetMass(ScopeSet):
|
371
381
|
"""ScopeSet measuring mass in kg."""
|
372
382
|
|
373
|
-
allowed_units = "kg"
|
383
|
+
allowed_units: ClassVar[str | tuple[str, ...] | None] = "kg"
|
374
384
|
|
375
385
|
|
376
386
|
class ScopeSetVolume(ScopeSet):
|
377
387
|
"""ScopeSet measuring mass in kg."""
|
378
388
|
|
379
|
-
allowed_units = "m3"
|
389
|
+
allowed_units: ClassVar[str | tuple[str, ...] | None] = "m3"
|
380
390
|
|
381
391
|
|
382
392
|
class ScopeSetMassOrVolume(ScopeSet):
|
383
393
|
"""ScopeSet measuring mass in kg OR volume in m3, example: radioactive waste."""
|
384
394
|
|
385
|
-
allowed_units = ("kg", "m3")
|
395
|
+
allowed_units: ClassVar[str | tuple[str, ...] | None] = ("kg", "m3")
|
386
396
|
|
387
397
|
|
388
398
|
class ScopeSetEnergy(ScopeSet):
|
389
399
|
"""ScopeSet measuring mass in kg."""
|
390
400
|
|
391
|
-
allowed_units = "MJ"
|
401
|
+
allowed_units: ClassVar[str | tuple[str, ...] | None] = "MJ"
|
392
402
|
|
393
403
|
|
394
404
|
class ImpactSet(ScopesetByNameBase):
|
@@ -483,6 +493,19 @@ class ImpactSet(ScopesetByNameBase):
|
|
483
493
|
default=None,
|
484
494
|
description="Land use related impacts / Soil quality, in potential soil quality parameters.",
|
485
495
|
)
|
496
|
+
ADP_mineral: ScopeSetKgSbe | None = pydantic.Field(
|
497
|
+
alias="ADP-mineral",
|
498
|
+
default=None,
|
499
|
+
description='Abiotic depletion potential for non-fossil resources. EN15804 calls this "ADP - minerals and metals".',
|
500
|
+
)
|
501
|
+
|
502
|
+
ADP_fossil: ScopeSetEnergy | None = pydantic.Field(
|
503
|
+
alias="ADP-fossil",
|
504
|
+
default=None,
|
505
|
+
description="Abiotic depletion potential for fossil resources",
|
506
|
+
)
|
507
|
+
|
508
|
+
model_config = pydantic.ConfigDict(from_attributes=True)
|
486
509
|
|
487
510
|
|
488
511
|
class LCIAMethod(StrEnum):
|
@@ -13,11 +13,11 @@
|
|
13
13
|
# See the License for the specific language governing permissions and
|
14
14
|
# limitations under the License.
|
15
15
|
#
|
16
|
-
from typing import
|
16
|
+
from typing import Any, Optional
|
17
17
|
|
18
18
|
from openlocationcode import openlocationcode
|
19
19
|
import pydantic
|
20
|
-
from pydantic import ConfigDict
|
20
|
+
from pydantic import ConfigDict
|
21
21
|
|
22
22
|
from openepd.model.base import BaseOpenEpdSchema
|
23
23
|
from openepd.model.common import Location, WithAltIdsMixin, WithAttachmentsMixin
|
@@ -47,36 +47,46 @@ class OrgRef(BaseOpenEpdSchema):
|
|
47
47
|
class Org(WithAttachmentsMixin, WithAltIdsMixin, OrgRef):
|
48
48
|
"""Represent an organization."""
|
49
49
|
|
50
|
-
alt_names: (
|
51
|
-
Annotated[
|
52
|
-
list[str],
|
53
|
-
Annotated[
|
54
|
-
List[Annotated[str, StringConstraints(max_length=200)]],
|
55
|
-
Field(max_length=255),
|
56
|
-
],
|
57
|
-
]
|
58
|
-
| None
|
59
|
-
) = pydantic.Field(
|
50
|
+
alt_names: list[str] | None = pydantic.Field(
|
60
51
|
description="List of other names for organization",
|
61
52
|
examples=[["C-Change Labs", "C-Change Labs inc."]],
|
62
53
|
default=None,
|
54
|
+
max_length=255,
|
63
55
|
)
|
64
56
|
# TODO: NEW field, not in the spec
|
65
57
|
|
58
|
+
@pydantic.field_validator("alt_names", mode="before")
|
59
|
+
def _validate_alt_names(cls, value: Any) -> list[str] | None:
|
60
|
+
if value is None:
|
61
|
+
return value
|
62
|
+
|
63
|
+
if not isinstance(value, list):
|
64
|
+
raise TypeError(f"Expected type list or None, got {type(value)}")
|
65
|
+
|
66
|
+
if any((len(item) > 200) for item in value):
|
67
|
+
raise ValueError("One or more alt_names are longer than 200 characters")
|
68
|
+
|
69
|
+
return value
|
70
|
+
|
66
71
|
owner: Optional["OrgRef"] = pydantic.Field(description="Organization that controls this organization", default=None)
|
67
|
-
subsidiaries: (
|
68
|
-
|
69
|
-
list["OrgRef"],
|
70
|
-
Annotated[
|
71
|
-
List[Annotated[str, StringConstraints(max_length=200)]],
|
72
|
-
Field(max_length=255),
|
73
|
-
],
|
74
|
-
]
|
75
|
-
| None
|
76
|
-
) = pydantic.Field(
|
77
|
-
description="Organizations controlled by this organization",
|
78
|
-
default=None,
|
72
|
+
subsidiaries: list["OrgRef"] | None = pydantic.Field(
|
73
|
+
description="Organizations controlled by this organization", default=None, max_length=255
|
79
74
|
)
|
75
|
+
|
76
|
+
@pydantic.field_validator("subsidiaries", mode="before")
|
77
|
+
def _validate_subsidiaries(cls, value: Any) -> list[str] | None:
|
78
|
+
if value is None:
|
79
|
+
return value
|
80
|
+
|
81
|
+
if not isinstance(value, list):
|
82
|
+
raise TypeError(f"Expected type list or None, got {type(value)}")
|
83
|
+
|
84
|
+
for item in value:
|
85
|
+
if len(item.name) > 200:
|
86
|
+
raise ValueError("One or more subsidiaries name are longer than 200 characters")
|
87
|
+
|
88
|
+
return value
|
89
|
+
|
80
90
|
hq_location: Location | None = pydantic.Field(
|
81
91
|
default=None,
|
82
92
|
description="Location of a place of business, preferably the corporate headquarters.",
|
@@ -111,8 +121,10 @@ class Plant(PlantRef, WithAttachmentsMixin, WithAltIdsMixin):
|
|
111
121
|
pluscode: str | None = pydantic.Field(
|
112
122
|
default=None,
|
113
123
|
description="(deprecated) Plus code (aka Open Location Code) of plant's location",
|
114
|
-
|
115
|
-
|
124
|
+
json_schema_extra={
|
125
|
+
"deprecated": "Pluscode field is deprecated. If users need a pluscode they can obtain it from "
|
126
|
+
"`id` like this: `id.spit('.', maxsplit=1)[0]`",
|
127
|
+
},
|
116
128
|
)
|
117
129
|
latitude: float | None = pydantic.Field(
|
118
130
|
default=None,
|
@@ -20,10 +20,10 @@ from typing import Any
|
|
20
20
|
import pydantic
|
21
21
|
from pydantic import ConfigDict
|
22
22
|
|
23
|
-
from openepd.model.base import BaseOpenEpdSchema
|
23
|
+
from openepd.model.base import BaseOpenEpdSchema
|
24
24
|
from openepd.model.validation.common import validate_version_compatibility, validate_version_format
|
25
25
|
from openepd.model.validation.quantity import ExternalValidationConfig, QuantityValidator
|
26
|
-
from openepd.model.versioning import WithExtVersionMixin
|
26
|
+
from openepd.model.versioning import Version, WithExtVersionMixin
|
27
27
|
|
28
28
|
|
29
29
|
class BaseOpenEpdSpec(BaseOpenEpdSchema):
|
@@ -35,6 +35,7 @@ class BaseOpenEpdSpec(BaseOpenEpdSchema):
|
|
35
35
|
class BaseOpenEpdHierarchicalSpec(BaseOpenEpdSpec, WithExtVersionMixin):
|
36
36
|
"""Base class for new specs (hierarchical, versioned)."""
|
37
37
|
|
38
|
+
# TODO: Refactor work with class-based and instance-based _EXT_VERSION
|
38
39
|
def __init__(self, **data: Any) -> None:
|
39
40
|
# ensure that all the concrete spec objects fail on creations if they dont have _EXT_VERSION declared to
|
40
41
|
# something meaningful
|
@@ -19,11 +19,11 @@ __all__ = (
|
|
19
19
|
"CountertopsRangeV1",
|
20
20
|
"DemountablePartitionsRangeV1",
|
21
21
|
"OtherFurnishingsRangeV1",
|
22
|
-
"
|
23
|
-
"
|
24
|
-
"
|
25
|
-
"
|
26
|
-
"
|
22
|
+
"OpenStorageFurnitureRangeV1",
|
23
|
+
"ClosedStorageFurnitureRangeV1",
|
24
|
+
"RetractableStorageFurnitureRangeV1",
|
25
|
+
"MobileStorageFurnitureRangeV1",
|
26
|
+
"WallMountedStorageShelvingRangeV1",
|
27
27
|
"OtherStorageFurnitureRangeV1",
|
28
28
|
"StorageFurnitureRangeV1",
|
29
29
|
"TablesRangeV1",
|
@@ -97,7 +97,7 @@ class OtherFurnishingsRangeV1(BaseOpenEpdHierarchicalSpec):
|
|
97
97
|
_EXT_VERSION = "1.0"
|
98
98
|
|
99
99
|
|
100
|
-
class
|
100
|
+
class OpenStorageFurnitureRangeV1(BaseOpenEpdHierarchicalSpec):
|
101
101
|
"""
|
102
102
|
Open Storage.
|
103
103
|
|
@@ -109,7 +109,7 @@ class OpenStorageRangeV1(BaseOpenEpdHierarchicalSpec):
|
|
109
109
|
_EXT_VERSION = "1.0"
|
110
110
|
|
111
111
|
|
112
|
-
class
|
112
|
+
class ClosedStorageFurnitureRangeV1(BaseOpenEpdHierarchicalSpec):
|
113
113
|
"""
|
114
114
|
Closed Storage.
|
115
115
|
|
@@ -121,7 +121,7 @@ class ClosedStorageRangeV1(BaseOpenEpdHierarchicalSpec):
|
|
121
121
|
_EXT_VERSION = "1.0"
|
122
122
|
|
123
123
|
|
124
|
-
class
|
124
|
+
class RetractableStorageFurnitureRangeV1(BaseOpenEpdHierarchicalSpec):
|
125
125
|
"""
|
126
126
|
Retractable Storage.
|
127
127
|
|
@@ -133,7 +133,7 @@ class RetractableStorageRangeV1(BaseOpenEpdHierarchicalSpec):
|
|
133
133
|
_EXT_VERSION = "1.0"
|
134
134
|
|
135
135
|
|
136
|
-
class
|
136
|
+
class MobileStorageFurnitureRangeV1(BaseOpenEpdHierarchicalSpec):
|
137
137
|
"""
|
138
138
|
Mobile Storage.
|
139
139
|
|
@@ -145,7 +145,7 @@ class MobileStorageRangeV1(BaseOpenEpdHierarchicalSpec):
|
|
145
145
|
_EXT_VERSION = "1.0"
|
146
146
|
|
147
147
|
|
148
|
-
class
|
148
|
+
class WallMountedStorageShelvingRangeV1(BaseOpenEpdHierarchicalSpec):
|
149
149
|
"""
|
150
150
|
Wall Mounted Shelving.
|
151
151
|
|
@@ -176,11 +176,11 @@ class StorageFurnitureRangeV1(BaseOpenEpdHierarchicalSpec):
|
|
176
176
|
|
177
177
|
_EXT_VERSION = "1.1"
|
178
178
|
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
179
|
+
OpenStorageFurniture: OpenStorageFurnitureRangeV1 | None = None
|
180
|
+
ClosedStorageFurniture: ClosedStorageFurnitureRangeV1 | None = None
|
181
|
+
RetractableStorageFurniture: RetractableStorageFurnitureRangeV1 | None = None
|
182
|
+
MobileStorageFurniture: MobileStorageFurnitureRangeV1 | None = None
|
183
|
+
WallMountedStorageShelving: WallMountedStorageShelvingRangeV1 | None = None
|
184
184
|
OtherStorageFurniture: OtherStorageFurnitureRangeV1 | None = None
|
185
185
|
|
186
186
|
|
@@ -35,6 +35,8 @@ __all__ = (
|
|
35
35
|
"SteelRangeV1",
|
36
36
|
)
|
37
37
|
|
38
|
+
from typing import ClassVar
|
39
|
+
|
38
40
|
import pydantic
|
39
41
|
|
40
42
|
from openepd.model.common import RangeFloat, RangeRatioFloat
|
@@ -313,7 +315,7 @@ class SteelRangeV1(BaseOpenEpdHierarchicalSpec):
|
|
313
315
|
Range version.
|
314
316
|
"""
|
315
317
|
|
316
|
-
_EXT_VERSION = "1.1"
|
318
|
+
_EXT_VERSION: ClassVar[str] = "1.1"
|
317
319
|
|
318
320
|
yield_tensile_str: AmountRangePressureMpa | None = pydantic.Field(
|
319
321
|
default=None,
|
@@ -57,7 +57,7 @@ class OtherFurnishingsV1(BaseOpenEpdHierarchicalSpec):
|
|
57
57
|
_EXT_VERSION = "1.0"
|
58
58
|
|
59
59
|
|
60
|
-
class
|
60
|
+
class OpenStorageFurnitureV1(BaseOpenEpdHierarchicalSpec):
|
61
61
|
"""
|
62
62
|
Open Storage.
|
63
63
|
|
@@ -67,7 +67,7 @@ class OpenStorageV1(BaseOpenEpdHierarchicalSpec):
|
|
67
67
|
_EXT_VERSION = "1.0"
|
68
68
|
|
69
69
|
|
70
|
-
class
|
70
|
+
class ClosedStorageFurnitureV1(BaseOpenEpdHierarchicalSpec):
|
71
71
|
"""
|
72
72
|
Closed Storage.
|
73
73
|
|
@@ -77,7 +77,7 @@ class ClosedStorageV1(BaseOpenEpdHierarchicalSpec):
|
|
77
77
|
_EXT_VERSION = "1.0"
|
78
78
|
|
79
79
|
|
80
|
-
class
|
80
|
+
class RetractableStorageFurnitureV1(BaseOpenEpdHierarchicalSpec):
|
81
81
|
"""
|
82
82
|
Retractable Storage.
|
83
83
|
|
@@ -87,7 +87,7 @@ class RetractableStorageV1(BaseOpenEpdHierarchicalSpec):
|
|
87
87
|
_EXT_VERSION = "1.0"
|
88
88
|
|
89
89
|
|
90
|
-
class
|
90
|
+
class MobileStorageFurnitureV1(BaseOpenEpdHierarchicalSpec):
|
91
91
|
"""
|
92
92
|
Mobile Storage.
|
93
93
|
|
@@ -97,7 +97,7 @@ class MobileStorageV1(BaseOpenEpdHierarchicalSpec):
|
|
97
97
|
_EXT_VERSION = "1.0"
|
98
98
|
|
99
99
|
|
100
|
-
class
|
100
|
+
class WallMountedStorageShelvingV1(BaseOpenEpdHierarchicalSpec):
|
101
101
|
"""
|
102
102
|
Wall Mounted Shelving.
|
103
103
|
|
@@ -119,11 +119,11 @@ class StorageFurnitureV1(BaseOpenEpdHierarchicalSpec):
|
|
119
119
|
_EXT_VERSION = "1.1"
|
120
120
|
|
121
121
|
# Nested specs:
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
122
|
+
OpenStorageFurniture: OpenStorageFurnitureV1 | None = None
|
123
|
+
ClosedStorageFurniture: ClosedStorageFurnitureV1 | None = None
|
124
|
+
RetractableStorageFurniture: RetractableStorageFurnitureV1 | None = None
|
125
|
+
MobileStorageFurniture: MobileStorageFurnitureV1 | None = None
|
126
|
+
WallMountedStorageShelving: WallMountedStorageShelvingV1 | None = None
|
127
127
|
OtherStorageFurniture: OtherStorageFurnitureV1 | None = None
|
128
128
|
|
129
129
|
|