openepd 6.12.0__py3-none-any.whl → 6.13.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.
@@ -0,0 +1,178 @@
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
+ from typing import Collection
17
+
18
+ from openepd.m49.const import (
19
+ COUNTRY_VERBOSE_NAME_TO_M49,
20
+ ISO3166_ALPHA2_TO_M49,
21
+ M49_TO_COUNTRY_VERBOSE_NAME,
22
+ M49_TO_ISO3166_ALPHA2,
23
+ M49_TO_REGION_VERBOSE_NAME,
24
+ OPENEPD_SPECIAL_REGIONS,
25
+ REGION_VERBOSE_NAME_TO_M49,
26
+ )
27
+
28
+
29
+ def iso_to_m49(regions: Collection[str]) -> set[str]:
30
+ """
31
+ Convert ISO3166 alpha2 country codes to M49 region codes.
32
+
33
+ :param regions: List of ISO3166 alpha2 country codes (e.g., ["US", "CA", "MX"])
34
+ :return: Set of M49 region codes (e.g., {"840", "124", "484"})
35
+ :raises ValueError: If a country code is not found in M49 region codes.
36
+ """
37
+
38
+ if not regions:
39
+ return set()
40
+
41
+ result = set()
42
+ for code in regions:
43
+ m49_code = ISO3166_ALPHA2_TO_M49.get(code.upper())
44
+ if m49_code:
45
+ result.add(m49_code)
46
+ else:
47
+ raise ValueError(f"Country code '{code}' not found in M49 region codes.")
48
+
49
+ return result
50
+
51
+
52
+ def m49_to_iso(regions: Collection[str]) -> set[str]:
53
+ """
54
+ Convert M49 region codes to ISO3166 alpha2 country codes.
55
+
56
+ :param regions: List of M49 region codes (e.g., ["840", "124", "484"])
57
+ :return: Set of ISO3166 alpha2 country codes (e.g., {"US", "CA", "MX"})
58
+ :raises ValueError: If a region code is not found in ISO3166.
59
+ """
60
+
61
+ if not regions:
62
+ return set()
63
+
64
+ result = set()
65
+ for code in regions:
66
+ iso_code = M49_TO_ISO3166_ALPHA2.get(code.upper())
67
+ if iso_code:
68
+ result.add(iso_code)
69
+ else:
70
+ raise ValueError(f"Region code '{code}' not found in ISO3166.")
71
+
72
+ return result
73
+
74
+
75
+ def region_and_country_names_to_m49(regions: Collection[str]) -> set[str]:
76
+ """
77
+ Convert user-friendly region and country names to M49 region codes.
78
+
79
+ :param regions: List of user-friendly region and country names (e.g., ["Europe",
80
+ "North America", "Austria", "Germany"])
81
+ :return: Set of M49 region codes (e.g., {"150", "003", "040", "276"})
82
+ :raises ValueError: If a region or country name is not found in M49 region codes.
83
+ """
84
+
85
+ if not regions:
86
+ return set()
87
+
88
+ result = set()
89
+ for name in regions:
90
+ m49_code = REGION_VERBOSE_NAME_TO_M49.get(name.title()) or COUNTRY_VERBOSE_NAME_TO_M49.get(name.title())
91
+ if not m49_code:
92
+ raise ValueError(f"Region or country name '{name}' not found in M49 region codes.")
93
+ result.add(m49_code)
94
+
95
+ return result
96
+
97
+
98
+ def m49_to_region_and_country_names(regions: Collection[str]) -> set[str]:
99
+ """
100
+ Convert M49 region codes to user-friendly region names and country names.
101
+
102
+ :param regions: List of M49 region codes (e.g., ["150", "003", "040", "276"])
103
+ :return: Set of user-friendly region and country names (e.g., {"Europe", "North America", "Austria", "Germany"})
104
+ :raises ValueError: If a region code is not found in M49 region codes.
105
+ """
106
+
107
+ if not regions:
108
+ return set()
109
+
110
+ result = set()
111
+ for code in regions:
112
+ if code not in M49_TO_REGION_VERBOSE_NAME and code not in M49_TO_COUNTRY_VERBOSE_NAME:
113
+ raise ValueError(f"Region code '{code}' not found in M49 region codes.")
114
+
115
+ name = M49_TO_REGION_VERBOSE_NAME.get(code) or M49_TO_COUNTRY_VERBOSE_NAME.get(code, code)
116
+ result.add(name)
117
+ return result
118
+
119
+
120
+ def openepd_to_m49(regions: Collection[str]) -> set[str]:
121
+ """
122
+ Convert OpenEPD geography definitions to pure M49 region codes.
123
+
124
+ :param regions: list of OpenEPD geography definitions including letter codes and aliases
125
+ like "EU27" or "NAFTA" (e.g., ["EU27", "NAFTA"], ["US", "CA, MX"])
126
+ :return: Set of M49 region codes (e.g., {"040", "056", "100", "191", "196", "203", "208", "233", "246", "250",
127
+ "276", "300", "348", "372", "380", "428", "440", "442", "470", "528", "616", "620", "642", "703", "705", "724",
128
+ "752", "840", "124", "484"}, {"840", "124", "484"})
129
+ :raises ValueError: If a region or country name is not found in ISO3166 or OpenEPD special regions.
130
+ """
131
+
132
+ if not regions:
133
+ return set()
134
+
135
+ result = set()
136
+ for region in regions:
137
+ if region in OPENEPD_SPECIAL_REGIONS:
138
+ result.update(OPENEPD_SPECIAL_REGIONS[region].m49_codes)
139
+ else:
140
+ m49_code = ISO3166_ALPHA2_TO_M49.get(region.upper())
141
+ if m49_code:
142
+ result.add(m49_code)
143
+ else:
144
+ raise ValueError(f"Region '{region}' not found in ISO3166 or OpenEPD special regions.")
145
+ return result
146
+
147
+
148
+ def m49_to_openepd(regions: list[str]) -> set[str]:
149
+ """
150
+ Convert M49 region codes to OpenEPD geography definitions.
151
+
152
+ :param regions: List of M49 region codes (e.g., ["040", "056", "100", "191", "196", "203", "208", "233", "246",
153
+ "250", "276", "300", "348", "372", "380", "428", "440", "442", "470", "528", "616", "620", "642", "703", "705",
154
+ "724", "752", "840", "124", "484"], ["840", "124", "484"], ["040", "056", "100"])
155
+ :return: Set of OpenEPD geography definitions including letter codes and aliases
156
+ like "EU27" or "NAFTA" (e.g., {"EU27", "NAFTA"}, {"NAFTA"}, {"AT", "BE", "BG"})
157
+ :raises ValueError: If a region code is not found in ISO3166 or OpenEPD special regions.
158
+ """
159
+
160
+ if not regions:
161
+ return set()
162
+
163
+ result = set()
164
+ remaining_codes = set(regions)
165
+ for special_region, special_region_data in OPENEPD_SPECIAL_REGIONS.items():
166
+ special_region_codes = set(special_region_data.m49_codes)
167
+ if special_region_codes.issubset(remaining_codes):
168
+ result.add(special_region)
169
+ remaining_codes -= special_region_codes
170
+
171
+ for code in remaining_codes:
172
+ iso_code = M49_TO_ISO3166_ALPHA2.get(code)
173
+ if iso_code:
174
+ result.add(iso_code)
175
+ else:
176
+ raise ValueError(f"Region code '{code}' not found in ISO3166 or OpenEPD special regions.")
177
+
178
+ return result
@@ -236,12 +236,12 @@ class FurnishingsRangeV1(BaseOpenEpdHierarchicalSpec):
236
236
  default=None, title="Functional Storage Volume", description=""
