openepd 4.11.3__py3-none-any.whl → 4.13.0__py3-none-any.whl

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/__version__.py CHANGED
@@ -13,4 +13,4 @@
13
13
  # See the License for the specific language governing permissions and
14
14
  # limitations under the License.
15
15
  #
16
- VERSION = "4.11.3"
16
+ VERSION = "4.13.0"
@@ -151,6 +151,7 @@ class SyncHttpClient:
151
151
 
152
152
  HTTP_DATE_TIME_FORMAT = "%a, %d %b %Y %H:%M:%S %Z"
153
153
  DEFAULT_RETRY_INTERVAL_SEC = 10
154
+ DEFAULT_TIMEOUT_SEC = (15, 2 * 60)
154
155
 
155
156
  def __init__(
156
157
  self,
@@ -183,7 +184,7 @@ class SyncHttpClient:
183
184
  else throttle_retry_timeout.total_seconds()
184
185
  )
185
186
  self.user_agent = user_agent
186
- self.timeout = timeout_sec
187
+ self.timeout = timeout_sec or self.DEFAULT_TIMEOUT_SEC
187
188
  self._session: Session | None = None
188
189
  self._auth: AuthBase | None = auth
189
190
  self._retry_count: int = retry_count
openepd/api/errors.py CHANGED
@@ -55,9 +55,22 @@ class ValidationError(ApiError):
55
55
  super().__init__(http_status, error_summary, response, error_code)
56
56
  self.validation_errors: dict[str, list[str]] = validation_errors or {}
57
57
 
58
+ @staticmethod
59
+ def __flatten(nested_errors: dict[str, Any], parent_key: str = "") -> dict[str, list[str]]:
60
+ result: dict[str, list[str]] = {}
61
+ for key, value in nested_errors.items():
62
+ full_key = f"{parent_key}.{key}" if parent_key else key
63
+ if isinstance(value, dict):
64
+ nested_result = ValidationError.__flatten(value, full_key)
65
+ for nested_key, nested_errors in nested_result.items(): # type: ignore[assignment]
66
+ result.setdefault(nested_key, []).extend(nested_errors)
67
+ else:
68
+ result.setdefault(full_key, []).extend(value)
69
+ return result
70
+
58
71
  def __str__(self) -> str:
59
72
  result: list[str] = ["Validation errors:"]
60
- for code, errors in self.validation_errors.items():
73
+ for code, errors in self.__flatten(self.validation_errors).items(): # type: ignore[arg-type]
61
74
  result.append(f"{code}:")
62
75
  for e in errors:
63
76
  result.append(f" {e}")
openepd/model/base.py CHANGED
@@ -18,6 +18,8 @@ from enum import StrEnum
18
18
  import json
19
19
  from typing import Any, Callable, ClassVar, Generic, Optional, Type, TypeAlias, TypeVar
20
20
 
21
+ from cqd import open_xpd_uuid # type:ignore[import-untyped]
22
+
21
23
  from openepd.compat.pydantic import pyd, pyd_generics
22
24
  from openepd.model.validation.common import validate_version_compatibility, validate_version_format
23
25
  from openepd.model.versioning import OpenEpdVersions, Version
@@ -183,7 +185,7 @@ class RootDocument(abc.ABC, BaseOpenEpdSchema):
183
185
 
184
186
  doctype: str = pyd.Field(
185
187
  description='Describes the type and schema of the document. Must always always read "openEPD".',
186
- default="OpenEPD",
188
+ default="openEPD",
187
189
  )
188
190
  openepd_version: str = pyd.Field(
189
191
  description="Version of the document format, related to /doctype",
@@ -233,3 +235,31 @@ class BaseDocumentFactory(Generic[TRootDocument]):
233
235
  )
234
236
  supported_versions = ", ".join(f"{v.major}.x" for v in cls.VERSION_MAP.keys())
235
237
  raise ValueError(f"Version {version} is not supported. Supported versions are: {supported_versions}")
