openepd 7.1.0__py3-none-any.whl → 7.2.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.
Files changed (77) hide show
  1. openepd/__version__.py +1 -1
  2. openepd/api/average_dataset/generic_estimate_sync_api.py +2 -1
  3. openepd/api/average_dataset/industry_epd_sync_api.py +2 -1
  4. openepd/api/base_sync_client.py +10 -8
  5. openepd/api/common.py +17 -11
  6. openepd/api/dto/common.py +1 -1
  7. openepd/api/epd/sync_api.py +2 -1
  8. openepd/api/org/__init__.py +15 -0
  9. openepd/api/org/sync_api.py +79 -0
  10. openepd/api/pcr/sync_api.py +35 -0
  11. openepd/api/plant/__init__.py +15 -0
  12. openepd/api/plant/sync_api.py +79 -0
  13. openepd/api/standard/__init__.py +15 -0
  14. openepd/api/standard/sync_api.py +79 -0
  15. openepd/api/sync_client.py +27 -0
  16. openepd/bundle/base.py +5 -4
  17. openepd/bundle/reader.py +13 -7
  18. openepd/bundle/writer.py +11 -6
  19. openepd/m49/__init__.py +2 -0
  20. openepd/m49/const.py +1 -1
  21. openepd/m49/utils.py +16 -10
  22. openepd/model/base.py +20 -15
  23. openepd/model/common.py +10 -5
  24. openepd/model/declaration.py +2 -2
  25. openepd/model/epd.py +2 -1
  26. openepd/model/factory.py +5 -3
  27. openepd/model/generic_estimate.py +4 -0
  28. openepd/model/lcia.py +10 -10
  29. openepd/model/org.py +14 -7
  30. openepd/model/pcr.py +2 -2
  31. openepd/model/specs/__init__.py +37 -0
  32. openepd/model/specs/asphalt.py +3 -3
  33. openepd/model/specs/base.py +2 -1
  34. openepd/model/specs/enums.py +9 -1
  35. openepd/model/specs/range/__init__.py +5 -3
  36. openepd/model/specs/range/accessories.py +1 -1
  37. openepd/model/specs/range/aluminium.py +1 -1
  38. openepd/model/specs/range/cladding.py +10 -10
  39. openepd/model/specs/range/cmu.py +0 -2
  40. openepd/model/specs/range/concrete.py +25 -2
  41. openepd/model/specs/range/conveying_equipment.py +2 -2
  42. openepd/model/specs/range/electrical.py +18 -18
  43. openepd/model/specs/range/electrical_transmission_and_distribution_equipment.py +1 -1
  44. openepd/model/specs/range/exterior_improvements.py +47 -0
  45. openepd/model/specs/range/finishes.py +19 -40
  46. openepd/model/specs/range/fire_and_smoke_protection.py +3 -3
  47. openepd/model/specs/range/furnishings.py +7 -7
  48. openepd/model/specs/range/manufacturing_inputs.py +17 -5
  49. openepd/model/specs/range/masonry.py +1 -1
  50. openepd/model/specs/range/mechanical.py +6 -6
  51. openepd/model/specs/range/mixins/__init__.py +15 -0
  52. openepd/model/specs/range/mixins/access_flooring_mixin.py +43 -0
  53. openepd/model/specs/range/network_infrastructure.py +3 -3
  54. openepd/model/specs/range/openings.py +17 -17
  55. openepd/model/specs/range/other_materials.py +4 -4
  56. openepd/model/specs/range/plumbing.py +5 -5
  57. openepd/model/specs/range/precast_concrete.py +2 -2
  58. openepd/model/specs/range/steel.py +18 -9
  59. openepd/model/specs/range/thermal_moisture_protection.py +12 -12
  60. openepd/model/specs/range/wood.py +4 -6
  61. openepd/model/specs/singular/__init__.py +119 -2
  62. openepd/model/specs/singular/aluminium.py +2 -1
  63. openepd/model/specs/singular/concrete.py +25 -1
  64. openepd/model/specs/singular/deprecated/__init__.py +1 -1
  65. openepd/model/specs/singular/exterior_improvements.py +47 -0
  66. openepd/model/specs/singular/finishes.py +3 -59
  67. openepd/model/specs/singular/manufacturing_inputs.py +13 -1
  68. openepd/model/specs/singular/mixins/access_flooring_mixin.py +55 -0
  69. openepd/model/specs/singular/steel.py +10 -2
  70. openepd/model/validation/common.py +10 -6
  71. openepd/model/validation/enum.py +4 -2
  72. openepd/model/validation/quantity.py +13 -6
  73. openepd/model/versioning.py +8 -6
  74. {openepd-7.1.0.dist-info → openepd-7.2.0.dist-info}/METADATA +3 -4
  75. {openepd-7.1.0.dist-info → openepd-7.2.0.dist-info}/RECORD +77 -66
  76. {openepd-7.1.0.dist-info → openepd-7.2.0.dist-info}/WHEEL +1 -1
  77. {openepd-7.1.0.dist-info → openepd-7.2.0.dist-info}/LICENSE +0 -0
