openepd 4.13.0__py3-none-any.whl → 5.0.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.13.0"
16
+ VERSION = "5.0.0"
openepd/model/common.py CHANGED
@@ -42,7 +42,7 @@ class Measurement(BaseOpenEpdSchema):
42
42
  """A scientific value with units and uncertainty."""
43
43
 
44
44
  mean: float = pyd.Field(description="Mean (expected) value of the measurement")
45
- unit: str = pyd.Field(description="Measurement unit")
45
+ unit: str | None = pyd.Field(description="Measurement unit")
46
46
  rsd: pyd.PositiveFloat | None = pyd.Field(
47
47
  description="Relative standard deviation, i.e. standard_deviation/mean", default=None
48
48
  )
@@ -24,7 +24,7 @@ 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
+ from openepd.model.validation.quantity import AmountGWP, AmountMass
28
28
 
29
29
  DEVELOPER_DESCRIPTION = "The organization responsible for the underlying LCA (and subsequent summarization as EPD)."
30
30
  PROGRAM_OPERATOR_DESCRIPTION = "JSON object for program operator Org"
@@ -136,19 +136,19 @@ class BaseDeclaration(RootDocument, abc.ABC):
136
136
  description="Link to data object on original registrar's site",
137
137
  example="https://epd-online.com/EmbeddedEpdList/Download/6029",
138
138
  )