238
+
239
+
240
+ class OpenXpdUUID(str):
241
+ """
242
+ An open xpd UUID format for IDs of openEPD documents.
243
+
244
+ See https://github.com/cchangelabs/open-xpd-uuid-lib for details.
245
+ """
246
+
247
+ def _validate_id(cls, v: str | None) -> str | None:
248
+ if v is None:
249
+ return v
250
+
251
+ try:
252
+ open_xpd_uuid.validate(open_xpd_uuid.sanitize(str(v)))
253
+ return v
254
+ except open_xpd_uuid.GuidValidationError as e:
255
+ raise ValueError("Invalid format") from e
256
+
257
+ @classmethod
258
+ def __get_validators__(cls):
259
+ yield cls._validate_id
260
+
261
+ @classmethod
262
+ def __modify_schema__(cls, field_schema):
263
+ field_schema.update(
264
+ example="XC300001",
265
+ )
@@ -17,13 +17,14 @@ import abc
17
17
  import datetime
18
18
 
19
19
  from openepd.compat.pydantic import pyd
20
- from openepd.model.base import BaseOpenEpdSchema, RootDocument
20
+ from openepd.model.base import BaseOpenEpdSchema, OpenXpdUUID, RootDocument
21
21
  from openepd.model.common import Amount
22
22
  from openepd.model.geography import Geography
23
23
  from openepd.model.org import Org
24
24
  from openepd.model.pcr import Pcr
25
25
  from openepd.model.standard import Standard
26
26
  from openepd.model.validation.common import ReferenceStr
27
+ from openepd.model.validation.quantity import AmountMass
27
28
 
28
29
  DEVELOPER_DESCRIPTION = "The organization responsible for the underlying LCA (and subsequent summarization as EPD)."
29
30
  PROGRAM_OPERATOR_DESCRIPTION = "JSON object for program operator Org"
@@ -33,8 +34,7 @@ THIRD_PARTY_VERIFIER_DESCRIPTION = "JSON object for Org that performed a critica
33
34
  class BaseDeclaration(RootDocument, abc.ABC):
34
35
  """Base class for declaration-related documents (EPDs, Industry-wide EPDs, Generic Estimates)."""
35
36
 
