openepd 6.24.0__tar.gz → 6.26.0__tar.gz

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 (162) hide show
  1. {openepd-6.24.0 → openepd-6.26.0}/PKG-INFO +1 -1
  2. {openepd-6.24.0 → openepd-6.26.0}/pyproject.toml +5 -1
  3. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/__version__.py +1 -1
  4. openepd-6.26.0/src/openepd/constants/scope_phases.py +168 -0
  5. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/declaration.py +3 -0
  6. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/lcia.py +1 -0
  7. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/singular/__init__.py +2 -4
  8. openepd-6.26.0/src/openepd/model/validation/__init__.py +15 -0
  9. openepd-6.26.0/src/openepd/utils/__init__.py +15 -0
  10. openepd-6.26.0/src/openepd/utils/functional.py +36 -0
  11. openepd-6.26.0/src/openepd/utils/mapping/__init__.py +15 -0
  12. openepd-6.26.0/src/openepd/utils/mapping/common.py +192 -0
  13. {openepd-6.24.0 → openepd-6.26.0}/LICENSE +0 -0
  14. {openepd-6.24.0 → openepd-6.26.0}/README.md +0 -0
  15. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/__init__.py +0 -0
  16. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/api/__init__.py +0 -0
  17. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/api/average_dataset/__init__.py +0 -0
  18. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/api/average_dataset/generic_estimate_sync_api.py +0 -0
  19. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/api/average_dataset/industry_epd_sync_api.py +0 -0
  20. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/api/base_sync_client.py +0 -0
  21. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/api/category/__init__.py +0 -0
  22. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/api/category/dto.py +0 -0
  23. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/api/category/sync_api.py +0 -0
  24. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/api/common.py +0 -0
  25. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/api/dto/__init__.py +0 -0
  26. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/api/dto/base.py +0 -0
  27. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/api/dto/common.py +0 -0
  28. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/api/dto/meta.py +0 -0
  29. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/api/dto/mf.py +0 -0
  30. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/api/dto/params.py +0 -0
  31. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/api/epd/__init__.py +0 -0
  32. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/api/epd/dto.py +0 -0
  33. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/api/epd/sync_api.py +0 -0
  34. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/api/errors.py +0 -0
  35. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/api/org/__init__.py +0 -0
  36. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/api/org/sync_api.py +0 -0
  37. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/api/pcr/__init__.py +0 -0
  38. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/api/pcr/sync_api.py +0 -0
  39. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/api/plant/__init__.py +0 -0
  40. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/api/plant/sync_api.py +0 -0
  41. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/api/standard/__init__.py +0 -0
  42. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/api/standard/sync_api.py +0 -0
  43. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/api/sync_client.py +0 -0
  44. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/api/test/__init__.py +0 -0
  45. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/api/utils.py +0 -0
  46. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/bundle/__init__.py +0 -0
  47. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/bundle/base.py +0 -0
  48. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/bundle/model.py +0 -0
  49. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/bundle/reader.py +0 -0
  50. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/bundle/writer.py +0 -0
  51. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/compat/__init__.py +0 -0
  52. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/compat/compat_functional_validators.py +0 -0
  53. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/compat/pydantic.py +0 -0
  54. {openepd-6.24.0/src/openepd/model → openepd-6.26.0/src/openepd/constants}/__init__.py +0 -0
  55. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/m49/__init__.py +0 -0
  56. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/m49/const.py +0 -0
  57. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/m49/utils.py +0 -0
  58. {openepd-6.24.0/src/openepd/model/specs/range/mixins → openepd-6.26.0/src/openepd/model}/__init__.py +0 -0
  59. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/base.py +0 -0
  60. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/category.py +0 -0
  61. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/common.py +0 -0
  62. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/epd.py +0 -0
  63. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/factory.py +0 -0
  64. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/generic_estimate.py +0 -0
  65. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/geography.py +0 -0
  66. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/industry_epd.py +0 -0
  67. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/org.py +0 -0
  68. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/pcr.py +0 -0
  69. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/README.md +0 -0
  70. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/__init__.py +0 -0
  71. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/asphalt.py +0 -0
  72. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/base.py +0 -0
  73. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/concrete.py +0 -0
  74. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/enums.py +0 -0
  75. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/range/__init__.py +0 -0
  76. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/range/accessories.py +0 -0
  77. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/range/aggregates.py +0 -0
  78. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/range/aluminium.py +0 -0
  79. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/range/asphalt.py +0 -0
  80. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/range/bulk_materials.py +0 -0
  81. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/range/cast_decks_and_underlayment.py +0 -0
  82. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/range/cladding.py +0 -0
  83. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/range/cmu.py +0 -0
  84. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/range/concrete.py +0 -0
  85. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/range/conveying_equipment.py +0 -0
  86. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/range/electrical.py +0 -0
  87. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/range/electrical_transmission_and_distribution_equipment.py +0 -0
  88. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/range/electricity.py +0 -0
  89. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/range/exterior_improvements.py +0 -0
  90. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/range/finishes.py +0 -0
  91. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/range/fire_and_smoke_protection.py +0 -0
  92. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/range/furnishings.py +0 -0
  93. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/range/grouting.py +0 -0
  94. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/range/manufacturing_inputs.py +0 -0
  95. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/range/masonry.py +0 -0
  96. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/range/material_handling.py +0 -0
  97. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/range/mechanical.py +0 -0
  98. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/range/mechanical_insulation.py +0 -0
  99. {openepd-6.24.0/src/openepd/model/specs/singular → openepd-6.26.0/src/openepd/model/specs/range}/mixins/__init__.py +0 -0
  100. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/range/mixins/access_flooring_mixin.py +0 -0
  101. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/range/network_infrastructure.py +0 -0
  102. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/range/openings.py +0 -0
  103. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/range/other_electrical_equipment.py +0 -0
  104. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/range/other_materials.py +0 -0
  105. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/range/plumbing.py +0 -0
  106. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/range/precast_concrete.py +0 -0
  107. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/range/sheathing.py +0 -0
  108. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/range/steel.py +0 -0
  109. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/range/thermal_moisture_protection.py +0 -0
  110. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/range/utility_piping.py +0 -0
  111. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/range/wood.py +0 -0
  112. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/range/wood_joists.py +0 -0
  113. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/singular/accessories.py +0 -0
  114. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/singular/aggregates.py +0 -0
  115. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/singular/aluminium.py +0 -0
  116. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/singular/asphalt.py +0 -0
  117. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/singular/bulk_materials.py +0 -0
  118. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/singular/cast_decks_and_underlayment.py +0 -0
  119. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/singular/cladding.py +0 -0
  120. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/singular/cmu.py +0 -0
  121. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/singular/common.py +0 -0
  122. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/singular/concrete.py +0 -0
  123. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/singular/conveying_equipment.py +0 -0
  124. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/singular/deprecated/__init__.py +0 -0
  125. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/singular/deprecated/concrete.py +0 -0
  126. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/singular/deprecated/steel.py +0 -0
  127. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/singular/electrical.py +0 -0
  128. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/singular/electrical_transmission_and_distribution_equipment.py +0 -0
  129. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/singular/electricity.py +0 -0
  130. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/singular/exterior_improvements.py +0 -0
  131. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/singular/finishes.py +0 -0
  132. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/singular/fire_and_smoke_protection.py +0 -0
  133. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/singular/furnishings.py +0 -0
  134. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/singular/grouting.py +0 -0
  135. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/singular/manufacturing_inputs.py +0 -0
  136. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/singular/masonry.py +0 -0
  137. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/singular/material_handling.py +0 -0
  138. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/singular/mechanical.py +0 -0
  139. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/singular/mechanical_insulation.py +0 -0
  140. {openepd-6.24.0/src/openepd/model/validation → openepd-6.26.0/src/openepd/model/specs/singular/mixins}/__init__.py +0 -0
  141. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/singular/mixins/access_flooring_mixin.py +0 -0
  142. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/singular/mixins/conduit_mixin.py +0 -0
  143. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/singular/network_infrastructure.py +0 -0
  144. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/singular/openings.py +0 -0
  145. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/singular/other_electrical_equipment.py +0 -0
  146. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/singular/other_materials.py +0 -0
  147. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/singular/plumbing.py +0 -0
  148. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/singular/precast_concrete.py +0 -0
  149. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/singular/sheathing.py +0 -0
  150. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/singular/steel.py +0 -0
  151. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/singular/thermal_moisture_protection.py +0 -0
  152. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/singular/utility_piping.py +0 -0
  153. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/singular/wood.py +0 -0
  154. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/specs/singular/wood_joists.py +0 -0
  155. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/standard.py +0 -0
  156. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/validation/common.py +0 -0
  157. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/validation/enum.py +0 -0
  158. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/validation/numbers.py +0 -0
  159. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/validation/quantity.py +0 -0
  160. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/model/versioning.py +0 -0
  161. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/patch_pydantic.py +0 -0
  162. {openepd-6.24.0 → openepd-6.26.0}/src/openepd/py.typed +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: openepd
