openepd 6.25.0__py3-none-any.whl → 6.27.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/constants/__init__.py +15 -0
- openepd/constants/scope_phases.py +168 -0
- openepd/model/common.py +23 -0
- openepd/model/declaration.py +3 -0
- openepd/model/lcia.py +1 -0
- openepd/model/org.py +26 -2
- openepd/utils/functional.py +36 -0
- {openepd-6.25.0.dist-info → openepd-6.27.0.dist-info}/METADATA +1 -1
- {openepd-6.25.0.dist-info → openepd-6.27.0.dist-info}/RECORD +12 -9
- {openepd-6.25.0.dist-info → openepd-6.27.0.dist-info}/LICENSE +0 -0
- {openepd-6.25.0.dist-info → openepd-6.27.0.dist-info}/WHEEL +0 -0
openepd/__version__.py
CHANGED
@@ -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,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)
|
openepd/model/common.py
CHANGED
@@ -13,13 +13,24 @@
|
|
13
13
|
# See the License for the specific language governing permissions and
|
14
14
|
# limitations under the License.
|
15
15
|
#
|
16
|
+
from collections.abc import Callable, Generator
|
16
17
|
from enum import StrEnum
|
18
|
+
import re
|
17
19
|
from typing import Annotated, Any
|
18
20
|
|
19
21
|
from openepd.compat.pydantic import pyd
|
20
22
|
from openepd.model.base import BaseOpenEpdSchema
|
21
23
|
from openepd.model.validation.numbers import RatioFloat
|
22
24
|
|
25
|
+
DATA_URL_REGEX = r"^data:([-\w]+\/[-+\w.]+)?(;?\w+=[-\w]+)*(;base64)?,.*$"
|
26
|
+
"""
|
27
|
+
Regular expression pattern for matching Data URLs.
|
28
|
+
|
29
|
+
A Data URL is a URI scheme that allows you to embed small data items inline
|
30
|
+
in web pages as if they were external resources.
|
31
|
+
The pattern matches the following format: data:[<media-type>][;base64],<data>
|
32
|
+
"""
|
33
|
+
|
23
34
|
|
24
35
|
class Amount(BaseOpenEpdSchema):
|
25
36
|
"""A value-and-unit pairing for amounts that do not have an uncertainty."""
|
@@ -313,3 +324,15 @@ class EnumGroupingAware:
|
|
313
324
|
def get_groupings(cls) -> list[list]:
|
314
325
|
"""Return logical groupings of the values."""
|
315
326
|
return []
|
327
|
+
|
328
|
+
|
329
|
+
class DataUrl(str):
|
330
|
+
@classmethod
|
331
|
+
def __get_validators__(cls) -> Generator[Callable[[str], str], None, None]:
|
332
|
+
def validator(v: str) -> str:
|
333
|
+
if re.compile(DATA_URL_REGEX).match(v):
|
334
|
+
return v
|
335
|
+
msg = "Value must be a valid dataUrl"
|
336
|
+
raise ValueError(msg)
|
337
|
+
|
338
|
+
yield validator
|
openepd/model/declaration.py
CHANGED
@@ -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):
|
openepd/model/lcia.py
CHANGED
openepd/model/org.py
CHANGED
@@ -13,15 +13,24 @@
|
|
13
13
|
# See the License for the specific language governing permissions and
|
14
14
|
# limitations under the License.
|
15
15
|
#
|
16
|
-
|
16
|
+
import math
|
17
|
+
from typing import Annotated, Final, Optional
|
17
18
|
|
18
19
|
from openlocationcode import openlocationcode
|
19
20
|
|
20
21
|
from openepd.compat.pydantic import pyd
|
21
22
|
from openepd.model.base import BaseOpenEpdSchema
|
22
|
-
from openepd.model.common import Location, WithAltIdsMixin, WithAttachmentsMixin
|
23
|
+
from openepd.model.common import DataUrl, Location, WithAltIdsMixin, WithAttachmentsMixin
|
23
24
|
from openepd.model.validation.common import ReferenceStr
|
24
25
|
|
26
|
+
ORG_LOGO_MAX_LENGTH: Final[int] = math.ceil(32 * 1024 * 4 / 3)
|
27
|
+
"""
|
28
|
+
Maximum length of Org.logo field.
|
29
|
+
|
30
|
+
Logo file size must be less than 32KB. Base64 encoding overhead (approximately 33%) requires
|
31
|
+
limiting the encoded string length to 4/3 of the file size limit.
|
32
|
+
"""
|
33
|
+
|
25
34
|
|
26
35
|
class OrgRef(BaseOpenEpdSchema):
|
27
36
|
"""Represents Organisation with minimal data."""
|
@@ -85,6 +94,21 @@ class Org(WithAttachmentsMixin, WithAltIdsMixin, OrgRef):
|
|
85
94
|
default=None,
|
86
95
|
description="Location of a place of business, preferably the corporate headquarters.",
|
87
96
|
)
|
97
|
+
logo: pyd.AnyUrl | DataUrl | None = pyd.Field(
|
98
|
+
default=None,
|
99
|
+
description=(
|
100
|
+
"URL pointer to, or dataURL, for a square logo for the company, preferably 300x300 pixels."
|
101
|
+
"A logo of the type used on social media platforms such as LinkedIn is recommended."
|
102
|
+
),
|
103
|
+
example="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...",
|
104
|
+
)
|
105
|
+
|
106
|
+
@pyd.validator("logo")
|
107
|
+
def validate_logo(cls, v: str | None) -> str | None:
|
108
|
+
if v and len(v) > ORG_LOGO_MAX_LENGTH:
|
109
|
+
msg = f"Logo URL must not exceed {ORG_LOGO_MAX_LENGTH} characters"
|
110
|
+
raise ValueError(msg)
|
111
|
+
return v
|
88
112
|
|
89
113
|
|
90
114
|
class PlantRef(BaseOpenEpdSchema):
|
@@ -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
|
@@ -1,5 +1,5 @@
|
|
1
1
|
openepd/__init__.py,sha256=fhxfEyEurLvSfvQci-vb3njzl_lvhcLXiZrecCOaMU8,794
|
2
|
-
openepd/__version__.py,sha256=
|
2
|
+
openepd/__version__.py,sha256=HmKYYpYPt8TvWu0Hh2PXVwOFCv8fcayILaKwQX8TeWk,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=_eZt_jGVL1a3p9cr-EF39Ve9Vl5sB8zwzTc_slnRL50,7975
|
@@ -38,21 +38,23 @@ openepd/bundle/writer.py,sha256=kX1vJ3z96W1TxYwy152hHbmHul_7jcxUXV8psOhtr18,8564
|
|
38
38
|
openepd/compat/__init__.py,sha256=9THJcV3LT7JDBOMz1px-QFf_sdJ0LOqJ5dmA9Dvvtd4,620
|
39
39
|
openepd/compat/compat_functional_validators.py,sha256=aWg3a80fqT8zjN0S260N-Ad2WFKAaB8ByN7ArBW3NMA,834
|
40
40
|
openepd/compat/pydantic.py,sha256=HZJmAiYO7s-LLcFHuj3iXS4MGOITExZYn2rPmteoqNI,1146
|
41
|
+
openepd/constants/__init__.py,sha256=9THJcV3LT7JDBOMz1px-QFf_sdJ0LOqJ5dmA9Dvvtd4,620
|
42
|
+
openepd/constants/scope_phases.py,sha256=zcK7ytSbCNE76tX0vZMWbEUAFgFSifXU9YG_s3vvMqw,4035
|
41
43
|
openepd/m49/__init__.py,sha256=AApOMp9PJPMXZbPB4piedqKtgHE01mlj_MyF3kf519U,718
|
42
44
|
openepd/m49/const.py,sha256=bkYu6J7dQNVb2-nNkjy97uMpt64vICX5o-PHr0lk_90,31833
|
43
45
|
openepd/m49/utils.py,sha256=vQl0wMXtYS2b7NeLIWilDNUopq3MATmLnhEFcMYTeZA,7256
|
44
46
|
openepd/model/__init__.py,sha256=9THJcV3LT7JDBOMz1px-QFf_sdJ0LOqJ5dmA9Dvvtd4,620
|
45
47
|
openepd/model/base.py,sha256=6rP6r-7NwKC6JLFB24v2w4Z-_f_w6ZSNLte5zyj5o70,9928
|
46
48
|
openepd/model/category.py,sha256=reeOVRDuZPYU77EMwG0K5VjnK2H9yOGxT0PJXXqrjEk,1639
|
47
|
-
openepd/model/common.py,sha256=
|
48
|
-
openepd/model/declaration.py,sha256=
|
49
|
+
openepd/model/common.py,sha256=WM6ankkeZazVzKwbn4s5FHwENMTIKWsgdRJkX7DYmpg,13875
|
50
|
+
openepd/model/declaration.py,sha256=1WXTRlSRIUXTJJhL_e7BkoFyZ9gJKZH7c8pFVxihGFU,13980
|
49
51
|
openepd/model/epd.py,sha256=_1P9-etXfLAVmg44pglnBUvoEyQ50odNBsUrv_OeS9M,12308
|
50
52
|
openepd/model/factory.py,sha256=UWSGpfCr3GiMTP4rzBkwqxzbXB6GKZ_5Okb1Dqa_4aA,2701
|
51
53
|
openepd/model/generic_estimate.py,sha256=zLGTyf4Uzmp2C0m-J1ePWItSz2RGdZ0OiGPWC5nhKHk,3992
|
52
54
|
openepd/model/geography.py,sha256=Jx7NIDdk_sIvwyh-7YxnIjAwIHW2HCQK7UtFGM2xKtw,42095
|
53
55
|
openepd/model/industry_epd.py,sha256=QZr7OhgGkzqZ8H5p6dCIVk9zSHEYtK3y9Nk-DvkFMyk,4011
|
54
|
-
openepd/model/lcia.py,sha256=
|
55
|
-
openepd/model/org.py,sha256=
|
56
|
+
openepd/model/lcia.py,sha256=PdaSZz02PMBJl_t2B_dmkcbzIFjA7iACrWj1fllHfw0,26230
|
57
|
+
openepd/model/org.py,sha256=zxBt-c2f1nhJUqygUzhsq6AEEwfNiYGquhgYvcUfvSo,7771
|
56
58
|
openepd/model/pcr.py,sha256=7nf6ATofdrlPt81vdU6p0E8n_ftFEuCEIKxtYlFwclw,5476
|
57
59
|
openepd/model/specs/README.md,sha256=UGhSiFJ9hOxT1mZl-5ZrhkOrPKf1W_gcu5CI9hzV7LU,2430
|
58
60
|
openepd/model/specs/__init__.py,sha256=toVWd8_jxmAf7gRwpoXQpLfZW6Cl-NwveoduMXhECms,3903
|
@@ -151,9 +153,10 @@ openepd/model/versioning.py,sha256=wBZdOVL3ND9FMIRU9PS3vx9M_7MBiO70xYPQvPez6po,4
|
|
151
153
|
openepd/patch_pydantic.py,sha256=bO7U5HqthFol0vfycb0a42UAGL3KOQ8-9MW4yCWOFP0,4150
|
152
154
|
openepd/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
153
155
|
openepd/utils/__init__.py,sha256=9THJcV3LT7JDBOMz1px-QFf_sdJ0LOqJ5dmA9Dvvtd4,620
|
156
|
+
openepd/utils/functional.py,sha256=sm7od2_UE-cNToezBlwFQ1TCUJub1tz6VykA1X8XH-I,1274
|
154
157
|
openepd/utils/mapping/__init__.py,sha256=9THJcV3LT7JDBOMz1px-QFf_sdJ0LOqJ5dmA9Dvvtd4,620
|
155
158
|
openepd/utils/mapping/common.py,sha256=WphCzwQQlzX11tUk88Ubyq3QPBLvH0tBPSIuH0kmiug,7339
|
156
|
-
openepd-6.
|
157
|
-
openepd-6.
|
158
|
-
openepd-6.
|
159
|
-
openepd-6.
|
159
|
+
openepd-6.27.0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
160
|
+
openepd-6.27.0.dist-info/METADATA,sha256=1qocBkldct5oXEPyBq3C6KN9M9OErubIul8X68L_M1U,9827
|
161
|
+
openepd-6.27.0.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
162
|
+
openepd-6.27.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|