openepd 6.26.0__tar.gz → 6.28.0__tar.gz

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 (162) hide show
  1. {openepd-6.26.0 → openepd-6.28.0}/PKG-INFO +1 -1
  2. {openepd-6.26.0 → openepd-6.28.0}/pyproject.toml +1 -1
  3. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/__version__.py +1 -1
  4. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/api/epd/sync_api.py +6 -3
  5. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/common.py +23 -0
  6. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/epd.py +1 -0
  7. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/org.py +26 -2
  8. {openepd-6.26.0 → openepd-6.28.0}/LICENSE +0 -0
  9. {openepd-6.26.0 → openepd-6.28.0}/README.md +0 -0
  10. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/__init__.py +0 -0
  11. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/api/__init__.py +0 -0
  12. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/api/average_dataset/__init__.py +0 -0
  13. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/api/average_dataset/generic_estimate_sync_api.py +0 -0
  14. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/api/average_dataset/industry_epd_sync_api.py +0 -0
  15. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/api/base_sync_client.py +0 -0
  16. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/api/category/__init__.py +0 -0
  17. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/api/category/dto.py +0 -0
  18. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/api/category/sync_api.py +0 -0
  19. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/api/common.py +0 -0
  20. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/api/dto/__init__.py +0 -0
  21. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/api/dto/base.py +0 -0
  22. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/api/dto/common.py +0 -0
  23. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/api/dto/meta.py +0 -0
  24. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/api/dto/mf.py +0 -0
  25. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/api/dto/params.py +0 -0
  26. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/api/epd/__init__.py +0 -0
  27. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/api/epd/dto.py +0 -0
  28. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/api/errors.py +0 -0
  29. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/api/org/__init__.py +0 -0
  30. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/api/org/sync_api.py +0 -0
  31. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/api/pcr/__init__.py +0 -0
  32. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/api/pcr/sync_api.py +0 -0
  33. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/api/plant/__init__.py +0 -0
  34. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/api/plant/sync_api.py +0 -0
  35. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/api/standard/__init__.py +0 -0
  36. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/api/standard/sync_api.py +0 -0
  37. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/api/sync_client.py +0 -0
  38. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/api/test/__init__.py +0 -0
  39. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/api/utils.py +0 -0
  40. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/bundle/__init__.py +0 -0
  41. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/bundle/base.py +0 -0
  42. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/bundle/model.py +0 -0
  43. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/bundle/reader.py +0 -0
  44. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/bundle/writer.py +0 -0
  45. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/compat/__init__.py +0 -0
  46. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/compat/compat_functional_validators.py +0 -0
  47. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/compat/pydantic.py +0 -0
  48. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/constants/__init__.py +0 -0
  49. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/constants/scope_phases.py +0 -0
  50. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/m49/__init__.py +0 -0
  51. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/m49/const.py +0 -0
  52. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/m49/utils.py +0 -0
  53. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/__init__.py +0 -0
  54. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/base.py +0 -0
  55. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/category.py +0 -0
  56. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/declaration.py +0 -0
  57. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/factory.py +0 -0
  58. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/generic_estimate.py +0 -0
  59. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/geography.py +0 -0
  60. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/industry_epd.py +0 -0
  61. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/lcia.py +0 -0
  62. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/pcr.py +0 -0
  63. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/README.md +0 -0
  64. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/__init__.py +0 -0
  65. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/asphalt.py +0 -0
  66. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/base.py +0 -0
  67. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/concrete.py +0 -0
  68. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/enums.py +0 -0
  69. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/range/__init__.py +0 -0
  70. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/range/accessories.py +0 -0
  71. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/range/aggregates.py +0 -0
  72. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/range/aluminium.py +0 -0
  73. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/range/asphalt.py +0 -0
  74. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/range/bulk_materials.py +0 -0
  75. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/range/cast_decks_and_underlayment.py +0 -0
  76. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/range/cladding.py +0 -0
  77. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/range/cmu.py +0 -0
  78. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/range/concrete.py +0 -0
  79. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/range/conveying_equipment.py +0 -0
  80. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/range/electrical.py +0 -0
  81. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/range/electrical_transmission_and_distribution_equipment.py +0 -0
  82. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/range/electricity.py +0 -0
  83. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/range/exterior_improvements.py +0 -0
  84. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/range/finishes.py +0 -0
  85. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/range/fire_and_smoke_protection.py +0 -0
  86. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/range/furnishings.py +0 -0
  87. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/range/grouting.py +0 -0
  88. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/range/manufacturing_inputs.py +0 -0
  89. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/range/masonry.py +0 -0
  90. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/range/material_handling.py +0 -0
  91. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/range/mechanical.py +0 -0
  92. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/range/mechanical_insulation.py +0 -0
  93. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/range/mixins/__init__.py +0 -0
  94. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/range/mixins/access_flooring_mixin.py +0 -0
  95. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/range/network_infrastructure.py +0 -0
  96. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/range/openings.py +0 -0
  97. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/range/other_electrical_equipment.py +0 -0
  98. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/range/other_materials.py +0 -0
  99. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/range/plumbing.py +0 -0
  100. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/range/precast_concrete.py +0 -0
  101. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/range/sheathing.py +0 -0
  102. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/range/steel.py +0 -0
  103. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/range/thermal_moisture_protection.py +0 -0
  104. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/range/utility_piping.py +0 -0
  105. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/range/wood.py +0 -0
  106. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/range/wood_joists.py +0 -0
  107. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/singular/__init__.py +0 -0
  108. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/singular/accessories.py +0 -0
  109. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/singular/aggregates.py +0 -0
  110. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/singular/aluminium.py +0 -0
  111. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/singular/asphalt.py +0 -0
  112. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/singular/bulk_materials.py +0 -0
  113. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/singular/cast_decks_and_underlayment.py +0 -0
  114. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/singular/cladding.py +0 -0
  115. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/singular/cmu.py +0 -0
  116. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/singular/common.py +0 -0
  117. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/singular/concrete.py +0 -0
  118. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/singular/conveying_equipment.py +0 -0
  119. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/singular/deprecated/__init__.py +0 -0
  120. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/singular/deprecated/concrete.py +0 -0
  121. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/singular/deprecated/steel.py +0 -0
  122. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/singular/electrical.py +0 -0
  123. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/singular/electrical_transmission_and_distribution_equipment.py +0 -0
  124. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/singular/electricity.py +0 -0
  125. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/singular/exterior_improvements.py +0 -0
  126. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/singular/finishes.py +0 -0
  127. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/singular/fire_and_smoke_protection.py +0 -0
  128. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/singular/furnishings.py +0 -0
  129. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/singular/grouting.py +0 -0
  130. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/singular/manufacturing_inputs.py +0 -0
  131. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/singular/masonry.py +0 -0
  132. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/singular/material_handling.py +0 -0
  133. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/singular/mechanical.py +0 -0
  134. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/singular/mechanical_insulation.py +0 -0
  135. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/singular/mixins/__init__.py +0 -0
  136. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/singular/mixins/access_flooring_mixin.py +0 -0
  137. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/singular/mixins/conduit_mixin.py +0 -0
  138. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/singular/network_infrastructure.py +0 -0
  139. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/singular/openings.py +0 -0
  140. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/singular/other_electrical_equipment.py +0 -0
  141. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/singular/other_materials.py +0 -0
  142. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/singular/plumbing.py +0 -0
  143. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/singular/precast_concrete.py +0 -0
  144. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/singular/sheathing.py +0 -0
  145. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/singular/steel.py +0 -0
  146. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/singular/thermal_moisture_protection.py +0 -0
  147. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/singular/utility_piping.py +0 -0
  148. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/singular/wood.py +0 -0
  149. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/specs/singular/wood_joists.py +0 -0
  150. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/standard.py +0 -0
  151. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/validation/__init__.py +0 -0
  152. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/validation/common.py +0 -0
  153. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/validation/enum.py +0 -0
  154. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/validation/numbers.py +0 -0
  155. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/validation/quantity.py +0 -0
  156. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/model/versioning.py +0 -0
  157. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/patch_pydantic.py +0 -0
  158. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/py.typed +0 -0
  159. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/utils/__init__.py +0 -0
  160. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/utils/functional.py +0 -0
  161. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/utils/mapping/__init__.py +0 -0
  162. {openepd-6.26.0 → openepd-6.28.0}/src/openepd/utils/mapping/common.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: openepd