237
237
  )
238
238
  functional_seating_capacity: RangeInt | None = pyd.Field(
239
- default=1,
239
+ default=None,
240
240
  title="Functional Seating Capacity",
241
241
  description="Intended number of individuals the product seats. This value is used in calculating impact per functional unit.",
242
242
  )
243
243
  installation_waste_factor: RangeRatioFloat | None = pyd.Field(
244
- default=0.01,
244
+ default=None,
245
245
  title="Installation Waste Factor",
246
246
  description="Typical increase in impacts to account for installation waste.",
247
247
  )
@@ -175,13 +175,13 @@ class FurnishingsV1(BaseOpenEpdHierarchicalSpec):
175
175
  example="1 m3",
176
176
  )
177
177
  functional_seating_capacity: pyd.NonNegativeInt | None = pyd.Field(
178
- default=1,
178
+ default=None,
179
179
  title="Functional Seating Capacity",
180
180
  description="Intended number of individuals the product seats. This value is used in calculating impact per functional unit.",
181
181
  example=1,
182
182
  )
183
183
  installation_waste_factor: RatioFloat | None = pyd.Field(
184
- default=0.01,
184
+ default=None,
185
185
  title="Installation Waste Factor",
186
186
  description="Typical increase in impacts to account for installation waste.",
187
187
  example=0.01,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: openepd
3
- Version: 6.12.0
3
+ Version: 6.13.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
@@ -1,5 +1,5 @@
1
1
  openepd/__init__.py,sha256=fhxfEyEurLvSfvQci-vb3njzl_lvhcLXiZrecCOaMU8,794
2
- openepd/__version__.py,sha256=4igRnJ4prO4O--ghi78JXsgSz4xobA4SHm1zlb0MpHU,639
2
+ openepd/__version__.py,sha256=a5hKzjDC_6ktbu65wQ9YE2NrU-x54HWyTkooE0uHe-g,639
3
3
  openepd/api/__init__.py,sha256=9THJcV3LT7JDBOMz1px-QFf_sdJ0LOqJ5dmA9Dvvtd4,620
4
4
  openepd/api/average_dataset/__init__.py,sha256=9THJcV3LT7JDBOMz1px-QFf_sdJ0LOqJ5dmA9Dvvtd4,620
5
5
  openepd/api/average_dataset/generic_estimate_sync_api.py,sha256=KHCmSKMOJTQct6vhdhAatAENoouStc_yVRza5AFNoIo,7953
@@ -32,6 +32,9 @@ openepd/bundle/writer.py,sha256=gHK1D-F-td2C18QFWerg666JUTLaGUkTUSQURJi9o74,8136
32
32
  openepd/compat/__init__.py,sha256=9THJcV3LT7JDBOMz1px-QFf_sdJ0LOqJ5dmA9Dvvtd4,620
33
33
  openepd/compat/compat_functional_validators.py,sha256=aWg3a80fqT8zjN0S260N-Ad2WFKAaB8ByN7ArBW3NMA,834
34
34
  openepd/compat/pydantic.py,sha256=dNwPXK1X5xq9sdkd0kcbKQAUIter1GAfcxXOl6hmITQ,1146
35
+ openepd/m49/__init__.py,sha256=9THJcV3LT7JDBOMz1px-QFf_sdJ0LOqJ5dmA9Dvvtd4,620
36
+ openepd/m49/const.py,sha256=O31VJ5_sR4GAOvJnFsi3An2YMaMa96H7XBqaQ0kNwnw,31734
37
+ openepd/m49/geo_converter.py,sha256=mRamQTVwd35kwn5-23N3E8cxqpYG7l9eW787SJYM7nw,6515
35
38
  openepd/model/__init__.py,sha256=9THJcV3LT7JDBOMz1px-QFf_sdJ0LOqJ5dmA9Dvvtd4,620
36
39
  openepd/model/base.py,sha256=1GeaEDn-ppy94GaOaZI1y6Yew_0eBBho1COaxfzzLCw,9834
37
40
  openepd/model/category.py,sha256=reeOVRDuZPYU77EMwG0K5VjnK2H9yOGxT0PJXXqrjEk,1639
@@ -67,7 +70,7 @@ openepd/model/specs/range/electrical_transmission_and_distribution_equipment.py,
67
70
  openepd/model/specs/range/electricity.py,sha256=yn_S25C_tZqseb8hiJ1yiHszu2mQ49FOFWWCqNpOBY0,997
68
71
  openepd/model/specs/range/finishes.py,sha256=oCaPVYJja1KYxwEzTHCl7HtxQ2mokzeyahjyP33zHSg,21291
69
72
  openepd/model/specs/range/fire_and_smoke_protection.py,sha256=u5veICQznf-woYlh49S_IbvPA23CLWEy7Ngvic2FIpw,3337
70
- openepd/model/specs/range/furnishings.py,sha256=Es3rPvWT1opiMtyp7PdzKLe74pFRgs1jkROU51AoNDA,6528
73
+ openepd/model/specs/range/furnishings.py,sha256=7r9albaHmuf0ttqkSQ6elJcSzgFgOHSrwAfhEpJMm5k,6531
71
74
  openepd/model/specs/range/grouting.py,sha256=BQPxH6BvlXpdhLEZBui9zTuY93K_9syjoa6rdnF8GGY,1095
72
75
  openepd/model/specs/range/manufacturing_inputs.py,sha256=xzgkNz7yG8cp715hxS0LtJX9nfJ5RQR8GxlD2bAduwE,5574
73
76
  openepd/model/specs/range/masonry.py,sha256=IyjQMb8Mx3tpJlHIIam0OrMkFsJXKvc0WXRLRBupozY,2867
@@ -106,7 +109,7 @@ openepd/model/specs/singular/electrical_transmission_and_distribution_equipment.
106
109
  openepd/model/specs/singular/electricity.py,sha256=f1cJ9eklNUf97khCIZiT5-z8V_TbOcvH2me_1UTQVrE,823
107
110
  openepd/model/specs/singular/finishes.py,sha256=bAlNZiUP-HwePaJyC1D4j2fpv0xnMp3V5mhtAs9iaxQ,22571
108
111
  openepd/model/specs/singular/fire_and_smoke_protection.py,sha256=1uyEGdMAsboYORHvSFN1wftRVAps_UJ1Ep3Dk9arT3s,3058
109
- openepd/model/specs/singular/furnishings.py,sha256=rogO5XZl1341xPnVMQ1CSIcLiKbh66ZqmXXXR_em0MA,5600
112
+ openepd/model/specs/singular/furnishings.py,sha256=6V6Dc4xph8PcIQhuuLFyFGL4w2Yh_ByEKCNvQfUkxZQ,5603
110
113
  openepd/model/specs/singular/grouting.py,sha256=pg2tX3W7a2TQ3z_eyFYGlBJY3WEwn6JlZyqM3PQIJcU,934
111
114
  openepd/model/specs/singular/manufacturing_inputs.py,sha256=9WSDOVN0mUqvFXqWf82lkpHi-XRQSMcEpe9bweegwhU,5093
112
115
  openepd/model/specs/singular/masonry.py,sha256=f6nph-gscAmVeJ60bG-ebto5kz0fgh0LY27n0VutGFA,3165
@@ -136,7 +139,7 @@ openepd/model/validation/quantity.py,sha256=vfSe-3DGQf84bCp_sMIU0ZPAA1wIilodpTjL
136
139
  openepd/model/versioning.py,sha256=cm3LaAUODnbbu3W3pC6baJzxKusTQ1kZH-PwwScCj3c,4473
137
140
  openepd/patch_pydantic.py,sha256=LVqDMKn723VYYf_V-RgTLxIb1xiUtYOfPYCQP6-7RoM,4122
138
141
  openepd/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
139
- openepd-6.12.0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
140
- openepd-6.12.0.dist-info/METADATA,sha256=S3MymGCa7n1SY6ew2g3Z4LRvO0z462bqjgdBTQRuym8,9039
141
- openepd-6.12.0.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
142
- openepd-6.12.0.dist-info/RECORD,,
142
+ openepd-6.13.0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
143
+ openepd-6.13.0.dist-info/METADATA,sha256=ta3TH3-bGEhZt0NaiXcVVkEtbLuKY2YyX8ZwNhMv2Fo,9039
144
+ openepd-6.13.0.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
145
+ openepd-6.13.0.dist-info/RECORD,,