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.
Files changed (95) hide show
  1. openepd/__init__.py +1 -1
  2. openepd/__version__.py +2 -2
  3. openepd/api/__init__.py +19 -0
  4. openepd/api/base_sync_client.py +550 -0
  5. openepd/api/category/__init__.py +19 -0
  6. openepd/api/category/dto.py +25 -0
  7. openepd/api/category/sync_api.py +44 -0
  8. openepd/api/common.py +239 -0
  9. openepd/api/dto/__init__.py +19 -0
  10. openepd/api/dto/base.py +41 -0
  11. openepd/api/dto/common.py +115 -0
  12. openepd/api/dto/meta.py +69 -0
  13. openepd/api/dto/mf.py +59 -0
  14. openepd/api/dto/params.py +19 -0
  15. openepd/api/epd/__init__.py +19 -0
  16. openepd/api/epd/dto.py +121 -0
  17. openepd/api/epd/sync_api.py +105 -0
  18. openepd/api/errors.py +86 -0
  19. openepd/api/pcr/__init__.py +19 -0
  20. openepd/api/pcr/dto.py +41 -0
  21. openepd/api/pcr/sync_api.py +49 -0
  22. openepd/api/sync_client.py +67 -0
  23. openepd/api/test/__init__.py +19 -0
  24. openepd/bundle/__init__.py +1 -1
  25. openepd/bundle/base.py +1 -1
  26. openepd/bundle/model.py +5 -6
  27. openepd/bundle/reader.py +5 -5
  28. openepd/bundle/writer.py +5 -4
  29. openepd/compat/__init__.py +19 -0
  30. openepd/compat/pydantic.py +29 -0
  31. openepd/model/__init__.py +1 -1
  32. openepd/model/base.py +114 -15
  33. openepd/model/category.py +39 -0
  34. openepd/model/common.py +33 -25
  35. openepd/model/epd.py +97 -78
  36. openepd/model/factory.py +48 -0
  37. openepd/model/lcia.py +24 -13
  38. openepd/model/org.py +28 -18
  39. openepd/model/pcr.py +42 -14
  40. openepd/model/specs/README.md +19 -0
  41. openepd/model/specs/__init__.py +72 -4
  42. openepd/model/specs/aluminium.py +67 -0
  43. openepd/model/specs/asphalt.py +87 -0
  44. openepd/model/specs/base.py +60 -0
  45. openepd/model/specs/concrete.py +288 -24
  46. openepd/model/specs/generated/accessories.py +63 -0
  47. openepd/model/specs/generated/aggregates.py +71 -0
  48. openepd/model/specs/generated/aluminium.py +66 -0
  49. openepd/model/specs/generated/asphalt.py +86 -0
  50. openepd/model/specs/generated/bulk_materials.py +26 -0
  51. openepd/model/specs/generated/cast_decks_and_underlayment.py +26 -0
  52. openepd/model/specs/generated/cladding.py +214 -0
  53. openepd/model/specs/generated/cmu.py +46 -0
  54. openepd/model/specs/generated/common.py +27 -0
  55. openepd/model/specs/generated/concrete.py +151 -0
  56. openepd/model/specs/generated/conveying_equipment.py +57 -0
  57. openepd/model/specs/generated/electrical.py +297 -0
  58. openepd/model/specs/generated/electrical_transmission_and_distribution_equipment.py +63 -0
  59. openepd/model/specs/generated/electricity.py +26 -0
  60. openepd/model/specs/generated/enums.py +2420 -0
  61. openepd/model/specs/generated/finishes.py +519 -0
  62. openepd/model/specs/generated/fire_and_smoke_protection.py +79 -0
  63. openepd/model/specs/generated/furnishings.py +95 -0
  64. openepd/model/specs/generated/grouting.py +26 -0
  65. openepd/model/specs/generated/manufacturing_inputs.py +131 -0
  66. openepd/model/specs/generated/masonry.py +77 -0
  67. openepd/model/specs/generated/material_handling.py +35 -0
  68. openepd/model/specs/generated/mechanical.py +271 -0
  69. openepd/model/specs/generated/mechanical_insulation.py +41 -0
  70. openepd/model/specs/generated/network_infrastructure.py +181 -0
  71. openepd/model/specs/generated/openings.py +423 -0
  72. openepd/model/specs/generated/other_electrical_equipment.py +26 -0
  73. openepd/model/specs/generated/other_materials.py +123 -0
  74. openepd/model/specs/generated/plumbing.py +153 -0
  75. openepd/model/specs/generated/precast_concrete.py +68 -0
  76. openepd/model/specs/generated/sheathing.py +74 -0
  77. openepd/model/specs/generated/steel.py +224 -0
  78. openepd/model/specs/generated/thermal_moisture_protection.py +233 -0
  79. openepd/model/specs/generated/utility_piping.py +65 -0
  80. openepd/model/specs/generated/wood.py +167 -0
  81. openepd/model/specs/generated/wood_joists.py +38 -0
  82. openepd/model/specs/glass.py +360 -0
  83. openepd/model/specs/steel.py +184 -0
  84. openepd/model/specs/wood.py +130 -0
  85. openepd/model/standard.py +2 -3
  86. openepd/model/validation/__init__.py +19 -0
  87. openepd/model/validation/common.py +59 -0
  88. openepd/model/validation/numbers.py +26 -0
  89. openepd/model/validation/quantity.py +132 -0
  90. openepd/model/versioning.py +129 -0
  91. {openepd-2.0.0.dist-info → openepd-3.1.0.dist-info}/METADATA +36 -5
  92. openepd-3.1.0.dist-info/RECORD +95 -0
  93. openepd-2.0.0.dist-info/RECORD +0 -22
  94. {openepd-2.0.0.dist-info → openepd-3.1.0.dist-info}/LICENSE +0 -0
  95. {openepd-2.0.0.dist-info → openepd-3.1.0.dist-info}/WHEEL +0 -0
