openepd 7.13.5__tar.gz → 7.14.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-7.13.5 → openepd-7.14.0}/PKG-INFO +1 -1
  2. {openepd-7.13.5 → openepd-7.14.0}/pyproject.toml +1 -1
  3. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/__version__.py +1 -1
  4. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/common.py +28 -1
  5. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/declaration.py +2 -18
  6. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/epd.py +12 -1
  7. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/org.py +9 -18
  8. {openepd-7.13.5 → openepd-7.14.0}/LICENSE +0 -0
  9. {openepd-7.13.5 → openepd-7.14.0}/README.md +0 -0
  10. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/__init__.py +0 -0
  11. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/api/__init__.py +0 -0
  12. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/api/average_dataset/__init__.py +0 -0
  13. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/api/average_dataset/generic_estimate_sync_api.py +0 -0
  14. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/api/average_dataset/industry_epd_sync_api.py +0 -0
  15. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/api/base_sync_client.py +0 -0
  16. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/api/category/__init__.py +0 -0
  17. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/api/category/dto.py +0 -0
  18. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/api/category/sync_api.py +0 -0
  19. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/api/common.py +0 -0
  20. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/api/dto/__init__.py +0 -0
  21. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/api/dto/base.py +0 -0
  22. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/api/dto/common.py +0 -0
  23. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/api/dto/meta.py +0 -0
  24. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/api/dto/mf.py +0 -0
  25. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/api/dto/params.py +0 -0
  26. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/api/epd/__init__.py +0 -0
  27. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/api/epd/dto.py +0 -0
  28. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/api/epd/sync_api.py +0 -0
  29. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/api/errors.py +0 -0
  30. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/api/org/__init__.py +0 -0
  31. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/api/org/sync_api.py +0 -0
  32. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/api/pcr/__init__.py +0 -0
  33. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/api/pcr/sync_api.py +0 -0
  34. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/api/plant/__init__.py +0 -0
  35. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/api/plant/sync_api.py +0 -0
  36. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/api/standard/__init__.py +0 -0
  37. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/api/standard/sync_api.py +0 -0
  38. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/api/sync_client.py +0 -0
  39. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/api/test/__init__.py +0 -0
  40. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/api/utils.py +0 -0
  41. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/bundle/__init__.py +0 -0
  42. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/bundle/base.py +0 -0
  43. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/bundle/model.py +0 -0
  44. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/bundle/reader.py +0 -0
  45. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/bundle/writer.py +0 -0
  46. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/constants/__init__.py +0 -0
  47. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/constants/scope_phases.py +0 -0
  48. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/m49/__init__.py +0 -0
  49. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/m49/const.py +0 -0
  50. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/m49/utils.py +0 -0
  51. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/__init__.py +0 -0
  52. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/base.py +0 -0
  53. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/category.py +0 -0
  54. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/factory.py +0 -0
  55. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/generic_estimate.py +0 -0
  56. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/geography.py +0 -0
  57. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/industry_epd.py +0 -0
  58. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/lcia.py +0 -0
  59. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/pcr.py +0 -0
  60. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/README.md +0 -0
  61. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/__init__.py +0 -0
  62. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/asphalt.py +0 -0
  63. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/base.py +0 -0
  64. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/concrete.py +0 -0
  65. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/enums.py +0 -0
  66. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/range/__init__.py +0 -0
  67. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/range/accessories.py +0 -0
  68. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/range/aggregates.py +0 -0
  69. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/range/aluminium.py +0 -0
  70. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/range/asphalt.py +0 -0
  71. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/range/bulk_materials.py +0 -0
  72. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/range/cast_decks_and_underlayment.py +0 -0
  73. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/range/cladding.py +0 -0
  74. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/range/cmu.py +0 -0
  75. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/range/concrete.py +0 -0
  76. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/range/conveying_equipment.py +0 -0
  77. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/range/electrical.py +0 -0
  78. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/range/electrical_transmission_and_distribution_equipment.py +0 -0
  79. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/range/electricity.py +0 -0
  80. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/range/exterior_improvements.py +0 -0
  81. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/range/finishes.py +0 -0
  82. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/range/fire_and_smoke_protection.py +0 -0
  83. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/range/furnishings.py +0 -0
  84. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/range/grouting.py +0 -0
  85. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/range/manufacturing_inputs.py +0 -0
  86. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/range/masonry.py +0 -0
  87. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/range/material_handling.py +0 -0
  88. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/range/mechanical.py +0 -0
  89. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/range/mechanical_insulation.py +0 -0
  90. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/range/mixins/__init__.py +0 -0
  91. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/range/mixins/access_flooring_mixin.py +0 -0
  92. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/range/network_infrastructure.py +0 -0
  93. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/range/non_construction.py +0 -0
  94. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/range/openings.py +0 -0
  95. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/range/other_electrical_equipment.py +0 -0
  96. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/range/other_materials.py +0 -0
  97. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/range/plumbing.py +0 -0
  98. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/range/precast_concrete.py +0 -0
  99. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/range/sheathing.py +0 -0
  100. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/range/steel.py +0 -0
  101. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/range/thermal_moisture_protection.py +0 -0
  102. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/range/utility_piping.py +0 -0
  103. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/range/wood.py +0 -0
  104. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/range/wood_joists.py +0 -0
  105. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/singular/__init__.py +0 -0
  106. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/singular/accessories.py +0 -0
  107. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/singular/aggregates.py +0 -0
  108. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/singular/aluminium.py +0 -0
  109. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/singular/asphalt.py +0 -0
  110. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/singular/bulk_materials.py +0 -0
  111. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/singular/cast_decks_and_underlayment.py +0 -0
  112. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/singular/cladding.py +0 -0
  113. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/singular/cmu.py +0 -0
  114. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/singular/common.py +0 -0
  115. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/singular/concrete.py +0 -0
  116. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/singular/conveying_equipment.py +0 -0
  117. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/singular/deprecated/__init__.py +0 -0
  118. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/singular/deprecated/concrete.py +0 -0
  119. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/singular/deprecated/steel.py +0 -0
  120. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/singular/electrical.py +0 -0
  121. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/singular/electrical_transmission_and_distribution_equipment.py +0 -0
  122. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/singular/electricity.py +0 -0
  123. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/singular/exterior_improvements.py +0 -0
  124. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/singular/finishes.py +0 -0
  125. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/singular/fire_and_smoke_protection.py +0 -0
  126. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/singular/furnishings.py +0 -0
  127. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/singular/grouting.py +0 -0
  128. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/singular/manufacturing_inputs.py +0 -0
  129. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/singular/masonry.py +0 -0
  130. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/singular/material_handling.py +0 -0
  131. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/singular/mechanical.py +0 -0
  132. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/singular/mechanical_insulation.py +0 -0
  133. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/singular/mixins/__init__.py +0 -0
  134. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/singular/mixins/access_flooring_mixin.py +0 -0
  135. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/singular/mixins/conduit_mixin.py +0 -0
  136. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/singular/network_infrastructure.py +0 -0
  137. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/singular/non_construction.py +0 -0
  138. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/singular/openings.py +0 -0
  139. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/singular/other_electrical_equipment.py +0 -0
  140. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/singular/other_materials.py +0 -0
  141. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/singular/plumbing.py +0 -0
  142. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/singular/precast_concrete.py +0 -0
  143. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/singular/sheathing.py +0 -0
  144. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/singular/steel.py +0 -0
  145. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/singular/thermal_moisture_protection.py +0 -0
  146. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/singular/utility_piping.py +0 -0
  147. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/singular/wood.py +0 -0
  148. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/specs/singular/wood_joists.py +0 -0
  149. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/standard.py +0 -0
  150. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/validation/__init__.py +0 -0
  151. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/validation/common.py +0 -0
  152. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/validation/enum.py +0 -0
  153. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/validation/numbers.py +0 -0
  154. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/validation/quantity.py +0 -0
  155. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/model/versioning.py +0 -0
  156. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/py.typed +0 -0
  157. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/utils/__init__.py +0 -0
  158. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/utils/functional.py +0 -0
  159. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/utils/mapping/__init__.py +0 -0
  160. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/utils/mapping/common.py +0 -0
  161. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/utils/mapping/geography.py +0 -0
  162. {openepd-7.13.5 → openepd-7.14.0}/src/openepd/utils/markdown.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: openepd