openepd/bundle/writer.py CHANGED
@@ -31,8 +31,9 @@ class DefaultBundleWriter(BaseBundleWriter):
31
31
  """Default bundle writer implementation. Writes the bundle to a ZIP file."""
32
32
 
33
33
  def __init__(self, bundle_file: str | PathLike | IO[bytes], comment: str | None = None):
34
- if isinstance(bundle_file, (PathLike, str)) and Path(bundle_file).exists():
35
- raise ValueError("Amending existing files is not supported yet.")
34
+ if isinstance(bundle_file, PathLike | str) and Path(bundle_file).exists():
35
+ msg = "Amending existing files is not supported yet."
36
+ raise ValueError(msg)
36
37
  self._bundle_archive = zipfile.ZipFile(bundle_file, mode="w")
37
38
  self.__manifest = BundleManifest(
38
39
  format="openEPD Bundle/1.0",
@@ -96,7 +97,8 @@ class DefaultBundleWriter(BaseBundleWriter):
96
97
  """Write an object asset to the bundle. Object means subclass of BaseOpenEpdSchem."""
97
98
  asset_type_str = obj.get_asset_type()
98
99
  if asset_type_str is None:
99
- raise ValueError(f"Object {obj} does not have a valid asset type and can't be written to a bundle.")
100
+ msg = f"Object {obj} does not have a valid asset type and can't be written to a bundle."
101
+ raise ValueError(msg)
100
102
  asset_type = AssetType(asset_type_str)
101
103
  rel_ref_str = self._asset_ref_to_str(rel_asset) if rel_asset is not None else None
102
104
  ref_str = self.__generate_entry_name(
@@ -139,14 +141,16 @@ class DefaultBundleWriter(BaseBundleWriter):
139
141
 
140
142
  def __register_entry(self, asset_info: AssetInfo):
141
143
  if asset_info.ref in self.__added_entries:
142
- raise ValueError(f"Asset {asset_info.ref} already exists in the bundle.")
144
+ msg = f"Asset {asset_info.ref} already exists in the bundle."
145
+ raise ValueError(msg)
143
146
  self._toc_writer.writerow(asset_info.model_dump(exclude_unset=True, exclude_none=True))
144
147
  self.__added_entries.add(asset_info.ref)
145
148
  type_counter = self.__manifest.assets.count_by_type.get(asset_info.type, 0) + 1
146
149
  self.__manifest.assets.count_by_type[asset_info.type] = type_counter
147
150
  self.__manifest.assets.total_count += 1
148
151
  if asset_info.size is None:
149
- raise ValueError("Size of asset is not set.")
152
+ msg = "Size of asset is not set."
153
+ raise ValueError(msg)
150
154
  self.__manifest.assets.total_size += asset_info.size
151
155
 
152
156
  def __generate_entry_name(
@@ -168,7 +172,8 @@ class DefaultBundleWriter(BaseBundleWriter):
168
172
  info = self._bundle_archive.getinfo(f"{asset_type}/")
169
173
  if info.is_dir():
170
174
  return
171
- raise ValueError(f"Object with name {asset_type} already exists in the bundle.")
175
+ msg = f"Object with name {asset_type} already exists in the bundle."
176
+ raise ValueError(msg)
172
177
  except KeyError:
173
178
  self._bundle_archive.mkdir(str(asset_type))
174
179
 
openepd/m49/__init__.py CHANGED
@@ -14,4 +14,6 @@
14
14
  # limitations under the License.
15
15
  #
16
16
 
17
+ __all__ = ["geo_converter"]
18
+
17
19
  from . import utils as geo_converter # for backwards compatibility
openepd/m49/const.py CHANGED
@@ -1169,7 +1169,7 @@ def is_m49_code(to_check: str) -> bool:
1169
1169
  :param to_check: any string
1170
1170
  :return: `True` if passed string is M49 code, `False` otherwise
1171
1171
  """
1172
- warnings.warn("Use m49.utils.is_m49_code instead.", DeprecationWarning)
1172
+ warnings.warn("Use m49.utils.is_m49_code instead.", DeprecationWarning, stacklevel=2)
1173
1173
  from . import utils
1174
1174
 
1175
1175
  return utils.is_m49_code(to_check)
openepd/m49/utils.py CHANGED
@@ -14,15 +14,15 @@
14
14
  # limitations under the License.
15
15
  #
16
16
  __all__ = [
17
+ "is_m49_code",
17
18
  "iso_to_m49",
18
19
  "m49_to_iso",
19
- "region_and_country_names_to_m49",
20
+ "m49_to_openepd",
20
21
  "m49_to_region_and_country_names",
21
22
  "openepd_to_m49",
22
- "m49_to_openepd",
23
- "is_m49_code",
23
+ "region_and_country_names_to_m49",
24
24
  ]
25
- from typing import Collection
25
+ from collections.abc import Collection
26
26
 
27
27
  from openepd.m49.const import (
28
28
  COUNTRY_VERBOSE_NAME_TO_M49,
@@ -54,7 +54,8 @@ def iso_to_m49(regions: Collection[str]) -> set[str]:
54
54
  if m49_code:
55
55
  result.add(m49_code)
56
56
  else:
57
- raise ValueError(f"Country code '{code}' not found in M49 region codes.")
57
+ msg = f"Country code '{code}' not found in M49 region codes."
58
+ raise ValueError(msg)
58
59
 
59
60
  return result
60
61
 
@@ -77,7 +78,8 @@ def m49_to_iso(regions: Collection[str]) -> set[str]:
77
78
  if iso_code:
78
79
  result.add(iso_code)
79
80
  else:
80
- raise ValueError(f"Region code '{code}' not found in ISO3166.")
81
+ msg = f"Region code '{code}' not found in ISO3166."
82
+ raise ValueError(msg)
81
83
 
82
84
  return result
83
85
 
@@ -99,7 +101,8 @@ def region_and_country_names_to_m49(regions: Collection[str]) -> set[str]:
99
101
  for name in regions:
100
102
  m49_code = REGION_VERBOSE_NAME_TO_M49.get(name.title()) or COUNTRY_VERBOSE_NAME_TO_M49.get(name.title())
101
103
  if not m49_code:
102
- raise ValueError(f"Region or country name '{name}' not found in M49 region codes.")
104
+ msg = f"Region or country name '{name}' not found in M49 region codes."
105
+ raise ValueError(msg)
103
106
  result.add(m49_code)
104
107
 
105
108
  return result
@@ -120,7 +123,8 @@ def m49_to_region_and_country_names(regions: Collection[str]) -> set[str]:
120
123
  result = set()
121
124
  for code in regions:
122
125
  if code not in M49_TO_REGION_VERBOSE_NAME and code not in M49_TO_COUNTRY_VERBOSE_NAME:
123
- raise ValueError(f"Region code '{code}' not found in M49 region codes.")
126
+ msg = f"Region code '{code}' not found in M49 region codes."
127
+ raise ValueError(msg)
124
128
 
125
129
  name = M49_TO_REGION_VERBOSE_NAME.get(code) or M49_TO_COUNTRY_VERBOSE_NAME.get(code, code)
126
130
  result.add(name)
@@ -153,7 +157,8 @@ def openepd_to_m49(regions: Collection[str]) -> set[str]:
153
157
  elif is_m49_code(region):
154
158
  result.add(region)
155
159
  else:
156
- raise ValueError(f"Region '{region}' not found in ISO3166 or OpenEPD special regions.")
160
+ msg = f"Region '{region}' not found in ISO3166 or OpenEPD special regions."
161
+ raise ValueError(msg)
157
162
  return result
158
163
 
159
164
 
@@ -185,7 +190,8 @@ def m49_to_openepd(regions: list[str]) -> set[str]:
185
190
  if iso_code:
186
191
  result.add(iso_code)
187
192
  else:
188
- raise ValueError(f"Region code '{code}' not found in ISO3166 or OpenEPD special regions.")
193
+ msg = f"Region code '{code}' not found in ISO3166 or OpenEPD special regions."
194
+ raise ValueError(msg)
189
195
 
190
196
  return result
191
197
 
openepd/model/base.py CHANGED
@@ -17,7 +17,7 @@ import abc
17
17
  from collections.abc import Callable
18
18
  from enum import StrEnum
19
19
  import json
20
- from typing import Any, ClassVar, Generic, Optional, Type, TypeAlias, TypeVar
20
+ from typing import Any, ClassVar, Generic, Optional, TypeAlias, TypeVar
21
21
 
22
22
  from cqd import open_xpd_uuid # type:ignore[import-untyped]
23
23
  import pydantic
@@ -109,8 +109,8 @@ class BaseOpenEpdSchema(pydantic.BaseModel):
109
109
  def get_typed_ext_field(
110
110
  self,
111
111
  key: str,
112
- target_type: Type[TAnySerializable],
113
- default: Optional[TAnySerializable] = None,
112
+ target_type: type[TAnySerializable],
113
+ default: TAnySerializable | None = None,
114
114
  ) -> TAnySerializable:
115
115
  """
116
116
  Get an extension field from the model and convert it to the target type.
@@ -124,13 +124,14 @@ class BaseOpenEpdSchema(pydantic.BaseModel):
124
124
  return target_type.model_validate(value) # type: ignore[return-value]
125
125
  elif isinstance(value, target_type):
126
126
  return value
127
- raise ValueError(f"Cannot convert {value} to {target_type}")
127
+ msg = f"Cannot convert {value} to {target_type}"
128
+ raise ValueError(msg)
128
129
 
129
- def get_ext(self, ext_type: Type["TOpenEpdExtension"]) -> Optional["TOpenEpdExtension"]:
130
+ def get_ext(self, ext_type: type["TOpenEpdExtension"]) -> Optional["TOpenEpdExtension"]:
130
131
  """Get an extension field from the model or None if it doesn't exist."""
131
132
  return self.get_typed_ext_field(ext_type.get_extension_name(), ext_type, None)
132
133
 
133
- def get_ext_or_empty(self, ext_type: Type["TOpenEpdExtension"]) -> "TOpenEpdExtension":
134
+ def get_ext_or_empty(self, ext_type: type["TOpenEpdExtension"]) -> "TOpenEpdExtension":
134
135
  """Get an extension field from the model or an empty instance if it doesn't exist."""
135
136
  return self.get_typed_ext_field(ext_type.get_extension_name(), ext_type, ext_type.model_construct(**{})) # type: ignore[return-value]
136
137
 
@@ -178,7 +179,7 @@ class OpenEpdExtension(BaseOpenEpdSchema, metaclass=abc.ABCMeta):
178
179
 
179
180
  TOpenEpdExtension = TypeVar("TOpenEpdExtension", bound=OpenEpdExtension)
180
181
  TOpenEpdObject = TypeVar("TOpenEpdObject", bound=BaseOpenEpdSchema)
181
- TOpenEpdObjectClass = TypeVar("TOpenEpdObjectClass", bound=Type[BaseOpenEpdSchema])
182
+ TOpenEpdObjectClass = TypeVar("TOpenEpdObjectClass", bound=type[BaseOpenEpdSchema])
182
183
 
183
184
 
184
185
  class RootDocument(abc.ABC, BaseOpenEpdSchema):
@@ -225,22 +226,24 @@ class BaseDocumentFactory(Generic[TRootDocument]):
225
226
  """Create a document from a dictionary."""
226
227
  doctype: str | None = data.get("doctype")
227
228
  if doctype is None:
228
- raise ValueError("Doctype not found in the data.")
229
+ msg = "Doctype not found in the data."
230
+ raise ValueError(msg)
229
231
  if doctype.lower() != cls.DOCTYPE_CONSTRAINT.lower():
230
- raise ValueError(
231
- f"Document type {doctype} not supported. This factory supports {cls.DOCTYPE_CONSTRAINT} only."
232
- )
232
+ msg = f"Document type {doctype} not supported. This factory supports {cls.DOCTYPE_CONSTRAINT} only."
233
+ raise ValueError(msg)
233
234
  version = Version.parse_version(data.get(OPENEPD_VERSION_FIELD, ""))
234
235
  for x, doc_cls in cls.VERSION_MAP.items():
235
236
  if x.major == version.major:
236
237
  if version.minor <= x.minor:
237
238
  return doc_cls.model_validate(data)
238
239
  else:
239
- raise ValueError(
240
+ msg = (
240
241
  f"Unsupported version: {version}. The highest supported version from branch {x.major}.x is {x}"
241
242
  )
243
+ raise ValueError(msg)
242
244
  supported_versions = ", ".join(f"{v.major}.x" for v in cls.VERSION_MAP.keys())
243
- raise ValueError(f"Version {version} is not supported. Supported versions are: {supported_versions}")
245
+ msg = f"Version {version} is not supported. Supported versions are: {supported_versions}"
246
+ raise ValueError(msg)
244
247
 
245
248
 
246
249
  class OpenXpdUUID(str):
@@ -253,13 +256,15 @@ class OpenXpdUUID(str):
253
256
  @classmethod
254
257
  def _validate_id(cls, value: Any, _: pydantic_core.core_schema.ValidatorFunctionWrapHandler) -> Any:
255
258
  if not isinstance(value, str | None):
256
- raise ValueError(f"Invalid value type: {type(value)}")
259
+ msg = f"Invalid value type: {type(value)}"
260
+ raise ValueError(msg)
257
261
 
258
262
  try:
259
263
  open_xpd_uuid.validate(open_xpd_uuid.sanitize(str(value)))
260
264
  return value
261
265
  except open_xpd_uuid.GuidValidationError as e:
262
- raise ValueError("Invalid format") from e
266
+ msg = "Invalid format"
267
+ raise ValueError(msg) from e
263
268
 
264
269
  @classmethod
265
270
  def __get_pydantic_core_schema__(
openepd/model/common.py CHANGED
@@ -39,7 +39,8 @@ class Amount(BaseOpenEpdSchema):
39
39
  """Ensure that qty or unit is provided."""
40
40
 
41
41
  if self.qty is None and self.unit is None:
42
- raise ValueError("Either qty or unit must be provided.")
42
+ msg = "Either qty or unit must be provided."
43
+ raise ValueError(msg)
43
44
  return self
44
45
 
45
46
  def to_quantity_str(self):
@@ -135,9 +136,11 @@ class Ingredient(BaseOpenEpdSchema):
135
136
  # for in the calculation of uncertainty
136
137
  if values.get("gwp_fraction"):
137
138
  if not values.get("evidence_type"):
138
- raise ValueError("evidence_type is required if gwp_fraction is provided")
139
+ msg = "evidence_type is required if gwp_fraction is provided"
140
+ raise ValueError(msg)
139
141
  if not (values.get("citation") or values.get("link")):
140
- raise ValueError("link or citation is required if gwp_fraction is provided")
142
+ msg = "link or citation is required if gwp_fraction is provided"
143
+ raise ValueError(msg)
141
144
 
142
145
  return values
143
146
 
@@ -207,7 +210,8 @@ class AttachmentDict(dict[str, pydantic.AnyUrl]):
207
210
  def _validate(cls, value: Any) -> "AttachmentDict":
208
211
  # Ensure the input is a dict.
209
212
  if not isinstance(value, dict):
210
- raise TypeError("AttachmentDict must be a dict")
213
+ msg = "AttachmentDict must be a dict"
214
+ raise TypeError(msg)
211
215
 
212
216
  return cls(value)
213
217
 
@@ -315,7 +319,8 @@ class RangeBase(BaseOpenEpdSchema):
315
319
  min_boundary = values.get("min")
316
320
  max_boundary = values.get("max")
317
321
  if min_boundary is not None and max_boundary is not None and min_boundary > max_boundary:
318
- raise ValueError("Max should be greater than min")
322
+ msg = "Max should be greater than min"
323
+ raise ValueError(msg)
319
324
  return values
320
325
 
321
326
 
@@ -44,12 +44,12 @@ class BaseDeclaration(RootDocument, abc.ABC):
44
44
  default=None,
45
45
  )
46
46
  date_of_issue: datetime.datetime | None = pydantic.Field(
47
- examples=[datetime.datetime(day=11, month=9, year=2019, tzinfo=datetime.timezone.utc)],
47
+ examples=[datetime.datetime(day=11, month=9, year=2019, tzinfo=datetime.UTC)],
48
48
  description="Date the document was issued. This should be the first day on which the document is valid.",
49
49
  default=None,
50
50
  )
51
51
  valid_until: datetime.datetime | None = pydantic.Field(
52
- examples=[datetime.datetime(day=11, month=9, year=2028, tzinfo=datetime.timezone.utc)],
52
+ examples=[datetime.datetime(day=11, month=9, year=2028, tzinfo=datetime.UTC)],
53
53
  description="Last date the document is valid on, including any extensions.",
54
54
  default=None,
55
55
  )
openepd/model/epd.py CHANGED
@@ -239,7 +239,8 @@ class EpdPreviewV0(
239
239
  """
240
240
  if not v or v.lower() == "openepd":
241
241
  return "openEPD"
242
- raise ValueError("Invalid doctype")
242
+ msg = "Invalid doctype"
243
+ raise ValueError(msg)
243
244
 
244
245
 
245
246
  EpdPreview = EpdPreviewV0
openepd/model/factory.py CHANGED
@@ -38,7 +38,8 @@ class DocumentFactory:
38
38
  :raise ValueError if doctype not supported or not found.
39
39
  """
40
40
  if doctype is None:
41
- raise ValueError("The document type is not specified.")
41
+ msg = "The document type is not specified."
42
+ raise ValueError(msg)
42
43
  factory = cls.DOCTYPE_TO_FACTORY.get(doctype)
43
44
  if factory is None:
44
45
  raise ValueError(
@@ -56,11 +57,12 @@ class DocumentFactory:
56
57
  :raise ValueError: if the document type is not specified or not supported.
57
58
  """
58
59
  doctype = data.get("doctype")
59
- if doctype is None or not isinstance(doctype, (str, OpenEpdDoctypes)):
60
- raise ValueError(
60
+ if doctype is None or not isinstance(doctype, str | OpenEpdDoctypes):
61
+ msg = (
61
62
  f"The document type is not specified or not supported. "
62
63
  f"Please specify it in `doctype` field. Supported are: {','.join(cls.DOCTYPE_TO_FACTORY)}"
63
64
  )
65
+ raise ValueError(msg)
64
66
 
65
67
  factory = cls.get_factory(OpenEpdDoctypes(doctype))
66
68
  return factory.from_dict(data)
@@ -87,6 +87,10 @@ class GenericEstimatePreviewV0(
87
87
  description="A link to the shared git repository containing the LCA model used for this estimate.",
88
88
  )
89
89
 
90
+ model_config = pydantic.ConfigDict(
91
+ protected_namespaces=(),
92
+ )
93
+
90
94
 
91
95
  GenericEstimatePreview = GenericEstimatePreviewV0
92
96
 
openepd/model/lcia.py CHANGED
@@ -14,11 +14,10 @@
14
14
  # limitations under the License.
15
15
  #
16
16
  from enum import StrEnum
17
- from typing import Any, ClassVar
17
+ from typing import Any, ClassVar, Self
18
18
 
19
19
  import pydantic
20
20
  from pydantic import ConfigDict
21
- from typing_extensions import Self
22
21
 
23
22
  from openepd.model.base import BaseOpenEpdSchema
24
23
  from openepd.model.common import Measurement
@@ -215,7 +214,8 @@ class ScopeSet(BaseOpenEpdSchema):
215
214
  if not self.allowed_units:
216
215
  # For unknown units - only units should be the same across all measurements (textually)
217
216
  if len(all_units) > 1:
218
- raise ValueError("All scopes and measurements should be expressed in the same unit.")
217
+ msg = "All scopes and measurements should be expressed in the same unit."
218
+ raise ValueError(msg)
219
219
  else:
220
220
  # might be multiple variations of the same unit (kgCFC-11e, kgCFC11e)
221
221
  if len(all_units) > 1 and ExternalValidationConfig.QUANTITY_VALIDATOR:
@@ -237,9 +237,8 @@ class ScopeSet(BaseOpenEpdSchema):
237
237
  except ValueError:
238
238
  ...
239
239
  if not matched_unit:
240
- raise ValueError(
241
- f"'{', '.join(allowed_units)}' is only allowed unit for this scopeset. Provided '{unit}'"
242
- )
240
+ msg = f"'{', '.join(allowed_units)}' is only allowed unit for this scopeset. Provided '{unit}'"
241
+ raise ValueError(msg)
243
242
 
244
243
  return self
245
244
 
@@ -254,10 +253,10 @@ class ScopesetByNameBase(BaseOpenEpdSchema, extra="allow"):
254
253
  :return: set of names, for example ['gwp', 'odp']
255
254
  """
256
255
  result = []
257
- for f in self.__fields_set__:
256
+ for f in self.model_fields_set:
258
257
  if f in ("ext",):
259
258
  continue
260
- field = self.__fields__.get(f)
259
+ field = self.model_fields.get(f)
261
260
  # field can be explicitly specified, or can be an unknown impact covered by extra='allow'
262
261
  result.append(field.alias if field and field.alias else f)
263
262
 
@@ -271,7 +270,7 @@ class ScopesetByNameBase(BaseOpenEpdSchema, extra="allow"):
271
270
  :return: A scopeset if found, None otherwise
272
271
  """
273
272
  # check known impacts first
274
- for f_name, f in self.__fields__.items():
273
+ for f_name, f in self.model_fields.items():
275
274
  if f.alias == name:
276
275
  return getattr(self, f_name)
277
276
  if f_name == name:
@@ -294,7 +293,8 @@ class ScopesetByNameBase(BaseOpenEpdSchema, extra="allow"):
294
293
  case dict():
295
294
  values[f] = ScopeSet(**extra_scopeset)
296
295
  case _:
297
- raise ValueError(f"{f} must be a ScopeSet schema")
296
+ msg = f"{f} must be a ScopeSet schema"
297
+ raise ValueError(msg)
298
298
 
299
299
  return values
300
300
 
openepd/model/org.py CHANGED
@@ -61,10 +61,12 @@ class Org(WithAttachmentsMixin, WithAltIdsMixin, OrgRef):
61
61
  return value
62
62
 
63
63
  if not isinstance(value, list):
64
- raise TypeError(f"Expected type list or None, got {type(value)}")
64
+ msg = f"Expected type list or None, got {type(value)}"
65
+ raise TypeError(msg)
65
66
 
66
67
  if any((len(item) > 200) for item in value):
67
- raise ValueError("One or more alt_names are longer than 200 characters")
68
+ msg = "One or more alt_names are longer than 200 characters"
69
+ raise ValueError(msg)
68
70
 
69
71
  return value
70
72
 
@@ -79,11 +81,13 @@ class Org(WithAttachmentsMixin, WithAltIdsMixin, OrgRef):
79
81
  return value
80
82
 
81
83
  if not isinstance(value, list):
82
- raise TypeError(f"Expected type list or None, got {type(value)}")
84
+ msg = f"Expected type list or None, got {type(value)}"
85
+ raise TypeError(msg)
83
86
 
84
87
  for item in value:
85
88
  if len(item.name) > 200:
86
- raise ValueError("One or more subsidiaries name are longer than 200 characters")
89
+ msg = "One or more subsidiaries name are longer than 200 characters"
90
+ raise ValueError(msg)
87
91
 
88
92
  return value
89
93
 
@@ -158,13 +162,16 @@ class Plant(PlantRef, WithAttachmentsMixin, WithAltIdsMixin):
158
162
  try:
159
163
  pluscode, web_domain = v.split(".", maxsplit=1)
160
164
  except ValueError as e:
161
- raise ValueError("Incorrectly formed id: should be pluscode.owner_web_domain") from e
165
+ msg = "Incorrectly formed id: should be pluscode.owner_web_domain"
166
+ raise ValueError(msg) from e
162
167
 
163
168
  if not openlocationcode.isValid(pluscode):
164
- raise ValueError("Incorrect pluscode for plant")
169
+ msg = "Incorrect pluscode for plant"
170
+ raise ValueError(msg)
165
171
 
166
172
  if not web_domain:
167
- raise ValueError("Incorrect web_domain for plant")
173
+ msg = "Incorrect web_domain for plant"
174
+ raise ValueError(msg)
168
175
  return v
169
176
 
170
177
  model_config = ConfigDict(populate_by_name=True)
openepd/model/pcr.py CHANGED
@@ -93,12 +93,12 @@ class Pcr(WithAttachmentsMixin, WithAltIdsMixin, BaseOpenEpdSchema):
93
93
  default=None,
94
94
  )
95
95
  date_of_issue: datetime.datetime | None = pydantic.Field(
96
- examples=[datetime.datetime(day=11, month=9, year=2019, tzinfo=datetime.timezone.utc)],
96
+ examples=[datetime.datetime(day=11, month=9, year=2019, tzinfo=datetime.UTC)],
97
97
  default=None,
98
98
  description="First day on which the document is valid",
99
99
  )
100
100
  valid_until: datetime.datetime | None = pydantic.Field(
101
- examples=[datetime.datetime(day=11, month=9, year=2019, tzinfo=datetime.timezone.utc)],
101
+ examples=[datetime.datetime(day=11, month=9, year=2019, tzinfo=datetime.UTC)],
102
102
  default=None,
103
103
  description="Last day on which the document is valid",
104
104
  )
@@ -14,6 +14,43 @@
14
14
  # limitations under the License.
15
15
  #
16
16
 
17
+ __all__ = [
18
+ "CMUV1",
19
+ "AccessoriesV1",
20
+ "AggregatesV1",
21
+ "AluminiumV1",
22
+ "AsphaltV1",
23
+ "BulkMaterialsV1",
24
+ "CastDecksAndUnderlaymentV1",
25
+ "CladdingV1",
26
+ "ConcreteV1",
27
+ "ConveyingEquipmentV1",
28
+ "ElectricalTransmissionAndDistributionEquipmentV1",
29
+ "ElectricalV1",
30
+ "ElectricityV1",
31
+ "FinishesV1",
32
+ "FireAndSmokeProtectionV1",
33
+ "FurnishingsV1",
34
+ "GroutingV1",
35
+ "ManufacturingInputsV1",
36
+ "MasonryV1",
37
+ "MaterialHandlingV1",
38
+ "MechanicalInsulationV1",
39
+ "MechanicalV1",
40
+ "NetworkInfrastructureV1",
41
+ "OpeningsV1",
42
+ "OtherElectricalEquipmentV1",
43
+ "OtherMaterialsV1",
44
+ "PlumbingV1",
45
+ "PrecastConcreteV1",
46
+ "SheathingV1",
47
+ "SteelV1",
48
+ "ThermalMoistureProtectionV1",
49
+ "UtilityPipingV1",
50
+ "WoodJoistsV1",
51
+ "WoodV1",
52
+ ]
53
+
17
54
  from openepd.model.specs.singular.accessories import AccessoriesV1
18
55
  from openepd.model.specs.singular.aggregates import AggregatesV1
19
56
  from openepd.model.specs.singular.aluminium import AluminiumV1
@@ -51,19 +51,19 @@ class AsphaltV1(BaseOpenEpdHierarchicalSpec):
51
51
 
52
52
  asphalt_rap: float | None = pydantic.Field(
53
53
  default=None,
54
- description="Percent of mixture that has been replaced by recycled " "asphalt pavement (RAP).",
54
+ description="Percent of mixture that has been replaced by recycled asphalt pavement (RAP).",
55
55
  ge=0,
56
56
  le=1,
57
57
  )
58
58
  asphalt_ras: float | None = pydantic.Field(
59
59
  default=None,
60
- description="Percent of mixture that has been replaced by recycled " "asphalt shingles (RAS).",
60
+ description="Percent of mixture that has been replaced by recycled asphalt shingles (RAS).",
61
61
  ge=0,
62
62
  le=1,
63
63
  )
64
64
  asphalt_ground_tire_rubber: float | None = pydantic.Field(
65
65
  default=None,
66
- description="Percent of mixture that has been replaced " "by ground tire rubber (GTR).",
66
+ description="Percent of mixture that has been replaced by ground tire rubber (GTR).",
67
67
  ge=0,
68
68
  le=1,
69
69
  )
@@ -40,7 +40,8 @@ class BaseOpenEpdHierarchicalSpec(BaseOpenEpdSpec, WithExtVersionMixin):
40
40
  # ensure that all the concrete spec objects fail on creations if they dont have _EXT_VERSION declared to
41
41
  # something meaningful
42
42
  if not hasattr(self, "_EXT_VERSION") or self._EXT_VERSION is None:
43
- raise ValueError(f"Class {self.__class__} must declare an extension version")
43
+ msg = f"Class {self.__class__} must declare an extension version"
44
+ raise ValueError(msg)
44
45
  Version.parse_version(self._EXT_VERSION) # validate format correctness
45
46
  super().__init__(**{"ext_version": self._EXT_VERSION, **data})
46
47
 
@@ -2017,7 +2017,8 @@ class InsulatingMaterial(StrEnum):
2017
2017
  - Polyisocyanurate: Polyisocyanurate
2018
2018
  - Expanded Polyethylene: Expanded Polyethylene
2019
2019
  - EPS: EPS
2020
- - Other: Other
2020
+ - Wood Fiber = Wood Fiber
2021
+ - Other: Other
2021
2022
 
2022
2023
  """
2023
2024
 
@@ -2029,6 +2030,8 @@ class InsulatingMaterial(StrEnum):
2029
2030
  POLYISOCYANURATE = "Polyisocyanurate"
2030
2031
  EXPANDED_POLYETHYLENE = "Expanded Polyethylene"
2031
2032
  EPS = "EPS"
2033
+ WOOD_FIBER = "Wood Fiber"
2034
+ POLYURETHANE = "Polyurethane"
2032
2035
  OTHER = "Other"
2033
2036
 
2034
2037
 
@@ -2439,13 +2442,18 @@ class CementBoardThickness(StrEnum):
2439
2442
  INCH_3_8 = '3/8"'
2440
2443
  INCH_1_2 = '1/2"'
2441
2444
  INCH_5_8 = '5/8"'
2445
+ MM_4 = "4 mm"
2446
+ MM_6 = "6 mm"
2442
2447
  MM_6_5 = "6.5 mm"
2443
2448
  MM_8_0 = "8.0 mm"
2444
2449
  MM_9_0 = "9.0 mm"
2445
2450
  MM_9_5 = "9.5 mm"
2451
+ MM_10 = "10 mm"
2452
+ MM_12 = "12 mm"
2446
2453
  MM_12_5 = "12.5 mm"
2447
2454
  MM_13 = "13 mm"
2448
2455
  MM_15 = "15 mm"
2456
+ MM_16 = "16 mm"
2449
2457
  MM_18 = "18 mm"
2450
2458
 
2451
2459
 
@@ -13,9 +13,9 @@
13
13
  # See the License for the specific language governing permissions and
14
14
  # limitations under the License.
15
15
  #
16
- __all__ = ("SpecsRange",)
17
-
18
- # NB! This is a generated code. Do not edit it manually. Please see src/openepd/model/specs/README.md
16
+ __all__ = [
17
+ "SpecsRange",
18
+ ]
19
19
 
20
20
 
21
21
  from openepd.model.specs.base import BaseOpenEpdHierarchicalSpec
@@ -33,6 +33,7 @@ from .conveying_equipment import ConveyingEquipmentRangeV1
33
33
  from .electrical import ElectricalRangeV1
34
34
  from .electrical_transmission_and_distribution_equipment import ElectricalTransmissionAndDistributionEquipmentRangeV1
35
35
  from .electricity import ElectricityRangeV1
36
+ from .exterior_improvements import ExteriorImprovementsRangeV1
36
37
  from .finishes import FinishesRangeV1
37
38
  from .fire_and_smoke_protection import FireAndSmokeProtectionRangeV1
38
39
  from .furnishings import FurnishingsRangeV1
@@ -99,3 +100,4 @@ class SpecsRange(BaseOpenEpdHierarchicalSpec):
99
100
  MechanicalInsulation: MechanicalInsulationRangeV1 | None = None
100
101
  OtherElectricalEquipment: OtherElectricalEquipmentRangeV1 | None = None
101
102
  WoodJoists: WoodJoistsRangeV1 | None = None
103
+ ExteriorImprovements: ExteriorImprovementsRangeV1 | None = None
@@ -14,12 +14,12 @@
14
14
  # limitations under the License.
15
15
  #
16
16
  __all__ = (
17
+ "AccessoriesRangeV1",
17
18
  "BlanketFacingRangeV1",
18
19
  "DoorsHardwareRangeV1",
19
20
  "FlooringAccessoriesRangeV1",
20
21
  "MortarRangeV1",
21
22
  "TileGroutRangeV1",
22
- "AccessoriesRangeV1",
23
23
  )
24
24
 
25
25
  # NB! This is a generated code. Do not edit it manually. Please see src/openepd/model/specs/README.md
@@ -16,9 +16,9 @@
16
16
  __all__ = (
17
17
  "AluminiumBilletsRangeV1",
18
18
  "AluminiumExtrusionsRangeV1",
19
+ "AluminiumRangeV1",
19
20
  "AluminiumSheetGoodsRangeV1",
20
21
  "AluminiumSuspensionAssemblyRangeV1",
21
- "AluminiumRangeV1",
22
22
  )
23
23
 
24
24
  import pydantic