openepd 2.0.0__py3-none-any.whl → 3.1.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 (95) hide show
  1. openepd/__init__.py +1 -1
  2. openepd/__version__.py +2 -2
  3. openepd/api/__init__.py +19 -0
  4. openepd/api/base_sync_client.py +550 -0
  5. openepd/api/category/__init__.py +19 -0
  6. openepd/api/category/dto.py +25 -0
  7. openepd/api/category/sync_api.py +44 -0
  8. openepd/api/common.py +239 -0
  9. openepd/api/dto/__init__.py +19 -0
  10. openepd/api/dto/base.py +41 -0
  11. openepd/api/dto/common.py +115 -0
  12. openepd/api/dto/meta.py +69 -0
  13. openepd/api/dto/mf.py +59 -0
  14. openepd/api/dto/params.py +19 -0
  15. openepd/api/epd/__init__.py +19 -0
  16. openepd/api/epd/dto.py +121 -0
  17. openepd/api/epd/sync_api.py +105 -0
  18. openepd/api/errors.py +86 -0
  19. openepd/api/pcr/__init__.py +19 -0
  20. openepd/api/pcr/dto.py +41 -0
  21. openepd/api/pcr/sync_api.py +49 -0
  22. openepd/api/sync_client.py +67 -0
  23. openepd/api/test/__init__.py +19 -0
  24. openepd/bundle/__init__.py +1 -1
  25. openepd/bundle/base.py +1 -1
  26. openepd/bundle/model.py +5 -6
  27. openepd/bundle/reader.py +5 -5
  28. openepd/bundle/writer.py +5 -4
  29. openepd/compat/__init__.py +19 -0
  30. openepd/compat/pydantic.py +29 -0
  31. openepd/model/__init__.py +1 -1
  32. openepd/model/base.py +114 -15
  33. openepd/model/category.py +39 -0
  34. openepd/model/common.py +33 -25
  35. openepd/model/epd.py +97 -78
  36. openepd/model/factory.py +48 -0
  37. openepd/model/lcia.py +24 -13
  38. openepd/model/org.py +28 -18
  39. openepd/model/pcr.py +42 -14
  40. openepd/model/specs/README.md +19 -0
  41. openepd/model/specs/__init__.py +72 -4
  42. openepd/model/specs/aluminium.py +67 -0
  43. openepd/model/specs/asphalt.py +87 -0
  44. openepd/model/specs/base.py +60 -0
  45. openepd/model/specs/concrete.py +288 -24
  46. openepd/model/specs/generated/accessories.py +63 -0
  47. openepd/model/specs/generated/aggregates.py +71 -0
  48. openepd/model/specs/generated/aluminium.py +66 -0
  49. openepd/model/specs/generated/asphalt.py +86 -0
  50. openepd/model/specs/generated/bulk_materials.py +26 -0
  51. openepd/model/specs/generated/cast_decks_and_underlayment.py +26 -0
  52. openepd/model/specs/generated/cladding.py +214 -0
  53. openepd/model/specs/generated/cmu.py +46 -0
  54. openepd/model/specs/generated/common.py +27 -0
  55. openepd/model/specs/generated/concrete.py +151 -0
  56. openepd/model/specs/generated/conveying_equipment.py +57 -0
  57. openepd/model/specs/generated/electrical.py +297 -0
  58. openepd/model/specs/generated/electrical_transmission_and_distribution_equipment.py +63 -0
  59. openepd/model/specs/generated/electricity.py +26 -0
  60. openepd/model/specs/generated/enums.py +2420 -0
  61. openepd/model/specs/generated/finishes.py +519 -0
  62. openepd/model/specs/generated/fire_and_smoke_protection.py +79 -0
  63. openepd/model/specs/generated/furnishings.py +95 -0
  64. openepd/model/specs/generated/grouting.py +26 -0
  65. openepd/model/specs/generated/manufacturing_inputs.py +131 -0
  66. openepd/model/specs/generated/masonry.py +77 -0
  67. openepd/model/specs/generated/material_handling.py +35 -0
  68. openepd/model/specs/generated/mechanical.py +271 -0
  69. openepd/model/specs/generated/mechanical_insulation.py +41 -0
  70. openepd/model/specs/generated/network_infrastructure.py +181 -0
  71. openepd/model/specs/generated/openings.py +423 -0
  72. openepd/model/specs/generated/other_electrical_equipment.py +26 -0
  73. openepd/model/specs/generated/other_materials.py +123 -0
  74. openepd/model/specs/generated/plumbing.py +153 -0
  75. openepd/model/specs/generated/precast_concrete.py +68 -0
  76. openepd/model/specs/generated/sheathing.py +74 -0
  77. openepd/model/specs/generated/steel.py +224 -0
  78. openepd/model/specs/generated/thermal_moisture_protection.py +233 -0
  79. openepd/model/specs/generated/utility_piping.py +65 -0
  80. openepd/model/specs/generated/wood.py +167 -0
  81. openepd/model/specs/generated/wood_joists.py +38 -0
  82. openepd/model/specs/glass.py +360 -0
  83. openepd/model/specs/steel.py +184 -0
  84. openepd/model/specs/wood.py +130 -0
  85. openepd/model/standard.py +2 -3
  86. openepd/model/validation/__init__.py +19 -0
  87. openepd/model/validation/common.py +59 -0
  88. openepd/model/validation/numbers.py +26 -0
  89. openepd/model/validation/quantity.py +132 -0
  90. openepd/model/versioning.py +129 -0
  91. {openepd-2.0.0.dist-info → openepd-3.1.0.dist-info}/METADATA +36 -5
  92. openepd-3.1.0.dist-info/RECORD +95 -0
  93. openepd-2.0.0.dist-info/RECORD +0 -22
  94. {openepd-2.0.0.dist-info → openepd-3.1.0.dist-info}/LICENSE +0 -0
  95. {openepd-2.0.0.dist-info → openepd-3.1.0.dist-info}/WHEEL +0 -0