36
- # TODO: Add validator for open-xpd-uuid on this field
37
- id: str | None = pyd.Field(
37
+ id: OpenXpdUUID | None = pyd.Field(
38
38
  description="The unique ID for this document. To ensure global uniqueness, should be registered at "
39
39
  "open-xpd-uuid.cqd.io/register or a coordinating registry.",
40
40
  example="1u7zsed8",
@@ -61,7 +61,7 @@ class BaseDeclaration(RootDocument, abc.ABC):
61
61
  "utilized, the declared unit shall refer to the amount of "
62
62
  "product associated with the A1-A3 life cycle stage."
63
63
  )
64
- kg_per_declared_unit: Amount | None = pyd.Field(
64
+ kg_per_declared_unit: AmountMass | None = pyd.Field(
65
65
  default=None,
66
66
  description="Mass of the product, in kilograms, per declared unit",
67
67
  example=Amount(qty=12.5, unit="kg").to_serializable(exclude_unset=True),
@@ -126,6 +126,39 @@ class BaseDeclaration(RootDocument, abc.ABC):
126
126
  """,
127
127
  )
128
128
 
129
+ product_image_small: pyd.AnyUrl | None = pyd.Field(
130
+ description="Pointer to image illustrating the product, which is no more than 200x200 pixels", default=None
131
+ )
132
+ product_image: pyd.AnyUrl | pyd.FileUrl | None = pyd.Field(
133
+ description="pointer to image illustrating the product no more than 10MB", default=None
134
+ )
135
+ declaration_url: str | None = pyd.Field(
136
+ description="Link to data object on original registrar's site",
137
+ example="https://epd-online.com/EmbeddedEpdList/Download/6029",
138
+ )
139
+ kg_C_per_declared_unit: AmountMass | None = pyd.Field(
140
+ default=None,
141
+ description="Mass of elemental carbon, per declared unit, contained in the product itself at the manufacturing "
142
+ "facility gate. Used (among other things) to check a carbon balance or calculate incineration "
143
+ "emissions. The source of carbon (e.g. biogenic) is not relevant in this field.",
144
+ example=Amount(qty=8.76, unit="kg"),
145
+ )
146
+ kg_C_biogenic_per_declared_unit: AmountMass | None = pyd.Field(
147
+ default=None,
148
+ description="Mass of elemental carbon from biogenic sources, per declared unit, contained in the product "
149
+ "itself at the manufacturing facility gate. It may be presumed that any biogenic carbon content "
150
+ "has been accounted for as -44/12 kgCO2e per kg C in stages A1-A3, per EN15804 and ISO 21930.",
151
+ example=Amount(qty=8.76, unit="kg"),
152
+ )
153
+ product_service_life_years: float | None = pyd.Field(
154
+ gt=0.0009,
155
+ lt=101,
156
+ description="Reference service life of the product, in years. Serves as a maximum for replacement interval, "
157
+ "which may also be constrained by usage or the service life of what the product goes into "
158
+ "(e.g. a building).",
159
+ example=50.0,
160
+ )
161
+
129
162
 
130
163
  class AverageDatasetMixin(pyd.BaseModel, title="Average Dataset"):
131
164
  """Fields common for average dataset (Industry-wide EPDs, Generic Estimates)."""
@@ -184,7 +217,7 @@ class WithEpdDeveloperMixin(pyd.BaseModel):
184
217
  class RefBase(BaseOpenEpdSchema, title="Ref Object"):
185
218
  """Base class for reference-style objects."""
186
219
 
187
- id: str | None = pyd.Field(
220
+ id: OpenXpdUUID | None = pyd.Field(
188
221
  description="The unique ID for this object. To ensure global uniqueness, should be registered at "
189
222
  "open-xpd-uuid.cqd.io/register or a coordinating registry.",
190
223
  example="1u7zsed8",
openepd/model/epd.py CHANGED
@@ -17,7 +17,7 @@ from typing import Annotated
17
17
 
18
18
  from openepd.compat.pydantic import pyd
19
19
  from openepd.model.base import BaseDocumentFactory, OpenEpdDoctypes
20
- from openepd.model.common import Amount, Ingredient, WithAltIdsMixin, WithAttachmentsMixin
20
+ from openepd.model.common import Ingredient, WithAltIdsMixin, WithAttachmentsMixin
21
21
  from openepd.model.declaration import (
22
22
  DEVELOPER_DESCRIPTION,
23
23
  PROGRAM_OPERATOR_DESCRIPTION,
@@ -53,6 +53,11 @@ class EpdPreviewV0(
53
53
 
54
54
  """
55
55
 
56
+ doctype: str = pyd.Field(
57
+ description='Describes the type and schema of the document. Must always always read "openEPD".',
58
+ default="openEPD",
59
+ )
60
+
56
61
  product_name: str | None = pyd.Field(
57
62
  max_length=200, description="The name of the product described by this EPD", example="Mix 12345AC", default=None
58
63
  )
@@ -63,44 +68,13 @@ class EpdPreviewV0(
63
68
  max_length=2000,
64
69
  description="1-paragraph description of product. Supports plain text or github flavored markdown.",
65
70
  )
66
- product_image_small: pyd.AnyUrl | None = pyd.Field(
67
- description="Pointer to image illustrating the product, which is no more than 200x200 pixels", default=None
68
- )
69
- product_image: pyd.AnyUrl | pyd.FileUrl | None = pyd.Field(
70
- description="pointer to image illustrating the product no more than 10MB", default=None
71
- )
72
- declaration_url: str | None = pyd.Field(
73
- description="Link to data object on original registrar's site",
74
- example="https://epd-online.com/EmbeddedEpdList/Download/6029",
75
- )
76
71
  manufacturer: Org | None = pyd.Field(description=MANUFACTURER_DESCRIPTION)
77
72
  plants: list[Plant] = pyd.Field(
78
73
  max_items=32,
79
74
  description="List of object(s) for one or more plant(s) that this declaration applies to.",
80
75
  default_factory=list,
81
76
  )
82
- kg_C_per_declared_unit: Amount | None = pyd.Field(
83
- default=None,
84
- description="Mass of elemental carbon, per declared unit, contained in the product itself at the manufacturing "
85
- "facility gate. Used (among other things) to check a carbon balance or calculate incineration "
86
- "emissions. The source of carbon (e.g. biogenic) is not relevant in this field.",
87
- example=Amount(qty=8.76, unit="kg"),
88
- )
89
- kg_C_biogenic_per_declared_unit: Amount | None = pyd.Field(
90
- default=None,
91
- description="Mass of elemental carbon from biogenic sources, per declared unit, contained in the product "
92
- "itself at the manufacturing facility gate. It may be presumed that any biogenic carbon content "
93
- "has been accounted for as -44/12 kgCO2e per kg C in stages A1-A3, per EN15804 and ISO 21930.",
94
- example=Amount(qty=8.76, unit="kg"),
95
- )
96
- product_service_life_years: float | None = pyd.Field(
97
- gt=0.0009,
98
- lt=101,
99
- description="Reference service life of the product, in years. Serves as a maximum for replacement interval, "
100
- "which may also be constrained by usage or the service life of what the product goes into "
101
- "(e.g. a building).",
102
- example=50.0,
103
- )
77
+
104
78
  annual_production: float | None = pyd.Field(
105
79
  gt=0,
106
80
  default=None,
@@ -149,6 +123,17 @@ class EpdPreviewV0(
149
123
  default_factory=list,
150
124
  )
151
125
 
126
+ @pyd.validator("doctype")
127
+ def validate_doctype(cls, v: str | None) -> str:
128
+ """
129
+ Handle possible mixed case options for doctype.
130
+
131
+ Required for backward compatibility as some code might have already used 'doctype: OpenEPD' instead of 'openEPD'
132
+ """
133
+ if not v or v.lower() == "openepd":
134
+ return "openEPD"
135
+ raise ValueError("Invalid doctype")
136
+
152
137
 
153
138
  EpdPreview = EpdPreviewV0
154
139
 
@@ -14,6 +14,7 @@
14
14
  # limitations under the License.
15
15
  #
16
16
  from enum import StrEnum
17
+ from typing import Literal
17
18
 
18
19
  from openepd.compat.pydantic import pyd
19
20
  from openepd.model.base import BaseDocumentFactory, OpenEpdDoctypes
@@ -60,7 +61,7 @@ class GenericEstimatePreviewV0(
60
61
 
61
62
  _FORMAT_VERSION = OpenEpdVersions.Version0.as_str()
62
63
 
63
- doctype: str = pyd.Field(
64
+ doctype: Literal["openGenericEstimate"] = pyd.Field(
64
65
  description='Describes the type and schema of the document. Must always be "openGenericEstimate"',
65
66
  default="openGenericEstimate",
66
67
  )
@@ -13,6 +13,8 @@
13
13
  # See the License for the specific language governing permissions and
14
14
  # limitations under the License.
15
15
  #
16
+ from typing import Literal
17
+
16
18
  from openepd.compat.pydantic import pyd
17
19
  from openepd.model.base import BaseDocumentFactory, OpenEpdDoctypes
18
20
  from openepd.model.common import WithAltIdsMixin, WithAttachmentsMixin
@@ -51,7 +53,7 @@ class IndustryEpdPreviewV0(
51
53
 
52
54
  _FORMAT_VERSION = OpenEpdVersions.Version0.as_str()
53
55
 
54
- doctype: str = pyd.Field(
56
+ doctype: Literal["openIndustryEpd"] = pyd.Field(
55
57
  description='Describes the type and schema of the document. Must always be "openIndustryEpd"',
56
58
  default="openIndustryEpd",
57
59
  )
openepd/model/lcia.py CHANGED
@@ -204,7 +204,7 @@ class ScopesetByNameBase(BaseOpenEpdSchema):
204
204
 
205
205
  :return: set of names, for example ['gwp', 'odp]
206
206
  """
207
- return [self.__fields__[f].alias or f for f in self.__fields_set__]
207
+ return [self.__fields__[f].alias or f for f in self.__fields_set__ if f not in ("ext",)]
208
208
 
209
209
  def get_scopeset_by_name(self, name: str) -> ScopeSet | None:
210
210
  """
openepd/model/pcr.py CHANGED
@@ -18,7 +18,7 @@ from enum import StrEnum
18
18
  from typing import Annotated, Optional
19
19
 
20
20
  from openepd.compat.pydantic import pyd
21
- from openepd.model.base import BaseOpenEpdSchema
21
+ from openepd.model.base import BaseOpenEpdSchema, OpenXpdUUID
22
22
  from openepd.model.common import Amount, WithAltIdsMixin, WithAttachmentsMixin
23
23
  from openepd.model.org import Org
24
24
 
@@ -36,7 +36,7 @@ class PcrStatus(StrEnum):
36
36
  class PcrRef(BaseOpenEpdSchema):
37
37
  """Reference to a PCR."""
38
38
 
39
- id: str | None = pyd.Field(
39
+ id: OpenXpdUUID | None = pyd.Field(
40
40
  description="The unique ID for this PCR. To ensure global uniqueness, should be registered "
41
41
  "at open-xpd-uuid.cqd.io/register or a coordinating registry.",
42
42
  example="ec3xpgq2",
@@ -56,7 +56,7 @@ class PcrRef(BaseOpenEpdSchema):
56
56
  class Pcr(WithAttachmentsMixin, WithAltIdsMixin, BaseOpenEpdSchema):
57
57
  """Represent a PCR (Product Category Rules)."""
58
58
 
59
- id: str | None = pyd.Field(
59
+ id: OpenXpdUUID | None = pyd.Field(
60
60
  description="The unique ID for this PCR. To ensure global uniqueness, should be registered "
61
61
  "at open-xpd-uuid.cqd.io/register or a coordinating registry.",
62
62
  example="ec3xpgq2",
@@ -14,9 +14,10 @@
14
14
  # limitations under the License.
15
15
  #
16
16
  from abc import ABC, abstractmethod
17
- from typing import TYPE_CHECKING, Callable, ClassVar
17
+ from typing import TYPE_CHECKING, Any, Callable, ClassVar
18
18
 
19
- from openepd.model.common import OpenEPDUnit
19
+ from openepd.compat.pydantic import pyd
20
+ from openepd.model.common import Amount, OpenEPDUnit
20
21
 
21
22
  if TYPE_CHECKING:
22
23
  from openepd.model.specs.base import BaseOpenEpdHierarchicalSpec
@@ -127,6 +128,32 @@ def validate_quantity_for_new_validator(max_value: str) -> Callable:
127
128
  # todo these types should be replaced by Annotated[str, AfterValidator...] as we move completely to pydantic 2
128
129
 
129
130
 
131
+ class AmountWithDimensionality(Amount, ABC):
132
+ """Class for dimensionality-validated amounts."""
133
+
134
+ dimensionality_unit: ClassVar[str | None] = None
135
+
136
+ # Unit for dimensionality to validate against, for example "kg"
137
+
138
+ @pyd.root_validator
139
+ def check_dimensionality_matches(cls, values: dict[str, Any]) -> dict[str, Any]:
140
+ """Check that this amount conforms to the same dimensionality as dimensionality_unit."""
141
+ if not cls.dimensionality_unit:
142
+ return values
143
+
144
+ from openepd.model.specs.base import BaseOpenEpdHierarchicalSpec
145
+
146
+ str_repr = f"{values['qty']} {values['unit']}"
147
+ validate_unit_factory(cls.dimensionality_unit)(BaseOpenEpdHierarchicalSpec, str_repr)
148
+ return values
149
+
150
+
151
+ class AmountMass(AmountWithDimensionality):
152
+ """Amount of mass, measured in kg, t, etc."""
153
+
154
+ dimensionality_unit = OpenEPDUnit.kg
155
+
156
+
130
157
  class QuantityStr(str):
131
158
  """
132
159
  Quantity string type.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: openepd
3
- Version: 4.11.3
3
+ Version: 4.13.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
@@ -1,10 +1,10 @@
1
1
  openepd/__init__.py,sha256=Shkfh0Kun0YRhmRDw7LkUj2eQL3X-HnP55u2THOEALw,794
2
- openepd/__version__.py,sha256=3ww6aECKcVyN52aVZ0we7ffz83m6WDgt2jI21Jsz_lw,639
2
+ openepd/__version__.py,sha256=ct2ojZmiO087rEAYSPIKIFv4c6Tf57TQ7E0AKUt3Iyw,639
3
3
  openepd/api/__init__.py,sha256=UGmZGEyMnASrYwEBPHuXmVzHiuCUskUsJEPoHTIo-lg,620
4
4
  openepd/api/average_dataset/__init__.py,sha256=UGmZGEyMnASrYwEBPHuXmVzHiuCUskUsJEPoHTIo-lg,620
5
5
  openepd/api/average_dataset/generic_estimate_sync_api.py,sha256=DM7i8gugUIEAutEsakEUxFjwgtKmcytkjvNAvfhVYUs,7690
6
6
  openepd/api/average_dataset/industry_epd_sync_api.py,sha256=xoicuZdclf4BTBa2ndG1K2Aog-iZd7_jOqdDPZSCLq4,6286
7
- openepd/api/base_sync_client.py,sha256=jviqtQgsOVdRq5x7_Yh_Tg8zIdWtVTIUqNCgebf6YDg,20925
7
+ openepd/api/base_sync_client.py,sha256=IX-q6JdWLMqYHC-hKLXUFRPs-DZZ9Co0NRZ5pSAipAs,20992
8
8
  openepd/api/category/__init__.py,sha256=UGmZGEyMnASrYwEBPHuXmVzHiuCUskUsJEPoHTIo-lg,620
9
9
  openepd/api/category/dto.py,sha256=tDojagSwT7CTtcYq31Qe_c0P3xKKUWXKdzT5iN6odtk,850
10
10
  openepd/api/category/sync_api.py,sha256=VHHOVbblZGyc4AtbsgQza00trSLuaCO6KfQw6r8vzgg,1371
@@ -18,7 +18,7 @@ openepd/api/dto/params.py,sha256=UGmZGEyMnASrYwEBPHuXmVzHiuCUskUsJEPoHTIo-lg,620
18
18
  openepd/api/epd/__init__.py,sha256=UGmZGEyMnASrYwEBPHuXmVzHiuCUskUsJEPoHTIo-lg,620
19
19
  openepd/api/epd/dto.py,sha256=NZ76vfUkCEkwDibQd2QCEQP5DZms_NFc9tjoP-mcw3o,4874
20
20
  openepd/api/epd/sync_api.py,sha256=5wuPiM_zVciVtTvsGaaRdf7T6q2CUp7QMZCW0DAjQng,7072
21
- openepd/api/errors.py,sha256=Pcg2JqjLoUXUFJTVdIFbd8rgjGoAT5HpnivTpFRACVU,2159
21
+ openepd/api/errors.py,sha256=Iipd0QW2tbhUu_UjUbmiFWgmrPVgOFHc3uxZN-SX-g4,2868
22
22
  openepd/api/pcr/__init__.py,sha256=UGmZGEyMnASrYwEBPHuXmVzHiuCUskUsJEPoHTIo-lg,620
23
23
  openepd/api/pcr/sync_api.py,sha256=Riu77h8uLJngKpITOiXYmO7mzjAHpYskUJ6ynyfNG78,1557
24
24
  openepd/api/sync_client.py,sha256=IurnhZrkBQoQIbbfon6TPuhjGpAV_CSTJNeXjIiN0QI,3105
@@ -33,18 +33,18 @@ openepd/compat/__init__.py,sha256=UGmZGEyMnASrYwEBPHuXmVzHiuCUskUsJEPoHTIo-lg,62
33
33
  openepd/compat/compat_functional_validators.py,sha256=yz6DfWeg7knBHEN_enpCGGTLRknEsecXfpzD1FDlywY,834
34
34
  openepd/compat/pydantic.py,sha256=DOjSixsylLqMtFAIARu50sGcT4VPXN_c473q_2JwZQ0,1146
35
35
  openepd/model/__init__.py,sha256=UGmZGEyMnASrYwEBPHuXmVzHiuCUskUsJEPoHTIo-lg,620
36
- openepd/model/base.py,sha256=o6miTbb4d2BRQ6epN5Jn8mIMkAeoAucd_3op7aEMELc,9044
36
+ openepd/model/base.py,sha256=OEYNFUTL4BivBNAt_LGowTlDuUjvKMHgf5U5ZBncZwQ,9805
37
37
  openepd/model/category.py,sha256=IQXNGQFQmFZ_H9PRONloX_UOSf1sTMDq1rM1yz8JR0Y,1639
38
38
  openepd/model/common.py,sha256=hXnz2QiQDx3Z7F4BMHlHQ2iddq3OonKrveltl-nOiEI,5468
39
- openepd/model/declaration.py,sha256=RFkjoEKD9QRzOMzd-svXVezzHZsrbPziZhME8SjmV44,8938
40
- openepd/model/epd.py,sha256=FfU36K5B1iVYksPPvKsg8rZgaO1EKxQar9QcAjAGyiE,8731
39
+ openepd/model/declaration.py,sha256=9KnmYr3ZLpkbBFVkesaEdDtGpG-SrVFuIOO2ekRyTgk,10796
40
+ openepd/model/epd.py,sha256=GU7OW6hp9GeLB9PUGqacDYMAJVWmAAHzGp3dZs2aGqo,7479
41
41
  openepd/model/factory.py,sha256=XP7eeQNW5tqwX_4hfuEb3lK6BFQDb4KB0fSN0r8-lCU,2656
42
- openepd/model/generic_estimate.py,sha256=kBRtW6yAXgV3YkflXOI1jy8Tav5k5y8M3MPnuKPCcA4,3938
42
+ openepd/model/generic_estimate.py,sha256=bbU0cR4izSqjZcfxUHNbdO4pllqqd8OaUFikrEgCFoA,3992
43
43
  openepd/model/geography.py,sha256=eCt15zXKDtiteNwXQ675cFwBXQqSpiGpIqwDo4nkOek,42091
44
- openepd/model/industry_epd.py,sha256=39D8WlLrEUwHLwhUcOQgjcLFQq5xz3fpwkpIfTaXRek,3234
45
- openepd/model/lcia.py,sha256=bnDVsEo429Go_EneVOkXYIxDu2soxrgxyfzU4OhDR3Q,18827
44
+ openepd/model/industry_epd.py,sha256=rgXhCUDAgzZ9eGio7ExqE3ymP3zTXnrrwcIDvg5YP1A,3285
45
+ openepd/model/lcia.py,sha256=pVh4LbZgzDuLuM3epdCNLlAp5B0BnUrwggaaDiuPccM,18848
46
46
  openepd/model/org.py,sha256=FHcYh2WOOQrCMyzm0Ow-iP79jMTBPcneidjH6NXIklA,3760
47
- openepd/model/pcr.py,sha256=SwqLWMj9k_jqIzxz5mh6ttqvtLCspKSpywF5YTBOMsA,5397
47
+ openepd/model/pcr.py,sha256=1abCO00f_zaZrGiBN4fzauQ-5zPhNc31RXFUJzLVIJ0,5426
48
48
  openepd/model/specs/README.md,sha256=W5LSMpZuW5x36cKS4HRfeFsClsRf8J9yHMMICghdc0s,862
49
49
  openepd/model/specs/__init__.py,sha256=CfuCZKFE6xysags8XY9VlW-o_zHEyCu9fGekoj-RJOg,4959
50
50
  openepd/model/specs/asphalt.py,sha256=kyv-WvqujYD5xqi-lS2nSculXLQAuuBbJU_YRCb-5ug,3332
@@ -93,11 +93,11 @@ openepd/model/standard.py,sha256=QhGpWN3U27fDcS0Yy1Dk8ElJfD0etet6i_PzoTD6B48,131
93
93
  openepd/model/validation/__init__.py,sha256=UGmZGEyMnASrYwEBPHuXmVzHiuCUskUsJEPoHTIo-lg,620
94
94
  openepd/model/validation/common.py,sha256=FLYqK8gYFagx08LCkS0jy3qo4-Zq9VAv5i8ZwF2svkc,2435
95
95
  openepd/model/validation/numbers.py,sha256=tgirqrDGgrSo6APGlW1ozNuVV8mJz_4HCAXS2OUENq0,888
96
- openepd/model/validation/quantity.py,sha256=kzug0MZ3Ao0zeVzN-aleyxUg5hA_7D5tNOOerverfRQ,7415
96
+ openepd/model/validation/quantity.py,sha256=h4AXXMijFtJRFUb5bi_iRzK2v7v1CtggLi7CyK5bA4s,8349
97
97
  openepd/model/versioning.py,sha256=R_zm6rCrgF3vlJQYbpyWhirdS_Oek16cv_mvZmpuE8I,4473
98
98
  openepd/patch_pydantic.py,sha256=xrkzblatmU9HBzukWkp1cPq9ZSuohoz1p0pQqVKSlKs,4122
99
99
  openepd/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
100
- openepd-4.11.3.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
101
- openepd-4.11.3.dist-info/METADATA,sha256=yParHG3ytGnOqmdUk5pDgyVhPBiwwHF-NOxqjl3aZaM,8705
102
- openepd-4.11.3.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
103
- openepd-4.11.3.dist-info/RECORD,,
100
+ openepd-4.13.0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
101
+ openepd-4.13.0.dist-info/METADATA,sha256=eO3P8R_93hVQpLWCZOuDVScaXDdq8wsYkahzx5N7ifo,8705
102
+ openepd-4.13.0.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
103
+ openepd-4.13.0.dist-info/RECORD,,