openepd/api/common.py ADDED
@@ -0,0 +1,239 @@
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 collections.abc import Iterable
21
+ from contextlib import contextmanager
22
+ from datetime import datetime, timedelta
23
+ import threading
24
+ from time import sleep
25
+ from typing import Callable, Generic, Iterator, cast
26
+
27
+ from openepd.api.dto.common import DEFAULT_PAGE_SIZE, MetaCollectionDto, OpenEpdApiResponse
28
+ from openepd.api.dto.meta import PagingMeta, PagingMetaMixin
29
+ from openepd.model.base import TOpenEpdObject
30
+
31
+
32
+ class Throttler:
33
+ """Throttle calls to a function to a certain rate."""
34
+
35
+ def __init__(self, rate_per_sec: float) -> None:
36
+ """
37
+ Construct a throttler.
38
+
39
+ :param rate_per_sec: number of calls to throttle per second
40
+ """
41
+ super().__init__()
42
+
43
+ self.rate: float = rate_per_sec
44
+
45
+ self.__count_lock = threading.Lock()
46
+ self.__time_lock = threading.Lock()
47
+
48
+ self.__count = 0
49
+ self.__start = datetime.now()
50
+ self.__time_limit = timedelta(seconds=1)
51
+
52
+ @contextmanager
53
+ def throttle(self):
54
+ """Create context manager which throttles inside the context."""
55
+ with self.__count_lock:
56
+ count = self.__count
57
+
58
+ with self.__time_lock:
59
+ diff = datetime.now() - self.__start
60
+ if diff < self.__time_limit and count >= self.rate:
61
+ seconds = (self.__time_limit - diff).total_seconds()
62
+ sleep(seconds)
63
+ self.__start = datetime.now()
64
+ self.__count = count - self.rate
65
+
66
+ with self.__count_lock:
67
+ self.__count += 1
68
+
69
+ yield
70
+
71
+
72
+ class StreamingListResponse(Iterable[TOpenEpdObject], Generic[TOpenEpdObject]):
73
+ """
74
+ Iterator over a list of objects which could be from remote API in batches by given fetch function.
75
+
76
+ This class could work only with OpenEpdApiResponse which:
77
+ a) contains a list of objects in payload
78
+ b) contains PagingMeta in meta
79
+
80
+ Typical use case:
81
+
82
+ stream: StreamingListResponse[Epd] # Assume we collected this from somewhere (e.g. from API Client)
83
+
84
+ # Iterate over all EPDs (this will fetch the next page when needed, one single page is buffered)
85
+ for epd in stream:
86
+ print(epd)
87
+ print("Current page: ", stream.current_page)
88
+
89
+ # Get specific page
90
+ page = stream.goto_page(4)
91
+
92
+ # Get paging information
93
+ stream.get_paging_meta()
94
+ # or
95
+ stream.get_get_total_pages(), stream.get_total_items()
96
+
97
+ # Iterate starting from specific page
98
+ for epd in stream.iterator(4):
99
+ print(epd)
100
+ # Alternative approach
101
+ stream.goto_page(4)
102
+ for epd in stream:
103
+ print(epd)
104
+ """
105
+
106
+ def __init__(
107
+ self,
108
+ fetch_handler: Callable[[int, int], OpenEpdApiResponse[list[TOpenEpdObject], MetaCollectionDto]],
109
+ auto_init: bool = True,
110
+ page_size: int | None = None,
111
+ ):
112
+ self.__fetch_handler = fetch_handler
113
+ self.__page_size = page_size or DEFAULT_PAGE_SIZE
114
+ self.__current_page = 0
115
+ self.__recent_response: OpenEpdApiResponse[list[TOpenEpdObject], MetaCollectionDto] | None = None
116
+ if auto_init:
117
+ self.goto_page(1)
118
+
119
+ def goto_page(self, page_num: int, force_reload: bool = False) -> list[TOpenEpdObject]:
120
+ """
121
+ Go to the given page and return items as result.
122
+
123
+ This will query remote server to retrieve given page, the state of iterator will be updated accordingly.
124
+ All information from the internal buffer will be overridden.
125
+
126
+ :param page_num: page number to retrieve
127
+ :param force_reload: if True, force reload of the page even if it was already loaded
128
+ :return: list of items on the page
129
+ """
130
+ if page_num <= 0:
131
+ raise ValueError("Page number must be positive")
132
+ if self.__current_page != page_num or force_reload:
133
+ self.__recent_response = self.__fetch_handler(page_num, self.__page_size)
134
+ self.__current_page = page_num
135
+ if self.__recent_response is None:
136
+ raise RuntimeError("Response is empty, this should not happen, check if fetch_handler is compatible")
137
+ if self.__recent_response.payload is None:
138
+ raise ValueError("Response does not contain payload")
139
+ if not isinstance(self.__recent_response.payload, list):
140
+ raise ValueError("Response does not contain a list")
141
+ if self.__recent_response.meta is None:
142
+ raise ValueError("Response does not contain meta")
143
+ if not isinstance(self.__recent_response.meta, PagingMetaMixin):
144
+ raise ValueError("Response does not contain paging meta")
145
+ return self.__recent_response.payload
146
+
147
+ def get_paging_meta(self) -> PagingMeta:
148
+ """
149
+ Get paging meta from the most recent response.
150
+
151
+ If object is not initialized (no pages were retrieved yet), the first page will be fetched automatically.
152
+ See also: get_meta()
153
+ :return: paging meta
154
+ """
155
+ paging_meta = cast(PagingMetaMixin, self.get_meta()).paging
156
+ if paging_meta is None:
157
+ raise ValueError("Response does not contain paging meta")
158
+ return paging_meta
159
+
160
+ def get_meta(self) -> MetaCollectionDto:
161
+ """
162
+ Get meta from the most recent response.
163
+
164
+ If object is not initialized (no pages were retrieved yet), the first page will be fetched automatically.
165
+ :return: paging meta
166
+ """
167
+ self._ensure_initialized()
168
+ return self.__recent_response.meta # type: ignore
169
+
170
+ @property
171
+ def current_page(self) -> int:
172
+ """Get current page number (numbering is 1-based)."""
173
+ return self.__current_page
174
+
175
+ def get_total_pages(self) -> int:
176
+ """Get total number of pages."""
177
+ return self.get_paging_meta().total_pages
178
+
179
+ def get_total_count(self) -> int:
180
+ """Get total number of items."""
181
+ return self.get_paging_meta().total_count
182
+
183
+ def has_next_page(self) -> bool:
184
+ """Check if there is a next page."""
185
+ return self.current_page < self.get_total_pages()
186
+
187
+ def reset(self):
188
+ """Reset iterator to the very beginning, cleanup internal buffers."""
189
+ self.__current_page = 0
190
+ self.__recent_response = None
191
+
192
+ def __iter__(self) -> Iterator[TOpenEpdObject]:
193
+ """
194
+ Iterate over all items, when needed the new pages from server will be requested.
195
+
196
+ Iteration from starts from the current page.
197
+ """
198
+ return self.iterator(self.current_page)
199
+
200
+ def iterator(self, start_from_page: int = 1) -> Iterator[TOpenEpdObject]:
201
+ """
202
+ Iterate over all items, when needed the new pages from server will be requested.
203
+
204
+ :param start_from_page: page number to start from (1-based)
205
+ """
206
+ if start_from_page <= 0:
207
+ start_from_page = 1
208
+ self.goto_page(start_from_page)
209
+ while True:
210
+ items = self.goto_page(self.current_page)
211
+ for x in items:
212
+ yield x
213
+ if not self.has_next_page():
214
+ return # no more pages
215
+ else:
216
+ self.goto_page(self.current_page + 1)
217
+
218
+ def __len__(self):
219
+ return self.get_total_count()
220
+
221
+ def _is_initialized(self) -> bool:
222
+ """Check if object is initialized (at least one page was retrieved)."""
223
+ return self.__recent_response is not None
224
+
225
+ def _ensure_initialized(self):
226
+ """Ensure that object is initialized, if not - request the first page."""
227
+ if not self._is_initialized():
228
+ self.goto_page(1)
229
+
230
+
231
+ def no_trailing_slash(val: str) -> str:
232
+ """
233
+ Remove all trailing slashes from the given string. Might be useful to normalize URLs.
234
+
235
+ Non-string input is considered as error.
236
+ """
237
+ while val.endswith("/"):
238
+ val = val[:-1]
239
+ return val
@@ -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,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
+ import abc
21
+
22
+ from openepd.compat.pydantic import pyd
23
+
24
+
25
+ class BaseOpenEpdApiModel(pyd.BaseModel):
26
+ """Base class for OpenEPD API DTOs."""
27
+
28
+ class Config:
29
+ extra = pyd.Extra.ignore
30
+
31
+
32
+ class BaseMetaDto(BaseOpenEpdApiModel, metaclass=abc.ABCMeta):
33
+ """Base class for all meta DTOs."""
34
+
35
+ pass
36
+
37
+
38
+ class MetaExtensionBase(BaseOpenEpdApiModel):
39
+ """Base class for custom meta extensions."""
40
+
41
+ pass
@@ -0,0 +1,115 @@
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
+ import abc
21
+ import datetime
22
+ from typing import Final, Generic, TypeAlias, TypeVar
23
+
24
+ from openepd.api.dto.base import BaseMetaDto, BaseOpenEpdApiModel, MetaExtensionBase
25
+ from openepd.api.dto.meta import PerformanceMetaMixin
26
+ from openepd.compat.pydantic import pyd, pyd_generics
27
+ from openepd.model.base import AnySerializable, BaseOpenEpdSchema
28
+
29
+ DEFAULT_PAGE_SIZE: Final[int] = 100
30
+ MAX_PAGE_SIZE: Final[int] = 250
31
+
32
+
33
+ class AuditableDto(BaseOpenEpdApiModel, metaclass=abc.ABCMeta):
34
+ """Base class for all DTOs that hold audit information."""
35
+
36
+ created_by: str = pyd.Field(
37
+ title="Created By",
38
+ example="johnsmith@cqd.io",
39
+ description="User's email or script name that created this list.",
40
+ )
41
+ updated_by: str = pyd.Field(
42
+ title="Updated By",
43
+ example="bobbuilder@buildingtransparency.org",
44
+ description="User's email or script name that updated this list last time.",
45
+ )
46
+ created_on: datetime.datetime = pyd.Field(
47
+ title="Created On",
48
+ example="2019-06-13T13:17:09+00:00",
49
+ description="A timestamp when this object has been created "
50
+ "in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format.",
51
+ )
52
+ updated_on: datetime.datetime = pyd.Field(
53
+ title="Updated On",
54
+ example="2020-07-13T13:17:09+00:00",
55
+ description="A timestamp when this object has been updated last time "
56
+ "in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format.",
57
+ )
58
+
59
+
60
+ Payload: TypeAlias = AnySerializable
61
+ TPayload = TypeVar("TPayload", bound=Payload)
62
+ TMetaDto = TypeVar("TMetaDto", bound=BaseMetaDto)
63
+ TMetaExtension = TypeVar("TMetaExtension", bound=MetaExtensionBase)
64
+
65
+
66
+ class MetaCollectionDto(BaseOpenEpdApiModel, pyd_generics.GenericModel, Generic[TMetaExtension]):
67
+ """
68
+ This class is intended to be used as a container for different meta objects.
69
+
70
+ From a specific controller, you should return a specific subclass of MetaCollectionDto and appropriate mixins. This
71
+ would allow to retain type information to generate schema for meta section of response.
72
+
73
+ For example, EPD Search method, which should return some metadata about performance, material filter, and paging,
74
+ would return a concrete subclass of MetaCollectionDto like this one:
75
+
76
+ `class EpdSearchViewMeta(MaterialFilterMetaMixin, PagingMetaMixin, WarningMetaMixin, MetaCollectionDto)`
77
+
78
+ Mixins are used to define the resulting meta's dictionary-like structure, this would result in a json like:
79
+ ```
80
+ {
81
+ "paging": {"page_size": 100, "total_count": 1, "total_pages": 1},
82
+ "ec3_warnings": null,
83
+ "material_filter": {...}
84
+ }
85
+ ```
86
+
87
+ It is generified by the extension type of similar structure (key-dict value), which should not be a part of OpenEPD
88
+ spec. This allows to add custom meta objects to the response, which would be ignored by OpenEPD implementors.
89
+ """
90
+
91
+ ext: TMetaExtension | None = None
92
+
93
+ class Config(BaseOpenEpdSchema.Config):
94
+ schema_extra = {
95
+ "description": "Base structure of the response meta section",
96
+ }
97
+
98
+
99
+ class BaseMeta(PerformanceMetaMixin, MetaCollectionDto[TMetaExtension], Generic[TMetaExtension], metaclass=abc.ABCMeta):
100
+ """Base class for creating meta objects specific to a controller."""
101
+
102
+ pass
103
+
104
+
105
+ TMeta = TypeVar("TMeta", bound=MetaCollectionDto, covariant=True)
106
+
107
+
108
+ class OpenEpdApiResponse(pyd_generics.GenericModel, BaseOpenEpdApiModel, Generic[TPayload, TMeta]):
109
+ """Standard DTO representing response from OpenEPD API server."""
110
+
111
+ payload: TPayload
112
+ # there is an issue with using covariant type variables as parameters when used for container mutable types,
113
+ # as described in https://github.com/python/mypy/issues/7049
114
+ # However, in our workflow TMeta is not a container type which is further returned back to caller.
115
+ meta: TMeta # type: ignore[misc]
@@ -0,0 +1,69 @@
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 BaseMetaDto, BaseOpenEpdApiModel
21
+ from openepd.compat.pydantic import pyd
22
+
23
+
24
+ class PerformanceMeta(BaseMetaDto):
25
+ """Meta for performance information."""
26
+
27
+ execution_time_ms: int
28
+
29
+
30
+ class PerformanceMetaMixin(BaseMetaDto):
31
+ """Mixin for adding performance meta to MetaCollection."""
32
+
33
+ performance: PerformanceMeta | None = None
34
+
35
+
36
+ class PagingMeta(BaseMetaDto):
37
+ """Meta for paging information."""
38
+
39
+ total_count: int = pyd.Field(
40
+ title="Total results", example=1233, description="Total number of records for the search"
41
+ )
42
+ total_pages: int = pyd.Field(title="Total pages", example=20, description="Total pages available")
43
+ page_size: int = pyd.Field(title="Page size", example=150, description="Number of records in page")
44
+
45
+
46
+ class PagingMetaMixin(BaseOpenEpdApiModel):
47
+ """Mixin for adding paging meta to MetaCollection."""
48
+
49
+ paging: PagingMeta | None
50
+
51
+
52
+ class WarningMessageDto(BaseOpenEpdApiModel):
53
+ """DTO for warning messages."""
54
+
55
+ message: str = pyd.Field(
56
+ title="Warning message", example="Categories limited during search, see effective_omf in meta"
57
+ )
58
+ code: str = pyd.Field(title="Warning code", example="CATEGORIES_LIMITED")
59
+ field: str | None = pyd.Field(
60
+ title="Field",
61
+ description="Field to which the warning relates",
62
+ example="subcategories",
63
+ )
64
+
65
+
66
+ class WarningMetaMixin(BaseOpenEpdApiModel):
67
+ """Mixin for adding warnings meta to MetaCollection."""
68
+
69
+ warnings: list[WarningMessageDto] | None = None
openepd/api/dto/mf.py ADDED
@@ -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 openepd.api.dto.base import BaseOpenEpdApiModel
21
+ from openepd.compat.pydantic import pyd
22
+
23
+
24
+ class MaterialFilterDefinition(BaseOpenEpdApiModel):
25
+ """
26
+ Material filter definition.
27
+
28
+ This object includes material filter itself as well as its hash.
29
+ """
30
+
31
+ mf: str = pyd.Field(
32
+ title="MaterialFilter",
33
+ example='!EC3 search("AluminiumBillets") !pragma oMF("1.0/1")',
34
+ description="MaterialFilter in string format",
35
+ )
36
+ mf_hash: str = pyd.Field(
37
+ title="MaterialFilter hash",
38
+ example="22bf5b78cee5b79e1c76e818873d521c3972688b",
39
+ description="MaterialFilter hash. Can be used to compare filters for equality, put to cache etc.",
40
+ )
41
+
42
+
43
+ class MaterialFilterMeta(BaseOpenEpdApiModel):
44
+ """Meta holding supplementary information about OMF query execution."""
45
+
46
+ excluded_fields: list[str] | None = pyd.Field(
47
+ example=["building_jurisdiction", "jurisdiction"],
48
+ description="list of fields excluded by server process for any reason",
49
+ default=None,
50
+ )
51
+ effective_omf: MaterialFilterDefinition = pyd.Field(
52
+ description="Effective OpenMaterialFilter as applied to search, after transformations if any"
53
+ )
54
+
55
+
56
+ class MaterialFilterMetaMixin(BaseOpenEpdApiModel):
57
+ """Mixing for adding MaterialFilterMeta to MetaCollection."""
58
+
59
+ material_filter: MaterialFilterMeta | None
@@ -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,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
+ #