openepd/model/pcr.py CHANGED
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright 2023 by C Change Labs Inc. www.c-change-labs.com
2
+ # Copyright 2024 by C Change Labs Inc. www.c-change-labs.com
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
5
5
  # you may not use this file except in compliance with the License.
@@ -18,65 +18,93 @@
18
18
  # Find out more at www.BuildingTransparency.org
19
19
  #
20
20
  import datetime
21
- from typing import Optional
22
-
23
- import pydantic as pyd
21
+ from enum import StrEnum
22
+ from typing import Annotated, Optional
24
23
 
24
+ from openepd.compat.pydantic import pyd
25
25
  from openepd.model.base import BaseOpenEpdSchema
26
26
  from openepd.model.common import WithAltIdsMixin, WithAttachmentsMixin
27
27
  from openepd.model.org import Org
28
28
 
29
29
 
30
+ class PcrStatus(StrEnum):
31
+ """Status of a PCR."""
32
+
33
+ InDevelopment = "InDevelopment"
34
+ Published = "Published"
35
+ NonPublic = "NonPublic"
36
+ Expired = "Expired"
37
+ Sunset = "Sunset"
38
+
39
+
30
40
  class Pcr(WithAttachmentsMixin, WithAltIdsMixin, BaseOpenEpdSchema):
31
41
  """Represent a PCR (Product Category Rules)."""
32
42
 
33
43
  id: str | None = pyd.Field(
34
44
  description="The unique ID for this PCR. To ensure global uniqueness, should be registered "
35
45
  "at open-xpd-uuid.cqd.io/register or a coordinating registry.",
46
+ example="ec3xpgq2",
36
47
  default=None,
37
- json_schema_extra={"example": "ec3xpgq2"},
38
48
  )
39
49
  issuer: Org | None = pyd.Field(description="Organization issuing this PCR", default=None)