3
- Version: 6.24.0
3
+ Version: 6.26.0
4
4
  Summary: Python library to work with OpenEPD format
5
5
  License: Apache-2.0
6
6
  Author: C-Change Labs
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "openepd"
3
- version = "6.24.0"
3
+ version = "6.26.0"
4
4
  license = "Apache-2.0"
5
5
  description = "Python library to work with OpenEPD format"
6
6
  authors = ["C-Change Labs <support@c-change-labs.com>"]
@@ -172,3 +172,7 @@ extend-ignore = [
172
172
  "D212", # We want to require second line for multiline docstrings
173
173
  ]
174
174
  extend-select = ["S", "E", "B", "A", "EM", "UP", "LOG", "G", "I", "D"]
175
+
176
+ [build-system]
177
+ requires = ["poetry-core>=2.0.0,<3.0.0"]
178
+ build-backend = "poetry.core.masonry.api"
@@ -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 = "6.24.0"
16
+ VERSION = "6.26.0"
@@ -0,0 +1,168 @@
1
+ #
2
+ # Copyright 2025 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
+ __all__ = ["ScopePhases"]
17
+
18
+
19
+ from enum import StrEnum
20
+
21
+ from openepd.utils.functional import classproperty
22
+
23
+
24
+ # noinspection PyMethodParameters
25
+ class ScopePhases(StrEnum):
26
+ """Container of common way to specify known scope phases under OpenEPD standard."""
27
+
28
+ A1 = "A1"
29
+ A2 = "A2"
30
+ A3 = "A3"
31
+ A1A2A3 = "A1A2A3"
32
+ A4 = "A4"
33
+ A5 = "A5"
34
+
35
+ B1 = "B1"
36
+ B2 = "B2"
37
+ B3 = "B3"
38
+ B4 = "B4"
39
+ B5 = "B5"
40
+ B6 = "B6"
41
+ B7 = "B7"
42
+
43
+ C1 = "C1"
44
+ C2 = "C2"
45
+ C3 = "C3"
46
+ C4 = "C4"
47
+
48
+ D = "D"
49
+
50
+ @classproperty
51
+ def a1a2a3(cls) -> str:
52
+ return cls.A1A2A3.lower()
53
+
54
+ @classproperty
55
+ def a1(cls) -> str:
56
+ return cls.A1.lower()
57
+
58
+ @classproperty
59
+ def a2(cls) -> str:
60
+ return cls.A2.lower()
61
+
62
+ @classproperty
63
+ def a3(cls) -> str:
64
+ return cls.A3.lower()
65
+
66
+ @classproperty
67
+ def a4(cls) -> str:
68
+ return cls.A4.lower()
69
+
70
+ @classproperty
71
+ def a5(cls) -> str:
72
+ return cls.A5.lower()
73
+
74
+ @classproperty
75
+ def b1(cls) -> str:
76
+ return cls.B1.lower()
77
+
78
+ @classproperty
79
+ def b2(cls) -> str:
80
+ return cls.B2.lower()
81
+
82
+ @classproperty
83
+ def b3(cls) -> str:
84
+ return cls.B3.lower()
85
+
86
+ @classproperty
87
+ def b4(cls) -> str:
88
+ return cls.B4.lower()
89
+
90
+ @classproperty
91
+ def b5(cls) -> str:
92
+ return cls.B5.lower()
93
+
94
+ @classproperty
95
+ def b6(cls) -> str:
96
+ return cls.B6.lower()
97
+
98
+ @classproperty
99
+ def b7(cls) -> str:
100
+ return cls.B7.lower()
101
+
102
+ @classproperty
103
+ def c1(cls) -> str:
104
+ return cls.C1.lower()
105
+
106
+ @classproperty
107
+ def c2(cls) -> str:
108
+ return cls.C2.lower()
109
+
110
+ @classproperty
111
+ def c3(cls) -> str:
112
+ return cls.C3.lower()
113
+
114
+ @classproperty
115
+ def c4(cls) -> str:
116
+ return cls.C4.lower()
117
+
118
+ @classproperty
119
+ def d(cls) -> str:
120
+ return cls.D.lower()
121
+
122
+ @classmethod
123
+ def a_scopes(cls, *, lowercase: bool = False) -> tuple[str, ...]:
124
+ result = (ScopePhases.A1A2A3, ScopePhases.A1, ScopePhases.A2, ScopePhases.A3, ScopePhases.A4, ScopePhases.A5)
125
+ if lowercase:
126
+ return cls._lowercase(result)
127
+ return result
128
+
129
+ @classmethod
130
+ def b_scopes(cls, *, lowercase: bool = False) -> tuple[str, ...]:
131
+ result = (
132
+ ScopePhases.B1,
133
+ ScopePhases.B2,
134
+ ScopePhases.B3,
135
+ ScopePhases.B4,
136
+ ScopePhases.B5,
137
+ ScopePhases.B6,
138
+ ScopePhases.B7,
139
+ )
140
+ if lowercase:
141
+ return cls._lowercase(result)
142
+
143
+ return result
144
+
145
+ @classmethod
146
+ def c_scopes(cls, *, lowercase: bool = False) -> tuple[str, ...]:
147
+ result = (ScopePhases.C1, ScopePhases.C2, ScopePhases.C3, ScopePhases.C4)
148
+ if lowercase:
149
+ return cls._lowercase(result)
150
+ return result
151
+
152
+ @classmethod
153
+ def d_scopes(cls, *, lowercase: bool = False) -> tuple[str, ...]:
154
+ result = (ScopePhases.D,)
155
+ if lowercase:
156
+ return cls._lowercase(result)
157
+ return result
158
+
159
+ @classmethod
160
+ def all_scopes(cls, *, lowercase: bool = False) -> tuple[str, ...]:
161
+ result = cls.a_scopes() + cls.b_scopes() + cls.c_scopes() + cls.d_scopes()
162
+ if lowercase:
163
+ return cls._lowercase(result)
164
+ return result
165
+
166
+ @classmethod
167
+ def _lowercase(cls, scopes: tuple[str, ...]) -> tuple[str, ...]:
168
+ return tuple(scope.lower() for scope in scopes)
@@ -203,6 +203,9 @@ class WithVerifierMixin(pyd.BaseModel):
203
203
  third_party_verifier_email: pyd.EmailStr | None = pyd.Field(
204
204
  description="Email address of the third party verifier", example="john.doe@example.com", default=None
205
205
  )
