openepd 4.12.0__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.12.0"
16
+ VERSION = "4.13.0"
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
@@ -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,7 +17,7 @@ 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
@@ -34,8 +34,7 @@ THIRD_PARTY_VERIFIER_DESCRIPTION = "JSON object for Org that performed a critica
34
34
  class BaseDeclaration(RootDocument, abc.ABC):
35
35
  """Base class for declaration-related documents (EPDs, Industry-wide EPDs, Generic Estimates)."""
36
36
 
37
- # TODO: Add validator for open-xpd-uuid on this field
38
- id: str | None = pyd.Field(
37
+ id: OpenXpdUUID | None = pyd.Field(
39
38
  description="The unique ID for this document. To ensure global uniqueness, should be registered at "
40
39
  "open-xpd-uuid.cqd.io/register or a coordinating registry.",
41
40
  example="1u7zsed8",
@@ -127,6 +126,39 @@ class BaseDeclaration(RootDocument, abc.ABC):
127
126
  """,
128
127
  )
129
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
+
130
162
 
131
163
  class AverageDatasetMixin(pyd.BaseModel, title="Average Dataset"):
132
164
  """Fields common for average dataset (Industry-wide EPDs, Generic Estimates)."""
@@ -185,7 +217,7 @@ class WithEpdDeveloperMixin(pyd.BaseModel):
185
217
  class RefBase(BaseOpenEpdSchema, title="Ref Object"):
186
218
  """Base class for reference-style objects."""
187
219
 
188
- id: str | None = pyd.Field(
220
+ id: OpenXpdUUID | None = pyd.Field(
189
221
  description="The unique ID for this object. To ensure global uniqueness, should be registered at "
190
222
  "open-xpd-uuid.cqd.io/register or a coordinating registry.",
191
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,
@@ -30,7 +30,6 @@ from openepd.model.declaration import (
30
30
  from openepd.model.lcia import WithLciaMixin
31
31
  from openepd.model.org import Org, Plant
32
32
  from openepd.model.specs import Specs
33
- from openepd.model.validation.quantity import AmountMass
34
33
  from openepd.model.versioning import OpenEpdVersions, Version
35
34
 
36
35
  MANUFACTURER_DESCRIPTION = (
@@ -69,44 +68,13 @@ class EpdPreviewV0(
69
68
  max_length=2000,
70
69
  description="1-paragraph description of product. Supports plain text or github flavored markdown.",
71
70
  )
72
- product_image_small: pyd.AnyUrl | None = pyd.Field(
73
- description="Pointer to image illustrating the product, which is no more than 200x200 pixels", default=None
74
- )
75
- product_image: pyd.AnyUrl | pyd.FileUrl | None = pyd.Field(
76
- description="pointer to image illustrating the product no more than 10MB", default=None
77
- )
78
- declaration_url: str | None = pyd.Field(
79
- description="Link to data object on original registrar's site",
80
- example="https://epd-online.com/EmbeddedEpdList/Download/6029",
81
- )
82
71
  manufacturer: Org | None = pyd.Field(description=MANUFACTURER_DESCRIPTION)
83
72
  plants: list[Plant] = pyd.Field(
84
73
  max_items=32,
85
74
  description="List of object(s) for one or more plant(s) that this declaration applies to.",
86
75
  default_factory=list,
87
76
  )
88
- kg_C_per_declared_unit: AmountMass | None = pyd.Field(
89
- default=None,
90
- description="Mass of elemental carbon, per declared unit, contained in the product itself at the manufacturing "
91
- "facility gate. Used (among other things) to check a carbon balance or calculate incineration "
92
- "emissions. The source of carbon (e.g. biogenic) is not relevant in this field.",
93
- example=Amount(qty=8.76, unit="kg"),
94
- )
95
- kg_C_biogenic_per_declared_unit: AmountMass | None = pyd.Field(
96
- default=None,
97
- description="Mass of elemental carbon from biogenic sources, per declared unit, contained in the product "
98
- "itself at the manufacturing facility gate. It may be presumed that any biogenic carbon content "
99
- "has been accounted for as -44/12 kgCO2e per kg C in stages A1-A3, per EN15804 and ISO 21930.",
100
- example=Amount(qty=8.76, unit="kg"),
101
- )
102
- product_service_life_years: float | None = pyd.Field(
103
- gt=0.0009,
104
- lt=101,
105
- description="Reference service life of the product, in years. Serves as a maximum for replacement interval, "
106
- "which may also be constrained by usage or the service life of what the product goes into "
107
- "(e.g. a building).",
108
- example=50.0,
109
- )
77
+
110
78
  annual_production: float | None = pyd.Field(
111
79
  gt=0,
112
80
  default=None,
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",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: openepd
3
- Version: 4.12.0
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,5 +1,5 @@
1
1
  openepd/__init__.py,sha256=Shkfh0Kun0YRhmRDw7LkUj2eQL3X-HnP55u2THOEALw,794
2
- openepd/__version__.py,sha256=21tmVPyiMCbot4ApEc5ODeqH2q7qPuAfknYig3smndc,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
@@ -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=h3lUv5XuROAuCyJ68FZ0hkoyiqDRxXpjfxp2c0CGvOU,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=W4bTm423_vMf8i0nsrEy9iw0fwNGOD-9yvpuRlXt-4o,8999
40
- openepd/model/epd.py,sha256=fZJxgbpPUc8IINfrVpHG9fUWSf_XWJfMPK9lMhN0qME,9368
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
42
  openepd/model/generic_estimate.py,sha256=bbU0cR4izSqjZcfxUHNbdO4pllqqd8OaUFikrEgCFoA,3992
43
43
  openepd/model/geography.py,sha256=eCt15zXKDtiteNwXQ675cFwBXQqSpiGpIqwDo4nkOek,42091
44
44
  openepd/model/industry_epd.py,sha256=rgXhCUDAgzZ9eGio7ExqE3ymP3zTXnrrwcIDvg5YP1A,3285
45
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
@@ -97,7 +97,7 @@ openepd/model/validation/quantity.py,sha256=h4AXXMijFtJRFUb5bi_iRzK2v7v1CtggLi7C
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.12.0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
101
- openepd-4.12.0.dist-info/METADATA,sha256=1mNyCyep7uh_aBL0WoQIM303D5gIkA30Upv_E3pHSx4,8705
102
- openepd-4.12.0.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
103
- openepd-4.12.0.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,,