40
50
  issuer_doc_id: str | None = pyd.Field(
41
51
  max_length=40,
42
52
  default=None,
43
53
  description="Document ID or code created by issuer",
44
- json_schema_extra=dict(example="c-PCR-003"),
54
+ example="c-PCR-003",
45
55
  )
46
56
  name: str | None = pyd.Field(
47
57
  max_length=200,
48
58
  default=None,
49
59
  description="Full document name as listed in source document",
50
- json_schema_extra=dict(example="c-PCR-003 Concrete and concrete elements (EN 16757)"),
60
+ example="c-PCR-003 Concrete and concrete elements (EN 16757)",
51
61
  )
52
62
  short_name: str | None = pyd.Field(
53
- max_length=40,
54
63
  default=None,
55
64
  description="A shortened name without boilerplate text.",
56
- json_schema_extra=dict(example="Concrete and Concrete Elements"),
65
+ example="Concrete and Concrete Elements",
57
66
  )
58
67
  version: str | None = pyd.Field(
59
68
  description="Document version, as expressed in document.",
69
+ example="1.0.2",
60
70
  default=None,
61
- json_schema_extra=dict(example="1.0.2"),
62
71
  )
63
- date_of_issue: datetime.date | None = pyd.Field(
72
+ date_of_issue: datetime.datetime | None = pyd.Field(
73
+ example=datetime.date(day=11, month=2, year=2022),
64
74
  default=None,
65
75
  description="First day on which the document is valid",
66
- json_schema_extra=dict(example=datetime.date(day=11, month=2, year=2022)),
67
76
  )
68
- valid_until: datetime.date | None = pyd.Field(
77
+ valid_until: datetime.datetime | None = pyd.Field(
78
+ example=datetime.date(day=11, month=2, year=2024),
69
79
  default=None,
70
80
  description="Last day on which the document is valid",
71
- json_schema_extra=dict(example=datetime.date(day=11, month=2, year=2024)),
72
81
  )
82
+ doc: str | None = pyd.Field(default=None, description="URL to original document, preferably directly to a PDF.")
73
83
  parent: Optional["Pcr"] = pyd.Field(
74
84
  description="The parent PCR, base PCR, `Part A` PCR",
75
85
  default=None,
76
86
  )
87
+ status: PcrStatus | None = pyd.Field(
88
+ default=None,
89
+ description="The current release status of this PCR. "
90
+ "A PCR with valid_until in the past must have status Expired or Sunset; a PCR with valid_until "
91
+ "more than 5 years in the past must have status Sunset. Compliant systems should automatically "
92
+ "update these fields within 24 hours.",
93
+ )
77
94
  product_classes: dict[str, str | list[str]] = pyd.Field(
78
95
  description="List of classifications, including Masterformat and UNSPC", default_factory=dict
79
96
  )
97
+ applicable_in: list[Annotated[str, pyd.Field(min_length=2, max_length=2)]] | None = pyd.Field(
98
+ max_items=100,
99
+ default=None,
100
+ description="Jurisdiction(s) in which EPD is applicable. An empty array, or absent properties, "
101
+ "implies global applicability. Accepts "
102
+ "[2-letter country codes](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2), "
103
+ "[M49 region codes](https://unstats.un.org/unsd/methodology/m49/), "
104
+ 'or the alias "EU27" for the 27 members of the Euro bloc, or the alias "NAFTA" '
105
+ "for the members of North American Free Trade Agreement",
106
+ example=["US", "CA", "MX", "EU27", "NAFTA"],
107
+ )
80
108
 
81
109
  @classmethod
82
110
  def get_asset_type(cls) -> str | None:
@@ -0,0 +1,19 @@
1
+ # Material Extensions
2
+
3
+ This package contains openEPD material extensions. They are used to represent the material properties of the openEPD
4
+ materials, and are a more dynamic, frequently changing part of the standard.
5
+
6
+ ## Versioning
7
+
8
+ Extensions are versioned separately from the openEPD standard or openEPD API.
9
+
10
+ Each material extension is named after the corresponding EC3 product class, and is located in the relevant place
11
+ in the specs tree. For example, `RebarSteel` is nested under `Steel`.
12
+
13
+ Extensions are versioned as Major.Minor, for example "2.4".
14
+
15
+ Rules:
16
+ 1. Minor versions for the same major version should be backwards compatible.
17
+ 2. Major versions are not compatible between themselves.
18
+ 3. Pydantic models representing versions are named in a pattern SpecNameV1, where 1 is major version and SpecName is
19
+ the name of the material extension.
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright 2023 by C Change Labs Inc. www.c-change-labs.com
2
+ # Copyright 2024 by C Change Labs Inc. www.c-change-labs.com
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
5
5
  # you may not use this file except in compliance with the License.
@@ -17,13 +17,81 @@
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
- import pydantic as pyd
21
20
 
22
21
  from openepd.model.base import BaseOpenEpdSchema
23
- from openepd.model.specs.concrete import CmuSpec
22
+ from openepd.model.specs.generated.accessories import AccessoriesV1
23
+ from openepd.model.specs.generated.aggregates import AggregatesV1
24
+ from openepd.model.specs.generated.aluminium import AluminiumV1
25
+ from openepd.model.specs.generated.asphalt import AsphaltV1
26
+ from openepd.model.specs.generated.bulk_materials import BulkMaterialsV1
27
+ from openepd.model.specs.generated.cast_decks_and_underlayment import CastDecksAndUnderlaymentV1
28
+ from openepd.model.specs.generated.cladding import CladdingV1
29
+ from openepd.model.specs.generated.cmu import CMUV1
30
+ from openepd.model.specs.generated.concrete import ConcreteV1
31
+ from openepd.model.specs.generated.conveying_equipment import ConveyingEquipmentV1
32
+ from openepd.model.specs.generated.electrical import ElectricalV1
33
+ from openepd.model.specs.generated.electrical_transmission_and_distribution_equipment import (
34
+ ElectricalTransmissionAndDistributionEquipmentV1,
35
+ )
36
+ from openepd.model.specs.generated.electricity import ElectricityV1
37
+ from openepd.model.specs.generated.finishes import FinishesV1
38
+ from openepd.model.specs.generated.fire_and_smoke_protection import FireAndSmokeProtectionV1
39
+ from openepd.model.specs.generated.furnishings import FurnishingsV1
40
+ from openepd.model.specs.generated.grouting import GroutingV1
41
+ from openepd.model.specs.generated.manufacturing_inputs import ManufacturingInputsV1
42
+ from openepd.model.specs.generated.masonry import MasonryV1
43
+ from openepd.model.specs.generated.material_handling import MaterialHandlingV1
44
+ from openepd.model.specs.generated.mechanical import MechanicalV1
45
+ from openepd.model.specs.generated.mechanical_insulation import MechanicalInsulationV1
46
+ from openepd.model.specs.generated.network_infrastructure import NetworkInfrastructureV1
47
+ from openepd.model.specs.generated.openings import OpeningsV1
48
+ from openepd.model.specs.generated.other_electrical_equipment import OtherElectricalEquipmentV1
49
+ from openepd.model.specs.generated.other_materials import OtherMaterialsV1
50
+ from openepd.model.specs.generated.plumbing import PlumbingV1
51
+ from openepd.model.specs.generated.precast_concrete import PrecastConcreteV1
52
+ from openepd.model.specs.generated.sheathing import SheathingV1
53
+ from openepd.model.specs.generated.steel import SteelV1
54
+ from openepd.model.specs.generated.thermal_moisture_protection import ThermalMoistureProtectionV1
55
+ from openepd.model.specs.generated.utility_piping import UtilityPipingV1
56
+ from openepd.model.specs.generated.wood import WoodV1
57
+ from openepd.model.specs.generated.wood_joists import WoodJoistsV1
24
58
 
25
59
 
26
60
  class Specs(BaseOpenEpdSchema):
27
61
  """Material specific specs."""
28
62
 
29
- cmu: CmuSpec | None = pyd.Field(default=None, description="Concrete Masonry Unit-specific (CMU) specs")
63
+ # Nested specs:
64
+ CMU: CMUV1 | None = None
65
+ Masonry: MasonryV1 | None = None
66
+ Steel: SteelV1 | None = None
67
+ NetworkInfrastructure: NetworkInfrastructureV1 | None = None
68
+ Finishes: FinishesV1 | None = None
69
+ ManufacturingInputs: ManufacturingInputsV1 | None = None
70
+ Accessories: AccessoriesV1 | None = None
71
+ ElectricalTransmissionAndDistributionEquipment: ElectricalTransmissionAndDistributionEquipmentV1 | None = None
72
+ Aggregates: AggregatesV1 | None = None
73
+ ThermalMoistureProtection: ThermalMoistureProtectionV1 | None = None
74
+ Mechanical: MechanicalV1 | None = None
75
+ Aluminium: AluminiumV1 | None = None
76
+ Cladding: CladdingV1 | None = None
77
+ FireAndSmokeProtection: FireAndSmokeProtectionV1 | None = None
78
+ PrecastConcrete: PrecastConcreteV1 | None = None
79
+ Asphalt: AsphaltV1 | None = None
80
+ OtherMaterials: OtherMaterialsV1 | None = None
81
+ Plumbing: PlumbingV1 | None = None
82
+ Electrical: ElectricalV1 | None = None
83
+ UtilityPiping: UtilityPipingV1 | None = None
84
+ BulkMaterials: BulkMaterialsV1 | None = None
85
+ CastDecksAndUnderlayment: CastDecksAndUnderlaymentV1 | None = None
86
+ Concrete: ConcreteV1 | None = None
87
+ Sheathing: SheathingV1 | None = None
88
+ Furnishings: FurnishingsV1 | None = None
89
+ Wood: WoodV1 | None = None
90
+ ConveyingEquipment: ConveyingEquipmentV1 | None = None
91
+ MaterialHandling: MaterialHandlingV1 | None = None
92
+ Openings: OpeningsV1 | None = None
93
+ Electricity: ElectricityV1 | None = None
94
+ Grouting: GroutingV1 | None = None
95
+ MechanicalInsulation: MechanicalInsulationV1 | None = None
96
+ OtherElectricalEquipment: OtherElectricalEquipmentV1 | None = None
97
+ WoodJoists: WoodJoistsV1 | None = None
@@ -0,0 +1,67 @@
1
+ #
2
+ # Copyright 2024 by C Change Labs Inc. www.c-change-labs.com
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+ # This software was developed with support from the Skanska USA,
17
+ # Charles Pankow Foundation, Microsoft Sustainability Fund, Interface, MKA Foundation, and others.
18
+ # Find out more at www.BuildingTransparency.org
19
+ #
20
+ from enum import StrEnum
21
+
22
+ from openepd.compat.pydantic import pyd
23
+ from openepd.model.specs.base import BaseOpenEpdHierarchicalSpec
24
+ from openepd.model.validation.numbers import RatioFloat
25
+
26
+
27
+ class AluminiumAlloy(StrEnum):
28
+ """Aluminium alloy enum."""
29
+
30
+ ALLOY_1xxx = ("1xxx",)
31
+ ALLOY_2xxx = ("2xxx",)
32
+ ALLOY_3xxx = ("3xxx",)
33
+ ALLOY_4xxx = ("4xxx",)
34
+ ALLOY_5xxx = ("5xxx",)
35
+ ALLOY_6xxx = ("6xxx",)
36
+ ALLOY_7xxx = ("7xxx",)
37
+ ALLOY_8xxx = ("8xxx",)
38
+ ALLOY_1xx_x = ("1xx.x",)
39
+ ALLOY_2xx_x = ("2xx.x",)
40
+ ALLOY_3xx_x = ("3xx.x",)
41
+ ALLOY_4xx_x = ("4xx.x",)
42
+ ALLOY_5xx_x = ("5xx.x",)
43
+ ALLOY_7xx_x = ("7xx.x",)
44
+ ALLOY_8xx_x = ("8xx.x",)
45
+ ALLOY_9xx_x = ("9xx.x",)
46
+
47
+
48
+ class AluminiumExtrusionsV1(BaseOpenEpdHierarchicalSpec):
49
+ """Aluminium extrusions V1 spec."""
50
+
51
+ _EXT_VERSION = "1.0"
52
+
53
+ """Aluminium extrusions V1 spec."""
54
+ thermally_improved: bool | None = pyd.Field(default=None, description="Thermally improved")
55
+
56
+
57
+ class AluminiumV1(BaseOpenEpdHierarchicalSpec):
58
+ """Aluminium V1 spec."""
59
+
60
+ _EXT_VERSION = "1.0"
61
+ recycled_content: RatioFloat | None = pyd.Field(default=None, description="Recycled content")
62
+
63
+ alloy: AluminiumAlloy | None = pyd.Field(default=None, description="AluminiumAlloy")
64
+ anodized: bool | None = None
65
+ painted: bool | None = None
66
+
67
+ AluminiumExtrusions: AluminiumExtrusionsV1 | None = pyd.Field(title="AluminiumExtrusionsV1", default=None)
@@ -0,0 +1,87 @@
1
+ #
2
+ # Copyright 2024 by C Change Labs Inc. www.c-change-labs.com
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+ # This software was developed with support from the Skanska USA,
17
+ # Charles Pankow Foundation, Microsoft Sustainability Fund, Interface, MKA Foundation, and others.
18
+ # Find out more at www.BuildingTransparency.org
19
+ #
20
+ from enum import StrEnum
21
+
22
+ from openepd.compat.pydantic import pyd
23
+ from openepd.model.common import OpenEPDUnit
24
+ from openepd.model.specs.base import BaseOpenEpdHierarchicalSpec
25
+ from openepd.model.validation.numbers import RatioFloat
26
+ from openepd.model.validation.quantity import LengthMmStr, TemperatureCStr, validate_unit_factory
27
+
28
+
29
+ class AsphaltMixType(StrEnum):
30
+ """Asphalt mix type enum."""
31
+
32
+ HMA = "HMA"
33
+ WMA = "WMA"
34
+
35
+
36
+ class AsphaltGradation(StrEnum):
37
+ """Asphalt gradation enum."""
38
+
39
+ Dense_graded = "Dense-graded"
40
+ Open_graded = "Open-graded"
41
+ Gap_graded = "Gap-graded"
42
+
43
+
44
+ class AsphaltV1(BaseOpenEpdHierarchicalSpec):
45
+ """Asphalt spec."""
46
+
47
+ _EXT_VERSION = "1.0"
48
+
49
+ asphalt_aggregate_size_max: LengthMmStr | None = pyd.Field(
50
+ default=None, example="5mm", description="Max aggregate size"
51
+ )
52
+
53
+ asphalt_rap: RatioFloat | None = pyd.Field(
54
+ default=None, description="Percent of mixture that has been replaced by recycled " "asphalt pavement (RAP)."
55
+ )
56
+ asphalt_ras: RatioFloat | None = pyd.Field(
57
+ default=None, description="Percent of mixture that has been replaced by recycled " "asphalt shingles (RAS)."
58
+ )
59
+ asphalt_ground_tire_rubber: RatioFloat | None = pyd.Field(
60
+ default=None, description="Percent of mixture that has been replaced " "by ground tire rubber (GTR)."
61
+ )
62
+
63
+ asphalt_max_temperature: TemperatureCStr | None = pyd.Field(
64
+ default=None,
65
+ description="The upper threshold temperature to which an asphalt "
66
+ "binder can be heated preventing the asphalt mixture "
67
+ "from rutting",
68
+ )
69
+ asphalt_min_temperature: TemperatureCStr | None = pyd.Field(
70
+ default=None,
71
+ description="The lower threshold temperature for an asphalt "
72
+ "binder to prevent thermal cracking of the asphalt"
73
+ " mixture.",
74
+ )
75
+
76
+ asphalt_mix_type: AsphaltMixType | None = pyd.Field(default=None, description="Asphalt mix type")
77
+ asphalt_gradation: AsphaltGradation | None = pyd.Field(default=None, description="Asphalt gradation")
78
+
79
+ asphalt_sbr: bool | None = pyd.Field(default=None, description="Styrene-butadiene rubber (SBR)")
80
+ asphalt_sbs: bool | None = pyd.Field(default=None, description="Styrene-butadiene-styrene (SBS)")
81
+ asphalt_ppa: bool | None = pyd.Field(default=None, description="Polyphosphoric acid (PPA)")
82
+ asphalt_gtr: bool | None = pyd.Field(default=None, description="Ground tire rubber (GTR)")
83
+ asphalt_pmb: bool | None = pyd.Field(default=None, description="Polymer modified bitumen (PMB)")
84
+
85
+ _aggregate_size_max_validator = pyd.validator("asphalt_aggregate_size_max", allow_reuse=True)(
86
+ validate_unit_factory(OpenEPDUnit.m)
87
+ )
@@ -0,0 +1,60 @@
1
+ #
2
+ # Copyright 2024 by C Change Labs Inc. www.c-change-labs.com
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+ # This software was developed with support from the Skanska USA,
17
+ # Charles Pankow Foundation, Microsoft Sustainability Fund, Interface, MKA Foundation, and others.
18
+ # Find out more at www.BuildingTransparency.org
19
+ #
20
+ from typing import Any
21
+
22
+ from openepd.compat.pydantic import pyd
23
+ from openepd.model.base import BaseOpenEpdSchema, Version
24
+ from openepd.model.validation.common import validate_version_compatibility, validate_version_format
25
+ from openepd.model.validation.quantity import QuantityValidator
26
+ from openepd.model.versioning import WithExtVersionMixin
27
+
28
+
29
+ class BaseOpenEpdSpec(BaseOpenEpdSchema):
30
+ """Base class for all OpenEPD specs."""
31
+
32
+ class Config:
33
+ use_enum_values = False # we need to store enums as strings and not values
34
+
35
+
36
+ class BaseOpenEpdHierarchicalSpec(BaseOpenEpdSpec, WithExtVersionMixin):
37
+ """Base class for new specs (hierarchical, versioned)."""
38
+
39
+ # external validator for quantities (e.g. length, mass, etc.) which should be setup by the user of the library.
40
+ _QUANTITY_VALIDATOR: QuantityValidator | None = None
41
+
42
+ def __init__(self, **data: Any) -> None:
43
+ # ensure that all the concrete spec objects fail on creations if they dont have _EXT_VERSION declared to
44
+ # something meaningful
45
+ if not hasattr(self, "_EXT_VERSION") or self._EXT_VERSION is None:
46
+ raise ValueError(f"Class {self.__class__} must declare an extension version")
47
+ Version.parse_version(self._EXT_VERSION) # validate format correctness
48
+ super().__init__(**{"ext_version": self._EXT_VERSION, **data})
49
+
50
+ _version_format_validator = pyd.validator("ext_version", allow_reuse=True, check_fields=False)(
51
+ validate_version_format
52
+ )
53
+ _version_major_match_validator = pyd.validator("ext_version", allow_reuse=True, check_fields=False)(
54
+ validate_version_compatibility("_EXT_VERSION")
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