139
- kg_C_per_declared_unit: AmountMass | None = pyd.Field(
139
+ kg_C_per_declared_unit: AmountGWP | None = pyd.Field(
140
140
  default=None,
141
141
  description="Mass of elemental carbon, per declared unit, contained in the product itself at the manufacturing "
142
142
  "facility gate. Used (among other things) to check a carbon balance or calculate incineration "
143
143
  "emissions. The source of carbon (e.g. biogenic) is not relevant in this field.",
144
- example=Amount(qty=8.76, unit="kg"),
144
+ example=Amount(qty=8.76, unit="kgCO2e"),
145
145
  )
146
- kg_C_biogenic_per_declared_unit: AmountMass | None = pyd.Field(
146
+ kg_C_biogenic_per_declared_unit: AmountGWP | None = pyd.Field(
147
147
  default=None,
148
148
  description="Mass of elemental carbon from biogenic sources, per declared unit, contained in the product "
149
149
  "itself at the manufacturing facility gate. It may be presumed that any biogenic carbon content "
150
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"),
151
+ example=Amount(qty=8.76, unit="kgCO2e"),
152
152
  )
153
153
  product_service_life_years: float | None = pyd.Field(
154
154
  gt=0.0009,
openepd/model/lcia.py CHANGED
@@ -14,10 +14,12 @@
14
14
  # limitations under the License.
15
15
  #
16
16
  from enum import StrEnum
17
+ from typing import Any, ClassVar
17
18
 
18
19
  from openepd.compat.pydantic import pyd
19
20
  from openepd.model.base import BaseOpenEpdSchema
20
21
  from openepd.model.common import Measurement
22
+ from openepd.model.validation.quantity import ExternalValidationConfig
21
23
 
22
24
 
23
25
  class EolScenario(BaseOpenEpdSchema):
@@ -70,6 +72,8 @@ class ScopeSet(BaseOpenEpdSchema):
70
72
  The 'unit' field must be consistent across all scopes in a single scopeset.
71
73
  """
72
74
 
75
+ allowed_units: ClassVar[str | tuple[str, ...] | None] = None
76
+
73
77
  A1A2A3: Measurement | None = pyd.Field(
74
78
  description="Sum of A1..A3",
75
79
  default=None,
@@ -194,17 +198,56 @@ class ScopeSet(BaseOpenEpdSchema):
194
198
  description="Potential net benefits from reuse, recycling, and/or energy recovery beyond the system boundary.",
195
199
  )
196
200
 
201
+ @pyd.root_validator
202
+ def _unit_validator(cls, values: dict[str, Any]) -> dict[str, Any]:
203
+ all_units = set()
204
+
205
+ for k, v in values.items():
206
+ if isinstance(v, Measurement):
207
+ all_units.add(v.unit)
208
+
209
+ # units should be the same across all measurements (textually)
210
+ if len(all_units) > 1:
211
+ raise ValueError("All scopes and measurements should be expressed in the same unit.")
212
+
213
+ # can correctly validate unit
214
+ if cls.allowed_units is not None and len(all_units) == 1 and ExternalValidationConfig.QUANTITY_VALIDATOR:
215
+ unit = next(iter(all_units))
216
+ allowed_units = cls.allowed_units if isinstance(cls.allowed_units, tuple) else (cls.allowed_units,)
197
217
 
198
- class ScopesetByNameBase(BaseOpenEpdSchema):
218
+ matched_unit = False
219
+ for allowed_unit in allowed_units:
220
+ try:
221
+ ExternalValidationConfig.QUANTITY_VALIDATOR.validate_same_dimensionality(unit, allowed_unit)
222
+ matched_unit = True
223
+ except ValueError:
224
+ ...
225
+ if not matched_unit:
226
+ raise ValueError(
227
+ f"'{', '.join(allowed_units)}' is only allowed unit for this scopeset. Provided '{unit}'"
228
+ )
229
+
230
+ return values
231
+
232
+
233
+ class ScopesetByNameBase(BaseOpenEpdSchema, extra="allow"):
199
234
  """Base class for the data structures presented as typed name:scopeset mapping ."""
200
235
 
201
236
  def get_scopeset_names(self) -> list[str]:
202
237
  """
203
238
  Get the names of scopesets which have been set by model (not defaults).
204
239
 
205
- :return: set of names, for example ['gwp', 'odp]
240
+ :return: set of names, for example ['gwp', 'odp']
206
241
  """
207
- return [self.__fields__[f].alias or f for f in self.__fields_set__ if f not in ("ext",)]
242
+ result = []
243
+ for f in self.__fields_set__:
244
+ if f in ("ext",):
245
+ continue
246
+ field = self.__fields__.get(f)
247
+ # field can be explicitly specified, or can be an unknown impact covered by extra='allow'
248
+ result.append(field.alias if field and field.alias else f)
249
+
250
+ return result
208
251
 
209
252
  def get_scopeset_by_name(self, name: str) -> ScopeSet | None:
210
253
  """
@@ -213,39 +256,133 @@ class ScopesetByNameBase(BaseOpenEpdSchema):
213
256
  :param name: The name of the scopeset.
214
257
  :return: A scopeset if found, None otherwise
215
258
  """
259
+ # check known impacts first
216
260
  for f_name, f in self.__fields__.items():
217
261
  if f.alias == name:
218
262
  return getattr(self, f_name)
219
263
  if f_name == name:
220
264
  return getattr(self, f_name)
265
+ # probably unknown impact, coming from 'extra' fields
266
+ return getattr(self, name, None)
267
+
268
+ @pyd.root_validator(skip_on_failure=True)
269
+ def _extra_scopeset_validator(cls, values: dict[str, Any]) -> dict[str, Any]:
270
+ for f in values:
271
+ # only interested in validating the extra fields
272
+ if f in cls.__fields__:
273
+ continue
274
+
275
+ # extra impact of an unknown type - engage validation of ScopeSet
276
+ extra_scopeset = values.get(f)
277
+ match extra_scopeset:
278
+ case ScopeSet():
279
+ continue
280
+ case dict():
281
+ values[f] = ScopeSet(**extra_scopeset)
282
+ case _:
283
+ raise ValueError(f"{f} must be a ScopeSet schema")
284
+
285
+ return values
286
+
287
+
288
+ class ScopeSetGwp(ScopeSet):
289
+ """ScopeSet measured in kgCO2e."""
290
+
291
+ allowed_units = "kgCO2e"
292
+
293
+
294
+ class ScopeSetOdp(ScopeSet):
295
+ """ScopeSet measured in kgCFC11e."""
296
+
297
+ allowed_units = "kgCFC11e"
298
+
299
+
300
+ class ScopeSetAp(ScopeSet):
301
+ """ScopeSet measured in kgSO2e."""
302
+
303
+ allowed_units = ("kgSO2e", "molHe")
304
+
305
+
306
+ class ScopeSetEpNe(ScopeSet):
307
+ """ScopeSet measured in kgNe."""
308
+
309
+ allowed_units = "kgNe"
310
+
311
+
312
+ class ScopeSetPocp(ScopeSet):
313
+ """ScopeSet measured in kgO3e."""
314
+
315
+ allowed_units = ("kgO3e", "kgNMVOCe")
316
+
317
+
318
+ class ScopeSetEpFresh(ScopeSet):
319
+ """ScopeSet measured in kgPO4e."""
320
+
321
+ allowed_units = "kgPO4e"
322
+
323
+
324
+ class ScopeSetEpTerr(ScopeSet):
325
+ """ScopeSet measured in molNe."""
221
326
 
222
- return None
327
+ allowed_units = "molNe"
328
+
329
+
330
+ class ScopeSetIrp(ScopeSet):
331
+ """ScopeSet measured in kilo Becquerel equivalent of u235."""
332
+
333
+ allowed_units = "kBqU235e"
334
+
335
+
336
+ class ScopeSetCTUh(ScopeSet):
337
+ """ScopeSet measured in CTUh."""
338
+
339
+ allowed_units = "CTUh"
340
+
341
+
342
+ class ScopeSetM3Aware(ScopeSet):
343
+ """ScopeSet measured in m3AWARE Water consumption by AWARE method."""
344
+
345
+ allowed_units = "m3AWARE"
346
+
347
+
348
+ class ScopeSetCTUe(ScopeSet):
349
+ """ScopeSet measured in CTUe."""
350
+
351
+ allowed_units = "CTUe"
352
+
353
+
354
+ class ScopeSetDiseaseIncidence(ScopeSet):
355
+ """ScopeSet measuring disease incidence measured in AnnualPerCapita (cases)."""
356
+
357
+ allowed_units = "AnnualPerCapita"
223
358
 
224
359
 
225
360
  class ImpactSet(ScopesetByNameBase):
226
361
  """A set of impacts, such as GWP, ODP, AP, EP, POCP, EP-marine, EP-terrestrial, EP-freshwater, etc."""
227
362
 
228
- gwp: ScopeSet | None = pyd.Field(
363
+ gwp: ScopeSetGwp | None = pyd.Field(
229
364
  default=None,
230
365
  description="GWP100, calculated per IPCC guidelines. If any CO2 removals are "
231
366
  "part of this figure, the gwp-fossil, gwp-bioganic, gwp-luluc, an "
232
367
  "gwp-nonCO2 fields are required, as is "
233
368
  "kg_C_biogenic_per_declared_unit.",
234
369
  )
235
- odp: ScopeSet | None = pyd.Field(default=None, description="Ozone Depletion Potential")
236
- ap: ScopeSet | None = pyd.Field(default=None, description="Acidification Potential")
237
- ep: ScopeSet | None = pyd.Field(
370
+ odp: ScopeSetOdp | None = pyd.Field(default=None, description="Ozone Depletion Potential")
371
+ ap: ScopeSetAp | None = pyd.Field(default=None, description="Acidification Potential")
372
+ ep: ScopeSetEpNe | None = pyd.Field(
238
373
  default=None, description="Eutrophication Potential in Marine Ecosystems. Has the same meaning as ep-marine."
239
374
  )
240
- pocp: ScopeSet | None = pyd.Field(default=None, description="Photochemical Smog (Ozone) creation potential")
241
- ep_marine: ScopeSet | None = pyd.Field(alias="ep-marine", default=None, description="Has the same meaning as 'ep'")
242
- ep_fresh: ScopeSet | None = pyd.Field(
375
+ pocp: ScopeSetPocp | None = pyd.Field(default=None, description="Photochemical Smog (Ozone) creation potential")
376
+ ep_marine: ScopeSetEpNe | None = pyd.Field(
377
+ alias="ep-marine", default=None, description="Has the same meaning as 'ep'"
378
+ )
379
+ ep_fresh: ScopeSetEpFresh | None = pyd.Field(
243
380
  alias="ep-fresh", default=None, description="Eutrophication Potential in Freshwater Ecosystems"
244
381
  )
245
- ep_terr: ScopeSet | None = pyd.Field(
382
+ ep_terr: ScopeSetEpTerr | None = pyd.Field(
246
383
  alias="ep-terr", default=None, description="Eutrophication Potential in Terrestrial Ecosystems"
247
384
  )
248
- gwp_biogenic: ScopeSet | None = pyd.Field(
385
+ gwp_biogenic: ScopeSetGwp | None = pyd.Field(
249
386
  alias="gwp-biogenic",
250
387
  default=None,
251
388
  description="Net GWP from removals of atmospheric CO2 into biomass and emissions of CO2 from biomass sources. "
@@ -254,7 +391,7 @@ class ImpactSet(ScopesetByNameBase):
254
391
  "space (similar biome). They must not have been sold, committed, or credited to any other "
255
392
  "product. Harvesting from native forests is handled under GWP_luluc for EN15804.",
256
393
  )
257
- gwp_luluc: ScopeSet | None = pyd.Field(
394
+ gwp_luluc: ScopeSetGwp | None = pyd.Field(
258
395
  alias="gwp-luluc",
259
396
  default=None,
260
397
  description="Climate change effects related to land use and land use change, for example biogenic carbon "
@@ -262,17 +399,49 @@ class ImpactSet(ScopesetByNameBase):
262
399
  "emissions). All related emissions for native forests are included under this category. "
263
400
  "Uptake for native forests is set to 0 kgCO2 for EN15804.",
264
401
  )
265
- gwp_nonCO2: ScopeSet | None = pyd.Field(
402
+ gwp_nonCO2: ScopeSetGwp | None = pyd.Field(
266
403
  alias="gwp-nonCO2",
267
404
  default=None,
268
405
  description="GWP from non-CO2, non-fossil sources, such as livestock-sourced CH4 and agricultural N2O.",
269
406
  )
270
- gwp_fossil: ScopeSet | None = pyd.Field(
407
+ gwp_fossil: ScopeSetGwp | None = pyd.Field(
271
408
  alias="gwp-fossil",
272
409
  default=None,
273
410
  description="Climate change effects due to greenhouse gas emissions originating from the oxidation or "
274
411
  "reduction of fossil fuels or materials containing fossil carbon. [Source: EN15804]",
275
412
  )
413
+ WDP: ScopeSetM3Aware | None = pyd.Field(
414
+ default=None,
415
+ description="Deprivation-weighted water consumption, calculated by the AWARE method "
416
+ "(https://wulca-waterlca.org/aware/what-is-aware)",
417
+ )
418
+ PM: ScopeSetDiseaseIncidence | None = pyd.Field(
419
+ default=None,
420
+ description="Potential incidence of disease due to particulate matter emissions.",
421
+ )
422
+ IRP: ScopeSetIrp | None = pyd.Field(
423
+ default=None,
424
+ description="Potential ionizing radiation effect on human health, relative to U235.",
425
+ )
426
+ ETP_fw: ScopeSetCTUe | None = pyd.Field(
427
+ alias="ETP-fw",
428
+ default=None,
429
+ description="Ecotoxicity in freshwater, in potential Comparative Toxic Unit for ecosystems.",
430
+ )
431
+ HTP_c: ScopeSetCTUh | None = pyd.Field(
432
+ alias="HTP-c",
433
+ default=None,
434
+ description="Human toxicity, cancer effects in potential Comparative Toxic Units for humans.",
435
+ )
436
+ HTP_nc: ScopeSetCTUh | None = pyd.Field(
437
+ alias="HTP-nc",
438
+ default=None,
439
+ description="Human toxicity, noncancer effects in potential Comparative Toxic Units for humans.",
440
+ )
441
+ SQP: ScopeSet | None = pyd.Field(
442
+ default=None,
443
+ description="Land use related impacts / Soil quality, in potential soil quality parameters.",
444
+ )
276
445
 
277
446
 
278
447
  class LCIAMethod(StrEnum):
@@ -18,7 +18,7 @@ from typing import Any
18
18
  from openepd.compat.pydantic import pyd
19
19
  from openepd.model.base import BaseOpenEpdSchema, Version
20
20
  from openepd.model.validation.common import validate_version_compatibility, validate_version_format
21
- from openepd.model.validation.quantity import QuantityValidator
21
+ from openepd.model.validation.quantity import ExternalValidationConfig, QuantityValidator
22
22
  from openepd.model.versioning import WithExtVersionMixin
23
23
 
24
24
 
@@ -32,9 +32,6 @@ class BaseOpenEpdSpec(BaseOpenEpdSchema):
32
32
  class BaseOpenEpdHierarchicalSpec(BaseOpenEpdSpec, WithExtVersionMixin):
33
33
  """Base class for new specs (hierarchical, versioned)."""
34
34
 
35
- # external validator for quantities (e.g. length, mass, etc.) which should be setup by the user of the library.
36
- _QUANTITY_VALIDATOR: QuantityValidator | None = None
37
-
38
35
  def __init__(self, **data: Any) -> None:
39
36
  # ensure that all the concrete spec objects fail on creations if they dont have _EXT_VERSION declared to
40
37
  # something meaningful
@@ -53,4 +50,4 @@ class BaseOpenEpdHierarchicalSpec(BaseOpenEpdSpec, WithExtVersionMixin):
53
50
 
54
51
  def setup_external_validators(quantity_validator: QuantityValidator):
55
52
  """Set the implementation unit validator for specs."""
56
- BaseOpenEpdHierarchicalSpec._QUANTITY_VALIDATOR = quantity_validator
53
+ ExternalValidationConfig.QUANTITY_VALIDATOR = quantity_validator
@@ -33,6 +33,19 @@ class QuantityValidator(ABC):
33
33
  and set it with `set_unit_validator` function.
34
34
  """
35
35
 
36
+ @abstractmethod
37
+ def validate_same_dimensionality(self, unit: str | None, dimensionality_unit: str) -> None:
38
+ """
39
+ Validate that a given unit ('kg') has the same dimesnionality as provided dimensionality_unit ('g').
40
+
41
+ :param unit: unit to validate, not quantity
42
+ :param dimensionality_unit: unit to check against
43
+ :raise:
44
+ ValueError if dimensionality is different
45
+ :return: None
46
+ """
47
+ pass
48
+
36
49
  @abstractmethod
37
50
  def validate_unit_correctness(self, value: str, dimensionality: str) -> None:
38
51
  """
@@ -79,12 +92,23 @@ class QuantityValidator(ABC):
79
92
  pass
80
93
 
81
94
 
95
+ class ExternalValidationConfig:
96
+ """
97
+ Configuration holder for external validator.
98
+
99
+ Since openEPD library does not provide any facility for working with quantities/units, users should do this
100
+ by implementing the protocol for this validator and setting it with setup_external_validators function.
101
+ """
102
+
103
+ QUANTITY_VALIDATOR: ClassVar[QuantityValidator | None] = None
104
+
105
+
82
106
  def validate_unit_factory(dimensionality: OpenEPDUnit | str) -> "QuantityValidatorType":
83
107
  """Create validator for quantity field to check unit matching."""
84
108
 
85
109
  def validator(cls: "type[BaseOpenEpdHierarchicalSpec]", value: str) -> str:
86
- if hasattr(cls, "_QUANTITY_VALIDATOR") and cls._QUANTITY_VALIDATOR is not None:
87
- cls._QUANTITY_VALIDATOR.validate_unit_correctness(value, dimensionality)
110
+ if ExternalValidationConfig.QUANTITY_VALIDATOR is not None:
111
+ ExternalValidationConfig.QUANTITY_VALIDATOR.validate_unit_correctness(value, dimensionality)
88
112
  return value
89
113
 
90
114
  return validator
@@ -94,8 +118,8 @@ def validate_quantity_ge_factory(min_value: str) -> "QuantityValidatorType":
94
118
  """Create validator to check that quantity is greater than or equal to min_value."""
95
119
 
96
120
  def validator(cls: "type[BaseOpenEpdHierarchicalSpec]", value: str) -> str:
97
- if hasattr(cls, "_QUANTITY_VALIDATOR") and cls._QUANTITY_VALIDATOR is not None:
98
- cls._QUANTITY_VALIDATOR.validate_quantity_greater_or_equal(value, min_value)
121
+ if ExternalValidationConfig.QUANTITY_VALIDATOR is not None:
122
+ ExternalValidationConfig.QUANTITY_VALIDATOR.validate_quantity_greater_or_equal(value, min_value)
99
123
  return value
100
124
 
101
125
  return validator
@@ -105,8 +129,8 @@ def validate_quantity_le_factory(max_value: str) -> "QuantityValidatorType":
105
129
  """Create validator to check that quantity is less than or equal to max_value."""
106
130
 
107
131
  def validator(cls: "type[BaseOpenEpdHierarchicalSpec]", value: str) -> str:
108
- if hasattr(cls, "_QUANTITY_VALIDATOR") and cls._QUANTITY_VALIDATOR is not None:
109
- cls._QUANTITY_VALIDATOR.validate_quantity_less_or_equal(value, max_value)
132
+ if ExternalValidationConfig.QUANTITY_VALIDATOR is not None:
133
+ ExternalValidationConfig.QUANTITY_VALIDATOR.validate_quantity_less_or_equal(value, max_value)
110
134
  return value
111
135
 
112
136
  return validator
@@ -116,9 +140,8 @@ def validate_quantity_for_new_validator(max_value: str) -> Callable:
116
140
  """Create validator to check that quantity is less than or equal to max_value."""
117
141
 
118
142
  def validator(value: str) -> str:
119
- cls = BaseOpenEpdHierarchicalSpec
120
- if hasattr(cls, "_QUANTITY_VALIDATOR") and cls._QUANTITY_VALIDATOR is not None:
121
- cls._QUANTITY_VALIDATOR.validate_quantity_less_or_equal(value, max_value)
143
+ if ExternalValidationConfig.QUANTITY_VALIDATOR is not None:
144
+ ExternalValidationConfig.QUANTITY_VALIDATOR.validate_quantity_less_or_equal(value, max_value)
122
145
  return value
123
146
 
124
147
  return validator
@@ -154,6 +177,12 @@ class AmountMass(AmountWithDimensionality):
154
177
  dimensionality_unit = OpenEPDUnit.kg
155
178
 
156
179
 
180
+ class AmountGWP(AmountWithDimensionality):
181
+ """Amount of Global Warming Potential, measured in kgCO2e."""
182
+
183
+ dimensionality_unit = OpenEPDUnit.kg_co2
184
+
185
+
157
186
  class QuantityStr(str):
158
187
  """
159
188
  Quantity string type.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: openepd
3
- Version: 4.13.0
3
+ Version: 5.0.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
@@ -19,6 +19,7 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
19
19
  Provides-Extra: api-client
20
20
  Requires-Dist: email-validator (>=1.3.1)
21
21
  Requires-Dist: idna (>=3.7)
22
+ Requires-Dist: open-xpd-uuid (>=0.2.1,<0.3.0)
22
23
  Requires-Dist: pydantic (>=1.10,<3.0)
23
24
  Requires-Dist: requests (>=2.0) ; extra == "api-client"
24
25
  Project-URL: Repository, https://github.com/cchangelabs/openepd
@@ -1,5 +1,5 @@
1
1
  openepd/__init__.py,sha256=Shkfh0Kun0YRhmRDw7LkUj2eQL3X-HnP55u2THOEALw,794
2
- openepd/__version__.py,sha256=ct2ojZmiO087rEAYSPIKIFv4c6Tf57TQ7E0AKUt3Iyw,639
2
+ openepd/__version__.py,sha256=wohDuLeW4wo29tRLxN043j7FiKKlXir53cEc6CVJqkM,638
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
@@ -35,20 +35,20 @@ openepd/compat/pydantic.py,sha256=DOjSixsylLqMtFAIARu50sGcT4VPXN_c473q_2JwZQ0,11
35
35
  openepd/model/__init__.py,sha256=UGmZGEyMnASrYwEBPHuXmVzHiuCUskUsJEPoHTIo-lg,620
36
36
  openepd/model/base.py,sha256=OEYNFUTL4BivBNAt_LGowTlDuUjvKMHgf5U5ZBncZwQ,9805
37
37
  openepd/model/category.py,sha256=IQXNGQFQmFZ_H9PRONloX_UOSf1sTMDq1rM1yz8JR0Y,1639
38
- openepd/model/common.py,sha256=hXnz2QiQDx3Z7F4BMHlHQ2iddq3OonKrveltl-nOiEI,5468
39
- openepd/model/declaration.py,sha256=9KnmYr3ZLpkbBFVkesaEdDtGpG-SrVFuIOO2ekRyTgk,10796
38
+ openepd/model/common.py,sha256=DEMJHj6jWsezlHaO8T2GQD3rT4WGliOMmGBg1eA4nPc,5475
39
+ openepd/model/declaration.py,sha256=CpD08iZg4jcUu_p1962wIw-euB0mpqd3eu9KH_DoLx4,10813
40
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
- openepd/model/lcia.py,sha256=pVh4LbZgzDuLuM3epdCNLlAp5B0BnUrwggaaDiuPccM,18848
45
+ openepd/model/lcia.py,sha256=tkV36hxMK4_mbUtfg23vbCge1WHxXOv3lhC59HRWJ5w,24135
46
46
  openepd/model/org.py,sha256=FHcYh2WOOQrCMyzm0Ow-iP79jMTBPcneidjH6NXIklA,3760
47
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
51
- openepd/model/specs/base.py,sha256=aVEWnqliXAmUzT9VZNPBd5Zr8Y-fP5VV3T9eBeWf720,2480
51
+ openepd/model/specs/base.py,sha256=cItr0lUrH3TK9B8tmz2vieU4cPylZBGXmQge6trBmzY,2328
52
52
  openepd/model/specs/concrete.py,sha256=spMvijIv79c87PXT_YV7coG851AkJ1ILl9EHCxN2-6E,9593
53
53
  openepd/model/specs/generated/__init__.py,sha256=UGmZGEyMnASrYwEBPHuXmVzHiuCUskUsJEPoHTIo-lg,620
54
54
  openepd/model/specs/generated/accessories.py,sha256=hEnrW4Vdr9GopR_A9gF8QyUyKJ9i5sx4khrNYDVLwy8,2013
@@ -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=h4AXXMijFtJRFUb5bi_iRzK2v7v1CtggLi7CyK5bA4s,8349
96
+ openepd/model/validation/quantity.py,sha256=Me7-ZuUtVxQo9jKeq6NoXznXSFzvsiAMYEUyRYA2I_w,9317
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.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,,
100
+ openepd-5.0.0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
101
+ openepd-5.0.0.dist-info/METADATA,sha256=PW5ojpA0oXWAOwJlMCdHG6ngp9oxEWhn7beQJjoVy04,8750
102
+ openepd-5.0.0.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
103
+ openepd-5.0.0.dist-info/RECORD,,