openepd 1.5.0__py3-none-any.whl → 1.6.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 +1 -1
- openepd/model/base.py +18 -1
- openepd/model/common.py +16 -0
- openepd/model/org.py +8 -2
- openepd/model/specs/__init__.py +5 -2
- openepd/model/specs/base.py +8 -0
- openepd/model/specs/concrete.py +116 -11
- openepd/model/specs/steel.py +73 -18
- openepd/model/validation/numbers.py +42 -1
- openepd/model/versioning.py +1 -1
- {openepd-1.5.0.dist-info → openepd-1.6.0.dist-info}/METADATA +1 -1
- {openepd-1.5.0.dist-info → openepd-1.6.0.dist-info}/RECORD +14 -14
- {openepd-1.5.0.dist-info → openepd-1.6.0.dist-info}/LICENSE +0 -0
- {openepd-1.5.0.dist-info → openepd-1.6.0.dist-info}/WHEEL +0 -0
openepd/__version__.py
CHANGED
openepd/model/base.py
CHANGED
@@ -20,7 +20,7 @@
|
|
20
20
|
import abc
|
21
21
|
from enum import StrEnum
|
22
22
|
import json
|
23
|
-
from typing import Any, Generic, Optional, Type, TypeVar
|
23
|
+
from typing import Any, Callable, Generic, Optional, Type, TypeVar
|
24
24
|
|
25
25
|
import pydantic as pyd
|
26
26
|
from pydantic.generics import GenericModel
|
@@ -41,6 +41,22 @@ class OpenEpdDoctypes(StrEnum):
|
|
41
41
|
Epd = "openEPD"
|
42
42
|
|
43
43
|
|
44
|
+
def modify_pydantic_schema(schema_dict: dict, cls: type) -> dict:
|
45
|
+
"""
|
46
|
+
Modify the schema dictionary to add the required fields.
|
47
|
+
|
48
|
+
:param schema_dict: schema dictionary
|
49
|
+
:param cls: class for which the schema was generated
|
50
|
+
:return: modified schema dictionary
|
51
|
+
"""
|
52
|
+
ext = schema_dict.get("properties", {}).get("ext")
|
53
|
+
# move to bottom
|
54
|
+
if ext is not None:
|
55
|
+
del schema_dict["properties"]["ext"]
|
56
|
+
schema_dict["properties"]["ext"] = ext
|
57
|
+
return schema_dict
|
58
|
+
|
59
|
+
|
44
60
|
class BaseOpenEpdSchema(pyd.BaseModel):
|
45
61
|
"""Base class for all OpenEPD models."""
|
46
62
|
|
@@ -51,6 +67,7 @@ class BaseOpenEpdSchema(pyd.BaseModel):
|
|
51
67
|
validate_assignment = False
|
52
68
|
allow_population_by_field_name = True
|
53
69
|
use_enum_values = True
|
70
|
+
schema_extra: Callable | dict = modify_pydantic_schema
|
54
71
|
|
55
72
|
def to_serializable(self, *args, **kwargs) -> dict[str, Any]:
|
56
73
|
"""
|
openepd/model/common.py
CHANGED
@@ -17,6 +17,7 @@
|
|
17
17
|
# Charles Pankow Foundation, Microsoft Sustainability Fund, Interface, MKA Foundation, and others.
|
18
18
|
# Find out more at www.BuildingTransparency.org
|
19
19
|
#
|
20
|
+
from enum import StrEnum
|
20
21
|
from typing import Annotated, Any
|
21
22
|
|
22
23
|
import pydantic as pyd
|
@@ -137,3 +138,18 @@ class WithAltIdsMixin(BaseModel):
|
|
137
138
|
"""Set an alt_id if value is not None."""
|
138
139
|
if value is not None:
|
139
140
|
self.set_alt_id(domain_name, value)
|
141
|
+
|
142
|
+
|
143
|
+
class OpenEPDUnit(StrEnum):
|
144
|
+
"""OpenEPD allowed units."""
|
145
|
+
|
146
|
+
kg = "kg"
|
147
|
+
m2 = "m2"
|
148
|
+
m = "m"
|
149
|
+
M2_RSI = "m2 * RSI"
|
150
|
+
MJ = "MJ"
|
151
|
+
t_km = "t * km"
|
152
|
+
MPa = "MPa"
|
153
|
+
item = "item"
|
154
|
+
W = "W"
|
155
|
+
use = "use"
|
openepd/model/org.py
CHANGED
@@ -25,8 +25,8 @@ from openepd.model.base import BaseOpenEpdSchema
|
|
25
25
|
from openepd.model.common import Location, WithAltIdsMixin, WithAttachmentsMixin
|
26
26
|
|
27
27
|
|
28
|
-
class
|
29
|
-
"""
|
28
|
+
class OrgRef(BaseOpenEpdSchema):
|
29
|
+
"""Represents Organisation with minimal data."""
|
30
30
|
|
31
31
|
web_domain: str | None = pyd.Field(
|
32
32
|
description="A web domain owned by organization. Typically is the org's home website address", default=None
|
@@ -37,12 +37,18 @@ class Org(WithAttachmentsMixin, WithAltIdsMixin, BaseOpenEpdSchema):
|
|
37
37
|
example="C Change Labs",
|
38
38
|
default=None,
|
39
39
|
)
|
40
|
+
|
41
|
+
|
42
|
+
class Org(WithAttachmentsMixin, WithAltIdsMixin, OrgRef):
|
43
|
+
"""Represent an organization."""
|
44
|
+
|
40
45
|
alt_names: Annotated[list[str], pyd.conlist(pyd.constr(max_length=200), max_items=255)] | None = pyd.Field(
|
41
46
|
description="List of other names for organization",
|
42
47
|
example=["C-Change Labs", "C-Change Labs inc."],
|
43
48
|
default=None,
|
44
49
|
)
|
45
50
|
# TODO: NEW field, not in the spec
|
51
|
+
|
46
52
|
owner: Optional["Org"] = pyd.Field(description="Organization that controls this organization", default=None)
|
47
53
|
subsidiaries: Annotated[list[str], pyd.conlist(pyd.constr(max_length=200), max_items=255)] | None = pyd.Field(
|
48
54
|
description="Organizations controlled by this organization",
|
openepd/model/specs/__init__.py
CHANGED
@@ -17,6 +17,7 @@
|
|
17
17
|
# Charles Pankow Foundation, Microsoft Sustainability Fund, Interface, MKA Foundation, and others.
|
18
18
|
# Find out more at www.BuildingTransparency.org
|
19
19
|
#
|
20
|
+
|
20
21
|
import pydantic as pyd
|
21
22
|
|
22
23
|
from openepd.model.base import BaseOpenEpdSchema
|
@@ -28,5 +29,7 @@ class Specs(BaseOpenEpdSchema):
|
|
28
29
|
|
29
30
|
cmu: concrete.CmuSpec | None = pyd.Field(default=None, description="Concrete Masonry Unit-specific (CMU) specs")
|
30
31
|
CMU: concrete.CmuSpec | None = pyd.Field(default=None, description="Concrete Masonry Unit-specific (CMU) specs")
|
31
|
-
Steel: steel.SteelV1 | None = pyd.Field(default=None, description="Steel-specific specs")
|
32
|
-
Concrete: concrete.ConcreteV1 | None = pyd.Field(
|
32
|
+
Steel: steel.SteelV1 | None = pyd.Field(default=None, title="SteelV1", description="Steel-specific specs")
|
33
|
+
Concrete: concrete.ConcreteV1 | None = pyd.Field(
|
34
|
+
default=None, title="ConcreteV1", description="Concrete-specific specs"
|
35
|
+
)
|
openepd/model/specs/base.py
CHANGED
@@ -23,6 +23,7 @@ import pydantic as pyd
|
|
23
23
|
|
24
24
|
from openepd.model.base import BaseOpenEpdSchema, Version
|
25
25
|
from openepd.model.validation.common import validate_version_compatibility, validate_version_format
|
26
|
+
from openepd.model.validation.numbers import QuantityValidator
|
26
27
|
from openepd.model.versioning import WithExtVersionMixin
|
27
28
|
|
28
29
|
|
@@ -36,6 +37,8 @@ class BaseOpenEpdSpec(BaseOpenEpdSchema):
|
|
36
37
|
class BaseOpenEpdHierarchicalSpec(BaseOpenEpdSpec, WithExtVersionMixin):
|
37
38
|
"""Base class for new specs (hierarchical, versioned)."""
|
38
39
|
|
40
|
+
_QUANTITY_VALIDATOR: QuantityValidator | None = None
|
41
|
+
|
39
42
|
def __init__(self, **data: Any) -> None:
|
40
43
|
# ensure that all the concrete spec objects fail on creations if they dont have _EXT_VERSION declared to
|
41
44
|
# something meaningful
|
@@ -50,3 +53,8 @@ class BaseOpenEpdHierarchicalSpec(BaseOpenEpdSpec, WithExtVersionMixin):
|
|
50
53
|
_version_major_match_validator = pyd.validator("ext_version", allow_reuse=True, check_fields=False)(
|
51
54
|
validate_version_compatibility("_EXT_VERSION")
|
52
55
|
)
|
56
|
+
|
57
|
+
|
58
|
+
def setup_external_validators(quantity_validator: QuantityValidator):
|
59
|
+
"""Set the implementation unit validator for specs."""
|
60
|
+
BaseOpenEpdHierarchicalSpec._QUANTITY_VALIDATOR = quantity_validator
|
openepd/model/specs/concrete.py
CHANGED
@@ -23,9 +23,67 @@ from typing import Literal
|
|
23
23
|
import pydantic as pyd
|
24
24
|
|
25
25
|
from openepd.model.base import BaseOpenEpdSchema
|
26
|
+
from openepd.model.common import OpenEPDUnit
|
26
27
|
from openepd.model.specs.base import BaseOpenEpdHierarchicalSpec, BaseOpenEpdSpec
|
27
28
|
from openepd.model.validation.common import together_validator
|
28
|
-
from openepd.model.validation.numbers import RatioFloat
|
29
|
+
from openepd.model.validation.numbers import RatioFloat, validate_unit_factory
|
30
|
+
|
31
|
+
|
32
|
+
class AciExposureClass(StrEnum):
|
33
|
+
"""ACI Code (US)."""
|
34
|
+
|
35
|
+
F0 = "aci.F0"
|
36
|
+
F1 = "aci.F1"
|
37
|
+
F2 = "aci.F2"
|
38
|
+
F3 = "aci.F3"
|
39
|
+
S0 = "aci.S0"
|
40
|
+
S1 = "aci.S1"
|
41
|
+
S2 = "aci.S2"
|
42
|
+
S3 = "aci.S3"
|
43
|
+
C1 = "aci.C1"
|
44
|
+
C2 = "aci.C2"
|
45
|
+
W0 = "aci.W0"
|
46
|
+
W1 = "aci.W1"
|
47
|
+
W2 = "aci.W2"
|
48
|
+
|
49
|
+
|
50
|
+
class CsaExposureClass(StrEnum):
|
51
|
+
"""CSA Code (Canada)."""
|
52
|
+
|
53
|
+
N = "csa.N"
|
54
|
+
F2 = "csa.F-2"
|
55
|
+
F_1 = "csa.F-1"
|
56
|
+
C_1 = "csa.C-1"
|
57
|
+
S_3 = "csa.S-3"
|
58
|
+
S_2 = "csa.S-2"
|
59
|
+
S_1 = "csa.S-1"
|
60
|
+
A_1 = "csa.A-1"
|
61
|
+
A_2 = "csa.A-2"
|
62
|
+
A_3 = "csa.A-3"
|
63
|
+
A_4 = "csa.A-4"
|
64
|
+
|
65
|
+
|
66
|
+
class EnExposureClass(StrEnum):
|
67
|
+
"""EN 206 Class (Europe)."""
|
68
|
+
|
69
|
+
en206_0 = "en206.0"
|
70
|
+
F1 = "en206.F1"
|
71
|
+
F2 = "en206.F2"
|
72
|
+
F3 = "en206.F3"
|
73
|
+
F4 = "en206.F4"
|
74
|
+
A1 = "en206.A1"
|
75
|
+
A2 = "en206.A2"
|
76
|
+
A3 = "en206.A3"
|
77
|
+
D1 = "en206.D1"
|
78
|
+
D2 = "en206.D2"
|
79
|
+
D3 = "en206.D3"
|
80
|
+
S1 = "en206.S1"
|
81
|
+
S2 = "en206.S2"
|
82
|
+
S3 = "en206.S3"
|
83
|
+
C1 = "en206.C1"
|
84
|
+
C2 = "en206.C2"
|
85
|
+
C3 = "en206.C3"
|
86
|
+
C4 = "en206.C4"
|
29
87
|
|
30
88
|
|
31
89
|
class CmuWeightClassification(StrEnum):
|
@@ -157,16 +215,34 @@ class CmuSpec(BaseOpenEpdSpec):
|
|
157
215
|
class Cementitious(BaseOpenEpdSchema):
|
158
216
|
"""List of cementitious materials, and proportion by mass."""
|
159
217
|
|
160
|
-
opc: RatioFloat | None = pyd.Field(default=None,
|
161
|
-
wht: RatioFloat | None = pyd.Field(default=None,
|
162
|
-
ggbs: RatioFloat | None = pyd.Field(default=None,
|
163
|
-
flyAsh: RatioFloat | None = pyd.Field(default=None,
|
164
|
-
siFume: RatioFloat | None = pyd.Field(default=None,
|
165
|
-
gg45: RatioFloat | None = pyd.Field(default=None,
|
166
|
-
natPoz: RatioFloat | None = pyd.Field(default=None,
|
167
|
-
mk: RatioFloat | None = pyd.Field(default=None,
|
168
|
-
CaCO3: RatioFloat | None = pyd.Field(default=None,
|
169
|
-
other: RatioFloat | None = pyd.Field(default=None,
|
218
|
+
opc: RatioFloat | None = pyd.Field(default=None, description="Ordinary Gray Portland Cement")
|
219
|
+
wht: RatioFloat | None = pyd.Field(default=None, description="White Portland Cement")
|
220
|
+
ggbs: RatioFloat | None = pyd.Field(default=None, description="Ground Granulated Blast Furnace Slag")
|
221
|
+
flyAsh: RatioFloat | None = pyd.Field(default=None, description="Fly Ash, including types F, CL, and CH")
|
222
|
+
siFume: RatioFloat | None = pyd.Field(default=None, description="Silica Fume")
|
223
|
+
gg45: RatioFloat | None = pyd.Field(default=None, description="Ground Glass, 45um or smaller")
|
224
|
+
natPoz: RatioFloat | None = pyd.Field(default=None, description="Natural pozzolan")
|
225
|
+
mk: RatioFloat | None = pyd.Field(default=None, description="Metakaolin")
|
226
|
+
CaCO3: RatioFloat | None = pyd.Field(default=None, description="Limestone")
|
227
|
+
other: RatioFloat | None = pyd.Field(default=None, description="Other SCMs")
|
228
|
+
|
229
|
+
|
230
|
+
class TypicalApplication(BaseOpenEpdSchema):
|
231
|
+
"""Concrete typical application."""
|
232
|
+
|
233
|
+
fnd: bool | None = pyd.Field(description="Foundation", default=None)
|
234
|
+
sog: bool | None = pyd.Field(description="Slab on Grade", default=None)
|
235
|
+
hrz: bool | None = pyd.Field(description="Elevated Horizontal", default=None)
|
236
|
+
vrt_wall: bool | None = pyd.Field(description="Vertical Wall", default=None)
|
237
|
+
vrt_column: bool | None = pyd.Field(description="Vertical Column", default=None)
|
238
|
+
vrt_other: bool | None = pyd.Field(description="Vertical Other", default=None)
|
239
|
+
sht: bool | None = pyd.Field(description="Shotcrete", default=None)
|
240
|
+
cdf: bool | None = pyd.Field(description="Flowable Fill (CDF,default=None)", default=None)
|
241
|
+
sac: bool | None = pyd.Field(description="Sidewalk and Curb", default=None)
|
242
|
+
pav: bool | None = pyd.Field(description="Paving", default=None)
|
243
|
+
oil: bool | None = pyd.Field(description="Oil Patch", default=None)
|
244
|
+
grt: bool | None = pyd.Field(description="Cement Grout", default=None)
|
245
|
+
ota: bool | None = pyd.Field(description="Other", default=None)
|
170
246
|
|
171
247
|
|
172
248
|
class ConcreteV1(BaseOpenEpdHierarchicalSpec):
|
@@ -174,6 +250,16 @@ class ConcreteV1(BaseOpenEpdHierarchicalSpec):
|
|
174
250
|
|
175
251
|
_EXT_VERSION = "1.0"
|
176
252
|
|
253
|
+
class Options(BaseOpenEpdSchema):
|
254
|
+
lightweight: bool | None = pyd.Field(description="Lightweight", default=None)
|
255
|
+
plc: bool | None = pyd.Field(description="Portland Limestone Cement", default=None)
|
256
|
+
scc: bool | None = pyd.Field(description="Self Compacting", default=None)
|
257
|
+
finishable: bool | None = pyd.Field(description="Finishable", default=None)
|
258
|
+
air: bool | None = pyd.Field(description="Air Entrainment", default=None)
|
259
|
+
co2: bool | None = pyd.Field(description="CO2 Curing", default=None)
|
260
|
+
white: bool | None = pyd.Field(description="White Cement", default=None)
|
261
|
+
fiber_reinforced: bool | None = pyd.Field(description="Fiber reinforced", default=None)
|
262
|
+
|
177
263
|
strength_28d: str | None = pyd.Field(
|
178
264
|
default=None, title="Concrete Strength 28d", description="Concrete strength after 28 days"
|
179
265
|
)
|
@@ -194,11 +280,30 @@ class ConcreteV1(BaseOpenEpdHierarchicalSpec):
|
|
194
280
|
strength_late_d: Literal[42, 56, 72, 96, 120] | None = pyd.Field(
|
195
281
|
default=None, title="Test Day for the Late Strength", description="Test Day for the Late Strength"
|
196
282
|
)
|
283
|
+
slump: str | None = pyd.Field(description="Minimum test slump", default=None)
|
284
|
+
w_c_ratio: RatioFloat | None = pyd.Field(description="Ratio of water to cement", default=None)
|
285
|
+
aci_exposure_classes: list[AciExposureClass] = pyd.Field(
|
286
|
+
description="List of ACI318-19 exposure classes this product meets", default=None
|
287
|
+
)
|
288
|
+
csa_exposure_classes: list[CsaExposureClass] = pyd.Field(
|
289
|
+
description="List of CSA A23.1 exposure classes this product meets", default=None
|
290
|
+
)
|
291
|
+
en_exposure_classes: list[EnExposureClass] = pyd.Field(
|
292
|
+
description="List of EN206 exposure classes this product meets", default=None
|
293
|
+
)
|
294
|
+
|
295
|
+
application: TypicalApplication | None = pyd.Field(description="Typical Application", default=None)
|
296
|
+
|
297
|
+
options: Options = pyd.Field(description="Concrete options", default=None)
|
298
|
+
|
197
299
|
cementitious: Cementitious | None = pyd.Field(
|
198
300
|
default=None,
|
199
301
|
title="Cementitious Materials",
|
200
302
|
description="List of cementitious materials, and proportion by mass",
|
201
303
|
)
|
304
|
+
_compressive_strength_unit_validator = pyd.validator("strength_28d", allow_reuse=True)(
|
305
|
+
validate_unit_factory(OpenEPDUnit.MPa)
|
306
|
+
)
|
202
307
|
|
203
308
|
@pyd.root_validator
|
204
309
|
def _late_validator(cls, values):
|
openepd/model/specs/steel.py
CHANGED
@@ -23,15 +23,16 @@ import pydantic as pyd
|
|
23
23
|
|
24
24
|
from openepd.model.base import BaseOpenEpdSchema
|
25
25
|
from openepd.model.specs.base import BaseOpenEpdHierarchicalSpec
|
26
|
+
from openepd.model.standard import Standard
|
26
27
|
from openepd.model.validation.numbers import RatioFloat
|
27
28
|
|
28
29
|
|
29
30
|
class SteelMakingRoute(BaseOpenEpdSchema):
|
30
31
|
"""Steel making route."""
|
31
32
|
|
32
|
-
bof: bool | None = pyd.Field(default=None,
|
33
|
-
eaf: bool | None = pyd.Field(default=None,
|
34
|
-
ohf: bool | None = pyd.Field(default=None,
|
33
|
+
bof: bool | None = pyd.Field(default=None, description="Basic oxygen furnace")
|
34
|
+
eaf: bool | None = pyd.Field(default=None, description="Electric arc furnace")
|
35
|
+
ohf: bool | None = pyd.Field(default=None, description="Open hearth furnace")
|
35
36
|
|
36
37
|
|
37
38
|
class SteelComposition(StrEnum):
|
@@ -44,20 +45,67 @@ class SteelComposition(StrEnum):
|
|
44
45
|
OTHER = "Other"
|
45
46
|
|
46
47
|
|
48
|
+
class FabricatedOptionsMixin(pyd.BaseModel):
|
49
|
+
"""Fabricated options mixin."""
|
50
|
+
|
51
|
+
fabricated: bool | None = pyd.Field(default=None, description="Fabricated")
|
52
|
+
|
53
|
+
|
54
|
+
class WireMeshSteelV1(BaseOpenEpdHierarchicalSpec):
|
55
|
+
"""Spec for wire mesh steel."""
|
56
|
+
|
57
|
+
class Options(BaseOpenEpdSchema, FabricatedOptionsMixin):
|
58
|
+
"""Wire Mesh Options."""
|
59
|
+
|
60
|
+
pass
|
61
|
+
|
62
|
+
options: Options = pyd.Field(description="Rebar Steel options", default_factory=Options)
|
63
|
+
|
64
|
+
|
47
65
|
class RebarSteelV1(BaseOpenEpdHierarchicalSpec):
|
48
66
|
"""Rebar steel spec."""
|
49
67
|
|
50
68
|
_EXT_VERSION = "1.0"
|
51
69
|
|
52
|
-
class Options(BaseOpenEpdSchema):
|
53
|
-
"""Rebar
|
70
|
+
class Options(BaseOpenEpdSchema, FabricatedOptionsMixin):
|
71
|
+
"""Rebar Steel Options."""
|
54
72
|
|
55
|
-
epoxy: bool | None = pyd.Field(default=None,
|
56
|
-
fabricated: bool | None = pyd.Field(default=None, title="Fabricated", description="Fabricated")
|
73
|
+
epoxy: bool | None = pyd.Field(default=None, description="Epoxy Coated")
|
57
74
|
|
58
|
-
options: Options = pyd.Field(
|
59
|
-
|
60
|
-
|
75
|
+
options: Options = pyd.Field(description="Rebar Steel options", default_factory=Options)
|
76
|
+
|
77
|
+
|
78
|
+
class PlateSteelV1(BaseOpenEpdHierarchicalSpec):
|
79
|
+
"""Plate Steel Spec."""
|
80
|
+
|
81
|
+
class Options(BaseOpenEpdSchema, FabricatedOptionsMixin):
|
82
|
+
"""Plate Steel Options."""
|
83
|
+
|
84
|
+
pass
|
85
|
+
|
86
|
+
options: Options = pyd.Field(description="Plate Steel options", default_factory=Options)
|
87
|
+
|
88
|
+
|
89
|
+
class HollowV1(BaseOpenEpdHierarchicalSpec):
|
90
|
+
"""Hollow Sections Spec."""
|
91
|
+
|
92
|
+
class Options(FabricatedOptionsMixin, BaseOpenEpdSchema):
|
93
|
+
"""Hollow Sections Options."""
|
94
|
+
|
95
|
+
pass
|
96
|
+
|
97
|
+
options: Options = pyd.Field(description="Hollow Steel options", default_factory=Options)
|
98
|
+
|
99
|
+
|
100
|
+
class HotRolledV1(BaseOpenEpdHierarchicalSpec):
|
101
|
+
"""Hot Rolled spec."""
|
102
|
+
|
103
|
+
class Options(FabricatedOptionsMixin, BaseOpenEpdSchema):
|
104
|
+
"""Hot Rolled options."""
|
105
|
+
|
106
|
+
pass
|
107
|
+
|
108
|
+
options: Options = pyd.Field(description="Hollow Steel options", default_factory=Options)
|
61
109
|
|
62
110
|
|
63
111
|
class SteelV1(BaseOpenEpdHierarchicalSpec):
|
@@ -68,24 +116,31 @@ class SteelV1(BaseOpenEpdHierarchicalSpec):
|
|
68
116
|
class Options(BaseOpenEpdSchema):
|
69
117
|
"""Steel spec options."""
|
70
118
|
|
71
|
-
galvanized: bool | None = pyd.Field(default=None,
|
72
|
-
cold_finished: bool | None = pyd.Field(default=None,
|
119
|
+
galvanized: bool | None = pyd.Field(default=None, description="Galvanized")
|
120
|
+
cold_finished: bool | None = pyd.Field(default=None, description="Cold Finished")
|
73
121
|
|
122
|
+
form_factor: str | None = pyd.Field(description="Product's form factor", example="Steel >> RebarSteel")
|
123
|
+
steel_composition: SteelComposition | None = pyd.Field(default=None, description="Basic chemical composition")
|
74
124
|
recycled_content: RatioFloat | None = pyd.Field(
|
75
125
|
default=None,
|
76
|
-
title="Scrap Recycled Content",
|
77
126
|
description="Scrap steel inputs from other processes. Includes "
|
78
127
|
"Post-Consumer content, if any. This percentage may be "
|
79
128
|
"used to evaluate the EPD w.r.t. targets or limits that are"
|
80
129
|
" different for primary and recycled content.",
|
81
130
|
)
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
making_route: SteelMakingRoute | None = pyd.Field(
|
86
|
-
default=None, title="Steel Making Route", description="Steel making route"
|
131
|
+
ASTM: list[Standard] = pyd.Field(description="ASTM standard to which this product complies", default_factory=list)
|
132
|
+
SAE: list[Standard] = pyd.Field(
|
133
|
+
description="AISA/SAE standard to which this product complies", default_factory=list
|
87
134
|
)
|
135
|
+
EN: list[Standard] = pyd.Field(description="EN 10027 number(s)", default_factory=list)
|
136
|
+
|
88
137
|
options: Options | None = pyd.Field(description="Steel options", default_factory=Options)
|
138
|
+
making_route: SteelMakingRoute | None = pyd.Field(default=None, description="Steel making route")
|
89
139
|
|
90
140
|
# Nested specs
|
141
|
+
|
142
|
+
WireMeshSteel: WireMeshSteelV1 | None = None
|
91
143
|
RebarSteel: RebarSteelV1 | None = None
|
144
|
+
PlateSteel: PlateSteelV1 | None = None
|
145
|
+
Hollow: HollowV1 | None = None
|
146
|
+
HotRolled: HotRolledV1 | None = None
|
@@ -17,9 +17,50 @@
|
|
17
17
|
# Charles Pankow Foundation, Microsoft Sustainability Fund, Interface, MKA Foundation, and others.
|
18
18
|
# Find out more at www.BuildingTransparency.org
|
19
19
|
#
|
20
|
-
from
|
20
|
+
from abc import ABC, abstractmethod
|
21
|
+
from typing import TYPE_CHECKING, Annotated
|
21
22
|
|
22
23
|
import pydantic as pyd
|
23
24
|
|
25
|
+
from openepd.model.common import OpenEPDUnit
|
26
|
+
|
27
|
+
if TYPE_CHECKING:
|
28
|
+
from openepd.model.specs.base import BaseOpenEpdHierarchicalSpec
|
29
|
+
|
24
30
|
RatioFloat = Annotated[float, pyd.Field(ge=0, le=1, example=0.5)]
|
25
31
|
"""Float field which represents a percentage ratio between 0 and 1."""
|
32
|
+
|
33
|
+
|
34
|
+
class QuantityValidator(ABC):
|
35
|
+
"""
|
36
|
+
Interface for quantity validator.
|
37
|
+
|
38
|
+
The openEPD models are mapped using the simple types. Caller code should provide their own implementation of this
|
39
|
+
and set it with `set_unit_validator` function.
|
40
|
+
"""
|
41
|
+
|
42
|
+
@abstractmethod
|
43
|
+
def validate(self, value: str, dimensionality: str) -> None:
|
44
|
+
"""
|
45
|
+
Validate the given string value against the given dimensionality.
|
46
|
+
|
47
|
+
Args:
|
48
|
+
value: The value to validate, like "102.4 kg"
|
49
|
+
dimensionality: The dimensionality to validate against, like "kg"
|
50
|
+
Returns:
|
51
|
+
None if the value is valid, raises an error otherwise.
|
52
|
+
Raises:
|
53
|
+
ValueError: If the value is not valid.
|
54
|
+
"""
|
55
|
+
pass
|
56
|
+
|
57
|
+
|
58
|
+
def validate_unit_factory(dimensionality: OpenEPDUnit | str):
|
59
|
+
"""Create validator for unit field."""
|
60
|
+
|
61
|
+
def validator(cls: "BaseOpenEpdHierarchicalSpec", value: str) -> str:
|
62
|
+
if hasattr(cls, "_QUNATITY_VALIDATOR") and cls._QUANTITY_VALIDATOR is not None:
|
63
|
+
cls._QUANTITY_VALIDATOR.validate(value, dimensionality)
|
64
|
+
return value
|
65
|
+
|
66
|
+
return validator
|
openepd/model/versioning.py
CHANGED
@@ -37,7 +37,7 @@ class WithExtVersionMixin(ABC, BaseModel):
|
|
37
37
|
if hasattr(cls, "_EXT_VERSION"):
|
38
38
|
cls.__fields__["ext_version"].default = cls._EXT_VERSION
|
39
39
|
|
40
|
-
ext_version: str = pyd.Field(
|
40
|
+
ext_version: str = pyd.Field(description="Extension version", example="3.22", default=None)
|
41
41
|
|
42
42
|
|
43
43
|
class Version(NamedTuple):
|
@@ -1,5 +1,5 @@
|
|
1
1
|
openepd/__init__.py,sha256=rqQJWF5jpYAgRbbAycUfWMGsr5kGtfjmwzsTeqbElJw,837
|
2
|
-
openepd/__version__.py,sha256=
|
2
|
+
openepd/__version__.py,sha256=dydPFQzMC85AwH6lLCuy1P3UT2PsGjNSUOyDiyVRnhA,855
|
3
3
|
openepd/api/__init__.py,sha256=rqQJWF5jpYAgRbbAycUfWMGsr5kGtfjmwzsTeqbElJw,837
|
4
4
|
openepd/api/base_sync_client.py,sha256=JcNpWsGoIK_1Eg27CAQd7nIjbcfD56jQ1nS6B48Q0cI,21142
|
5
5
|
openepd/api/category/__init__.py,sha256=rqQJWF5jpYAgRbbAycUfWMGsr5kGtfjmwzsTeqbElJw,837
|
@@ -27,26 +27,26 @@ openepd/bundle/model.py,sha256=NWbpxorLzQ_OIFozV34wz76OWM_ORGTBkS0288chv0k,2647
|
|
27
27
|
openepd/bundle/reader.py,sha256=z4v_UWyaosktN3DdmnRx8GpLq4DkejjoUsckFfCgUac,6904
|
28
28
|
openepd/bundle/writer.py,sha256=AfvzzdAr9ybIbtiZfuX2mAGfddamTxXxUTxtHHrs2Gc,8353
|
29
29
|
openepd/model/__init__.py,sha256=rqQJWF5jpYAgRbbAycUfWMGsr5kGtfjmwzsTeqbElJw,837
|
30
|
-
openepd/model/base.py,sha256=
|
30
|
+
openepd/model/base.py,sha256=wLZUQSc6nOvoiESgxjEHTdGEV417SUolsXMNm2mA-po,8883
|
31
31
|
openepd/model/category.py,sha256=_6yFyldaDrkZk2_9ZAO2oRTz7ocvkg4T-D_aw5ncgvo,1840
|
32
|
-
openepd/model/common.py,sha256=
|
32
|
+
openepd/model/common.py,sha256=j-M_2T_Kl_Ea8l50pJT7LNatBW1fKrRcw4XNpeNvLU0,5635
|
33
33
|
openepd/model/epd.py,sha256=BxcKJd_LuApB2v_LGN0-hYLNaUNoqMu9ZH_V1Ffil-g,14269
|
34
34
|
openepd/model/factory.py,sha256=qQZNb4yFN1EQWHHVS-fHnJ2gB-vsKz9hVmXXJE7YaRU,1918
|
35
35
|
openepd/model/lcia.py,sha256=2GOdcY6ASJu653SFwTakT9boYF-3CYCHLupyxTPUQ30,16711
|
36
|
-
openepd/model/org.py,sha256=
|
36
|
+
openepd/model/org.py,sha256=quoYMrA6WXqs7ZRjdO1Bm47No4jGLqxz0QmnnqKgw7c,3741
|
37
37
|
openepd/model/pcr.py,sha256=t861yFntmy3ewrnGLP47cv3afV-aCCVRlrbpGeRh-7I,4604
|
38
38
|
openepd/model/specs/README.md,sha256=W5LSMpZuW5x36cKS4HRfeFsClsRf8J9yHMMICghdc0s,862
|
39
|
-
openepd/model/specs/__init__.py,sha256=
|
40
|
-
openepd/model/specs/base.py,sha256=
|
41
|
-
openepd/model/specs/concrete.py,sha256=
|
42
|
-
openepd/model/specs/steel.py,sha256=
|
39
|
+
openepd/model/specs/__init__.py,sha256=3DXyeFzH1bw4rf1KCFXPXl3X-ErMu0NpjvqqhorpnLc,1514
|
40
|
+
openepd/model/specs/base.py,sha256=hOE_hCuiY7xhR0yW_Pbxe_LQAnMWq8sq3rTqQqbeVOc,2564
|
41
|
+
openepd/model/specs/concrete.py,sha256=jdhuThy8QFaEs9HdJ53O7ua4Fw-ES2_YzoLDchyU8s0,13165
|
42
|
+
openepd/model/specs/steel.py,sha256=FoMYE6y_uvWg9Edkb7WV0f_hQNmviwtI1SM-UmdRh8E,4985
|
43
43
|
openepd/model/standard.py,sha256=YjxpC9nyz6LM8m3z1s5S0exJ1qvA3n59-FBoScg-FL0,1519
|
44
44
|
openepd/model/validation/__init__.py,sha256=rqQJWF5jpYAgRbbAycUfWMGsr5kGtfjmwzsTeqbElJw,837
|
45
45
|
openepd/model/validation/common.py,sha256=nNwLtj5Uu7TsFmJXuD8aRcx1hB_nf-LQBfEvHOZM1Ao,2420
|
46
|
-
openepd/model/validation/numbers.py,sha256=
|
47
|
-
openepd/model/versioning.py,sha256=
|
46
|
+
openepd/model/validation/numbers.py,sha256=AItjDAz9LODxLOH7shqfMMufT9ep_8ys2bVelIbME7g,2349
|
47
|
+
openepd/model/versioning.py,sha256=Z31TgCW0POsmXLrH-Rmpy-MrIw_bU_9wwh8T4Mq1yTM,4628
|
48
48
|
openepd/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
49
|
-
openepd-1.
|
50
|
-
openepd-1.
|
51
|
-
openepd-1.
|
52
|
-
openepd-1.
|
49
|
+
openepd-1.6.0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
50
|
+
openepd-1.6.0.dist-info/METADATA,sha256=_zIXgd030Dco2ykLRwhes7xe6ygQhgmtjiyEXqVvUo0,7762
|
51
|
+
openepd-1.6.0.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
|
52
|
+
openepd-1.6.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|