@hestia-earth/data-validation 0.37.7

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 (410) hide show
  1. package/.coveragerc +14 -0
  2. package/.dockerignore +19 -0
  3. package/.eslintignore +17 -0
  4. package/.eslintrc.js +11 -0
  5. package/.flake8 +5 -0
  6. package/.gitlab/issue_templates/new validation.md +82 -0
  7. package/.gitlab-ci.yml +216 -0
  8. package/.readthedocs.yml +24 -0
  9. package/CODEOWNERS +11 -0
  10. package/Dockerfile +13 -0
  11. package/LICENSE +21 -0
  12. package/MANIFEST.in +2 -0
  13. package/bin/hestia-validate-data +80 -0
  14. package/build_mocking.py +14 -0
  15. package/commitlint.config.js +1 -0
  16. package/docs/Makefile +20 -0
  17. package/docs/_static/styles.css +4 -0
  18. package/docs/_templates/custom-class-template.rst +34 -0
  19. package/docs/_templates/custom-module-template.rst +66 -0
  20. package/docs/_templates/layout.html +4 -0
  21. package/docs/conf.py +74 -0
  22. package/docs/index.rst +42 -0
  23. package/docs/make.bat +35 -0
  24. package/docs/requirements.txt +13 -0
  25. package/envs/.develop.env +1 -0
  26. package/envs/.master.env +1 -0
  27. package/guide-assets/.gitkeep +0 -0
  28. package/hestia_earth/validation/README.md +5 -0
  29. package/hestia_earth/validation/__init__.py +32 -0
  30. package/hestia_earth/validation/distribution.py +22 -0
  31. package/hestia_earth/validation/gee.py +162 -0
  32. package/hestia_earth/validation/log.py +44 -0
  33. package/hestia_earth/validation/models.py +141 -0
  34. package/hestia_earth/validation/preload_requests.py +61 -0
  35. package/hestia_earth/validation/terms.py +88 -0
  36. package/hestia_earth/validation/utils.py +444 -0
  37. package/hestia_earth/validation/validators/__init__.py +141 -0
  38. package/hestia_earth/validation/validators/aggregated_cycle.py +32 -0
  39. package/hestia_earth/validation/validators/aggregated_shared.py +37 -0
  40. package/hestia_earth/validation/validators/animal.py +88 -0
  41. package/hestia_earth/validation/validators/completeness.py +252 -0
  42. package/hestia_earth/validation/validators/cycle.py +1123 -0
  43. package/hestia_earth/validation/validators/distribution.py +86 -0
  44. package/hestia_earth/validation/validators/emission.py +109 -0
  45. package/hestia_earth/validation/validators/impact_assessment.py +138 -0
  46. package/hestia_earth/validation/validators/indicator.py +154 -0
  47. package/hestia_earth/validation/validators/infrastructure.py +25 -0
  48. package/hestia_earth/validation/validators/input.py +268 -0
  49. package/hestia_earth/validation/validators/management.py +131 -0
  50. package/hestia_earth/validation/validators/measurement.py +368 -0
  51. package/hestia_earth/validation/validators/organisation.py +43 -0
  52. package/hestia_earth/validation/validators/practice.py +590 -0
  53. package/hestia_earth/validation/validators/product.py +263 -0
  54. package/hestia_earth/validation/validators/property.py +266 -0
  55. package/hestia_earth/validation/validators/shared.py +940 -0
  56. package/hestia_earth/validation/validators/site.py +312 -0
  57. package/hestia_earth/validation/validators/source.py +20 -0
  58. package/hestia_earth/validation/validators/transformation.py +250 -0
  59. package/hestia_earth/validation/version.py +1 -0
  60. package/layer/build.sh +34 -0
  61. package/layer/deploy.sh +18 -0
  62. package/package.json +59 -0
  63. package/release.sh +11 -0
  64. package/requirements-ci.txt +6 -0
  65. package/requirements-test.txt +4 -0
  66. package/requirements.txt +2 -0
  67. package/run-docker-test.sh +7 -0
  68. package/run-docker.sh +9 -0
  69. package/run.py +99 -0
  70. package/scripts/build_docs.py +283 -0
  71. package/scripts/build_validation_list.py +160 -0
  72. package/scripts/guide-create-branch.sh +15 -0
  73. package/scripts/update-package-version.js +28 -0
  74. package/search-results.json +384 -0
  75. package/setup.cfg +2 -0
  76. package/setup.py +35 -0
  77. package/src/index.ts +1 -0
  78. package/src/validations.ts +22 -0
  79. package/src/version.ts +1 -0
  80. package/tests/Dockerfile +13 -0
  81. package/tests/__init__.py +3 -0
  82. package/tests/fixtures/aggregated/cycle/inputs-impactAssessment/invalid-no-impactAssessment.json +64 -0
  83. package/tests/fixtures/aggregated/cycle/inputs-impactAssessment/invalid-world.json +69 -0
  84. package/tests/fixtures/aggregated/cycle/inputs-impactAssessment/valid.json +69 -0
  85. package/tests/fixtures/animal/duplicated-input-cycle/invalid.json +98 -0
  86. package/tests/fixtures/animal/duplicated-input-cycle/valid.json +91 -0
  87. package/tests/fixtures/animal/pregnancyRateTotal/invalid.json +49 -0
  88. package/tests/fixtures/animal/pregnancyRateTotal/valid.json +60 -0
  89. package/tests/fixtures/animal/required/invalid.json +59 -0
  90. package/tests/fixtures/animal/required/valid.json +72 -0
  91. package/tests/fixtures/completeness/all-values/warning.json +22 -0
  92. package/tests/fixtures/completeness/animalPopulation/invalid.json +58 -0
  93. package/tests/fixtures/completeness/animalPopulation/valid-animals.json +71 -0
  94. package/tests/fixtures/completeness/animalPopulation/valid-incomplete.json +58 -0
  95. package/tests/fixtures/completeness/animalPopulation/valid-no-liveAnimals.json +37 -0
  96. package/tests/fixtures/completeness/blank-nodes/agri-food processor-invalid.json +52 -0
  97. package/tests/fixtures/completeness/blank-nodes/invalid.json +124 -0
  98. package/tests/fixtures/completeness/blank-nodes/valid.json +128 -0
  99. package/tests/fixtures/completeness/cropland/site.json +16 -0
  100. package/tests/fixtures/completeness/cropland/valid.json +22 -0
  101. package/tests/fixtures/completeness/cropland/warning.json +22 -0
  102. package/tests/fixtures/completeness/freshForage/error-animals.json +63 -0
  103. package/tests/fixtures/completeness/freshForage/error-products.json +65 -0
  104. package/tests/fixtures/completeness/freshForage/valid-animal-inputs.json +63 -0
  105. package/tests/fixtures/completeness/freshForage/valid-animals.json +63 -0
  106. package/tests/fixtures/completeness/freshForage/valid-not-grazing-liveAnimal.json +55 -0
  107. package/tests/fixtures/completeness/freshForage/valid-not-liveAnimal.json +47 -0
  108. package/tests/fixtures/completeness/freshForage/valid-products.json +68 -0
  109. package/tests/fixtures/completeness/ingredient/invalid-agri-food-processor.json +37 -0
  110. package/tests/fixtures/completeness/ingredient/invalid.json +49 -0
  111. package/tests/fixtures/completeness/ingredient/valid-agri-food-processor-complete.json +49 -0
  112. package/tests/fixtures/completeness/ingredient/valid-agri-food-processor-incomplete.json +37 -0
  113. package/tests/fixtures/completeness/ingredient/valid.json +49 -0
  114. package/tests/fixtures/completeness/material/error.json +49 -0
  115. package/tests/fixtures/completeness/material/valid-fuel-material.json +60 -0
  116. package/tests/fixtures/completeness/material/valid-incomplete.json +36 -0
  117. package/tests/fixtures/completeness/material/valid-no-fuel.json +36 -0
  118. package/tests/fixtures/completeness/valid.json +22 -0
  119. package/tests/fixtures/cycle/aboveGroundCropResidue/invalid.json +76 -0
  120. package/tests/fixtures/cycle/aboveGroundCropResidue/valid.json +76 -0
  121. package/tests/fixtures/cycle/aggregated-valid.json +102 -0
  122. package/tests/fixtures/cycle/coverCrop/invalid.json +64 -0
  123. package/tests/fixtures/cycle/coverCrop/valid-not-coverCrop.json +54 -0
  124. package/tests/fixtures/cycle/coverCrop/valid.json +64 -0
  125. package/tests/fixtures/cycle/cropResidue/complete/invalid.json +56 -0
  126. package/tests/fixtures/cycle/cropResidue/complete/valid.json +82 -0
  127. package/tests/fixtures/cycle/cropResidue/incomplete/invalid.json +42 -0
  128. package/tests/fixtures/cycle/cropResidue/incomplete/valid.json +56 -0
  129. package/tests/fixtures/cycle/dates/invalid-emissions.json +70 -0
  130. package/tests/fixtures/cycle/liveAnimal-animalProduct-mapping/invalid.json +63 -0
  131. package/tests/fixtures/cycle/liveAnimal-animalProduct-mapping/valid.json +63 -0
  132. package/tests/fixtures/cycle/maximumCycleDuration/invalid-dates-year-only.json +48 -0
  133. package/tests/fixtures/cycle/maximumCycleDuration/invalid-dates.json +48 -0
  134. package/tests/fixtures/cycle/maximumCycleDuration/invalid.json +48 -0
  135. package/tests/fixtures/cycle/maximumCycleDuration/valid-dates-year-only.json +48 -0
  136. package/tests/fixtures/cycle/maximumCycleDuration/valid-dates.json +48 -0
  137. package/tests/fixtures/cycle/maximumCycleDuration/valid.json +48 -0
  138. package/tests/fixtures/cycle/otherSites/cycleDuration/invalid.json +52 -0
  139. package/tests/fixtures/cycle/otherSites/cycleDuration/valid-no-siteDuration.json +40 -0
  140. package/tests/fixtures/cycle/otherSites/cycleDuration/valid.json +52 -0
  141. package/tests/fixtures/cycle/practices/stockingDensityPermanentPastureAverage/invalid.json +56 -0
  142. package/tests/fixtures/cycle/practices/stockingDensityPermanentPastureAverage/valid.json +65 -0
  143. package/tests/fixtures/cycle/primary-product-as-input/invalid.json +59 -0
  144. package/tests/fixtures/cycle/primary-product-as-input/valid.json +48 -0
  145. package/tests/fixtures/cycle/product-linked-ia/cycle.json +66 -0
  146. package/tests/fixtures/cycle/product-linked-ia/invalid-multiple.json +58 -0
  147. package/tests/fixtures/cycle/product-linked-ia/valid.json +57 -0
  148. package/tests/fixtures/cycle/products/animals/invalid.json +69 -0
  149. package/tests/fixtures/cycle/products/animals/valid.json +58 -0
  150. package/tests/fixtures/cycle/riceGrainInHuskFlooded-minimumCycleDuration/invalid.json +53 -0
  151. package/tests/fixtures/cycle/riceGrainInHuskFlooded-minimumCycleDuration/valid.json +53 -0
  152. package/tests/fixtures/cycle/siteDuration/crop/invalid.json +53 -0
  153. package/tests/fixtures/cycle/siteDuration/crop/valid-different-duration.json +53 -0
  154. package/tests/fixtures/cycle/siteDuration/crop/valid-same-duration.json +53 -0
  155. package/tests/fixtures/cycle/siteDuration/invalid.json +41 -0
  156. package/tests/fixtures/cycle/siteDuration/valid-no-siteDuration.json +40 -0
  157. package/tests/fixtures/cycle/siteDuration/valid-otherSites.json +48 -0
  158. package/tests/fixtures/cycle/siteDuration/valid.json +45 -0
  159. package/tests/fixtures/cycle/substrate/required/invalid.json +50 -0
  160. package/tests/fixtures/cycle/substrate/required/valid.json +60 -0
  161. package/tests/fixtures/cycle/valid.json +343 -0
  162. package/tests/fixtures/emission/linked-terms/inputs/invalid.json +78 -0
  163. package/tests/fixtures/emission/linked-terms/inputs/valid.json +106 -0
  164. package/tests/fixtures/emission/linked-terms/transformation/error.json +104 -0
  165. package/tests/fixtures/emission/linked-terms/transformation/valid.json +107 -0
  166. package/tests/fixtures/emission/linked-terms/transformation/warning.json +76 -0
  167. package/tests/fixtures/emission/methodTier-background/invalid.json +60 -0
  168. package/tests/fixtures/emission/methodTier-background/valid.json +60 -0
  169. package/tests/fixtures/emission/not-relevant/invalid.json +71 -0
  170. package/tests/fixtures/emission/not-relevant/valid.json +95 -0
  171. package/tests/fixtures/emission/not-relevant-methodTier/invalid.json +70 -0
  172. package/tests/fixtures/emission/not-relevant-methodTier/valid.json +95 -0
  173. package/tests/fixtures/impactAssessment/aggregated-valid.json +43 -0
  174. package/tests/fixtures/impactAssessment/cycle-contains-product/invalid.json +34 -0
  175. package/tests/fixtures/impactAssessment/cycle-contains-product/valid.json +34 -0
  176. package/tests/fixtures/impactAssessment/cycle-endDate/invalid.json +26 -0
  177. package/tests/fixtures/impactAssessment/cycle-endDate/valid.json +26 -0
  178. package/tests/fixtures/impactAssessment/valid.json +93 -0
  179. package/tests/fixtures/indicator/characterisedIndicator-methodModel/invalid.json +52 -0
  180. package/tests/fixtures/indicator/characterisedIndicator-methodModel/valid.json +52 -0
  181. package/tests/fixtures/indicator/ionisingCompounds/invalid.json +23 -0
  182. package/tests/fixtures/indicator/ionisingCompounds/valid.json +23 -0
  183. package/tests/fixtures/indicator/landTransformation/invalid-grouped.json +257 -0
  184. package/tests/fixtures/indicator/landTransformation/invalid.json +100 -0
  185. package/tests/fixtures/indicator/landTransformation/valid-grouped-full.json +507 -0
  186. package/tests/fixtures/indicator/landTransformation/valid-grouped.json +507 -0
  187. package/tests/fixtures/indicator/landTransformation/valid.json +100 -0
  188. package/tests/fixtures/infrastructure/lifespan/invalid.json +26 -0
  189. package/tests/fixtures/infrastructure/lifespan/valid.json +45 -0
  190. package/tests/fixtures/input/animalFeed-fate/invalid.json +103 -0
  191. package/tests/fixtures/input/animalFeed-fate/valid.json +90 -0
  192. package/tests/fixtures/input/country/invalid.json +64 -0
  193. package/tests/fixtures/input/country/valid.json +64 -0
  194. package/tests/fixtures/input/distribution/animalHousing.json +103 -0
  195. package/tests/fixtures/input/distribution/complete/invalid.json +177 -0
  196. package/tests/fixtures/input/distribution/complete/valid.json +163 -0
  197. package/tests/fixtures/input/distribution/incomplete/valid.json +139 -0
  198. package/tests/fixtures/input/impactAssessment/invalid.json +99 -0
  199. package/tests/fixtures/input/impactAssessment/valid.json +89 -0
  200. package/tests/fixtures/input/input-as-product/invalid.json +57 -0
  201. package/tests/fixtures/input/input-as-product/valid.json +59 -0
  202. package/tests/fixtures/input/mustIncludeId/invalid.json +13 -0
  203. package/tests/fixtures/input/mustIncludeId/valid-multiple-ids.json +31 -0
  204. package/tests/fixtures/input/mustIncludeId/valid.json +22 -0
  205. package/tests/fixtures/input/saplings/invalid.json +58 -0
  206. package/tests/fixtures/input/saplings/valid-no-saplings.json +58 -0
  207. package/tests/fixtures/input/saplings/valid-not-plantation.json +58 -0
  208. package/tests/fixtures/input/saplings/valid.json +58 -0
  209. package/tests/fixtures/integration/distribution/product-yield-invalid.json +54 -0
  210. package/tests/fixtures/management/cycle-overlap/cycles.json +39 -0
  211. package/tests/fixtures/management/cycle-overlap/invalid.json +26 -0
  212. package/tests/fixtures/management/cycle-overlap/valid.json +26 -0
  213. package/tests/fixtures/management/exists/invalid.json +13 -0
  214. package/tests/fixtures/management/exists/valid.json +25 -0
  215. package/tests/fixtures/management/fallow-dates/invalid.json +24 -0
  216. package/tests/fixtures/management/fallow-dates/valid.json +24 -0
  217. package/tests/fixtures/management/termType/invalid-cropland.json +35 -0
  218. package/tests/fixtures/management/termType/invalid-permanent-pasture.json +25 -0
  219. package/tests/fixtures/management/termType/valid-cropland.json +55 -0
  220. package/tests/fixtures/management/termType/valid-no-management.json +13 -0
  221. package/tests/fixtures/management/termType/valid-permanent-pasture.json +35 -0
  222. package/tests/fixtures/measurement/depths/invalid.json +44 -0
  223. package/tests/fixtures/measurement/depths/valid.json +50 -0
  224. package/tests/fixtures/measurement/models/valid.json +33 -0
  225. package/tests/fixtures/measurement/models/warning-no-value.json +30 -0
  226. package/tests/fixtures/measurement/models/warning.json +33 -0
  227. package/tests/fixtures/measurement/pond-measurements/invalid.json +11 -0
  228. package/tests/fixtures/measurement/pond-measurements/valid.json +23 -0
  229. package/tests/fixtures/measurement/required-depths/error.json +71 -0
  230. package/tests/fixtures/measurement/required-depths/valid.json +126 -0
  231. package/tests/fixtures/measurement/required-depths/warning.json +29 -0
  232. package/tests/fixtures/measurement/soilTexture/missing-texture-value.json +227 -0
  233. package/tests/fixtures/measurement/soilTexture/percent-invalid.json +110 -0
  234. package/tests/fixtures/measurement/soilTexture/percent-missing-value.json +43 -0
  235. package/tests/fixtures/measurement/soilTexture/percent-valid.json +110 -0
  236. package/tests/fixtures/measurement/startDate-endDate-required/invalid.json +32 -0
  237. package/tests/fixtures/measurement/startDate-endDate-required/valid.json +46 -0
  238. package/tests/fixtures/measurement/unique/invalid.json +28 -0
  239. package/tests/fixtures/measurement/unique/valid.json +16 -0
  240. package/tests/fixtures/measurement/value-length/invalid.json +46 -0
  241. package/tests/fixtures/measurement/value-length/valid.json +44 -0
  242. package/tests/fixtures/measurement/water-salinity/invalid.json +33 -0
  243. package/tests/fixtures/measurement/water-salinity/valid-brakish.json +40 -0
  244. package/tests/fixtures/measurement/water-salinity/valid.json +33 -0
  245. package/tests/fixtures/organisation/valid.json +26 -0
  246. package/tests/fixtures/practice/croppingDuration/riceGrainInHuskFlooded/invalid.json +63 -0
  247. package/tests/fixtures/practice/croppingDuration/riceGrainInHuskFlooded/valid.json +63 -0
  248. package/tests/fixtures/practice/defaultValue/invalid.json +12 -0
  249. package/tests/fixtures/practice/defaultValue/valid.json +15 -0
  250. package/tests/fixtures/practice/excretaManagement/invalid.json +50 -0
  251. package/tests/fixtures/practice/excretaManagement/valid.json +60 -0
  252. package/tests/fixtures/practice/irrigated-complete/invalid.json +47 -0
  253. package/tests/fixtures/practice/irrigated-complete/valid-incomplete.json +47 -0
  254. package/tests/fixtures/practice/irrigated-complete/valid.json +60 -0
  255. package/tests/fixtures/practice/landCover-products/invalid.json +58 -0
  256. package/tests/fixtures/practice/landCover-products/valid-coverCrop.json +69 -0
  257. package/tests/fixtures/practice/landCover-products/valid.json +58 -0
  258. package/tests/fixtures/practice/liveAnimal-system/invalid.json +58 -0
  259. package/tests/fixtures/practice/liveAnimal-system/valid.json +69 -0
  260. package/tests/fixtures/practice/longFallowDuration/invalid.json +20 -0
  261. package/tests/fixtures/practice/longFallowDuration/valid.json +20 -0
  262. package/tests/fixtures/practice/noTillage/invalid.json +23 -0
  263. package/tests/fixtures/practice/noTillage/valid-value-not-100.json +23 -0
  264. package/tests/fixtures/practice/noTillage/valid.json +21 -0
  265. package/tests/fixtures/practice/pastureGrass/key-termType/invalid.json +16 -0
  266. package/tests/fixtures/practice/pastureGrass/key-termType/valid.json +16 -0
  267. package/tests/fixtures/practice/pastureGrass/key-value/invalid-numbers.json +67 -0
  268. package/tests/fixtures/practice/pastureGrass/key-value/invalid.json +67 -0
  269. package/tests/fixtures/practice/pastureGrass/key-value/valid.json +67 -0
  270. package/tests/fixtures/practice/pastureGrass/permanent-pasture/invalid.json +37 -0
  271. package/tests/fixtures/practice/pastureGrass/permanent-pasture/valid.json +47 -0
  272. package/tests/fixtures/practice/primaryPercent/invalid.json +49 -0
  273. package/tests/fixtures/practice/primaryPercent/valid.json +49 -0
  274. package/tests/fixtures/practice/processingOperation/invalid-no-primary.json +48 -0
  275. package/tests/fixtures/practice/processingOperation/invalid.json +49 -0
  276. package/tests/fixtures/practice/processingOperation/valid-cropland.json +37 -0
  277. package/tests/fixtures/practice/processingOperation/valid.json +49 -0
  278. package/tests/fixtures/practice/productivePhasePermanentCrops/invalid.json +48 -0
  279. package/tests/fixtures/practice/productivePhasePermanentCrops/valid-0-value.json +58 -0
  280. package/tests/fixtures/practice/productivePhasePermanentCrops/valid-no-value.json +47 -0
  281. package/tests/fixtures/practice/productivePhasePermanentCrops/valid.json +48 -0
  282. package/tests/fixtures/practice/site-management/invalid.json +75 -0
  283. package/tests/fixtures/practice/site-management/valid.json +75 -0
  284. package/tests/fixtures/practice/tillage-siteType/valid.json +51 -0
  285. package/tests/fixtures/practice/tillage-siteType/warning.json +42 -0
  286. package/tests/fixtures/practice/tillage-values/invalid-fullTillage.json +61 -0
  287. package/tests/fixtures/practice/tillage-values/invalid-noTillage.json +61 -0
  288. package/tests/fixtures/practice/tillage-values/valid.json +61 -0
  289. package/tests/fixtures/practice/waterRegime/rice/invalid.json +59 -0
  290. package/tests/fixtures/practice/waterRegime/rice/valid-0-value.json +59 -0
  291. package/tests/fixtures/practice/waterRegime/rice/valid.json +58 -0
  292. package/tests/fixtures/product/economicValueShare/invalid.json +31 -0
  293. package/tests/fixtures/product/economicValueShare/valid.json +22 -0
  294. package/tests/fixtures/product/excreta/invalid.json +62 -0
  295. package/tests/fixtures/product/excreta/valid.json +62 -0
  296. package/tests/fixtures/product/excreta/warning.json +53 -0
  297. package/tests/fixtures/product/excreta/with-system/invalid.json +79 -0
  298. package/tests/fixtures/product/excreta/with-system/valid.json +88 -0
  299. package/tests/fixtures/product/excreta/with-system/warning.json +70 -0
  300. package/tests/fixtures/product/fu_ha/invalid.json +49 -0
  301. package/tests/fixtures/product/fu_ha/valid.json +49 -0
  302. package/tests/fixtures/product/primary/invalid.json +22 -0
  303. package/tests/fixtures/product/primary/valid.json +22 -0
  304. package/tests/fixtures/product/value/valid.json +26 -0
  305. package/tests/fixtures/product/value/value-0/error.json +40 -0
  306. package/tests/fixtures/product/value/value-empty/warning.json +23 -0
  307. package/tests/fixtures/product/yield/invalid.json +54 -0
  308. package/tests/fixtures/product/yield/no-value.json +75 -0
  309. package/tests/fixtures/product/yield/valid.json +54 -0
  310. package/tests/fixtures/property/default-value/valid-allowed-exception.json +61 -0
  311. package/tests/fixtures/property/default-value/valid.json +61 -0
  312. package/tests/fixtures/property/default-value/warning.json +61 -0
  313. package/tests/fixtures/property/termType/invalid.json +60 -0
  314. package/tests/fixtures/property/termType/valid.json +60 -0
  315. package/tests/fixtures/property/value-min-max/invalid.json +77 -0
  316. package/tests/fixtures/property/value-min-max/valid-skip-maximum.json +57 -0
  317. package/tests/fixtures/property/value-min-max/valid.json +78 -0
  318. package/tests/fixtures/property/valueType/invalid.json +79 -0
  319. package/tests/fixtures/property/valueType/valid.json +79 -0
  320. package/tests/fixtures/property/volatileSolidsContent/invalid.json +99 -0
  321. package/tests/fixtures/property/volatileSolidsContent/valid.json +99 -0
  322. package/tests/fixtures/shared/coordinates/invalid.json +18 -0
  323. package/tests/fixtures/shared/coordinates/valid.json +18 -0
  324. package/tests/fixtures/shared/data-duplicates/valid.json +113 -0
  325. package/tests/fixtures/shared/data-duplicates/warning.json +172 -0
  326. package/tests/fixtures/shared/duplicated-term-units/invalid-animalProduct.json +61 -0
  327. package/tests/fixtures/shared/duplicated-term-units/invalid-organicFertiliser.json +61 -0
  328. package/tests/fixtures/shared/duplicated-term-units/valid.json +49 -0
  329. package/tests/fixtures/shared/list-country-region/invalid.json +54 -0
  330. package/tests/fixtures/shared/list-country-region/valid.json +54 -0
  331. package/tests/fixtures/shared/list-percent-value/invalid.json +49 -0
  332. package/tests/fixtures/shared/list-percent-value/valid.json +52 -0
  333. package/tests/fixtures/shared/list-valueType/invalid.json +49 -0
  334. package/tests/fixtures/shared/list-valueType/valid.json +49 -0
  335. package/tests/fixtures/shared/list-values-sum-100/management/with-properties/valid.json +91 -0
  336. package/tests/fixtures/shared/list-values-sum-100/measurements/missing-soil.json +46 -0
  337. package/tests/fixtures/shared/list-values-sum-100/measurements/no-depth-high-value.json +63 -0
  338. package/tests/fixtures/shared/list-values-sum-100/measurements/no-depth-valid.json +40 -0
  339. package/tests/fixtures/shared/list-values-sum-100/measurements/with-depth-high-value.json +71 -0
  340. package/tests/fixtures/shared/list-values-sum-100/practices/total-100.json +61 -0
  341. package/tests/fixtures/shared/list-values-sum-100/practices/total-110.json +61 -0
  342. package/tests/fixtures/shared/list-values-sum-100/practices/total-90.json +61 -0
  343. package/tests/fixtures/shared/min-max/value-above.json +31 -0
  344. package/tests/fixtures/shared/min-max/value-below.json +31 -0
  345. package/tests/fixtures/shared/min-max/value-valid.json +45 -0
  346. package/tests/fixtures/shared/model/emissions/invalid.json +102 -0
  347. package/tests/fixtures/shared/model/emissions/valid-variable-tolerance.json +180 -0
  348. package/tests/fixtures/shared/model/emissions/valid.json +102 -0
  349. package/tests/fixtures/shared/model/impacts/invalid.json +75 -0
  350. package/tests/fixtures/shared/model/impacts/valid.json +75 -0
  351. package/tests/fixtures/shared/model/inputs/valid-no-value.json +84 -0
  352. package/tests/fixtures/shared/model/inputs/valid.json +87 -0
  353. package/tests/fixtures/shared/model/inputs/warning.json +87 -0
  354. package/tests/fixtures/shared/model/products/valid-no-value.json +88 -0
  355. package/tests/fixtures/shared/model/products/valid.json +91 -0
  356. package/tests/fixtures/shared/model/products/warning.json +91 -0
  357. package/tests/fixtures/shared/otherModel/invalid.json +69 -0
  358. package/tests/fixtures/shared/otherModel/valid.json +70 -0
  359. package/tests/fixtures/shared/properties-duplicate-values/invalid.json +61 -0
  360. package/tests/fixtures/shared/properties-duplicate-values/valid.json +57 -0
  361. package/tests/fixtures/shared/properties-same-length/invalid.json +62 -0
  362. package/tests/fixtures/shared/properties-same-length/valid.json +52 -0
  363. package/tests/fixtures/shared/unit-percent/invalid.json +34 -0
  364. package/tests/fixtures/shared/unit-percent/valid.json +60 -0
  365. package/tests/fixtures/shared/unit-percent/warning.json +52 -0
  366. package/tests/fixtures/site/cycles-linked-ia/invalid.json +129 -0
  367. package/tests/fixtures/site/cycles-linked-ia/valid.json +129 -0
  368. package/tests/fixtures/site/valid.json +138 -0
  369. package/tests/fixtures/source/valid.json +19 -0
  370. package/tests/fixtures/transformation/excretaManagement/invalid.json +47 -0
  371. package/tests/fixtures/transformation/excretaManagement/valid.json +59 -0
  372. package/tests/fixtures/transformation/inputs-products/invalid.json +43 -0
  373. package/tests/fixtures/transformation/inputs-products/valid.json +43 -0
  374. package/tests/fixtures/transformation/linked-emission/invalid.json +101 -0
  375. package/tests/fixtures/transformation/linked-emission/valid.json +107 -0
  376. package/tests/fixtures/transformation/previousTransformationId/invalid-no-previous.json +127 -0
  377. package/tests/fixtures/transformation/previousTransformationId/invalid-previous-input.json +100 -0
  378. package/tests/fixtures/transformation/previousTransformationId/invalid-product-input.json +106 -0
  379. package/tests/fixtures/transformation/previousTransformationId/invalid-wrong-order.json +136 -0
  380. package/tests/fixtures/transformation/previousTransformationId/valid.json +171 -0
  381. package/tests/integration/__init__.py +0 -0
  382. package/tests/integration/test_product.py +17 -0
  383. package/tests/test_gee.py +10 -0
  384. package/tests/test_utils.py +36 -0
  385. package/tests/test_validation.py +11 -0
  386. package/tests/utils.py +28 -0
  387. package/tests/validators/__init__.py +0 -0
  388. package/tests/validators/test_aggregated_cycle.py +44 -0
  389. package/tests/validators/test_aggregated_shared.py +63 -0
  390. package/tests/validators/test_animal.py +72 -0
  391. package/tests/validators/test_completeness.py +337 -0
  392. package/tests/validators/test_cycle.py +600 -0
  393. package/tests/validators/test_emission.py +170 -0
  394. package/tests/validators/test_impact_assessment.py +80 -0
  395. package/tests/validators/test_indicator.py +120 -0
  396. package/tests/validators/test_infrastructure.py +26 -0
  397. package/tests/validators/test_input.py +434 -0
  398. package/tests/validators/test_management.py +177 -0
  399. package/tests/validators/test_measurement.py +317 -0
  400. package/tests/validators/test_organisation.py +32 -0
  401. package/tests/validators/test_practice.py +490 -0
  402. package/tests/validators/test_product.py +291 -0
  403. package/tests/validators/test_property.py +143 -0
  404. package/tests/validators/test_shared.py +1139 -0
  405. package/tests/validators/test_site.py +151 -0
  406. package/tests/validators/test_source.py +15 -0
  407. package/tests/validators/test_transformation.py +151 -0
  408. package/tests/validators/test_validators.py +74 -0
  409. package/tsconfig.dist.json +9 -0
  410. package/tsconfig.json +25 -0