206
+ third_party_verifier_name: str | None = pyd.Field(
207
+ description="The publishable name of the third party verifier", example="John Doe", default=None
208
+ )
206
209
 
207
210
 
208
211
  class WithEpdDeveloperMixin(pyd.BaseModel):
@@ -522,6 +522,7 @@ class LCIAMethod(StrEnum):
522
522
  CML_1992 = "CML 1992"
523
523
  RECIPE_2016 = "ReCiPe 2016"
524
524
  RECIPE_2008 = "ReCiPe 2008"
525
+ GWP_GHG = "GWP-GHG"
525
526
  LIME2 = "LIME2"
526
527
 
527
528
  @classmethod
@@ -22,8 +22,6 @@ from collections.abc import Sequence
22
22
  import types
23
23
  from typing import Any, ClassVar, TypeVar
24
24
 
25
- from pydantic.v1.fields import FieldInfo
26
-
27
25
  from openepd.compat.pydantic import pyd
28
26
  from openepd.model.specs.base import BaseOpenEpdHierarchicalSpec
29
27
  from openepd.model.specs.singular.accessories import AccessoriesV1
@@ -208,9 +206,9 @@ class Specs(BaseOpenEpdHierarchicalSpec):
208
206
  """
209
207
  klass = self.__class__
210
208
 
211
- fields: dict[str, FieldInfo] = klass.__fields__ # type: ignore[assignment]
209
+ fields: dict[str, pyd.fields.FieldInfo] = klass.__fields__ # type: ignore[assignment]
212
210
 
213
- field: FieldInfo | None
211
+ field: pyd.fields.FieldInfo | None
214
212
  for i, key in enumerate(keys, start=1):
215
213
  field = fields.get(key)
216
214
  if field is None:
@@ -0,0 +1,15 @@
1
+ #
2
+ # Copyright 2025 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
+ #
@@ -0,0 +1,15 @@
1
+ #
2
+ # Copyright 2025 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
+ #
@@ -0,0 +1,36 @@
1
+ #
2
+ # Copyright 2025 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
+
17
+ # Portions of this file are sourced from https://github.com/django/django/blob/main/django/utils/functional.py
18
+ # Copyright (c) Django Software Foundation and individual contributors.
19
+ # Licensed under the BSD 3-Clause License.
20
+
21
+ # noinspection PyPep8Naming
22
+ class classproperty:
23
+ """
24
+ Decorator that converts a method with a single cls argument into a property
25
+ that can be accessed directly from the class.
26
+ """ # noqa: D205
27
+
28
+ def __init__(self, method=None):
29
+ self.fget = method
30
+
31
+ def __get__(self, instance, cls=None):
32
+ return self.fget(cls)
33
+
34
+ def getter(self, method):
35
+ self.fget = method
36
+ return self
@@ -0,0 +1,15 @@
1
+ #
2
+ # Copyright 2025 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
+ #
@@ -0,0 +1,192 @@
1
+ #
2
+ # Copyright 2025 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
+ __all__ = (
17
+ "BaseDataMapper",
18
+ "KeyValueMapper",
19
+ "ReferenceMapper",
20
+ "RegexMapper",
21
+ "SimpleDataMapper",
22
+ )
23
+
24
+ import abc
25
+ from collections.abc import Mapping
26
+ import re
27
+ from typing import Generic, TypeAlias, TypeVar, assert_never, cast
28
+
29
+ T = TypeVar("T")
30
+ K = TypeVar("K")
31
+
32
+
33
+ class BaseDataMapper(Generic[T, K], abc.ABC):
34
+ """
35
+ Base class for all data mappers.
36
+
37
+ Data mappers are objects used to map some input values to output values of different types.
38
+
39
+ Typical use case is mapping between different aliases of impact names with OpenEpd naming conventions.
40
+ """
41
+
42
+ @abc.abstractmethod
43
+ def map(self, input_value: T, default_value: K | None, *, raise_if_missing: bool = False) -> K | None:
44
+ """
45
+ Map the input value to the output value.
46
+
47
+ :param input_value: The input value to map.
48
+ :param default_value: The default value to return if there is no mapping for the input value.
49
+ :param raise_if_missing: Whether to raise an exception if there is no mapping for the input value.
50
+
51
+ :raise ValueError: If there is no mapping for the input value and raise_if_missing is True.
52
+ """
53
+ pass
54
+
55
+
56
+ class SimpleDataMapper(BaseDataMapper[T, T], Generic[T]):
57
+ """A data mapper that does not change the type of the input value."""
58
+
59
+ DATABASE: Mapping[T, T] = {}
60
+
61
+ def map(self, input_value: T, default_value: T | None, *, raise_if_missing: bool = False) -> T | None:
62
+ """
63
+ Map the input value to the output value.
64
+
65
+ :param input_value: The input value to map.
66
+ :param default_value: The default value to return if there is no mapping for the input value.
67
+ :param raise_if_missing: Whether to raise an exception if there is no mapping for the input value.
68
+
69
+ :raise ValueError: If there is no mapping for the input value and raise_if_missing is True.
70
+ """
71
+ if raise_if_missing and input_value not in self.DATABASE:
72
+ msg = f"No mapping for input value: {input_value}"
73
+ raise ValueError(msg)
74
+
75
+ return self.DATABASE.get(input_value, default_value)
76
+
77
+
78
+ class KeyValueMapper(BaseDataMapper[str, T], Generic[T]):
79
+ """
80
+ A data mapper that maps input values to output values using keywords.
81
+
82
+ List of values is expected to be a list string object or a list of objects easily castable to string.
83
+ """
84
+
85
+ KV: Mapping[str, list[T]] = {}
86
+
87
+ def map(self, input_value: str, default_value: T | None, *, raise_if_missing: bool = False) -> T | None:
88
+ """
89
+ Map the input value to the output value using keywords.
90
+
91
+ :param input_value: The input value to map.
92
+ :param default_value: The default value to return if there is no mapping for input value.
93
+ :param raise_if_missing: Whether to raise an exception if there is no mapping for the input value.
94
+
95
+ :raise ValueError: If there is no mapping for the input value and raise_if_missing is True.
96
+ """
97
+ for impact_name, keywords in self.KV.items():
98
+ for keyword in keywords:
99
+ if str(keyword).strip().lower() in input_value.strip().lower():
100
+ return cast(T, impact_name)
101
+
102
+ if raise_if_missing:
103
+ msg = f"No mapping for input value: {input_value}"
104
+ raise ValueError(msg)
105
+
106
+ return default_value
107
+
108
+
109
+ class RegexMapper(BaseDataMapper[str, T], Generic[T]):
110
+ """A data mapper that maps input values to output values using regex."""
111
+
112
+ PATTERNS: dict[str, str] = {}
113
+ _compiled_patterns: dict[str, re.Pattern]
114
+
115
+ def __init__(self) -> None:
116
+ self._compiled_patterns: dict[str, re.Pattern] = {
117
+ key: re.compile(pattern, re.IGNORECASE) for key, pattern in self.PATTERNS.items()
118
+ }
119
+
120
+ def map(self, input_value: str, default_value: T | None, *, raise_if_missing: bool = False) -> T | None:
121
+ """
122
+ Map the input value to the output value using regex.
123
+
124
+ :param input_value: The input value to map.
125
+ :param default_value: The default value to return if there is no mapping for an input value.
126
+
127
+ :param raise_if_missing: Whether to raise an exception if there is no mapping for the input value.
128
+
129
+ :raise ValueError: If there is no mapping for the input value and raise_if_missing is True.
130
+ """
131
+ for impact_name, pattern in self._compiled_patterns.items():
132
+ if pattern.search(input_value.strip().lower()):
133
+ return cast(T, impact_name)
134
+
135
+ if raise_if_missing:
136
+ msg = f"No mapping for input value: {input_value}"
137
+ raise ValueError(msg)
138
+
139
+ return default_value
140
+
141
+
142
+ _TRmRules: TypeAlias = str | re.Pattern | list[str | re.Pattern]
143
+
144
+
145
+ class ReferenceMapper(BaseDataMapper[str, _TRmRules]):
146
+ """
147
+ A mapper that maps input values of any form to the expected value format.
148
+
149
+ Expected values may be a value or a list of values. Values are expected to be a string object, regular expressions,
150
+ or objects easily castable to string.
151
+ """
152
+
153
+ def map(self, input_value: str, default_value: str | None, *, raise_if_missing: bool = False) -> str | None: # type: ignore[override]
154
+ """
155
+ Return specified key as a value if any of the values in the list matches the input value.
156
+
157
+ :param input_value: value to be checked against the list of specified rules
158
+ :param default_value: default value to return if no match is found
159
+ :param raise_if_missing: whether to raise an exception if no match is found
160
+
161
+ :return: mapped value if find any match, else default value
162
+
163
+ :raise ValueError: if no match is found and raise_if_missing is True
164
+ """
165
+ for key, value in self.MAPPING.items():
166
+ if not self._is_applied(input_value, value):
167
+ continue
168
+ return key
169
+
170
+ if raise_if_missing:
171
+ msg = f"No mapping for input value: {input_value}"
172
+ raise ValueError(msg)
173
+
174
+ return default_value
175
+
176
+ def _is_applied(self, input_value: str, rules: _TRmRules) -> bool:
177
+ if isinstance(rules, str | re.Pattern):
178
+ return self._is_applied_to_item(input_value, rules)
179
+ elif isinstance(rules, list):
180
+ return any(self._is_applied_to_item(input_value, rule) for rule in rules)
181
+ else:
182
+ assert_never(rules)
183
+
184
+ def _is_applied_to_item(self, input_value: str, rule: str | re.Pattern) -> bool:
185
+ if isinstance(rule, str):
186
+ return input_value.strip().lower() == rule.strip().lower()
187
+ elif isinstance(rule, re.Pattern):
188
+ return bool(rule.search(input_value.strip().lower()))
189
+ else:
190
+ assert_never(rule)
191
+
192
+ MAPPING: Mapping[str, _TRmRules] = {}
File without changes
File without changes
File without changes