@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.
- package/.coveragerc +14 -0
- package/.dockerignore +19 -0
- package/.eslintignore +17 -0
- package/.eslintrc.js +11 -0
- package/.flake8 +5 -0
- package/.gitlab/issue_templates/new validation.md +82 -0
- package/.gitlab-ci.yml +216 -0
- package/.readthedocs.yml +24 -0
- package/CODEOWNERS +11 -0
- package/Dockerfile +13 -0
- package/LICENSE +21 -0
- package/MANIFEST.in +2 -0
- package/bin/hestia-validate-data +80 -0
- package/build_mocking.py +14 -0
- package/commitlint.config.js +1 -0
- package/docs/Makefile +20 -0
- package/docs/_static/styles.css +4 -0
- package/docs/_templates/custom-class-template.rst +34 -0
- package/docs/_templates/custom-module-template.rst +66 -0
- package/docs/_templates/layout.html +4 -0
- package/docs/conf.py +74 -0
- package/docs/index.rst +42 -0
- package/docs/make.bat +35 -0
- package/docs/requirements.txt +13 -0
- package/envs/.develop.env +1 -0
- package/envs/.master.env +1 -0
- package/guide-assets/.gitkeep +0 -0
- package/hestia_earth/validation/README.md +5 -0
- package/hestia_earth/validation/__init__.py +32 -0
- package/hestia_earth/validation/distribution.py +22 -0
- package/hestia_earth/validation/gee.py +162 -0
- package/hestia_earth/validation/log.py +44 -0
- package/hestia_earth/validation/models.py +141 -0
- package/hestia_earth/validation/preload_requests.py +61 -0
- package/hestia_earth/validation/terms.py +88 -0
- package/hestia_earth/validation/utils.py +444 -0
- package/hestia_earth/validation/validators/__init__.py +141 -0
- package/hestia_earth/validation/validators/aggregated_cycle.py +32 -0
- package/hestia_earth/validation/validators/aggregated_shared.py +37 -0
- package/hestia_earth/validation/validators/animal.py +88 -0
- package/hestia_earth/validation/validators/completeness.py +252 -0
- package/hestia_earth/validation/validators/cycle.py +1123 -0
- package/hestia_earth/validation/validators/distribution.py +86 -0
- package/hestia_earth/validation/validators/emission.py +109 -0
- package/hestia_earth/validation/validators/impact_assessment.py +138 -0
- package/hestia_earth/validation/validators/indicator.py +154 -0
- package/hestia_earth/validation/validators/infrastructure.py +25 -0
- package/hestia_earth/validation/validators/input.py +268 -0
- package/hestia_earth/validation/validators/management.py +131 -0
- package/hestia_earth/validation/validators/measurement.py +368 -0
- package/hestia_earth/validation/validators/organisation.py +43 -0
- package/hestia_earth/validation/validators/practice.py +590 -0
- package/hestia_earth/validation/validators/product.py +263 -0
- package/hestia_earth/validation/validators/property.py +266 -0
- package/hestia_earth/validation/validators/shared.py +940 -0
- package/hestia_earth/validation/validators/site.py +312 -0
- package/hestia_earth/validation/validators/source.py +20 -0
- package/hestia_earth/validation/validators/transformation.py +250 -0
- package/hestia_earth/validation/version.py +1 -0
- package/layer/build.sh +34 -0
- package/layer/deploy.sh +18 -0
- package/package.json +59 -0
- package/release.sh +11 -0
- package/requirements-ci.txt +6 -0
- package/requirements-test.txt +4 -0
- package/requirements.txt +2 -0
- package/run-docker-test.sh +7 -0
- package/run-docker.sh +9 -0
- package/run.py +99 -0
- package/scripts/build_docs.py +283 -0
- package/scripts/build_validation_list.py +160 -0
- package/scripts/guide-create-branch.sh +15 -0
- package/scripts/update-package-version.js +28 -0
- package/search-results.json +384 -0
- package/setup.cfg +2 -0
- package/setup.py +35 -0
- package/src/index.ts +1 -0
- package/src/validations.ts +22 -0
- package/src/version.ts +1 -0
- package/tests/Dockerfile +13 -0
- package/tests/__init__.py +3 -0
- package/tests/fixtures/aggregated/cycle/inputs-impactAssessment/invalid-no-impactAssessment.json +64 -0
- package/tests/fixtures/aggregated/cycle/inputs-impactAssessment/invalid-world.json +69 -0
- package/tests/fixtures/aggregated/cycle/inputs-impactAssessment/valid.json +69 -0
- package/tests/fixtures/animal/duplicated-input-cycle/invalid.json +98 -0
- package/tests/fixtures/animal/duplicated-input-cycle/valid.json +91 -0
- package/tests/fixtures/animal/pregnancyRateTotal/invalid.json +49 -0
- package/tests/fixtures/animal/pregnancyRateTotal/valid.json +60 -0
- package/tests/fixtures/animal/required/invalid.json +59 -0
- package/tests/fixtures/animal/required/valid.json +72 -0
- package/tests/fixtures/completeness/all-values/warning.json +22 -0
- package/tests/fixtures/completeness/animalPopulation/invalid.json +58 -0
- package/tests/fixtures/completeness/animalPopulation/valid-animals.json +71 -0
- package/tests/fixtures/completeness/animalPopulation/valid-incomplete.json +58 -0
- package/tests/fixtures/completeness/animalPopulation/valid-no-liveAnimals.json +37 -0
- package/tests/fixtures/completeness/blank-nodes/agri-food processor-invalid.json +52 -0
- package/tests/fixtures/completeness/blank-nodes/invalid.json +124 -0
- package/tests/fixtures/completeness/blank-nodes/valid.json +128 -0
- package/tests/fixtures/completeness/cropland/site.json +16 -0
- package/tests/fixtures/completeness/cropland/valid.json +22 -0
- package/tests/fixtures/completeness/cropland/warning.json +22 -0
- package/tests/fixtures/completeness/freshForage/error-animals.json +63 -0
- package/tests/fixtures/completeness/freshForage/error-products.json +65 -0
- package/tests/fixtures/completeness/freshForage/valid-animal-inputs.json +63 -0
- package/tests/fixtures/completeness/freshForage/valid-animals.json +63 -0
- package/tests/fixtures/completeness/freshForage/valid-not-grazing-liveAnimal.json +55 -0
- package/tests/fixtures/completeness/freshForage/valid-not-liveAnimal.json +47 -0
- package/tests/fixtures/completeness/freshForage/valid-products.json +68 -0
- package/tests/fixtures/completeness/ingredient/invalid-agri-food-processor.json +37 -0
- package/tests/fixtures/completeness/ingredient/invalid.json +49 -0
- package/tests/fixtures/completeness/ingredient/valid-agri-food-processor-complete.json +49 -0
- package/tests/fixtures/completeness/ingredient/valid-agri-food-processor-incomplete.json +37 -0
- package/tests/fixtures/completeness/ingredient/valid.json +49 -0
- package/tests/fixtures/completeness/material/error.json +49 -0
- package/tests/fixtures/completeness/material/valid-fuel-material.json +60 -0
- package/tests/fixtures/completeness/material/valid-incomplete.json +36 -0
- package/tests/fixtures/completeness/material/valid-no-fuel.json +36 -0
- package/tests/fixtures/completeness/valid.json +22 -0
- package/tests/fixtures/cycle/aboveGroundCropResidue/invalid.json +76 -0
- package/tests/fixtures/cycle/aboveGroundCropResidue/valid.json +76 -0
- package/tests/fixtures/cycle/aggregated-valid.json +102 -0
- package/tests/fixtures/cycle/coverCrop/invalid.json +64 -0
- package/tests/fixtures/cycle/coverCrop/valid-not-coverCrop.json +54 -0
- package/tests/fixtures/cycle/coverCrop/valid.json +64 -0
- package/tests/fixtures/cycle/cropResidue/complete/invalid.json +56 -0
- package/tests/fixtures/cycle/cropResidue/complete/valid.json +82 -0
- package/tests/fixtures/cycle/cropResidue/incomplete/invalid.json +42 -0
- package/tests/fixtures/cycle/cropResidue/incomplete/valid.json +56 -0
- package/tests/fixtures/cycle/dates/invalid-emissions.json +70 -0
- package/tests/fixtures/cycle/liveAnimal-animalProduct-mapping/invalid.json +63 -0
- package/tests/fixtures/cycle/liveAnimal-animalProduct-mapping/valid.json +63 -0
- package/tests/fixtures/cycle/maximumCycleDuration/invalid-dates-year-only.json +48 -0
- package/tests/fixtures/cycle/maximumCycleDuration/invalid-dates.json +48 -0
- package/tests/fixtures/cycle/maximumCycleDuration/invalid.json +48 -0
- package/tests/fixtures/cycle/maximumCycleDuration/valid-dates-year-only.json +48 -0
- package/tests/fixtures/cycle/maximumCycleDuration/valid-dates.json +48 -0
- package/tests/fixtures/cycle/maximumCycleDuration/valid.json +48 -0
- package/tests/fixtures/cycle/otherSites/cycleDuration/invalid.json +52 -0
- package/tests/fixtures/cycle/otherSites/cycleDuration/valid-no-siteDuration.json +40 -0
- package/tests/fixtures/cycle/otherSites/cycleDuration/valid.json +52 -0
- package/tests/fixtures/cycle/practices/stockingDensityPermanentPastureAverage/invalid.json +56 -0
- package/tests/fixtures/cycle/practices/stockingDensityPermanentPastureAverage/valid.json +65 -0
- package/tests/fixtures/cycle/primary-product-as-input/invalid.json +59 -0
- package/tests/fixtures/cycle/primary-product-as-input/valid.json +48 -0
- package/tests/fixtures/cycle/product-linked-ia/cycle.json +66 -0
- package/tests/fixtures/cycle/product-linked-ia/invalid-multiple.json +58 -0
- package/tests/fixtures/cycle/product-linked-ia/valid.json +57 -0
- package/tests/fixtures/cycle/products/animals/invalid.json +69 -0
- package/tests/fixtures/cycle/products/animals/valid.json +58 -0
- package/tests/fixtures/cycle/riceGrainInHuskFlooded-minimumCycleDuration/invalid.json +53 -0
- package/tests/fixtures/cycle/riceGrainInHuskFlooded-minimumCycleDuration/valid.json +53 -0
- package/tests/fixtures/cycle/siteDuration/crop/invalid.json +53 -0
- package/tests/fixtures/cycle/siteDuration/crop/valid-different-duration.json +53 -0
- package/tests/fixtures/cycle/siteDuration/crop/valid-same-duration.json +53 -0
- package/tests/fixtures/cycle/siteDuration/invalid.json +41 -0
- package/tests/fixtures/cycle/siteDuration/valid-no-siteDuration.json +40 -0
- package/tests/fixtures/cycle/siteDuration/valid-otherSites.json +48 -0
- package/tests/fixtures/cycle/siteDuration/valid.json +45 -0
- package/tests/fixtures/cycle/substrate/required/invalid.json +50 -0
- package/tests/fixtures/cycle/substrate/required/valid.json +60 -0
- package/tests/fixtures/cycle/valid.json +343 -0
- package/tests/fixtures/emission/linked-terms/inputs/invalid.json +78 -0
- package/tests/fixtures/emission/linked-terms/inputs/valid.json +106 -0
- package/tests/fixtures/emission/linked-terms/transformation/error.json +104 -0
- package/tests/fixtures/emission/linked-terms/transformation/valid.json +107 -0
- package/tests/fixtures/emission/linked-terms/transformation/warning.json +76 -0
- package/tests/fixtures/emission/methodTier-background/invalid.json +60 -0
- package/tests/fixtures/emission/methodTier-background/valid.json +60 -0
- package/tests/fixtures/emission/not-relevant/invalid.json +71 -0
- package/tests/fixtures/emission/not-relevant/valid.json +95 -0
- package/tests/fixtures/emission/not-relevant-methodTier/invalid.json +70 -0
- package/tests/fixtures/emission/not-relevant-methodTier/valid.json +95 -0
- package/tests/fixtures/impactAssessment/aggregated-valid.json +43 -0
- package/tests/fixtures/impactAssessment/cycle-contains-product/invalid.json +34 -0
- package/tests/fixtures/impactAssessment/cycle-contains-product/valid.json +34 -0
- package/tests/fixtures/impactAssessment/cycle-endDate/invalid.json +26 -0
- package/tests/fixtures/impactAssessment/cycle-endDate/valid.json +26 -0
- package/tests/fixtures/impactAssessment/valid.json +93 -0
- package/tests/fixtures/indicator/characterisedIndicator-methodModel/invalid.json +52 -0
- package/tests/fixtures/indicator/characterisedIndicator-methodModel/valid.json +52 -0
- package/tests/fixtures/indicator/ionisingCompounds/invalid.json +23 -0
- package/tests/fixtures/indicator/ionisingCompounds/valid.json +23 -0
- package/tests/fixtures/indicator/landTransformation/invalid-grouped.json +257 -0
- package/tests/fixtures/indicator/landTransformation/invalid.json +100 -0
- package/tests/fixtures/indicator/landTransformation/valid-grouped-full.json +507 -0
- package/tests/fixtures/indicator/landTransformation/valid-grouped.json +507 -0
- package/tests/fixtures/indicator/landTransformation/valid.json +100 -0
- package/tests/fixtures/infrastructure/lifespan/invalid.json +26 -0
- package/tests/fixtures/infrastructure/lifespan/valid.json +45 -0
- package/tests/fixtures/input/animalFeed-fate/invalid.json +103 -0
- package/tests/fixtures/input/animalFeed-fate/valid.json +90 -0
- package/tests/fixtures/input/country/invalid.json +64 -0
- package/tests/fixtures/input/country/valid.json +64 -0
- package/tests/fixtures/input/distribution/animalHousing.json +103 -0
- package/tests/fixtures/input/distribution/complete/invalid.json +177 -0
- package/tests/fixtures/input/distribution/complete/valid.json +163 -0
- package/tests/fixtures/input/distribution/incomplete/valid.json +139 -0
- package/tests/fixtures/input/impactAssessment/invalid.json +99 -0
- package/tests/fixtures/input/impactAssessment/valid.json +89 -0
- package/tests/fixtures/input/input-as-product/invalid.json +57 -0
- package/tests/fixtures/input/input-as-product/valid.json +59 -0
- package/tests/fixtures/input/mustIncludeId/invalid.json +13 -0
- package/tests/fixtures/input/mustIncludeId/valid-multiple-ids.json +31 -0
- package/tests/fixtures/input/mustIncludeId/valid.json +22 -0
- package/tests/fixtures/input/saplings/invalid.json +58 -0
- package/tests/fixtures/input/saplings/valid-no-saplings.json +58 -0
- package/tests/fixtures/input/saplings/valid-not-plantation.json +58 -0
- package/tests/fixtures/input/saplings/valid.json +58 -0
- package/tests/fixtures/integration/distribution/product-yield-invalid.json +54 -0
- package/tests/fixtures/management/cycle-overlap/cycles.json +39 -0
- package/tests/fixtures/management/cycle-overlap/invalid.json +26 -0
- package/tests/fixtures/management/cycle-overlap/valid.json +26 -0
- package/tests/fixtures/management/exists/invalid.json +13 -0
- package/tests/fixtures/management/exists/valid.json +25 -0
- package/tests/fixtures/management/fallow-dates/invalid.json +24 -0
- package/tests/fixtures/management/fallow-dates/valid.json +24 -0
- package/tests/fixtures/management/termType/invalid-cropland.json +35 -0
- package/tests/fixtures/management/termType/invalid-permanent-pasture.json +25 -0
- package/tests/fixtures/management/termType/valid-cropland.json +55 -0
- package/tests/fixtures/management/termType/valid-no-management.json +13 -0
- package/tests/fixtures/management/termType/valid-permanent-pasture.json +35 -0
- package/tests/fixtures/measurement/depths/invalid.json +44 -0
- package/tests/fixtures/measurement/depths/valid.json +50 -0
- package/tests/fixtures/measurement/models/valid.json +33 -0
- package/tests/fixtures/measurement/models/warning-no-value.json +30 -0
- package/tests/fixtures/measurement/models/warning.json +33 -0
- package/tests/fixtures/measurement/pond-measurements/invalid.json +11 -0
- package/tests/fixtures/measurement/pond-measurements/valid.json +23 -0
- package/tests/fixtures/measurement/required-depths/error.json +71 -0
- package/tests/fixtures/measurement/required-depths/valid.json +126 -0
- package/tests/fixtures/measurement/required-depths/warning.json +29 -0
- package/tests/fixtures/measurement/soilTexture/missing-texture-value.json +227 -0
- package/tests/fixtures/measurement/soilTexture/percent-invalid.json +110 -0
- package/tests/fixtures/measurement/soilTexture/percent-missing-value.json +43 -0
- package/tests/fixtures/measurement/soilTexture/percent-valid.json +110 -0
- package/tests/fixtures/measurement/startDate-endDate-required/invalid.json +32 -0
- package/tests/fixtures/measurement/startDate-endDate-required/valid.json +46 -0
- package/tests/fixtures/measurement/unique/invalid.json +28 -0
- package/tests/fixtures/measurement/unique/valid.json +16 -0
- package/tests/fixtures/measurement/value-length/invalid.json +46 -0
- package/tests/fixtures/measurement/value-length/valid.json +44 -0
- package/tests/fixtures/measurement/water-salinity/invalid.json +33 -0
- package/tests/fixtures/measurement/water-salinity/valid-brakish.json +40 -0
- package/tests/fixtures/measurement/water-salinity/valid.json +33 -0
- package/tests/fixtures/organisation/valid.json +26 -0
- package/tests/fixtures/practice/croppingDuration/riceGrainInHuskFlooded/invalid.json +63 -0
- package/tests/fixtures/practice/croppingDuration/riceGrainInHuskFlooded/valid.json +63 -0
- package/tests/fixtures/practice/defaultValue/invalid.json +12 -0
- package/tests/fixtures/practice/defaultValue/valid.json +15 -0
- package/tests/fixtures/practice/excretaManagement/invalid.json +50 -0
- package/tests/fixtures/practice/excretaManagement/valid.json +60 -0
- package/tests/fixtures/practice/irrigated-complete/invalid.json +47 -0
- package/tests/fixtures/practice/irrigated-complete/valid-incomplete.json +47 -0
- package/tests/fixtures/practice/irrigated-complete/valid.json +60 -0
- package/tests/fixtures/practice/landCover-products/invalid.json +58 -0
- package/tests/fixtures/practice/landCover-products/valid-coverCrop.json +69 -0
- package/tests/fixtures/practice/landCover-products/valid.json +58 -0
- package/tests/fixtures/practice/liveAnimal-system/invalid.json +58 -0
- package/tests/fixtures/practice/liveAnimal-system/valid.json +69 -0
- package/tests/fixtures/practice/longFallowDuration/invalid.json +20 -0
- package/tests/fixtures/practice/longFallowDuration/valid.json +20 -0
- package/tests/fixtures/practice/noTillage/invalid.json +23 -0
- package/tests/fixtures/practice/noTillage/valid-value-not-100.json +23 -0
- package/tests/fixtures/practice/noTillage/valid.json +21 -0
- package/tests/fixtures/practice/pastureGrass/key-termType/invalid.json +16 -0
- package/tests/fixtures/practice/pastureGrass/key-termType/valid.json +16 -0
- package/tests/fixtures/practice/pastureGrass/key-value/invalid-numbers.json +67 -0
- package/tests/fixtures/practice/pastureGrass/key-value/invalid.json +67 -0
- package/tests/fixtures/practice/pastureGrass/key-value/valid.json +67 -0
- package/tests/fixtures/practice/pastureGrass/permanent-pasture/invalid.json +37 -0
- package/tests/fixtures/practice/pastureGrass/permanent-pasture/valid.json +47 -0
- package/tests/fixtures/practice/primaryPercent/invalid.json +49 -0
- package/tests/fixtures/practice/primaryPercent/valid.json +49 -0
- package/tests/fixtures/practice/processingOperation/invalid-no-primary.json +48 -0
- package/tests/fixtures/practice/processingOperation/invalid.json +49 -0
- package/tests/fixtures/practice/processingOperation/valid-cropland.json +37 -0
- package/tests/fixtures/practice/processingOperation/valid.json +49 -0
- package/tests/fixtures/practice/productivePhasePermanentCrops/invalid.json +48 -0
- package/tests/fixtures/practice/productivePhasePermanentCrops/valid-0-value.json +58 -0
- package/tests/fixtures/practice/productivePhasePermanentCrops/valid-no-value.json +47 -0
- package/tests/fixtures/practice/productivePhasePermanentCrops/valid.json +48 -0
- package/tests/fixtures/practice/site-management/invalid.json +75 -0
- package/tests/fixtures/practice/site-management/valid.json +75 -0
- package/tests/fixtures/practice/tillage-siteType/valid.json +51 -0
- package/tests/fixtures/practice/tillage-siteType/warning.json +42 -0
- package/tests/fixtures/practice/tillage-values/invalid-fullTillage.json +61 -0
- package/tests/fixtures/practice/tillage-values/invalid-noTillage.json +61 -0
- package/tests/fixtures/practice/tillage-values/valid.json +61 -0
- package/tests/fixtures/practice/waterRegime/rice/invalid.json +59 -0
- package/tests/fixtures/practice/waterRegime/rice/valid-0-value.json +59 -0
- package/tests/fixtures/practice/waterRegime/rice/valid.json +58 -0
- package/tests/fixtures/product/economicValueShare/invalid.json +31 -0
- package/tests/fixtures/product/economicValueShare/valid.json +22 -0
- package/tests/fixtures/product/excreta/invalid.json +62 -0
- package/tests/fixtures/product/excreta/valid.json +62 -0
- package/tests/fixtures/product/excreta/warning.json +53 -0
- package/tests/fixtures/product/excreta/with-system/invalid.json +79 -0
- package/tests/fixtures/product/excreta/with-system/valid.json +88 -0
- package/tests/fixtures/product/excreta/with-system/warning.json +70 -0
- package/tests/fixtures/product/fu_ha/invalid.json +49 -0
- package/tests/fixtures/product/fu_ha/valid.json +49 -0
- package/tests/fixtures/product/primary/invalid.json +22 -0
- package/tests/fixtures/product/primary/valid.json +22 -0
- package/tests/fixtures/product/value/valid.json +26 -0
- package/tests/fixtures/product/value/value-0/error.json +40 -0
- package/tests/fixtures/product/value/value-empty/warning.json +23 -0
- package/tests/fixtures/product/yield/invalid.json +54 -0
- package/tests/fixtures/product/yield/no-value.json +75 -0
- package/tests/fixtures/product/yield/valid.json +54 -0
- package/tests/fixtures/property/default-value/valid-allowed-exception.json +61 -0
- package/tests/fixtures/property/default-value/valid.json +61 -0
- package/tests/fixtures/property/default-value/warning.json +61 -0
- package/tests/fixtures/property/termType/invalid.json +60 -0
- package/tests/fixtures/property/termType/valid.json +60 -0
- package/tests/fixtures/property/value-min-max/invalid.json +77 -0
- package/tests/fixtures/property/value-min-max/valid-skip-maximum.json +57 -0
- package/tests/fixtures/property/value-min-max/valid.json +78 -0
- package/tests/fixtures/property/valueType/invalid.json +79 -0
- package/tests/fixtures/property/valueType/valid.json +79 -0
- package/tests/fixtures/property/volatileSolidsContent/invalid.json +99 -0
- package/tests/fixtures/property/volatileSolidsContent/valid.json +99 -0
- package/tests/fixtures/shared/coordinates/invalid.json +18 -0
- package/tests/fixtures/shared/coordinates/valid.json +18 -0
- package/tests/fixtures/shared/data-duplicates/valid.json +113 -0
- package/tests/fixtures/shared/data-duplicates/warning.json +172 -0
- package/tests/fixtures/shared/duplicated-term-units/invalid-animalProduct.json +61 -0
- package/tests/fixtures/shared/duplicated-term-units/invalid-organicFertiliser.json +61 -0
- package/tests/fixtures/shared/duplicated-term-units/valid.json +49 -0
- package/tests/fixtures/shared/list-country-region/invalid.json +54 -0
- package/tests/fixtures/shared/list-country-region/valid.json +54 -0
- package/tests/fixtures/shared/list-percent-value/invalid.json +49 -0
- package/tests/fixtures/shared/list-percent-value/valid.json +52 -0
- package/tests/fixtures/shared/list-valueType/invalid.json +49 -0
- package/tests/fixtures/shared/list-valueType/valid.json +49 -0
- package/tests/fixtures/shared/list-values-sum-100/management/with-properties/valid.json +91 -0
- package/tests/fixtures/shared/list-values-sum-100/measurements/missing-soil.json +46 -0
- package/tests/fixtures/shared/list-values-sum-100/measurements/no-depth-high-value.json +63 -0
- package/tests/fixtures/shared/list-values-sum-100/measurements/no-depth-valid.json +40 -0
- package/tests/fixtures/shared/list-values-sum-100/measurements/with-depth-high-value.json +71 -0
- package/tests/fixtures/shared/list-values-sum-100/practices/total-100.json +61 -0
- package/tests/fixtures/shared/list-values-sum-100/practices/total-110.json +61 -0
- package/tests/fixtures/shared/list-values-sum-100/practices/total-90.json +61 -0
- package/tests/fixtures/shared/min-max/value-above.json +31 -0
- package/tests/fixtures/shared/min-max/value-below.json +31 -0
- package/tests/fixtures/shared/min-max/value-valid.json +45 -0
- package/tests/fixtures/shared/model/emissions/invalid.json +102 -0
- package/tests/fixtures/shared/model/emissions/valid-variable-tolerance.json +180 -0
- package/tests/fixtures/shared/model/emissions/valid.json +102 -0
- package/tests/fixtures/shared/model/impacts/invalid.json +75 -0
- package/tests/fixtures/shared/model/impacts/valid.json +75 -0
- package/tests/fixtures/shared/model/inputs/valid-no-value.json +84 -0
- package/tests/fixtures/shared/model/inputs/valid.json +87 -0
- package/tests/fixtures/shared/model/inputs/warning.json +87 -0
- package/tests/fixtures/shared/model/products/valid-no-value.json +88 -0
- package/tests/fixtures/shared/model/products/valid.json +91 -0
- package/tests/fixtures/shared/model/products/warning.json +91 -0
- package/tests/fixtures/shared/otherModel/invalid.json +69 -0
- package/tests/fixtures/shared/otherModel/valid.json +70 -0
- package/tests/fixtures/shared/properties-duplicate-values/invalid.json +61 -0
- package/tests/fixtures/shared/properties-duplicate-values/valid.json +57 -0
- package/tests/fixtures/shared/properties-same-length/invalid.json +62 -0
- package/tests/fixtures/shared/properties-same-length/valid.json +52 -0
- package/tests/fixtures/shared/unit-percent/invalid.json +34 -0
- package/tests/fixtures/shared/unit-percent/valid.json +60 -0
- package/tests/fixtures/shared/unit-percent/warning.json +52 -0
- package/tests/fixtures/site/cycles-linked-ia/invalid.json +129 -0
- package/tests/fixtures/site/cycles-linked-ia/valid.json +129 -0
- package/tests/fixtures/site/valid.json +138 -0
- package/tests/fixtures/source/valid.json +19 -0
- package/tests/fixtures/transformation/excretaManagement/invalid.json +47 -0
- package/tests/fixtures/transformation/excretaManagement/valid.json +59 -0
- package/tests/fixtures/transformation/inputs-products/invalid.json +43 -0
- package/tests/fixtures/transformation/inputs-products/valid.json +43 -0
- package/tests/fixtures/transformation/linked-emission/invalid.json +101 -0
- package/tests/fixtures/transformation/linked-emission/valid.json +107 -0
- package/tests/fixtures/transformation/previousTransformationId/invalid-no-previous.json +127 -0
- package/tests/fixtures/transformation/previousTransformationId/invalid-previous-input.json +100 -0
- package/tests/fixtures/transformation/previousTransformationId/invalid-product-input.json +106 -0
- package/tests/fixtures/transformation/previousTransformationId/invalid-wrong-order.json +136 -0
- package/tests/fixtures/transformation/previousTransformationId/valid.json +171 -0
- package/tests/integration/__init__.py +0 -0
- package/tests/integration/test_product.py +17 -0
- package/tests/test_gee.py +10 -0
- package/tests/test_utils.py +36 -0
- package/tests/test_validation.py +11 -0
- package/tests/utils.py +28 -0
- package/tests/validators/__init__.py +0 -0
- package/tests/validators/test_aggregated_cycle.py +44 -0
- package/tests/validators/test_aggregated_shared.py +63 -0
- package/tests/validators/test_animal.py +72 -0
- package/tests/validators/test_completeness.py +337 -0
- package/tests/validators/test_cycle.py +600 -0
- package/tests/validators/test_emission.py +170 -0
- package/tests/validators/test_impact_assessment.py +80 -0
- package/tests/validators/test_indicator.py +120 -0
- package/tests/validators/test_infrastructure.py +26 -0
- package/tests/validators/test_input.py +434 -0
- package/tests/validators/test_management.py +177 -0
- package/tests/validators/test_measurement.py +317 -0
- package/tests/validators/test_organisation.py +32 -0
- package/tests/validators/test_practice.py +490 -0
- package/tests/validators/test_product.py +291 -0
- package/tests/validators/test_property.py +143 -0
- package/tests/validators/test_shared.py +1139 -0
- package/tests/validators/test_site.py +151 -0
- package/tests/validators/test_source.py +15 -0
- package/tests/validators/test_transformation.py +151 -0
- package/tests/validators/test_validators.py +74 -0
- package/tsconfig.dist.json +9 -0
- package/tsconfig.json +25 -0
package/release.sh
ADDED
package/requirements.txt
ADDED
package/run-docker.sh
ADDED
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
|
+
);
|