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
openepd/api/epd/dto.py
ADDED
@@ -0,0 +1,121 @@
|
|
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 TypeAlias
|
21
|
+
|
22
|
+
from openepd.api.dto.base import BaseOpenEpdApiModel
|
23
|
+
from openepd.api.dto.common import BaseMeta, OpenEpdApiResponse
|
24
|
+
from openepd.api.dto.meta import PagingMetaMixin, WarningMetaMixin
|
25
|
+
from openepd.api.dto.mf import MaterialFilterMetaMixin
|
26
|
+
from openepd.compat.pydantic import pyd
|
27
|
+
from openepd.model.common import Amount
|
28
|
+
from openepd.model.epd import Epd
|
29
|
+
|
30
|
+
|
31
|
+
class StatisticsDto(BaseOpenEpdApiModel):
|
32
|
+
"""
|
33
|
+
DTO describes statistics of EPD.
|
34
|
+
|
35
|
+
Statistics contains aggregated parameters such as percentiles distributions of GWP and other parameters.
|
36
|
+
"""
|
37
|
+
|
38
|
+
# percentiles
|
39
|
+
pct10_gwp: float = pyd.Field(
|
40
|
+
description="10th percentile GWP for this statistics measured in kgCO2e per declared unit"
|
41
|
+
)
|
42
|
+
achievable_target: float = pyd.Field(
|
43
|
+
description="Achievable target. 20th percentile of GWP measured in kgCO2e per declared unit", example=445.65
|
44
|
+
)
|
45
|
+
pct30_gwp: float = pyd.Field(
|
46
|
+
description="30th percentile GWP for this statistics measured in kgCO2e per declared unit"
|
47
|
+
)
|
48
|
+
pct40_gwp: float = pyd.Field(
|
49
|
+
description="40th percentile GWP for this statistics measured in kgCO2e per declared unit"
|
50
|
+
)
|
51
|
+
pct50_gwp: float = pyd.Field(
|
52
|
+
description="50th percentile GWP for this statistics measured in kgCO2e per declared unit"
|
53
|
+
)
|
54
|
+
pct60_gwp: float = pyd.Field(
|
55
|
+
description="60th percentile GWP for this statistics measured in kgCO2e per declared unit"
|
56
|
+
)
|
57
|
+
pct70_gwp: float = pyd.Field(
|
58
|
+
description="70th percentile GWP for this statistics measured in kgCO2e per declared unit"
|
59
|
+
)
|
60
|
+
conservative_estimate: float = pyd.Field(
|
61
|
+
description="Conservative estimate. 80th percentile of GWP per declared unit measured in kgCO2e",
|
62
|
+
example=640.778,
|
63
|
+
)
|
64
|
+
pct90_gwp: float = pyd.Field(
|
65
|
+
description="70th percentile GWP for this statistics measured in kgCO2e per declared unit"
|
66
|
+
)
|
67
|
+
|
68
|
+
# stats
|
69
|
+
average: float = pyd.Field(description="Average GWP in kgCO2e per declared unit", example=554.2)
|
70
|
+
min: float | None = pyd.Field(
|
71
|
+
description="Min GWP of returned results measured in kgCO2e per declared unit", example=998.3
|
72
|
+
)
|
73
|
+
max: float | None = pyd.Field(
|
74
|
+
description="Max GWP of returned results measured in kgCO2e per declared unit", example=120.0
|
75
|
+
)
|
76
|
+
|
77
|
+
# percentiles w/out burden of doubt
|
78
|
+
pct20_gwp_no_bod: float | None = pyd.Field(
|
79
|
+
description="20th percentile of GWP (kgCO2e per declared unit), no burden of doubt", example=120
|
80
|
+
)
|
81
|
+
pct40_gwp_no_bod: float | None = pyd.Field(
|
82
|
+
description="40th percentile of GWP (kgCO2e per declared unit), no burden of doubt", example=120
|
83
|
+
)
|
84
|
+
pct60_gwp_no_bod: float | None = pyd.Field(
|
85
|
+
description="60th percentile of GWP (kgCO2e per declared unit), no burden of doubt", example=120
|
86
|
+
)
|
87
|
+
pct80_gwp_no_bod: float | None = pyd.Field(
|
88
|
+
description="80th percentile of GWP (kgCO2e per declared unit), no burden of doubt", example=120
|
89
|
+
)
|
90
|
+
average_gwp_no_bod: float | None = pyd.Field(description="Average GWP, no burden of doubt", example=120)
|
91
|
+
|
92
|
+
# set parameters
|
93
|
+
standard_deviation: float = pyd.Field(description="Standard deviation", example=87.62)
|
94
|
+
epds_count: int = pyd.Field(description="Number of EPDs participated in statistics", example=55)
|
95
|
+
industry_epds_count: int = pyd.Field(
|
96
|
+
description="Number of Industry-wide EPDs participated in statistics", example=4
|
97
|
+
)
|
98
|
+
generic_estimates_count: int = pyd.Field(
|
99
|
+
description="Number of Generic Estimates participated in statistics", example=0
|
100
|
+
)
|
101
|
+
|
102
|
+
declared_unit: Amount = pyd.Field(
|
103
|
+
description="Declared unit for the statistics. "
|
104
|
+
"Statistical values - percentiles, averages etc - are based on this unit of product"
|
105
|
+
)
|
106
|
+
|
107
|
+
|
108
|
+
class EpdSearchMeta(MaterialFilterMetaMixin, PagingMetaMixin, WarningMetaMixin, BaseMeta):
|
109
|
+
"""Metadata for EPD Search endpoint."""
|
110
|
+
|
111
|
+
pass
|
112
|
+
|
113
|
+
|
114
|
+
class EpdStatisticsMeta(MaterialFilterMetaMixin, WarningMetaMixin, BaseMeta):
|
115
|
+
"""Metadata for EPD Statistics endpoint."""
|
116
|
+
|
117
|
+
pass
|
118
|
+
|
119
|
+
|
120
|
+
EpdSearchResponse: TypeAlias = OpenEpdApiResponse[list[Epd], EpdSearchMeta]
|
121
|
+
EpdStatisticsResponse: TypeAlias = OpenEpdApiResponse[StatisticsDto, EpdStatisticsMeta]
|
@@ -0,0 +1,105 @@
|
|
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 openepd.api.base_sync_client import BaseApiMethodGroup
|
21
|
+
from openepd.api.common import StreamingListResponse
|
22
|
+
from openepd.api.epd.dto import EpdSearchResponse, EpdStatisticsResponse, StatisticsDto
|
23
|
+
from openepd.model.epd import Epd
|
24
|
+
|
25
|
+
|
26
|
+
class EpdApi(BaseApiMethodGroup):
|
27
|
+
"""API methods for EPDs."""
|
28
|
+
|
29
|
+
def get_by_openxpd_uuid(self, uuid: str) -> Epd:
|
30
|
+
"""
|
31
|
+
Get EPD by OpenEPD UUID.
|
32
|
+
|
33
|
+
:param uuid: OpenEPD UUID
|
34
|
+
:return: EPD
|
35
|
+
:raise ObjectNotFound: if EPD is not found
|
36
|
+
"""
|
37
|
+
content = self._client.do_request("get", f"/epds/{uuid}").json()
|
38
|
+
return Epd.parse_obj(content)
|
39
|
+
|
40
|
+
def find_raw(self, omf: str, page_num: int = 1, page_size: int = 10) -> EpdSearchResponse:
|
41
|
+
"""
|
42
|
+
Find EPDs by Open Material Filter(OMF).
|
43
|
+
|
44
|
+
OMF is a query language for searching materials/EPDs, running statistics and so on.
|
45
|
+
It can exist in a string form, which is accepted by most of the endpoints.
|
46
|
+
|
47
|
+
:param omf: OMF - open material filter string (see OMF spec).
|
48
|
+
:param page_num: page number
|
49
|
+
:param page_size: page size
|
50
|
+
:return: the list of EPDs
|
51
|
+
"""
|
52
|
+
content = self._client.do_request(
|
53
|
+
"get",
|
54
|
+
"/v2/epds/search",
|
55
|
+
params=dict(
|
56
|
+
omf=omf,
|
57
|
+
page_number=page_num,
|
58
|
+
page_size=page_size,
|
59
|
+
),
|
60
|
+
).json()
|
61
|
+
return EpdSearchResponse.parse_obj(content)
|
62
|
+
|
63
|
+
def find(self, omf: str, page_size: int | None = None) -> StreamingListResponse[Epd]:
|
64
|
+
"""
|
65
|
+
Find EPDs by Open Material Filter(OMF).
|
66
|
+
|
67
|
+
OMF is a query language for searching materials/EPDs, running statistics and so on.
|
68
|
+
It can exist in a string form, which is accepted by most of the endpoints.
|
69
|
+
|
70
|
+
:param omf: OMF - open material filter string (see OMF spec).
|
71
|
+
:param page_size: page size, None for default
|
72
|
+
:return: streaming list of EPDs
|
73
|
+
"""
|
74
|
+
|
75
|
+
def _get_page(p_num: int, p_size: int) -> EpdSearchResponse:
|
76
|
+
return self.find_raw(omf, page_num=p_num, page_size=p_size)
|
77
|
+
|
78
|
+
return StreamingListResponse[Epd](_get_page, page_size=page_size)
|
79
|
+
|
80
|
+
def get_statistics_raw(self, omf: str) -> EpdStatisticsResponse:
|
81
|
+
"""
|
82
|
+
Get statistics for a given search query.
|
83
|
+
|
84
|
+
Statistics contains aggregated parameters such as percentiles distributions of GWP and other parameters.
|
85
|
+
Please note - in addition to product EPDs statistics might include industry-wide EPDs and even generic
|
86
|
+
estimates to provide a more complete picture where there is not enough product EPDs.
|
87
|
+
|
88
|
+
:param omf: OMF - open material filter string (see OMF spec).
|
89
|
+
:return: statistics wrapped in OpenEpdApiResponse
|
90
|
+
"""
|
91
|
+
content = self._client.do_request("get", "/v2/epds/statistics", params=dict(omf=omf)).json()
|
92
|
+
return EpdStatisticsResponse.parse_obj(content)
|
93
|
+
|
94
|
+
def get_statistics(self, omf: str) -> StatisticsDto:
|
95
|
+
"""
|
96
|
+
Get statistics for a given search query.
|
97
|
+
|
98
|
+
Statistics contains aggregated parameters such as percentiles distributions of GWP and other parameters.
|
99
|
+
Please note - in addition to product EPDs statistics might include industry-wide EPDs and even generic
|
100
|
+
estimates to provide a more complete picture where there is not enough product EPDs.
|
101
|
+
|
102
|
+
:param omf: OMF - open material filter string (see OMF spec).
|
103
|
+
:return: statistics wrapped in OpenEpdApiResponse
|
104
|
+
"""
|
105
|
+
return self.get_statistics_raw(omf).payload
|
openepd/api/errors.py
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2024 by C Change Labs Inc. www.c-change-labs.com
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
#
|
16
|
+
# This software was developed with support from the Skanska USA,
|
17
|
+
# Charles Pankow Foundation, Microsoft Sustainability Fund, Interface, MKA Foundation, and others.
|
18
|
+
# Find out more at www.BuildingTransparency.org
|
19
|
+
#
|
20
|
+
from typing import Any
|
21
|
+
|
22
|
+
|
23
|
+
class ApiError(Exception):
|
24
|
+
"""Base class for all API errors."""
|
25
|
+
|
26
|
+
def __init__(
|
27
|
+
self, http_status: int, error_summary: str, response: Any = None, error_code: str | None = None
|
28
|
+
) -> None:
|
29
|
+
super().__init__(error_summary)
|
30
|
+
self.error_summary = error_summary
|
31
|
+
self.http_status = http_status
|
32
|
+
self.error_code = error_code
|
33
|
+
self.response = response
|
34
|
+
|
35
|
+
|
36
|
+
class ObjectNotFound(ApiError):
|
37
|
+
"""Object not found error."""
|
38
|
+
|
39
|
+
pass
|
40
|
+
|
41
|
+
|
42
|
+
class ServerError(ApiError):
|
43
|
+
"""Server error."""
|
44
|
+
|
45
|
+
pass
|
46
|
+
|
47
|
+
|
48
|
+
class ValidationError(ApiError):
|
49
|
+
"""Validation error."""
|
50
|
+
|
51
|
+
def __init__(
|
52
|
+
self,
|
53
|
+
http_status: int,
|
54
|
+
error_summary: str,
|
55
|
+
validation_errors: dict[str, list[str]] | None,
|
56
|
+
response: Any,
|
57
|
+
error_code: str | None,
|
58
|
+
) -> None:
|
59
|
+
super().__init__(http_status, error_summary, response, error_code)
|
60
|
+
self.validation_errors: dict[str, list[str]] = validation_errors or {}
|
61
|
+
|
62
|
+
def __str__(self) -> str:
|
63
|
+
result: list[str] = ["Validation errors:"]
|
64
|
+
for code, errors in self.validation_errors.items():
|
65
|
+
result.append(f"{code}:")
|
66
|
+
for e in errors:
|
67
|
+
result.append(f" {e}")
|
68
|
+
return "\n".join(result)
|
69
|
+
|
70
|
+
|
71
|
+
class AuthError(ApiError):
|
72
|
+
"""Common class for any authentication related errors."""
|
73
|
+
|
74
|
+
pass
|
75
|
+
|
76
|
+
|
77
|
+
class NotAuthorizedError(AuthError):
|
78
|
+
"""Not authorized error."""
|
79
|
+
|
80
|
+
pass
|
81
|
+
|
82
|
+
|
83
|
+
class AccessDeniedError(AuthError):
|
84
|
+
"""Access denied error."""
|
85
|
+
|
86
|
+
pass
|
@@ -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
|
+
#
|
openepd/api/pcr/dto.py
ADDED
@@ -0,0 +1,41 @@
|
|
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 openepd.api.dto.base import BaseOpenEpdApiModel
|
21
|
+
from openepd.compat.pydantic import pyd
|
22
|
+
|
23
|
+
|
24
|
+
class PcrRef(BaseOpenEpdApiModel):
|
25
|
+
"""Reference to a PCR."""
|
26
|
+
|
27
|
+
id: str | None = pyd.Field(
|
28
|
+
description="The unique ID for this PCR. To ensure global uniqueness, should be registered "
|
29
|
+
"at open-xpd-uuid.cqd.io/register or a coordinating registry.",
|
30
|
+
example="ec3xpgq2",
|
31
|
+
default=None,
|
32
|
+
)
|
33
|
+
name: str | None = pyd.Field(
|
34
|
+
max_length=200,
|
35
|
+
description="Full document name as listed in source document",
|
36
|
+
example="c-PCR-003 Concrete and concrete elements (EN 16757)",
|
37
|
+
)
|
38
|
+
ref: pyd.AnyUrl | None = pyd.Field(
|
39
|
+
description="Reference to this PCR's JSON object",
|
40
|
+
example="https://openepd.buildingtransparency.org/api/pcrs/1u7zsed8",
|
41
|
+
)
|
@@ -0,0 +1,49 @@
|
|
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 openepd.api.base_sync_client import BaseApiMethodGroup
|
21
|
+
from openepd.api.pcr.dto import PcrRef
|
22
|
+
from openepd.model.pcr import Pcr
|
23
|
+
|
24
|
+
|
25
|
+
class PcrApi(BaseApiMethodGroup):
|
26
|
+
"""API methods for EPDs."""
|
27
|
+
|
28
|
+
def get_by_openxpd_uuid(self, uuid: str) -> Pcr:
|
29
|
+
"""
|
30
|
+
Get PCR by Open xPD UUID.
|
31
|
+
|
32
|
+
:param uuid: Open xPD UUID
|
33
|
+
:return: PCR
|
34
|
+
:raise ObjectNotFound: if PCR not found
|
35
|
+
:raise ValidationError: if openxpd_uuid is invalid
|
36
|
+
"""
|
37
|
+
content = self._client.do_request("get", f"/pcrs/{uuid}").json()
|
38
|
+
return Pcr.parse_obj(content)
|
39
|
+
|
40
|
+
def create(self, pcr: Pcr) -> PcrRef:
|
41
|
+
"""
|
42
|
+
Create a new PCR.
|
43
|
+
|
44
|
+
:param pcr: PCR to create
|
45
|
+
:return: reference to the created PCR
|
46
|
+
:raise ValidationError: if given object PCR is invalid
|
47
|
+
"""
|
48
|
+
pcr_ref_obj = self._client.do_request("post", "/pcrs", json=pcr.to_serializable()).json()
|
49
|
+
return PcrRef.parse_obj(pcr_ref_obj)
|
@@ -0,0 +1,67 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2024 by C Change Labs Inc. www.c-change-labs.com
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
#
|
16
|
+
# This software was developed with support from the Skanska USA,
|
17
|
+
# Charles Pankow Foundation, Microsoft Sustainability Fund, Interface, MKA Foundation, and others.
|
18
|
+
# Find out more at www.BuildingTransparency.org
|
19
|
+
#
|
20
|
+
__all__ = ("OpenEpdApiClientSync",)
|
21
|
+
|
22
|
+
from requests.auth import AuthBase
|
23
|
+
|
24
|
+
from openepd.api.base_sync_client import SyncHttpClient, TokenAuth
|
25
|
+
from openepd.api.category.sync_api import CategoryApi
|
26
|
+
from openepd.api.epd.sync_api import EpdApi
|
27
|
+
from openepd.api.pcr.sync_api import PcrApi
|
28
|
+
|
29
|
+
|
30
|
+
class OpenEpdApiClientSync:
|
31
|
+
"""Synchronous API client for OpenEPD."""
|
32
|
+
|
33
|
+
def __init__(self, base_url: str, auth_token: str | None, **kwargs) -> None:
|
34
|
+
"""
|
35
|
+
Construct an API client.
|
36
|
+
|
37
|
+
:param base_url: base URL of the API
|
38
|
+
:param auth_token: authentication token
|
39
|
+
:param kwargs: additional arguments to pass to the HTTP client. See SyncHttpClient constructor for details.
|
40
|
+
"""
|
41
|
+
super().__init__()
|
42
|
+
auth: AuthBase | None = TokenAuth(auth_token) if auth_token is not None else None
|
43
|
+
self._http_client = SyncHttpClient(base_url, auth=auth, **kwargs)
|
44
|
+
self.__epd_api: EpdApi | None = None
|
45
|
+
self.__pcr_api: PcrApi | None = None
|
46
|
+
self.__category_api: CategoryApi | None = None
|
47
|
+
|
48
|
+
@property
|
49
|
+
def epds(self) -> EpdApi:
|
50
|
+
"""Get the EPD API."""
|
51
|
+
if self.__epd_api is None:
|
52
|
+
self.__epd_api = EpdApi(self._http_client)
|
53
|
+
return self.__epd_api
|
54
|
+
|
55
|
+
@property
|
56
|
+
def pcrs(self) -> PcrApi:
|
57
|
+
"""Get the PCR API."""
|
58
|
+
if self.__pcr_api is None:
|
59
|
+
self.__pcr_api = PcrApi(self._http_client)
|
60
|
+
return self.__pcr_api
|
61
|
+
|
62
|
+
@property
|
63
|
+
def categories(self) -> CategoryApi:
|
64
|
+
"""Get the Category API."""
|
65
|
+
if self.__category_api is None:
|
66
|
+
self.__category_api = CategoryApi(self._http_client)
|
67
|
+
return self.__category_api
|
@@ -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
|
+
#
|
openepd/bundle/__init__.py
CHANGED
openepd/bundle/base.py
CHANGED
openepd/bundle/model.py
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
#
|
2
|
-
# Copyright
|
2
|
+
# Copyright 2024 by C Change Labs Inc. www.c-change-labs.com
|
3
3
|
#
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
5
|
# you may not use this file except in compliance with the License.
|
@@ -19,8 +19,7 @@
|
|
19
19
|
#
|
20
20
|
from enum import StrEnum
|
21
21
|
|
22
|
-
|
23
|
-
|
22
|
+
from openepd.compat.pydantic import pyd
|
24
23
|
from openepd.model.base import BaseOpenEpdSchema
|
25
24
|
|
26
25
|
|
@@ -77,10 +76,10 @@ class AssetInfo(BaseOpenEpdSchema):
|
|
77
76
|
"""The name of the asset."""
|
78
77
|
type: AssetType
|
79
78
|
"""The type of the asset."""
|
80
|
-
lang: str | None
|
79
|
+
lang: str | None
|
81
80
|
"""The language of the asset."""
|
82
|
-
rel_type: str | None
|
83
|
-
rel_asset: str | None
|
81
|
+
rel_type: str | None
|
82
|
+
rel_asset: str | None
|
84
83
|
comment: str | None = pyd.Field(default=None)
|
85
84
|
content_type: str | None = pyd.Field(default=None)
|
86
85
|
size: int | None = pyd.Field(default=None)
|
openepd/bundle/reader.py
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
#
|
2
|
-
# Copyright
|
2
|
+
# Copyright 2024 by C Change Labs Inc. www.c-change-labs.com
|
3
3
|
#
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
5
|
# you may not use this file except in compliance with the License.
|
@@ -35,7 +35,7 @@ class DefaultBundleReader(BaseBundleReader):
|
|
35
35
|
self._bundle_archive = zipfile.ZipFile(bundle_file, mode="r")
|
36
36
|
try:
|
37
37
|
with self._bundle_archive.open("manifest", "r") as manifest_stream:
|
38
|
-
self.__manifest = BundleManifest.
|
38
|
+
self.__manifest = BundleManifest.parse_raw(manifest_stream.read())
|
39
39
|
except Exception as e:
|
40
40
|
raise ValueError("The bundle file is not valid. Manifest reading error: " + str(e)) from e
|
41
41
|
try:
|
@@ -49,7 +49,7 @@ class DefaultBundleReader(BaseBundleReader):
|
|
49
49
|
|
50
50
|
def get_manifest(self) -> BundleManifest:
|
51
51
|
"""Get the manifest of the bundle. Manifest object is immutable."""
|
52
|
-
return self.__manifest.
|
52
|
+
return self.__manifest.copy(deep=True)
|
53
53
|
|
54
54
|
def __create_asset_filter(
|
55
55
|
self,
|
@@ -86,7 +86,7 @@ class DefaultBundleReader(BaseBundleReader):
|
|
86
86
|
with self._bundle_archive.open("toc", "r") as toc_stream:
|
87
87
|
toc_reader = csv.DictReader(io.TextIOWrapper(toc_stream, encoding="utf-8"), dialect="toc")
|
88
88
|
for x in toc_reader:
|
89
|
-
yield AssetInfo.
|
89
|
+
yield AssetInfo.parse_obj(self.__preprocess_csv_dict(x))
|
90
90
|
|
91
91
|
def __check_toc(self):
|
92
92
|
with self._bundle_archive.open("toc", "r") as toc_stream:
|
@@ -158,4 +158,4 @@ class DefaultBundleReader(BaseBundleReader):
|
|
158
158
|
if asset.type != obj_class.get_asset_type():
|
159
159
|
raise ValueError(f"Asset type mismatch. Expected {obj_class.get_asset_type()}, got {asset.type}")
|
160
160
|
with self._bundle_archive.open(asset.ref, "r") as asset_stream:
|
161
|
-
return obj_class.
|
161
|
+
return obj_class.parse_raw(asset_stream.read())
|
openepd/bundle/writer.py
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
#
|
2
|
-
# Copyright
|
2
|
+
# Copyright 2024 by C Change Labs Inc. www.c-change-labs.com
|
3
3
|
#
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
5
|
# you may not use this file except in compliance with the License.
|
@@ -117,7 +117,8 @@ class DefaultBundleWriter(BaseBundleWriter):
|
|
117
117
|
custom_data=custom_data,
|
118
118
|
)
|
119
119
|
self.__write_data_stream(
|
120
|
-
asset_info,
|
120
|
+
asset_info,
|
121
|
+
BytesIO(obj.json(indent=2, exclude_unset=True, exclude_none=True, by_alias=True).encode("utf-8")),
|
121
122
|
)
|
122
123
|
self.__register_entry(asset_info)
|
123
124
|
return asset_info
|
@@ -125,7 +126,7 @@ class DefaultBundleWriter(BaseBundleWriter):
|
|
125
126
|
def commit(self):
|
126
127
|
"""Write the manifest and TOC to the bundle. This will be called automatically when the bundle is closed."""
|
127
128
|
with self._bundle_archive.open("manifest", "w") as manifest_stream:
|
128
|
-
manifest_stream.write(self.__manifest.
|
129
|
+
manifest_stream.write(self.__manifest.json(indent=2, exclude_none=True).encode("utf-8"))
|
129
130
|
with self._bundle_archive.open("toc", "w") as toc_stream:
|
130
131
|
toc_stream.write(self.__toc_buffer.getvalue().encode("utf-8"))
|
131
132
|
|
@@ -137,7 +138,7 @@ class DefaultBundleWriter(BaseBundleWriter):
|
|
137
138
|
def __register_entry(self, asset_info: AssetInfo):
|
138
139
|
if asset_info.ref in self.__added_entries:
|
139
140
|
raise ValueError(f"Asset {asset_info.ref} already exists in the bundle.")
|
140
|
-
self._toc_writer.writerow(asset_info.
|
141
|
+
self._toc_writer.writerow(asset_info.dict(exclude_unset=True, exclude_none=True))
|
141
142
|
self.__added_entries.add(asset_info.ref)
|
142
143
|
type_counter = self.__manifest.assets.count_by_type.get(asset_info.type, 0) + 1
|
143
144
|
self.__manifest.assets.count_by_type[asset_info.type] = type_counter
|
@@ -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
|
+
#
|