package/release.sh ADDED
@@ -0,0 +1,11 @@
1
+ #!/bin/sh
2
+
3
+ FROM_BRANCH="develop"
4
+ TO_BRANCH="master"
5
+
6
+ git checkout $TO_BRANCH
7
+ git merge $FROM_BRANCH
8
+ npm run release
9
+ git checkout $FROM_BRANCH
10
+ git merge $TO_BRANCH
11
+ git push
@@ -0,0 +1,6 @@
1
+ -r requirements.txt
2
+ boto3
3
+ botocore
4
+ hestia-earth-models>=0.75.1
5
+ hestia-earth-earth-engine>=0.6.0
6
+ hestia-earth-distribution>=0.3.1
@@ -0,0 +1,4 @@
1
+ -r requirements-ci.txt
2
+ pytest-cov
3
+ pytest-xdist
4
+ coverage
@@ -0,0 +1,2 @@
1
+ hestia-earth-schema>=35.0.1
2
+ hestia-earth-utils>=0.16.9
@@ -0,0 +1,7 @@
1
+ #!/bin/sh
2
+ docker build -t hestia-data-validation:test -f tests/Dockerfile .
3
+
4
+ docker run --rm \
5
+ --env-file .env \
6
+ -v ${PWD}:/app \
7
+ hestia-data-validation:test "$@"
package/run-docker.sh ADDED
@@ -0,0 +1,9 @@
1
+ #!/bin/sh
2
+
3
+ docker build --progress=plain -t hestia-data-validation:latest .
4
+
5
+ docker run --rm \
6
+ --name hestia-data-validation \
7
+ -v ${PWD}:/app \
8
+ -v ${PWD}/samples:/app/samples \
9
+ hestia-data-validation:latest python run.py "$@"
package/run.py ADDED
@@ -0,0 +1,99 @@
1
+ # usage:
2
+ # 1. Install python-dotenv
3
+ # 2. Set your env variables in a `.env` file
4
+ # 3. Put your test nodes into the `samples` folder
5
+ # 3. Run `python run.py samples/nodes.jsonld`
6
+ from dotenv import load_dotenv
7
+
8
+ load_dotenv()
9
+
10
+
11
+ import os
12
+ import json
13
+ import argparse
14
+ from hestia_earth.utils.pipeline import to_string
15
+ from hestia_earth.utils.api import api_url, _safe_get_request, download_hestia
16
+ from hestia_earth.models.preload_requests import enable_preload as models_enable_preload
17
+ from hestia_earth.validation.preload_requests import enable_preload
18
+ from hestia_earth.validation import validate
19
+
20
+
21
+ parser = argparse.ArgumentParser(description="Run validation on nodes.")
22
+ parser.add_argument("--file-id", type=str, help="The ID of the file on HESTIA.")
23
+ parser.add_argument(
24
+ "--filepath", type=str, help="The path of the file containing the nodes."
25
+ )
26
+ parser.add_argument("--node-id", type=str, help="The ID of the node to recalculate.")
27
+ parser.add_argument(
28
+ "--node-type",
29
+ type=str,
30
+ help="The Type of the node to recalculate. Can be determined by the model to run when specified.",
31
+ )
32
+ parser.add_argument(
33
+ "--data-state",
34
+ type=str,
35
+ default="original",
36
+ choices=["original", "recalculated"],
37
+ help="DataState of nodes found in file or on the platform.",
38
+ )
39
+ args = parser.parse_args()
40
+
41
+
42
+ def _enable_mock_models():
43
+ models_enable_preload(
44
+ os.path.join(".", "models-search-results.json"),
45
+ overwrite_existing=False,
46
+ use_glossary=True,
47
+ )
48
+ enable_preload(
49
+ os.path.join(".", "validation-search-results.json"),
50
+ overwrite_existing=False,
51
+ use_glossary=True,
52
+ )
53
+
54
+
55
+ def _safe_download_node(node: dict, data_state):
56
+ return download_hestia(
57
+ node["@id"], node["@type"], data_state=data_state
58
+ ) or download_hestia(node["@id"], node["@type"])
59
+
60
+
61
+ def _download_node(node: dict):
62
+ data = _safe_download_node(node, data_state=args.data_state)
63
+ # include site and cycle
64
+ if data.get("cycle", {}).get("@id"):
65
+ data["cycle"] = _safe_download_node(data["cycle"], data_state="recalculated")
66
+ if data.get("site", {}).get("@id"):
67
+ data["site"] = _safe_download_node(data["site"], data_state="recalculated")
68
+ return data
69
+
70
+
71
+ def main():
72
+ if args.filepath:
73
+ print(f"processing {args.filepath}")
74
+ filename = args.filepath.split(".")[0]
75
+ with open(args.filepath) as f:
76
+ data = json.load(f)
77
+ elif args.file_id:
78
+ file_data = _safe_get_request(f"{api_url()}/files/{args.file_id}")
79
+ filename = f"samples/{args.file_id}"
80
+ data = _safe_get_request(file_data["hestiaPath"])
81
+ with open(f"{filename}.json", "w") as f:
82
+ f.write(to_string(data, indent=2))
83
+ elif args.node_type and args.node_id:
84
+ data = [_download_node({"@type": args.node_type, "@id": args.node_id})]
85
+ filename = f"samples/{'-'.join([args.node_type, args.node_id])}"
86
+ os.environ["VALIDATE_EXISTING_NODES"] = "true"
87
+
88
+ _enable_mock_models()
89
+
90
+ data = validate(data if isinstance(data, list) else data.get("nodes", [data]))
91
+
92
+ dest_filepath = f"{filename}-validated.json"
93
+ print("Storing results in", dest_filepath)
94
+ with open(dest_filepath, "w") as f:
95
+ f.write(to_string(data, indent=2))
96
+
97
+
98
+ if __name__ == "__main__":
99
+ main()
@@ -0,0 +1,283 @@
1
+ #!/usr/bin/env python3
2
+
3
+ """
4
+ Script to extract docstrings from a Python file and all its local imports
5
+ and compile them into a single Markdown file.
6
+ """
7
+
8
+ import ast
9
+ import argparse
10
+ import re
11
+ from pathlib import Path
12
+ from typing import List, Set, Optional
13
+
14
+ # Regex to detect common markdown list items
15
+ LIST_MARKER_REGEX = re.compile(r"^\s*(-|\*|\+|\d+\.)\s")
16
+
17
+
18
+ def format_docstring(
19
+ docstring: str, is_module: bool = False, node_name: Optional[str] = None
20
+ ) -> str:
21
+ """
22
+ Formats a single docstring into Markdown.
23
+
24
+ - First line becomes a header (# for module, ## for func/class).
25
+ - Body is cleaned.
26
+ - Sections like "Parameters", "Returns" are skipped.
27
+ - Backticks are removed from the header.
28
+ """
29
+ if not docstring:
30
+ return ""
31
+
32
+ lines = [line.strip() for line in docstring.strip().split("\n")]
33
+ if not lines:
34
+ return ""
35
+
36
+ header_level = "#" if is_module else "##"
37
+ # Remove backticks from the header line
38
+ title = lines[0].strip().replace("`", "")
39
+
40
+ if not is_module and node_name:
41
+ # For functions/classes, add the anchor link
42
+ header = f"{header_level} [{title}](#{node_name})"
43
+ else:
44
+ # For modules, just the title
45
+ header = f"{header_level} {title}"
46
+
47
+ body_lines = []
48
+ # Stop keywords for sections we want to skip
49
+ stop_keywords = (
50
+ "Parameters",
51
+ "Returns",
52
+ "Yields",
53
+ "Raises",
54
+ "Attributes",
55
+ "Parameters:",
56
+ "Returns:",
57
+ "Yields:",
58
+ "Raises:",
59
+ "Attributes:",
60
+ )
61
+
62
+ skipping_section = False
63
+ for line in lines[1:]:
64
+ if any(line.strip().startswith(keyword) for keyword in stop_keywords):
65
+ skipping_section = True
66
+
67
+ if not skipping_section:
68
+ body_lines.append(line)
69
+
70
+ # New logic to reflow/unwrap paragraphs
71
+ reflowed_body = []
72
+ current_line = ""
73
+
74
+ for line in body_lines:
75
+ if not line: # Line is empty
76
+ if current_line:
77
+ reflowed_body.append(current_line)
78
+ current_line = ""
79
+ reflowed_body.append("") # Keep the empty line
80
+
81
+ elif LIST_MARKER_REGEX.match(line): # Line is a list item
82
+ if current_line:
83
+ reflowed_body.append(current_line)
84
+ current_line = ""
85
+ reflowed_body.append(line) # Add list item as is
86
+
87
+ elif line.startswith((" ", "\t")): # Line is indented (code block, etc.)
88
+ if current_line:
89
+ reflowed_body.append(current_line)
90
+ current_line = ""
91
+ reflowed_body.append(line) # Add indented line as is
92
+
93
+ else: # Normal text line
94
+ if not current_line:
95
+ current_line = line
96
+ else:
97
+ current_line += " " + line
98
+
99
+ # Add any trailing paragraph
100
+ if current_line:
101
+ reflowed_body.append(current_line)
102
+
103
+ body = "\n".join(reflowed_body).strip()
104
+
105
+ if body:
106
+ return f"{header}\n\n{body}\n\n"
107
+ else:
108
+ return f"{header}\n\n"
109
+
110
+
111
+ def extract_documentation(
112
+ entry_file: str, output_file: str, include_empty: bool = False
113
+ ) -> None:
114
+ """
115
+ Main function to extract docs and write to the output file.
116
+ """
117
+ entry_path = Path(entry_file).resolve()
118
+ base_dir = entry_path.parent
119
+ files_to_process: List[Path] = [entry_path]
120
+ processed_files: Set[Path] = set()
121
+ all_markdown: List[str] = []
122
+
123
+ print(f"Starting documentation extraction from: {entry_file}")
124
+ print(f"Base directory: {base_dir}")
125
+
126
+ while files_to_process:
127
+ current_file = files_to_process.pop(0)
128
+ if current_file in processed_files:
129
+ continue
130
+
131
+ print(f"Processing: {current_file.relative_to(base_dir)}")
132
+ processed_files.add(current_file)
133
+
134
+ try:
135
+ with open(current_file, "r", encoding="utf-8") as f:
136
+ content = f.read()
137
+ tree = ast.parse(content)
138
+ except Exception as e:
139
+ print(f" [Error] Could not parse {current_file}: {e}")
140
+ continue
141
+
142
+ # --- 1. Extract docstrings from this file's top-level nodes ---
143
+
144
+ # Handle module docstring first
145
+ module_docstring = ast.get_docstring(tree)
146
+ if module_docstring:
147
+ formatted_doc = format_docstring(module_docstring, is_module=True)
148
+ all_markdown.append(formatted_doc)
149
+
150
+ # Handle top-level functions and classes
151
+ for node in tree.body:
152
+ if isinstance(node, (ast.FunctionDef, ast.ClassDef, ast.AsyncFunctionDef)):
153
+ docstring = ast.get_docstring(node)
154
+ node_name = node.name
155
+
156
+ if docstring:
157
+ # Found a real docstring, format and add it
158
+ formatted_doc = format_docstring(
159
+ docstring, is_module=False, node_name=node_name
160
+ )
161
+ all_markdown.append(formatted_doc)
162
+
163
+ elif include_empty and not node_name.startswith("_"):
164
+ # No docstring, not private, and flag is enabled:
165
+ # Generate a placeholder
166
+
167
+ # 1. Generate title from function name
168
+ title = node_name.replace("_", " ").capitalize()
169
+
170
+ # 2. Format as H2 with anchor
171
+ header = f"## [{title}](#{node_name})"
172
+
173
+ # 3. Add "Coming soon..." body
174
+ body = "Coming soon..."
175
+
176
+ all_markdown.append(f"{header}\n\n{body}\n\n")
177
+
178
+ # --- 2. Find new local imports to process ---
179
+ for node in ast.walk(tree):
180
+ import_path: Optional[Path] = None
181
+
182
+ if isinstance(node, ast.Import):
183
+ # e.g., import utils, other.module
184
+ for alias in node.names:
185
+ name_parts = alias.name.split(".")
186
+ potential_path = base_dir / name_parts[0]
187
+ if potential_path.is_dir():
188
+ import_path = potential_path / "__init__.py"
189
+ else:
190
+ import_path = potential_path.with_suffix(".py")
191
+
192
+ elif isinstance(node, ast.ImportFrom):
193
+ # e.g., from . import utils
194
+ # e.g., from .utils import helper
195
+ if node.level > 0: # Relative import
196
+ level_dir = current_file.parent
197
+ for _ in range(node.level - 1):
198
+ level_dir = level_dir.parent
199
+
200
+ if node.module:
201
+ module_parts = node.module.split(".")
202
+ potential_path = level_dir.joinpath(*module_parts)
203
+ else:
204
+ potential_path = level_dir
205
+
206
+ if potential_path.is_dir():
207
+ import_path = potential_path / "__init__.py"
208
+ else:
209
+ import_path = potential_path.with_suffix(".py")
210
+
211
+ elif node.module: # Absolute import (check if it's local)
212
+ name_parts = node.module.split(".")
213
+ potential_path = base_dir / name_parts[0]
214
+ if potential_path.is_dir():
215
+ import_path = potential_path / "__init__.py"
216
+ else:
217
+ import_path = potential_path.with_suffix(".py")
218
+
219
+ if (
220
+ import_path
221
+ and import_path.exists()
222
+ and import_path.is_file()
223
+ and import_path.parent.is_relative_to(base_dir)
224
+ ):
225
+ if (
226
+ import_path not in processed_files
227
+ and import_path not in files_to_process
228
+ ):
229
+ print(f" Found local import: {import_path.relative_to(base_dir)}")
230
+ files_to_process.append(import_path)
231
+
232
+ # --- 3. Write all collected markdown to the output file ---
233
+ try:
234
+ with open(output_file, "w", encoding="utf-8") as f:
235
+ f.writelines(all_markdown)
236
+ print(f"\nSuccessfully generated documentation at: {output_file}")
237
+ except Exception as e:
238
+ print(f"\n[Error] Could not write to output file {output_file}: {e}")
239
+
240
+
241
+ def main():
242
+ """
243
+ Parses command-line arguments and runs the extractor.
244
+ """
245
+ parser = argparse.ArgumentParser(
246
+ description="Extracts docstrings from a Python file and its local imports into a Markdown file.",
247
+ formatter_class=argparse.RawTextHelpFormatter,
248
+ epilog="""
249
+ Example Usage:
250
+ python doc_extractor.py --input-file my_app/main.py --output-file DOCS.md
251
+ """,
252
+ )
253
+
254
+ parser.add_argument(
255
+ "--input-file",
256
+ required=True,
257
+ help="The main Python file to start processing from.",
258
+ )
259
+ parser.add_argument(
260
+ "--output-file",
261
+ required=True,
262
+ help="The path to the final output Markdown file.",
263
+ )
264
+
265
+ parser.add_argument(
266
+ "--include-empty-functions",
267
+ action="store_true",
268
+ help="Include placeholders for functions without docstrings.",
269
+ )
270
+
271
+ args = parser.parse_args()
272
+
273
+ if not Path(args.input_file).exists():
274
+ print(f"[Error] Input file not found: {args.input_file}")
275
+ return
276
+
277
+ extract_documentation(
278
+ args.input_file, args.output_file, args.include_empty_functions
279
+ )
280
+
281
+
282
+ if __name__ == "__main__":
283
+ main()
@@ -0,0 +1,160 @@
1
+ import ast
2
+ import json
3
+ import sys
4
+ import os
5
+ import argparse
6
+
7
+
8
+ def get_str_value(node):
9
+ """
10
+ Safely extract a simple string value from an AST node.
11
+ Handles ast.Constant (Python 3.8+) and ast.Str (Python < 3.8).
12
+ Returns None if the node is not a simple string.
13
+ """
14
+ if isinstance(node, ast.Constant) and isinstance(node.value, str):
15
+ return node.value
16
+ # For compatibility with Python < 3.8
17
+ if isinstance(node, ast.Str):
18
+ return node.s
19
+ return None
20
+
21
+
22
+ def extract_validations_from_code(source_code: str) -> list:
23
+ """
24
+ Parses Python source code and extracts validation dicts
25
+ containing 'level' and 'message' keys.
26
+
27
+ Attributes all found dicts to the top-level function they are
28
+ defined within.
29
+ """
30
+ validations = []
31
+
32
+ try:
33
+ # Parse the source code into an Abstract Syntax Tree
34
+ tree = ast.parse(source_code)
35
+ except SyntaxError as e:
36
+ # We'll print the syntax error but continue processing other files
37
+ print(f"Warning: Skipping file due to SyntaxError: {e}", file=sys.stderr)
38
+ return []
39
+
40
+ # --- MODIFIED LOGIC ---
41
+
42
+ # Iterate through only the top-level nodes in the file's body
43
+ # This will find top-level functions and classes.
44
+ for top_node in tree.body:
45
+ top_level_name = None
46
+
47
+ # Check if the node is a top-level function
48
+ if isinstance(top_node, (ast.FunctionDef, ast.AsyncFunctionDef)):
49
+ top_level_name = top_node.name
50
+
51
+ # You could add logic for classes here if needed, e.g.:
52
+ # elif isinstance(top_node, ast.ClassDef):
53
+ # # Logic to handle methods within classes
54
+ # pass
55
+
56
+ # If we didn't find a top-level function, skip this node
57
+ if not top_level_name:
58
+ continue
59
+
60
+ # Now, walk through ALL nodes *within* this top-level function
61
+ for child_node in ast.walk(top_node):
62
+
63
+ # We are only looking for dictionaries. We don't care
64
+ # if we are inside a nested function or not.
65
+ if isinstance(child_node, ast.Dict):
66
+ details = {}
67
+
68
+ # Iterate over the (key, value) pairs in the dict
69
+ for key_node, value_node in zip(child_node.keys, child_node.values):
70
+ key_str = get_str_value(key_node)
71
+ value_str = get_str_value(value_node)
72
+
73
+ # We only care about 'level' and 'message' keys
74
+ # with simple string values
75
+ if key_str in ["level", "message"] and value_str is not None:
76
+ details[key_str] = value_str
77
+
78
+ # If we found both keys, add it to our list
79
+ # using the TOP-LEVEL function name.
80
+ if "level" in details and "message" in details:
81
+ validations.append(
82
+ {
83
+ "function": top_level_name, # <-- Uses the top-level name
84
+ "level": details["level"],
85
+ "message": details["message"],
86
+ }
87
+ )
88
+
89
+ # --- END MODIFIED LOGIC ---
90
+
91
+ return validations
92
+
93
+
94
+ def main():
95
+ """
96
+ Main function to parse arguments, scan files, extract validations,
97
+ and write the result to a JSON file.
98
+ """
99
+ # --- 1. Set up Argument Parser ---
100
+ parser = argparse.ArgumentParser(
101
+ description="Extracts validation 'level' and 'message' dicts from Python functions."
102
+ )
103
+ parser.add_argument(
104
+ "-i",
105
+ "--input-folder",
106
+ required=True,
107
+ help="The directory to scan for .py files (scans recursively).",
108
+ )
109
+ parser.add_argument(
110
+ "-o", "--output-file", required=True, help="The path to the output JSON file."
111
+ )
112
+ args = parser.parse_args()
113
+
114
+ # --- 2. Scan Directory and Process Files ---
115
+ all_validations = []
116
+ print(f"Scanning for .py files in: {args.input_folder}")
117
+
118
+ for root, dirs, files in os.walk(args.input_folder):
119
+ for filename in files:
120
+ if filename.endswith(".py"):
121
+ filepath = os.path.join(root, filename)
122
+ print(f"Processing: {filepath}")
123
+
124
+ try:
125
+ with open(filepath, "r", encoding="utf-8") as f:
126
+ source_code = f.read()
127
+
128
+ # Extract validations from this file's code
129
+ validations_from_file = extract_validations_from_code(source_code)
130
+ all_validations.extend(validations_from_file)
131
+
132
+ except FileNotFoundError:
133
+ print(f"Error: File not found at '{filepath}'", file=sys.stderr)
134
+ except Exception as e:
135
+ print(f"Error reading file {filepath}: {e}", file=sys.stderr)
136
+
137
+ # --- 3. Format and Write Output JSON ---
138
+ output_data = {"validations": all_validations}
139
+
140
+ try:
141
+ with open(args.output_file, "w", encoding="utf-8") as out_f:
142
+ json.dump(output_data, out_f, indent=2)
143
+
144
+ print(f"\n✅ Success! Extracted {len(all_validations)} validation(s).")
145
+ print(f"Results saved to: {args.output_file}")
146
+
147
+ except IOError as e:
148
+ print(
149
+ f"\n❌ Error: Could not write to output file {args.output_file}",
150
+ file=sys.stderr,
151
+ )
152
+ print(f"{e}", file=sys.stderr)
153
+ sys.exit(1)
154
+ except Exception as e:
155
+ print(f"\n❌ An unexpected error occurred during writing: {e}", file=sys.stderr)
156
+ sys.exit(1)
157
+
158
+
159
+ if __name__ == "__main__":
160
+ main()
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env bash
2
+
3
+ BRANCH="$1"
4
+ PRIVATE_TOKEN="$2"
5
+
6
+ GITLAB_URL="https://gitlab.com/api/v4"
7
+ SOURCE_BRANCH="develop"
8
+
9
+ PAYLOAD=$(jq -n --arg branch "$BRANCH" --arg ref "$SOURCE_BRANCH" '{branch: $branch, ref: $ref}')
10
+
11
+ curl --request POST \
12
+ --header "PRIVATE-TOKEN: $PRIVATE_TOKEN" \
13
+ --header "Content-Type: application/json" \
14
+ --data "$PAYLOAD" \
15
+ "$GITLAB_URL/projects/${GITLAB_GUIDE_PROJECT_ID}/repository/branches"
@@ -0,0 +1,28 @@
1
+ const { readFileSync, writeFileSync } = require("fs");
2
+ const { resolve, join } = require("path");
3
+
4
+ const ROOT = resolve(join(__dirname, "../"));
5
+ const encoding = "UTF-8";
6
+ const version = require(join(ROOT, "package.json")).version;
7
+
8
+ const PYTHON_VERSION_PATH = resolve(
9
+ join(ROOT, "hestia_earth", "validation", "version.py")
10
+ );
11
+ writeFileSync(
12
+ PYTHON_VERSION_PATH,
13
+ readFileSync(PYTHON_VERSION_PATH, encoding).replace(
14
+ /VERSION\s=\s\"[\d\-a-z\.]+\"/,
15
+ `VERSION = "${version}"`
16
+ ),
17
+ encoding
18
+ );
19
+
20
+ const JS_VERSION_PATH = resolve(join(ROOT, "src", "version.ts"));
21
+ writeFileSync(
22
+ JS_VERSION_PATH,
23
+ readFileSync(JS_VERSION_PATH, encoding).replace(
24
+ /\'[\d\-a-z\.]+\'/,
25
+ `'${version}'`
26
+ ),
27
+ encoding
28
+ );