3
- Version: 7.13.5
3
+ Version: 7.14.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 = "7.13.5"
3
+ version = "7.14.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 = "7.13.5"
16
+ VERSION = "7.14.0"
@@ -14,7 +14,9 @@
14
14
  # limitations under the License.
15
15
  #
16
16
  from enum import StrEnum
17
- from typing import Annotated, Any, Self
17
+ import math
18
+ import re
19
+ from typing import Annotated, Any, Final, Self
18
20
 
19
21
  import pydantic
20
22
  import pydantic_core
@@ -30,6 +32,14 @@ in web pages as if they were external resources.
30
32
  The pattern matches the following format: data:[<media-type>][;base64],<data>
31
33
  """
32
34
 
35
+ DATA_URL_IMAGE_MAX_LENGTH: Final[int] = math.ceil(32 * 1024 * 4 / 3)
36
+ """
37
+ Maximum allowed length of a data URL image string to ensure the decoded image is less than 32KB.
38
+
39
+ Base64 encoding overhead (approximately 33%) requires
40
+ limiting the encoded string length to 4/3 of the file size limit.
41
+ """
42
+
33
43
 
34
44
  class Amount(BaseOpenEpdSchema):
35
45
  """A value-and-unit pairing for amounts that do not have an uncertainty."""
@@ -372,3 +382,20 @@ class EnumGroupingAware:
372
382
  def get_groupings(cls) -> list[list]:
373
383
  """Return logical groupings of the values."""
374
384
  return []
385
+
386
+
387
+ def validate_data_url(v: pydantic.AnyUrl | None, max_length: int) -> None:
388
+ """
389
+ Validate data URL.
390
+
391
+ :param v: data URL string.
392
+ :param max_length: maximum allowed length.
393
+ :raises ValueError: if the data URL exceeds the maximum length or has invalid format.
394
+ :return: None
395
+ """
396
+ if v and len(v) > max_length:
397
+ msg = f"URL must not exceed {max_length} characters"
398
+ raise ValueError(msg)
399
+ if v and v.scheme == "data" and not re.compile(DATA_URL_REGEX).match(str(v)):
400
+ msg = "Invalid data URL format"
401
+ raise ValueError(msg)
@@ -16,14 +16,11 @@
16
16
  import abc
17
17
  import datetime
18
18
  from enum import StrEnum
19
- import math
20
- import re
21
- from typing import Final
22
19
 
23
20
  import pydantic
24
21
 
25
22
  from openepd.model.base import BaseOpenEpdSchema, OpenXpdUUID, RootDocument
26
- from openepd.model.common import DATA_URL_REGEX, Amount
23
+ from openepd.model.common import DATA_URL_IMAGE_MAX_LENGTH, Amount, validate_data_url
27
24
  from openepd.model.geography import Geography
28
25
  from openepd.model.org import Org
29
26
  from openepd.model.pcr import Pcr
@@ -36,14 +33,6 @@ DEVELOPER_DESCRIPTION = "The organization responsible for the underlying LCA (an
36
33
  PROGRAM_OPERATOR_DESCRIPTION = "JSON object for program operator Org"
37
34
  THIRD_PARTY_VERIFIER_DESCRIPTION = "JSON object for Org that performed a critical review of the EPD data"
38
35
 
39
- PRODUCT_IMAGE_MAX_LENGTH: Final[int] = math.ceil(32 * 1024 * 4 / 3)
40
- """
41
- Maximum length for product_image, product_image_small fields.
42
-
43
- Image file size must be less than 32KB. Base64 encoding overhead (approximately 33%) requires
44
- limiting the encoded string length to 4/3 of the file size limit.
45
- """
46
-
47
36
 
48
37
  class BaseDeclaration(RootDocument, abc.ABC):
49
38
  """Base class for declaration-related documents (EPDs, Industry-wide EPDs, Generic Estimates)."""
@@ -194,12 +183,7 @@ class BaseDeclaration(RootDocument, abc.ABC):
194
183
 
195
184
  @pydantic.field_validator("product_image", "product_image_small")
196
185
  def validate_product_image(cls, v: pydantic.AnyUrl | None) -> pydantic.AnyUrl | None:
197
- if v and len(v) > PRODUCT_IMAGE_MAX_LENGTH:
198
- msg = f"URL must not exceed {PRODUCT_IMAGE_MAX_LENGTH} characters"
199
- raise ValueError(msg)
200
- if v and v.scheme == "data" and not re.compile(DATA_URL_REGEX).match(str(v)):
201
- msg = "Invalid data URL format"
202
- raise ValueError(msg)
186
+ validate_data_url(v, DATA_URL_IMAGE_MAX_LENGTH)
203
187
  return v
204
188
 
205
189
 
@@ -16,7 +16,13 @@
16
16
  import pydantic
17
17
 
18
18
  from openepd.model.base import BaseDocumentFactory, OpenEpdDoctypes, OpenEpdExtension
19
- from openepd.model.common import Ingredient, WithAltIdsMixin, WithAttachmentsMixin
19
+ from openepd.model.common import (
20
+ DATA_URL_IMAGE_MAX_LENGTH,
21
+ Ingredient,
22
+ WithAltIdsMixin,
23
+ WithAttachmentsMixin,
24
+ validate_data_url,
25
+ )
20
26
  from openepd.model.declaration import (
21
27
  DEVELOPER_DESCRIPTION,
22
28
  PROGRAM_OPERATOR_DESCRIPTION,
@@ -243,6 +249,11 @@ class EpdPreviewV0(
243
249
  msg = "Invalid doctype"
244
250
  raise ValueError(msg)
245
251
 
252
+ @pydantic.field_validator("product_usage_image", "manufacturing_image")
253
+ def validate_product_usage_image(cls, v: pydantic.AnyUrl | None) -> pydantic.AnyUrl | None:
254
+ validate_data_url(v, DATA_URL_IMAGE_MAX_LENGTH)
255
+ return v
256
+
246
257
 
247
258
  EpdPreview = EpdPreviewV0
248
259
 
@@ -13,26 +13,22 @@
13
13
  # See the License for the specific language governing permissions and
14
14
  # limitations under the License.
15
15
  #
16
- import math
17
- import re
18
- from typing import Any, Final, Optional
16
+ from typing import Any, Optional
19
17
 
20
18
  from openlocationcode import openlocationcode
21
19
  import pydantic
22
20
  from pydantic import ConfigDict
23
21
 
24
22
  from openepd.model.base import BaseOpenEpdSchema
25
- from openepd.model.common import DATA_URL_REGEX, Location, WithAltIdsMixin, WithAttachmentsMixin
23
+ from openepd.model.common import (
24
+ DATA_URL_IMAGE_MAX_LENGTH,
25
+ Location,
26
+ WithAltIdsMixin,
27
+ WithAttachmentsMixin,
28
+ validate_data_url,
29
+ )
26
30
  from openepd.model.validation.common import ReferenceStr
27
31
 
28
- ORG_LOGO_MAX_LENGTH: Final[int] = math.ceil(32 * 1024 * 4 / 3)
29
- """
30
- Maximum length of Org.logo field.
31
-
32
- Logo file size must be less than 32KB. Base64 encoding overhead (approximately 33%) requires
33
- limiting the encoded string length to 4/3 of the file size limit.
34
- """
35
-
36
32
 
37
33
  class OrgRef(BaseOpenEpdSchema):
38
34
  """Represents Organisation with minimal data."""
@@ -142,12 +138,7 @@ class Org(WithAttachmentsMixin, WithAltIdsMixin, OrgRef):
142
138
 
143
139
  @pydantic.field_validator("logo")
144
140
  def _validate_logo(cls, v: pydantic.AnyUrl | None) -> pydantic.AnyUrl | None:
145
- if v and len(v) > ORG_LOGO_MAX_LENGTH:
146
- msg = f"Logo URL must not exceed {ORG_LOGO_MAX_LENGTH} characters"
147
- raise ValueError(msg)
148
- if v and v.scheme == "data" and not re.compile(DATA_URL_REGEX).match(str(v)):
149
- msg = "Invalid data URL format"
150
- raise ValueError(msg)
141
+ validate_data_url(v, DATA_URL_IMAGE_MAX_LENGTH)
151
142
  return v
152
143
 
153
144
 
File without changes
File without changes
File without changes