3
- Version: 6.26.0
3
+ Version: 6.28.0
4
4
  Summary: Python library to work with OpenEPD format
5
5
  License: Apache-2.0
6
6
  Author: C-Change Labs
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "openepd"
3
- version = "6.26.0"
3
+ version = "6.28.0"
4
4
  license = "Apache-2.0"
5
5
  description = "Python library to work with OpenEPD format"
6
6
  authors = ["C-Change Labs <support@c-change-labs.com>"]
@@ -13,4 +13,4 @@
13
13
  # See the License for the specific language governing permissions and
14
14
  # limitations under the License.
15
15
  #
16
- VERSION = "6.26.0"
16
+ VERSION = "6.28.0"
@@ -13,6 +13,7 @@
13
13
  # See the License for the specific language governing permissions and
14
14
  # limitations under the License.
15
15
  #
16
+ from collections.abc import Collection
16
17
  from typing import Literal, overload
17
18
 
18
19
  from requests import Response
@@ -27,15 +28,17 @@ from openepd.model.epd import Epd
27
28
  class EpdApi(BaseApiMethodGroup):
28
29
  """API methods for EPDs."""
29
30
 
30
- def get_by_openxpd_uuid(self, uuid: str) -> Epd:
31
+ def get_by_openxpd_uuid(self, uuid: str, *, fields: Collection[str] | None = None) -> Epd:
31
32
  """
32
33
  Get EPD by OpenEPD UUID.
33
34
 
34
35
  :param uuid: OpenEPD UUID
35
- :return: EPD
36
+ :param fields: Optional collection of field names to include in the response
37
+ :return: EPD, Response, or tuple of EPD and Response depending on return_type
36
38
  :raise ObjectNotFound: if EPD is not found
37
39
  """
