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.
- openepd/__init__.py +1 -1
- openepd/__version__.py +2 -2
- openepd/api/__init__.py +19 -0
- openepd/api/base_sync_client.py +550 -0
- openepd/api/category/__init__.py +19 -0
- openepd/api/category/dto.py +25 -0
- openepd/api/category/sync_api.py +44 -0
- openepd/api/common.py +239 -0
- openepd/api/dto/__init__.py +19 -0
- openepd/api/dto/base.py +41 -0
- openepd/api/dto/common.py +115 -0
- openepd/api/dto/meta.py +69 -0
- openepd/api/dto/mf.py +59 -0
- openepd/api/dto/params.py +19 -0
- openepd/api/epd/__init__.py +19 -0
- openepd/api/epd/dto.py +121 -0
- openepd/api/epd/sync_api.py +105 -0
- openepd/api/errors.py +86 -0
- openepd/api/pcr/__init__.py +19 -0
- openepd/api/pcr/dto.py +41 -0
- openepd/api/pcr/sync_api.py +49 -0
- openepd/api/sync_client.py +67 -0
- openepd/api/test/__init__.py +19 -0
- openepd/bundle/__init__.py +1 -1
- openepd/bundle/base.py +1 -1
- openepd/bundle/model.py +5 -6
- openepd/bundle/reader.py +5 -5
- openepd/bundle/writer.py +5 -4
- openepd/compat/__init__.py +19 -0
- openepd/compat/pydantic.py +29 -0
- openepd/model/__init__.py +1 -1
- openepd/model/base.py +114 -15
- openepd/model/category.py +39 -0
- openepd/model/common.py +33 -25
- openepd/model/epd.py +97 -78
- openepd/model/factory.py +48 -0
- openepd/model/lcia.py +24 -13
- openepd/model/org.py +28 -18
- openepd/model/pcr.py +42 -14
- openepd/model/specs/README.md +19 -0
- openepd/model/specs/__init__.py +72 -4
- openepd/model/specs/aluminium.py +67 -0
- openepd/model/specs/asphalt.py +87 -0
- openepd/model/specs/base.py +60 -0
- openepd/model/specs/concrete.py +288 -24
- openepd/model/specs/generated/accessories.py +63 -0
- openepd/model/specs/generated/aggregates.py +71 -0
- openepd/model/specs/generated/aluminium.py +66 -0
- openepd/model/specs/generated/asphalt.py +86 -0
- openepd/model/specs/generated/bulk_materials.py +26 -0
- openepd/model/specs/generated/cast_decks_and_underlayment.py +26 -0
- openepd/model/specs/generated/cladding.py +214 -0
- openepd/model/specs/generated/cmu.py +46 -0
- openepd/model/specs/generated/common.py +27 -0
- openepd/model/specs/generated/concrete.py +151 -0
- openepd/model/specs/generated/conveying_equipment.py +57 -0
- openepd/model/specs/generated/electrical.py +297 -0
- openepd/model/specs/generated/electrical_transmission_and_distribution_equipment.py +63 -0
- openepd/model/specs/generated/electricity.py +26 -0
- openepd/model/specs/generated/enums.py +2420 -0
- openepd/model/specs/generated/finishes.py +519 -0
- openepd/model/specs/generated/fire_and_smoke_protection.py +79 -0
- openepd/model/specs/generated/furnishings.py +95 -0
- openepd/model/specs/generated/grouting.py +26 -0
- openepd/model/specs/generated/manufacturing_inputs.py +131 -0
- openepd/model/specs/generated/masonry.py +77 -0
- openepd/model/specs/generated/material_handling.py +35 -0
- openepd/model/specs/generated/mechanical.py +271 -0
- openepd/model/specs/generated/mechanical_insulation.py +41 -0
- openepd/model/specs/generated/network_infrastructure.py +181 -0
- openepd/model/specs/generated/openings.py +423 -0
- openepd/model/specs/generated/other_electrical_equipment.py +26 -0
- openepd/model/specs/generated/other_materials.py +123 -0
- openepd/model/specs/generated/plumbing.py +153 -0
- openepd/model/specs/generated/precast_concrete.py +68 -0
- openepd/model/specs/generated/sheathing.py +74 -0
- openepd/model/specs/generated/steel.py +224 -0
- openepd/model/specs/generated/thermal_moisture_protection.py +233 -0
- openepd/model/specs/generated/utility_piping.py +65 -0
- openepd/model/specs/generated/wood.py +167 -0
- openepd/model/specs/generated/wood_joists.py +38 -0
- openepd/model/specs/glass.py +360 -0
- openepd/model/specs/steel.py +184 -0
- openepd/model/specs/wood.py +130 -0
- openepd/model/standard.py +2 -3
- openepd/model/validation/__init__.py +19 -0
- openepd/model/validation/common.py +59 -0
- openepd/model/validation/numbers.py +26 -0
- openepd/model/validation/quantity.py +132 -0
- openepd/model/versioning.py +129 -0
- {openepd-2.0.0.dist-info → openepd-3.1.0.dist-info}/METADATA +36 -5
- openepd-3.1.0.dist-info/RECORD +95 -0
- openepd-2.0.0.dist-info/RECORD +0 -22
- {openepd-2.0.0.dist-info → openepd-3.1.0.dist-info}/LICENSE +0 -0
- {openepd-2.0.0.dist-info → openepd-3.1.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,19 @@
|
|
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
|
+
#
|
@@ -0,0 +1,59 @@
|
|
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 Annotated, Any, Callable, Type, TypeAlias
|
21
|
+
|
22
|
+
from openepd.compat.pydantic import pyd
|
23
|
+
from openepd.model.versioning import Version
|
24
|
+
|
25
|
+
|
26
|
+
def together_validator(field1: str, field2: Any, values: dict[str, Any]) -> Any:
|
27
|
+
"""Shared validator to ensure that two fields are provided together or not provided at all."""
|
28
|
+
value1 = values.get(field1)
|
29
|
+
value2 = values.get(field2)
|
30
|
+
if value1 is not None and value2 is None or value1 is None and value2 is not None:
|
31
|
+
raise ValueError(f"Both or neither {field1} and {field2} days must be provided together")
|
32
|
+
|
33
|
+
|
34
|
+
def validate_version_format(v: str) -> str:
|
35
|
+
"""Ensure that the extension version is valid."""
|
36
|
+
Version.parse_version(v) # will raise an error if not valid
|
37
|
+
return v
|
38
|
+
|
39
|
+
|
40
|
+
def validate_version_compatibility(class_version_attribute_name: str) -> Callable[[Type, str], str]:
|
41
|
+
"""Ensure that the object which is passed for parsing and validation is compatible with the class."""
|
42
|
+
|
43
|
+
# we need closure to pass property name, since actual class will only be available in runtime
|
44
|
+
def internal_validate_version_compatibility(cls: Type, v: str) -> str:
|
45
|
+
if not hasattr(cls, class_version_attribute_name):
|
46
|
+
raise ValueError(f"Class {cls} must declare a class var extension var named {class_version_attribute_name}")
|
47
|
+
|
48
|
+
class_version = getattr(cls, class_version_attribute_name)
|
49
|
+
if Version.parse_version(v).major != Version.parse_version(class_version).major:
|
50
|
+
raise ValueError(f"Extension version {v} does not match class version {class_version}")
|
51
|
+
return v
|
52
|
+
|
53
|
+
return internal_validate_version_compatibility
|
54
|
+
|
55
|
+
|
56
|
+
ReferenceStr: TypeAlias = Annotated[
|
57
|
+
str,
|
58
|
+
pyd.Field(description="Reference to another object", example="https://buildingtransparency.org/ec3/epds/1u7zsed8"),
|
59
|
+
]
|
@@ -0,0 +1,26 @@
|
|
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 Annotated
|
21
|
+
|
22
|
+
from openepd.compat.pydantic import pyd
|
23
|
+
|
24
|
+
# todo when move to pydantic 2, check that validators are being enforced.
|
25
|
+
RatioFloat = Annotated[float, pyd.Field(ge=0, le=1, example=0.5)]
|
26
|
+
PositiveInt = Annotated[int, pyd.Field(ge=0, example=1)]
|
@@ -0,0 +1,132 @@
|
|
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 abc import ABC, abstractmethod
|
21
|
+
from typing import TYPE_CHECKING, Annotated, Callable, TypeAlias
|
22
|
+
|
23
|
+
from openepd.compat.pydantic import pyd
|
24
|
+
from openepd.model.common import OpenEPDUnit
|
25
|
+
|
26
|
+
if TYPE_CHECKING:
|
27
|
+
from openepd.model.specs.base import BaseOpenEpdHierarchicalSpec
|
28
|
+
|
29
|
+
QuantityValidatorType = Callable[[type[BaseOpenEpdHierarchicalSpec], str], str]
|
30
|
+
|
31
|
+
|
32
|
+
class QuantityValidator(ABC):
|
33
|
+
"""
|
34
|
+
Interface for quantity validator.
|
35
|
+
|
36
|
+
The openEPD models are mapped using the simple types. Caller code should provide their own implementation of this
|
37
|
+
and set it with `set_unit_validator` function.
|
38
|
+
"""
|
39
|
+
|
40
|
+
@abstractmethod
|
41
|
+
def validate_unit_correctness(self, value: str, dimensionality: str) -> None:
|
42
|
+
"""
|
43
|
+
Validate the given string value against the given dimensionality.
|
44
|
+
|
45
|
+
Args:
|
46
|
+
value: The value to validate, like "102.4 kg"
|
47
|
+
dimensionality: The dimensionality to validate against, like "kg"
|
48
|
+
Returns:
|
49
|
+
None if the value is valid, raises an error otherwise.
|
50
|
+
Raises:
|
51
|
+
ValueError: If the value is not valid.
|
52
|
+
"""
|
53
|
+
pass
|
54
|
+
|
55
|
+
@abstractmethod
|
56
|
+
def validate_quantity_greater_or_equal(self, value: str, min_value: str) -> None:
|
57
|
+
"""
|
58
|
+
Validate the quantity is greater than minimal value.
|
59
|
+
|
60
|
+
Args:
|
61
|
+
value: The value to validate, like "2.4 kg"
|
62
|
+
min_value: The value to compare with, like "102.4 kg"
|
63
|
+
Returns:
|
64
|
+
None if the value is valid, raises an error otherwise.
|
65
|
+
Raises:
|
66
|
+
ValueError: If the value is not valid.
|
67
|
+
"""
|
68
|
+
pass
|
69
|
+
|
70
|
+
@abstractmethod
|
71
|
+
def validate_quantity_less_or_equal(self, value: str, max_value: str) -> None:
|
72
|
+
"""
|
73
|
+
Validate the quantity is less than minimal value.
|
74
|
+
|
75
|
+
Args:
|
76
|
+
value: The value to validate, like "2.4 kg"
|
77
|
+
max_value: The value to compare with, like "0.4 kg"
|
78
|
+
Returns:
|
79
|
+
None if the value is valid, raises an error otherwise.
|
80
|
+
Raises:
|
81
|
+
ValueError: If the value is not valid.
|
82
|
+
"""
|
83
|
+
pass
|
84
|
+
|
85
|
+
|
86
|
+
def validate_unit_factory(dimensionality: OpenEPDUnit | str) -> "QuantityValidatorType":
|
87
|
+
"""Create validator for quantity field to check unit matching."""
|
88
|
+
|
89
|
+
def validator(cls: "type[BaseOpenEpdHierarchicalSpec]", value: str) -> str:
|
90
|
+
if hasattr(cls, "_QUANTITY_VALIDATOR") and cls._QUANTITY_VALIDATOR is not None:
|
91
|
+
cls._QUANTITY_VALIDATOR.validate_unit_correctness(value, dimensionality)
|
92
|
+
return value
|
93
|
+
|
94
|
+
return validator
|
95
|
+
|
96
|
+
|
97
|
+
def validate_quantity_ge_factory(min_value: str) -> "QuantityValidatorType":
|
98
|
+
"""Create validator to check that quantity is greater than or equal to min_value."""
|
99
|
+
|
100
|
+
def validator(cls: "type[BaseOpenEpdHierarchicalSpec]", value: str) -> str:
|
101
|
+
if hasattr(cls, "_QUANTITY_VALIDATOR") and cls._QUANTITY_VALIDATOR is not None:
|
102
|
+
cls._QUANTITY_VALIDATOR.validate_quantity_greater_or_equal(value, min_value)
|
103
|
+
return value
|
104
|
+
|
105
|
+
return validator
|
106
|
+
|
107
|
+
|
108
|
+
def validate_quantity_le_factory(max_value: str) -> "QuantityValidatorType":
|
109
|
+
"""Create validator to check that quantity is less than or equal to max_value."""
|
110
|
+
|
111
|
+
def validator(cls: "type[BaseOpenEpdHierarchicalSpec]", value: str) -> str:
|
112
|
+
if hasattr(cls, "_QUANTITY_VALIDATOR") and cls._QUANTITY_VALIDATOR is not None:
|
113
|
+
cls._QUANTITY_VALIDATOR.validate_quantity_less_or_equal(value, max_value)
|
114
|
+
return value
|
115
|
+
|
116
|
+
return validator
|
117
|
+
|
118
|
+
|
119
|
+
# todo with the migration to Pydantic 2 we will be able to use pydantic.funcational_validators.AfterDecorator
|
120
|
+
# this will let us bind the validator not to the model or the field, but to the type itself.
|
121
|
+
|
122
|
+
# for abitrary non-standard quantity
|
123
|
+
QuantityStr: TypeAlias = Annotated[str, pyd.Field()]
|
124
|
+
PressureMPaStr: TypeAlias = Annotated[str, pyd.Field(example="30 MPa")]
|
125
|
+
MassKgStr: TypeAlias = Annotated[str, pyd.Field(example="30 kg")]
|
126
|
+
AreaM2Str: TypeAlias = Annotated[str, pyd.Field(example="12 m2", gt=0)]
|
127
|
+
LengthMStr: TypeAlias = Annotated[str, pyd.Field(example="30 m", gt=0)]
|
128
|
+
LengthMmStr: TypeAlias = Annotated[str, pyd.Field(example="30 mm", gt=0)]
|
129
|
+
LengthInchStr: TypeAlias = Annotated[str, pyd.Field(example="30 m", gt=0)]
|
130
|
+
TemperatureCStr: TypeAlias = Annotated[str, pyd.Field(example="45 C")]
|
131
|
+
HeatConductanceUCIStr: TypeAlias = Annotated[str, pyd.Field(example="0.3 U")]
|
132
|
+
GwpKgCo2eStr: TypeAlias = Annotated[str, pyd.Field(example="300 kgCO2e")]
|
@@ -0,0 +1,129 @@
|
|
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 abc import ABC
|
21
|
+
from enum import ReprEnum
|
22
|
+
from typing import ClassVar, NamedTuple
|
23
|
+
|
24
|
+
from openepd.compat.pydantic import pyd
|
25
|
+
|
26
|
+
|
27
|
+
class WithExtVersionMixin(ABC, pyd.BaseModel):
|
28
|
+
"""Mixin for extensions supporting versions: recommended way."""
|
29
|
+
|
30
|
+
_EXT_VERSION: ClassVar[str]
|
31
|
+
"""Exact version (major, minor) of the spec extension"""
|
32
|
+
|
33
|
+
def __init_subclass__(cls):
|
34
|
+
"""Set the default value for the ext_version field from _EXT_VERSION class var."""
|
35
|
+
super().__init_subclass__()
|
36
|
+
if hasattr(cls, "_EXT_VERSION"):
|
37
|
+
cls.__fields__["ext_version"].default = cls._EXT_VERSION
|
38
|
+
|
39
|
+
# Note: default is set programmatically in __init_subclass__
|
40
|
+
ext_version: str | None = pyd.Field(description="Extension version", example="3.22", default=None)
|
41
|
+
|
42
|
+
|
43
|
+
class Version(NamedTuple):
|
44
|
+
"""Version of the object or specification."""
|
45
|
+
|
46
|
+
major: int
|
47
|
+
minor: int
|
48
|
+
|
49
|
+
@staticmethod
|
50
|
+
def parse_version(version: str) -> "Version":
|
51
|
+
"""Parse the version of extension or the format.
|
52
|
+
|
53
|
+
Version is expected to be major.minor
|
54
|
+
|
55
|
+
:param version: The extension version.
|
56
|
+
:return: A tuple of major and minor version numbers.
|
57
|
+
"""
|
58
|
+
splits = version.split(".", 1) if isinstance(version, str) else None
|
59
|
+
if len(splits) != 2:
|
60
|
+
raise ValueError(f"Invalid version: {version}")
|
61
|
+
if not splits[0].isdigit() or not splits[1].isdigit():
|
62
|
+
raise ValueError(f"Invalid version: {version}")
|
63
|
+
return Version(major=int(splits[0]), minor=int(splits[1]))
|
64
|
+
|
65
|
+
def __str__(self) -> str:
|
66
|
+
return self.as_str()
|
67
|
+
|
68
|
+
def __repr__(self) -> str:
|
69
|
+
return f"[Version] {self.as_str()}"
|
70
|
+
|
71
|
+
def as_str(self) -> str:
|
72
|
+
"""Return the version as a string."""
|
73
|
+
return f"{self.major}.{self.minor}"
|
74
|
+
|
75
|
+
|
76
|
+
class OpenEpdVersions(Version, ReprEnum):
|
77
|
+
"""
|
78
|
+
Enum of supported openEPD versions.
|
79
|
+
|
80
|
+
When adding a new version - make sure to add a new major version to the list of supported versions.
|
81
|
+
When doing non-breaking change - update minor version in a corresponding enum value.
|
82
|
+
"""
|
83
|
+
|
84
|
+
Version0 = Version(major=0, minor=1)
|
85
|
+
|
86
|
+
@classmethod
|
87
|
+
def get_supported_versions(cls) -> list[Version]:
|
88
|
+
"""Return a list of supported versions."""
|
89
|
+
return [x.value for x in cls]
|
90
|
+
|
91
|
+
@classmethod
|
92
|
+
def supported_versions_str(cls, major_only: bool = False) -> str:
|
93
|
+
"""
|
94
|
+
Return a comma separated list of the supported versions.
|
95
|
+
|
96
|
+
This is a utility method, might be helpful for logging, building error messages, etc.
|
97
|
+
|
98
|
+
:param major_only: If True, minor component will be replaced with 'x'. E.g. `2.x` instead of `2.1`
|
99
|
+
"""
|
100
|
+
if major_only:
|
101
|
+
return ", ".join(f"{x.major}.x" for x in cls.get_supported_versions())
|
102
|
+
return ", ".join(str(x) for x in cls.get_supported_versions())
|
103
|
+
|
104
|
+
@classmethod
|
105
|
+
def get_most_recent_version(cls, branch: int | None = 0) -> Version:
|
106
|
+
"""
|
107
|
+
Return the most recent version of the openEPD format.
|
108
|
+
|
109
|
+
If branch is specified - returns the most recent version of the specified branch, otherwise returns
|
110
|
+
the most recent version among all branches.
|
111
|
+
"""
|
112
|
+
if branch is None:
|
113
|
+
highest: Version = max(cls, key=lambda x: x.value.major) # type: ignore
|
114
|
+
if highest:
|
115
|
+
return highest
|
116
|
+
for x in cls:
|
117
|
+
if x.value.major == branch:
|
118
|
+
return x.value
|
119
|
+
raise ValueError(
|
120
|
+
f"No version {branch}.x is not supported. Supported versions are: {', '.join(str(x.value) for x in cls)}"
|
121
|
+
)
|
122
|
+
|
123
|
+
@classmethod
|
124
|
+
def get_current(cls) -> Version:
|
125
|
+
"""Return the most recent stable version of the format."""
|
126
|
+
return cls.get_most_recent_version()
|
127
|
+
|
128
|
+
def __repr__(self):
|
129
|
+
return f"[OpenEpdVesion] {self.name} - {self.value}"
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: openepd
|
3
|
-
Version:
|
3
|
+
Version: 3.1.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
|
@@ -16,8 +16,10 @@ Classifier: Operating System :: OS Independent
|
|
16
16
|
Classifier: Programming Language :: Python :: 3
|
17
17
|
Classifier: Programming Language :: Python :: 3.11
|
18
18
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
19
|
-
|
20
|
-
Requires-Dist:
|
19
|
+
Provides-Extra: api-client
|
20
|
+
Requires-Dist: email-validator (>=1.3.1)
|
21
|
+
Requires-Dist: pydantic (>=1.10,<3.0)
|
22
|
+
Requires-Dist: requests (>=2.0) ; extra == "api-client"
|
21
23
|
Project-URL: Repository, https://github.com/cchangelabs/openepd
|
22
24
|
Description-Content-Type: text/markdown
|
23
25
|
|
@@ -57,18 +59,47 @@ documenting supply-chain specific data.
|
|
57
59
|
|
58
60
|
## Usage
|
59
61
|
|
62
|
+
## Usage
|
63
|
+
|
60
64
|
**❗ ATTENTION**: Pick the right version. The cornerstone of this library models package representing openEPD models.
|
61
65
|
Models are defined with Pydantic library which is a dependency for openepd package. If you use Pydantic in your project
|
62
66
|
carefully pick the version:
|
63
67
|
|
64
|
-
* Use version below `
|
65
|
-
* Use version `
|
68
|
+
* Use version **below** `2.0.0` if your project uses Pydantic version below `2.0.0`
|
69
|
+
* Use version `2.x.x` or higher if your project uses Pydantic version `2.0.0` or above
|
66
70
|
|
67
71
|
### Models
|
68
72
|
|
69
73
|
The library provides the Pydantic models for all the OpenEPD entities. The models are available in the `openepd.models`
|
70
74
|
module. For mode details on the usage please refer to Pydantic documentation.
|
71
75
|
|
76
|
+
### API Client
|
77
|
+
|
78
|
+
The library provides the API client to work with the OpenEPD API. The client is available in the `openepd.client` module.
|
79
|
+
Currently, the only available implementation is based on synchronous [requests]() library. Client provides the following
|
80
|
+
features:
|
81
|
+
* Error handling - depending on HTTP status code the client raises different exceptions allowing to handle errors
|
82
|
+
in a more granular way.
|
83
|
+
* Throttling - the client is able to throttle the requests to the API to avoid hitting the rate limits.
|
84
|
+
* Retry - the client is able to retry the requests in case of the network errors.
|
85
|
+
|
86
|
+
#### API Client Usage
|
87
|
+
|
88
|
+
The following example illustrates the usage of the API client:
|
89
|
+
|
90
|
+
```python
|
91
|
+
from openepd.api.sync_client import OpenEpdApiClientSync
|
92
|
+
|
93
|
+
# Setup the client
|
94
|
+
api_client = OpenEpdApiClientSync(
|
95
|
+
"https://openepd.buildingtransparency.org/api",
|
96
|
+
"<Your API Token>",
|
97
|
+
)
|
98
|
+
|
99
|
+
# Use API, e.g. get EPD by ID
|
100
|
+
epd = api_client.epds.get_by_openxpd_uuid("ec3b9j5t")
|
101
|
+
```
|
102
|
+
|
72
103
|
### Bundle
|
73
104
|
|
74
105
|
Bundle is a format which allows to bundle multiple openEPD objects together (it might be EPDs, PCRs, Orgs + any
|
@@ -0,0 +1,95 @@
|
|
1
|
+
openepd/__init__.py,sha256=rqQJWF5jpYAgRbbAycUfWMGsr5kGtfjmwzsTeqbElJw,837
|
2
|
+
openepd/__version__.py,sha256=MqTqfn60ULDejKjKIAtuxNQxkhxVa4jNydXkB3BvLEg,855
|
3
|
+
openepd/api/__init__.py,sha256=rqQJWF5jpYAgRbbAycUfWMGsr5kGtfjmwzsTeqbElJw,837
|
4
|
+
openepd/api/base_sync_client.py,sha256=JcNpWsGoIK_1Eg27CAQd7nIjbcfD56jQ1nS6B48Q0cI,21142
|
5
|
+
openepd/api/category/__init__.py,sha256=rqQJWF5jpYAgRbbAycUfWMGsr5kGtfjmwzsTeqbElJw,837
|
6
|
+
openepd/api/category/dto.py,sha256=oyeJp_F_55OVXfTX7EVl6VYrzA_STm4h-x52CW06ExQ,1067
|
7
|
+
openepd/api/category/sync_api.py,sha256=QLEyga590UON9zv7864NjdSPkxSoeWObIOCUOLhD9tc,1588
|
8
|
+
openepd/api/common.py,sha256=rfxjaDd7gGoen85wSooHt0W9xHdUlSlkTlkr2CO1ccg,8681
|
9
|
+
openepd/api/dto/__init__.py,sha256=rqQJWF5jpYAgRbbAycUfWMGsr5kGtfjmwzsTeqbElJw,837
|
10
|
+
openepd/api/dto/base.py,sha256=s4CivEzgMNG4WKZJoEt3vALVZNWk76soBCNtzr_AVL4,1250
|
11
|
+
openepd/api/dto/common.py,sha256=M5S0WphcRhaeJSP9jJY-IqNgLZIRSYmqvlNh_9fNFVY,4705
|
12
|
+
openepd/api/dto/meta.py,sha256=_rJ6pZtXyJmoaMhCZ1xnnCzp127LJ6RkayI4sdm6QYc,2377
|
13
|
+
openepd/api/dto/mf.py,sha256=ac1W0QPg9ndsUq_Rnv5E7HR0AZXuKSifBoJXo_I4zFE,2211
|
14
|
+
openepd/api/dto/params.py,sha256=rqQJWF5jpYAgRbbAycUfWMGsr5kGtfjmwzsTeqbElJw,837
|
15
|
+
openepd/api/epd/__init__.py,sha256=rqQJWF5jpYAgRbbAycUfWMGsr5kGtfjmwzsTeqbElJw,837
|
16
|
+
openepd/api/epd/dto.py,sha256=vFQURo9QzK5fcDNSjc4BlrK2KpkWbnxFle5jPMq-KTw,5091
|
17
|
+
openepd/api/epd/sync_api.py,sha256=JEZR3WBZHqP4yOk1qkWsz0oKHMgegoZ7Pvoh6YTVefg,4391
|
18
|
+
openepd/api/errors.py,sha256=K6L_T91iJLFSl_7hVS6poV_BUjZJe3qJBUzp8mItm7g,2376
|
19
|
+
openepd/api/pcr/__init__.py,sha256=rqQJWF5jpYAgRbbAycUfWMGsr5kGtfjmwzsTeqbElJw,837
|
20
|
+
openepd/api/pcr/dto.py,sha256=vjkxHzbBLrG9cDoaAbPOPSPjhIRI8z3XewI5wfry6Hk,1649
|
21
|
+
openepd/api/pcr/sync_api.py,sha256=j8g23-FuaN2JkROVRo32v77JWpTWDIOWnIyomBbqNd4,1805
|
22
|
+
openepd/api/sync_client.py,sha256=DBkoB8rH8OG1d1sJ77h2YvIP8aFQX5eCfaX4sTN0vEs,2504
|
23
|
+
openepd/api/test/__init__.py,sha256=rqQJWF5jpYAgRbbAycUfWMGsr5kGtfjmwzsTeqbElJw,837
|
24
|
+
openepd/bundle/__init__.py,sha256=rqQJWF5jpYAgRbbAycUfWMGsr5kGtfjmwzsTeqbElJw,837
|
25
|
+
openepd/bundle/base.py,sha256=wSmRp6IjWtOSoSBpK_OhrxJ2OgHu_2hNnwYS4mmNFSA,7086
|
26
|
+
openepd/bundle/model.py,sha256=TdFkUTOnd_dklcP-Qahp2glvk1YgEM0HfiqQz5MikeA,2663
|
27
|
+
openepd/bundle/reader.py,sha256=z4v_UWyaosktN3DdmnRx8GpLq4DkejjoUsckFfCgUac,6904
|
28
|
+
openepd/bundle/writer.py,sha256=AfvzzdAr9ybIbtiZfuX2mAGfddamTxXxUTxtHHrs2Gc,8353
|
29
|
+
openepd/compat/__init__.py,sha256=rqQJWF5jpYAgRbbAycUfWMGsr5kGtfjmwzsTeqbElJw,837
|
30
|
+
openepd/compat/pydantic.py,sha256=wh7vExwILD9QUYAKbrALXtTqNvsc6E8tjVVYXF2WvTY,1174
|
31
|
+
openepd/model/__init__.py,sha256=rqQJWF5jpYAgRbbAycUfWMGsr5kGtfjmwzsTeqbElJw,837
|
32
|
+
openepd/model/base.py,sha256=Yerebn3KP8-CdeeKOuQ1h16qkZnNGn7yvZNiAWutpsI,9154
|
33
|
+
openepd/model/category.py,sha256=NhQzKYi02DcdDy83qI_WZuGfYAUcZgsEPEXRa0nGqxs,1856
|
34
|
+
openepd/model/common.py,sha256=Uuc-Rbo89Xw89YWbiMtqggvWeFptbgNw6KWdZI8f7wg,5659
|
35
|
+
openepd/model/epd.py,sha256=WWWZU1c7HWkZZqkhUNMjyaHJYThboN6ZjmZ2L4UNM6I,14299
|
36
|
+
openepd/model/factory.py,sha256=qQZNb4yFN1EQWHHVS-fHnJ2gB-vsKz9hVmXXJE7YaRU,1918
|
37
|
+
openepd/model/lcia.py,sha256=MTNbNoTiZn_11XIx9m5D_iyTr0mnfIJYAyfnc1CHaSY,17165
|
38
|
+
openepd/model/org.py,sha256=XJcfKGPN90IIGcN-S1ZNy8Zh_yM28iT2wbJD3pk3T08,4013
|
39
|
+
openepd/model/pcr.py,sha256=MypmvAR56ldoSMqtq0JKVeqUGHD-heyfsOPXjGcZFQ4,4620
|
40
|
+
openepd/model/specs/README.md,sha256=W5LSMpZuW5x36cKS4HRfeFsClsRf8J9yHMMICghdc0s,862
|
41
|
+
openepd/model/specs/__init__.py,sha256=lN1u5WN1H9uwWSHb4sFEBxJ3_tvGYar9iCvsDD67JTQ,5176
|
42
|
+
openepd/model/specs/aluminium.py,sha256=wRtkixZqO_27mulz0m60oZfAR1I0r_4cikvT-9U4lnk,2271
|
43
|
+
openepd/model/specs/asphalt.py,sha256=1Vmw3jsdTyhPaM9ftIjMvpLj7GbGnZLytaJYDDJQ7bU,3549
|
44
|
+
openepd/model/specs/base.py,sha256=T0HsHTFf5ew1sLgUf-7dfifrMPMup7WRc-C5DHAXUc4,2697
|
45
|
+
openepd/model/specs/concrete.py,sha256=FnvzZw1VhOGgzqzuwu_F6Pu_c9Unovd4rzZWUu5KKMw,15655
|
46
|
+
openepd/model/specs/generated/accessories.py,sha256=Gt7H0Xz6kWce05MfF_J0v5sah8xrZnGcc7XhJBDkE30,1934
|
47
|
+
openepd/model/specs/generated/aggregates.py,sha256=TMw2f7K7kuKP23MWkeFyqKUIXJFIamwX3gSR-48k3AY,3161
|
48
|
+
openepd/model/specs/generated/aluminium.py,sha256=MXOo_Hnue2kbxg9nwF5d-C1rubqHACGjcJymGoRgUGU,2440
|
49
|
+
openepd/model/specs/generated/asphalt.py,sha256=D-V1J-VxjoAhfYA6ACnujLNGkAmqQ-RQRB4TEZeALpQ,3837
|
50
|
+
openepd/model/specs/generated/bulk_materials.py,sha256=DJgOjpj1ZqwwieMV_4wNKEgLnX1hKbAM4jaLDSICxNM,1034
|
51
|
+
openepd/model/specs/generated/cast_decks_and_underlayment.py,sha256=2VwwCgVRb3SuDy-x4EsI-n7GRnq0E7xrNE78CHoEExg,1058
|
52
|
+
openepd/model/specs/generated/cladding.py,sha256=RHq6irHwXJNnKuBOsA3h2g1jedMDOJPmV4WNjkdesgs,6939
|
53
|
+
openepd/model/specs/generated/cmu.py,sha256=mSRYG67m2c01C1dmYNyQjXQmYCcM4frpLQ3wKurTa50,2324
|
54
|
+
openepd/model/specs/generated/common.py,sha256=n_wuE-VtNFytpE-uLZrSXeeIIoNl2lSutPodiGKH94E,1117
|
55
|
+
openepd/model/specs/generated/concrete.py,sha256=cbTwYGxw0ttPYcg8zrkQEehNqtI3wakI7XgTEec0W24,6617
|
56
|
+
openepd/model/specs/generated/conveying_equipment.py,sha256=HcMSuyng9OlRKvUVYSstJx0XtaD8_cQ9ka8l4vG76AY,2422
|
57
|
+
openepd/model/specs/generated/electrical.py,sha256=oEhWkm4Au9N0xtTLUNTUVMxQUuJuvoLgOKccnAI0oGQ,10457
|
58
|
+
openepd/model/specs/generated/electrical_transmission_and_distribution_equipment.py,sha256=CiI6wcfo_tf6QoGkpAjUskCUKNJQvz0_N0RD7tF1oiA,2117
|
59
|
+
openepd/model/specs/generated/electricity.py,sha256=8W_vkCn_MDoB5rqGbPp2g04VMEG0ULMexD4OImgAt9U,1029
|
60
|
+
openepd/model/specs/generated/enums.py,sha256=_7eYhDClgRPrtt06o9N11enoKIIEUaHTBu9QGW3CABw,57079
|
61
|
+
openepd/model/specs/generated/finishes.py,sha256=xGjhNfpecOT7H4zElK34VSJuy8mfJ8gFF85PurKatao,22337
|
62
|
+
openepd/model/specs/generated/fire_and_smoke_protection.py,sha256=sbyEIMHB8zkIREqvUW6SSYZO9Tlx_zEoJrHWhqIjboI,2801
|
63
|
+
openepd/model/specs/generated/furnishings.py,sha256=NkS59ulTVqzSWFveM4Qd0Wv6bh0E-sx45fLrTH3Ixs4,3076
|
64
|
+
openepd/model/specs/generated/grouting.py,sha256=JAdrgzQObw_6nBeM6KHoTShXXBhG_38qDi6vOHvxXWM,1023
|
65
|
+
openepd/model/specs/generated/manufacturing_inputs.py,sha256=zlGyBUyGIW7Ruyt5XkPgu_q9yGaTFJQ4sjkdGQ-LucA,4599
|
66
|
+
openepd/model/specs/generated/masonry.py,sha256=VUd5Noeqtv8NWQfTqnM_8O0GZroib_pMipll2-pYQr8,3177
|
67
|
+
openepd/model/specs/generated/material_handling.py,sha256=qiyo_nH4nXmvTZxebqhOMXytCtpcQolHf3OLs1LBOx8,1225
|
68
|
+
openepd/model/specs/generated/mechanical.py,sha256=fELaoqPbNhgOQ_6oB8NIRfgz7Nff_nB6rqb0um1MGKY,11561
|
69
|
+
openepd/model/specs/generated/mechanical_insulation.py,sha256=CAtiQCPZUnRnfnKS8upqxQGuWoizCk4Hq8hgxg_0ZoA,1900
|
70
|
+
openepd/model/specs/generated/network_infrastructure.py,sha256=rejSwn6i0Ns4K2tFbv212wroM-zT4B2vTeIr3g8MVeg,8644
|
71
|
+
openepd/model/specs/generated/openings.py,sha256=n-SSgG-xHNwHGnKK5JN1PxLkxspTtSf8rgizXf3KtJs,17100
|
72
|
+
openepd/model/specs/generated/other_electrical_equipment.py,sha256=06HI_DEoHlywStzhj8rQtkfnXyWc-4Q4O3iE2rv4h2I,1057
|
73
|
+
openepd/model/specs/generated/other_materials.py,sha256=DCVKE_C0aVdotwayPuMATTlEmDjfTUm3-cQ8gzdxGMw,3453
|
74
|
+
openepd/model/specs/generated/plumbing.py,sha256=MOtI9aRGHtW64hui2x9ayHgkhtXtem0Aa6GTHJMpHw8,5439
|
75
|
+
openepd/model/specs/generated/precast_concrete.py,sha256=0aMCCXnCCgt7GGvjAppFAe-Hidjn-rokpvhOL1YgK0A,2690
|
76
|
+
openepd/model/specs/generated/sheathing.py,sha256=pV_j6R8mZ7Kyr8WYxP6UzsP3qSAqwxbVfe9V7jjSYb0,3516
|
77
|
+
openepd/model/specs/generated/steel.py,sha256=F3YzUb7jxUISn04Ye1NtMKORiYaqFfqfixTMfQWUJQY,8102
|
78
|
+
openepd/model/specs/generated/thermal_moisture_protection.py,sha256=3rqYO7_ljDMF_SLy0SsMjuiciOyL2qgVCyYz3b9ouTM,7963
|
79
|
+
openepd/model/specs/generated/utility_piping.py,sha256=qUJEFUWMav-TTB4UcJhRW2E_xftBwr0NwL_oTZmLuLQ,2768
|
80
|
+
openepd/model/specs/generated/wood.py,sha256=w_z9b2bIBjNyLEsn8MvFxVo0sVSuK75Mmuh9fQbOqgo,6199
|
81
|
+
openepd/model/specs/generated/wood_joists.py,sha256=UFakZ93LJqr-gSUz8hTyjx7BwHBq0BzWjpSKZ1B5TgQ,1880
|
82
|
+
openepd/model/specs/glass.py,sha256=JNIYlG7ybWTBPWBHq6kRGT4ibkkWGkDbb61zLPhKOPk,13664
|
83
|
+
openepd/model/specs/steel.py,sha256=6yRH_HK36kqn5XfAuF0_Q6TQtmeAc163qGIeStFGCM0,6367
|
84
|
+
openepd/model/specs/wood.py,sha256=DtviU3TrcWW74of8si2PCmaHxrdKWO1M2rSCBo_n6v8,5144
|
85
|
+
openepd/model/standard.py,sha256=8F70DCagplBe7IvkGJOUmt7td-uh_8zzdpvuVpjk0GY,1535
|
86
|
+
openepd/model/validation/__init__.py,sha256=rqQJWF5jpYAgRbbAycUfWMGsr5kGtfjmwzsTeqbElJw,837
|
87
|
+
openepd/model/validation/common.py,sha256=TPxIUtTNqNLiDsvuSE_dVREtNBe6XsagVMH4u1MYfpA,2652
|
88
|
+
openepd/model/validation/numbers.py,sha256=4gkMS35zKnGyfdFyLTgHncNmKpg8-WOYX9qaUr5scE4,1105
|
89
|
+
openepd/model/validation/quantity.py,sha256=GIPPtzOVwMYgKpx1XbDmoLQERlcoQ-VSK6k_EyMNQoQ,5287
|
90
|
+
openepd/model/versioning.py,sha256=1gqeeAhc2lVonq9ErOD3Ws7XZ0CgZnmlFpKHKrc9IwI,4690
|
91
|
+
openepd/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
92
|
+
openepd-3.1.0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
93
|
+
openepd-3.1.0.dist-info/METADATA,sha256=Dt47lv5UUQqXIW6C9fmatu1y0m8qsnDqMe5LekwXqYM,7762
|
94
|
+
openepd-3.1.0.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
|
95
|
+
openepd-3.1.0.dist-info/RECORD,,
|
openepd-2.0.0.dist-info/RECORD
DELETED
@@ -1,22 +0,0 @@
|
|
1
|
-
openepd/__init__.py,sha256=7_QNx3x9RCC7Lj9fHDD3nzgq6wV4Fmou7OJNWkCPnEw,837
|
2
|
-
openepd/__version__.py,sha256=o25JgAoI-IMF0vY6N8fBeWWG4eM6srG-yDZ5JQMBUkI,855
|
3
|
-
openepd/bundle/__init__.py,sha256=7_QNx3x9RCC7Lj9fHDD3nzgq6wV4Fmou7OJNWkCPnEw,837
|
4
|
-
openepd/bundle/base.py,sha256=5fhPzpQug8GTroJ8N2BaL15Uo0hf05E8hmJRahlf4rY,7086
|
5
|
-
openepd/bundle/model.py,sha256=cBKmeGmoPlp_RRmvTYHz9JNiIacz87846NkM4qXzWaM,2668
|
6
|
-
openepd/bundle/reader.py,sha256=OsB7HmGzKwl4s1rBUeW6E2LTLN2E3-H81gt-o4LE0UM,6935
|
7
|
-
openepd/bundle/writer.py,sha256=m-XTDqvy2aHCxSUtrKjDYgZPTl9jUlHzdCE-nwT0mqs,8366
|
8
|
-
openepd/model/__init__.py,sha256=7_QNx3x9RCC7Lj9fHDD3nzgq6wV4Fmou7OJNWkCPnEw,837
|
9
|
-
openepd/model/base.py,sha256=tpExVmKlvli1wS33SNXJKf-Dv3ue_B7pJUB4W5kLjn8,5361
|
10
|
-
openepd/model/common.py,sha256=443ZHYUiaupipu4_fPsTEUtGg9moKDPFJimkQZNXZJE,5617
|
11
|
-
openepd/model/epd.py,sha256=jpmqRvANedeSNnfEEzzURHAnNiQGTo_YHb6B6B4BBY4,13737
|
12
|
-
openepd/model/lcia.py,sha256=OwmuDmeU2h8ScqlXV1t1scFcMJ840FqR3tXidx0bn3Q,16731
|
13
|
-
openepd/model/org.py,sha256=yQd6H6SY8kwUj5CN7RTZvHBQmGvL9YFPMjga_AlaUCE,3816
|
14
|
-
openepd/model/pcr.py,sha256=Z3Uqs5qa2YTSb01-iB1K5DpQSrCpOeSxEq9Zu7raxuk,3372
|
15
|
-
openepd/model/specs/__init__.py,sha256=JmbvfzwO0QmNcrxGqYE_KjvYHBmc2bnDm7cyZsaMG78,1137
|
16
|
-
openepd/model/specs/concrete.py,sha256=oIztCmkBwHa5fvPSlDwrYfAUpFaCG58FOg0FmFFykb4,3354
|
17
|
-
openepd/model/standard.py,sha256=Nv_H3Lhu79y4waWzzKpwODCPPGrlWwRIl6eZBMSc9Qg,1519
|
18
|
-
openepd/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
19
|
-
openepd-2.0.0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
20
|
-
openepd-2.0.0.dist-info/METADATA,sha256=H8k3xipw3m62vkB1ynhV1LrgazELL9gvz199pWquQp0,6691
|
21
|
-
openepd-2.0.0.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
|
22
|
-
openepd-2.0.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|