38
- content = self._client.do_request("get", f"/epds/{uuid}").json()
40
+ params = {"fields": ",".join(set(fields))} if fields else None
41
+ content = self._client.do_request("get", f"/epds/{uuid}", params=params).json()
39
42
  return Epd.parse_obj(content)
40
43
 
41
44
  def find_raw(self, omf: str, page_num: int = 1, page_size: int = 10) -> EpdSearchResponse:
@@ -13,13 +13,24 @@
13
13
  # See the License for the specific language governing permissions and
14
14
  # limitations under the License.
15
15
  #
16
+ from collections.abc import Callable, Generator
16
17
  from enum import StrEnum
18
+ import re
17
19
  from typing import Annotated, Any
18
20
 
19
21
  from openepd.compat.pydantic import pyd
20
22
  from openepd.model.base import BaseOpenEpdSchema
21
23
  from openepd.model.validation.numbers import RatioFloat
22
24
 
25
+ DATA_URL_REGEX = r"^data:([-\w]+\/[-+\w.]+)?(;?\w+=[-\w]+)*(;base64)?,.*$"
26
+ """
27
+ Regular expression pattern for matching Data URLs.
28
+
29
+ A Data URL is a URI scheme that allows you to embed small data items inline
30
+ in web pages as if they were external resources.
31
+ The pattern matches the following format: data:[<media-type>][;base64],<data>
32
+ """
33
+
23
34
 
24
35
  class Amount(BaseOpenEpdSchema):
25
36
  """A value-and-unit pairing for amounts that do not have an uncertainty."""
@@ -313,3 +324,15 @@ class EnumGroupingAware:
313
324
  def get_groupings(cls) -> list[list]:
314
325
  """Return logical groupings of the values."""
315
326
  return []
327
+
328
+
329
+ class DataUrl(str):
330
+ @classmethod
331
+ def __get_validators__(cls) -> Generator[Callable[[str], str], None, None]:
332
+ def validator(v: str) -> str:
333
+ if re.compile(DATA_URL_REGEX).match(v):
334
+ return v
335
+ msg = "Value must be a valid dataUrl"
336
+ raise ValueError(msg)
337
+
338
+ yield validator
@@ -139,6 +139,7 @@ class EpdPreviewV0(
139
139
  WithEpdDeveloperMixin,
140
140
  WithVerifierMixin,
141
141
  WithAltIdsMixin,
142
+ EpdRef,
142
143
  BaseDeclaration,
143
144
  title="EPD (Preview)",
144
145
  ):
@@ -13,15 +13,24 @@
13
13
  # See the License for the specific language governing permissions and
14
14
  # limitations under the License.
15
15
  #
16
- from typing import Annotated, Optional
16
+ import math
17
+ from typing import Annotated, Final, Optional
17
18
 
18
19
  from openlocationcode import openlocationcode
19
20
 
20
21
  from openepd.compat.pydantic import pyd
21
22
  from openepd.model.base import BaseOpenEpdSchema
22
- from openepd.model.common import Location, WithAltIdsMixin, WithAttachmentsMixin
23
+ from openepd.model.common import DataUrl, Location, WithAltIdsMixin, WithAttachmentsMixin
23
24
  from openepd.model.validation.common import ReferenceStr
24
25
 
26
+ ORG_LOGO_MAX_LENGTH: Final[int] = math.ceil(32 * 1024 * 4 / 3)
27
+ """
28
+ Maximum length of Org.logo field.
29
+
30
+ Logo file size must be less than 32KB. Base64 encoding overhead (approximately 33%) requires
31
+ limiting the encoded string length to 4/3 of the file size limit.
32
+ """
33
+
25
34
 
26
35
  class OrgRef(BaseOpenEpdSchema):
27
36
  """Represents Organisation with minimal data."""
@@ -85,6 +94,21 @@ class Org(WithAttachmentsMixin, WithAltIdsMixin, OrgRef):
85
94
  default=None,
86
95
  description="Location of a place of business, preferably the corporate headquarters.",
87
96
  )
97
+ logo: pyd.AnyUrl | DataUrl | None = pyd.Field(
98
+ default=None,
99
+ description=(
100
+ "URL pointer to, or dataURL, for a square logo for the company, preferably 300x300 pixels."
101
+ "A logo of the type used on social media platforms such as LinkedIn is recommended."
102
+ ),
103
+ example="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...",
104
+ )
105
+
106
+ @pyd.validator("logo")
107
+ def validate_logo(cls, v: str | None) -> str | None:
108
+ if v and len(v) > ORG_LOGO_MAX_LENGTH:
109
+ msg = f"Logo URL must not exceed {ORG_LOGO_MAX_LENGTH} characters"
110
+ raise ValueError(msg)
111
+ return v
88
112
 
89
113
 
90
114
  class PlantRef(BaseOpenEpdSchema):
File without changes
File without changes
File without changes