esgvoc 1.1.2__tar.gz → 1.1.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of esgvoc might be problematic. Click here for more details.
- {esgvoc-1.1.2 → esgvoc-1.1.3}/PKG-INFO +1 -1
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/__init__.py +1 -1
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/api/project_specs.py +24 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/apps/jsg/json_schema_generator.py +9 -7
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/apps/jsg/templates/template.jinja +1 -1
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/apps/test_cv/cv_tester.py +232 -31
- esgvoc-1.1.3/src/esgvoc/cli/clean.py +304 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/cli/config.py +226 -11
- esgvoc-1.1.3/src/esgvoc/cli/install.py +41 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/cli/main.py +4 -0
- esgvoc-1.1.3/src/esgvoc/cli/offline.py +269 -0
- esgvoc-1.1.3/src/esgvoc/cli/status.py +79 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/core/db/project_ingestion.py +6 -2
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/core/repo_fetcher.py +87 -4
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/core/service/__init__.py +4 -3
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/core/service/configuration/config_manager.py +17 -16
- esgvoc-1.1.3/src/esgvoc/core/service/configuration/setting.py +355 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/core/service/data_merger.py +2 -1
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/core/service/esg_voc.py +17 -19
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/core/service/state.py +65 -27
- {esgvoc-1.1.2 → esgvoc-1.1.3}/tests/conftest.py +12 -10
- esgvoc-1.1.3/tests/integration/README.md +239 -0
- esgvoc-1.1.3/tests/integration/__init__.py +28 -0
- esgvoc-1.1.3/tests/integration/conftest.py +110 -0
- esgvoc-1.1.3/tests/integration/test_config_path_resolution.py +296 -0
- esgvoc-1.1.3/tests/integration/test_end_to_end_scenarios.py +303 -0
- esgvoc-1.1.3/tests/integration/test_offline_mode_integration.py +465 -0
- esgvoc-1.1.3/tests/integration/test_shallow_clone_integration.py +275 -0
- esgvoc-1.1.3/tests/test_clean_command.py +282 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/tests/test_cli_config.py +44 -38
- {esgvoc-1.1.2 → esgvoc-1.1.3}/tests/test_config.py +2 -2
- esgvoc-1.1.3/tests/test_install.py +16 -0
- esgvoc-1.1.3/tests/test_offline_mode_cli.py +819 -0
- esgvoc-1.1.3/tests/test_offline_mode_config.py +328 -0
- esgvoc-1.1.3/tests/test_offline_mode_repo_fetcher.py +433 -0
- esgvoc-1.1.3/tests/test_offline_mode_state.py +425 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/tests/test_repo_fetcher.py +11 -4
- esgvoc-1.1.2/src/esgvoc/cli/install.py +0 -17
- esgvoc-1.1.2/src/esgvoc/cli/status.py +0 -47
- esgvoc-1.1.2/src/esgvoc/core/service/configuration/setting.py +0 -259
- esgvoc-1.1.2/tests/integration/test_scenario_basic.py +0 -34
- {esgvoc-1.1.2 → esgvoc-1.1.3}/.flake8 +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/.github/workflows/docs.yml +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/.github/workflows/pypi-publish.yml +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/.github/workflows/unit_tests.yml +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/.gitignore +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/.pre-commit-config.yaml +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/LICENSE.txt +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/README.md +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/docs/Makefile +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/docs/build.sh +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/docs/source/_static/API_Valid_Term.png +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/docs/source/_static/API_Valid_all_project.png +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/docs/source/_static/API_Valid_collection.png +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/docs/source/_static/API_Valid_project.png +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/docs/source/_static/API_drsgen_frombag.png +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/docs/source/_static/API_drsgen_map.png +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/docs/source/_static/API_drsvalid_one.png +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/docs/source/_static/CLI_Valid_all_project.png +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/docs/source/_static/CLI_Valid_collection.png +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/docs/source/_static/CLI_Valid_project.png +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/docs/source/_static/CLI_Valid_term.png +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/docs/source/_static/CLI_drsgen_frombag.png +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/docs/source/_static/CLI_drsvalid_one.png +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/docs/source/_static/Jup_one_term.png +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/docs/source/_static/Jup_one_term_from_one_CV.png +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/docs/source/_static/Jup_terms_from_one_collection.png +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/docs/source/_static/Jup_terms_from_one_dd.png +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/docs/source/_static/all_collection.png +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/docs/source/_static/all_term_from_one_collection.png +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/docs/source/_static/install.png +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/docs/source/_static/one_term.png +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/docs/source/_static/one_term_from_one_cv.png +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/docs/source/_static/status_after_install.png +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/docs/source/_static/status_before_install.png +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/docs/source/api_documentation/data_descriptors.md +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/docs/source/api_documentation/drs.md +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/docs/source/api_documentation/jsg.md +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/docs/source/api_documentation/miscellaneous.md +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/docs/source/api_documentation/project_specs.md +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/docs/source/api_documentation/projects.md +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/docs/source/api_documentation/universe.md +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/docs/source/conf.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/docs/source/guides/basics_drs.ipynb +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/docs/source/guides/basics_esgvoc.ipynb +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/docs/source/guides/status_install.png +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/docs/source/how_to/configuration.rst +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/docs/source/how_to/generate_drs.rst +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/docs/source/how_to/get.rst +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/docs/source/how_to/valid.rst +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/docs/source/how_to/validate_drs.rst +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/docs/source/index.md +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/docs/source/user/api.md +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/docs/source/user/cached_database.md +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/docs/source/user/cli.md +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/docs/source/user/introduction.md +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/docs/source/user/terms.md +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/pyproject.toml +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/api/__init__.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/api/data_descriptors/__init__.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/api/data_descriptors/activity.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/api/data_descriptors/archive.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/api/data_descriptors/area_label.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/api/data_descriptors/branded_suffix.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/api/data_descriptors/branded_variable.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/api/data_descriptors/citation_url.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/api/data_descriptors/consortium.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/api/data_descriptors/contact.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/api/data_descriptors/conventions.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/api/data_descriptors/creation_date.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/api/data_descriptors/data_descriptor.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/api/data_descriptors/data_specs_version.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/api/data_descriptors/date.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/api/data_descriptors/directory_date.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/api/data_descriptors/experiment.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/api/data_descriptors/forcing_index.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/api/data_descriptors/frequency.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/api/data_descriptors/further_info_url.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/api/data_descriptors/grid_label.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/api/data_descriptors/horizontal_label.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/api/data_descriptors/initialisation_index.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/api/data_descriptors/institution.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/api/data_descriptors/known_branded_variable.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/api/data_descriptors/license.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/api/data_descriptors/member_id.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/api/data_descriptors/mip_era.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/api/data_descriptors/model_component.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/api/data_descriptors/obs_type.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/api/data_descriptors/organisation.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/api/data_descriptors/physic_index.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/api/data_descriptors/product.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/api/data_descriptors/publication_status.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/api/data_descriptors/realisation_index.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/api/data_descriptors/realm.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/api/data_descriptors/regex.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/api/data_descriptors/region.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/api/data_descriptors/resolution.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/api/data_descriptors/source.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/api/data_descriptors/source_type.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/api/data_descriptors/sub_experiment.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/api/data_descriptors/table.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/api/data_descriptors/temporal_label.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/api/data_descriptors/time_range.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/api/data_descriptors/title.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/api/data_descriptors/tracking_id.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/api/data_descriptors/variable.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/api/data_descriptors/variant_label.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/api/data_descriptors/vertical_label.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/api/projects.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/api/py.typed +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/api/report.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/api/search.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/api/universe.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/apps/__init__.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/apps/drs/__init__.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/apps/drs/constants.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/apps/drs/generator.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/apps/drs/report.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/apps/drs/validator.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/apps/test_cv/README.md +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/apps/test_cv/__init__.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/apps/test_cv/example_usage.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/apps/vr/__init__.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/apps/vr/build_variable_registry.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/apps/vr/example_usage.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/apps/vr/vr_app.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/cli/drs.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/cli/find.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/cli/get.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/cli/test_cv.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/cli/valid.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/core/constants.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/core/convert.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/core/data_handler.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/core/db/__init__.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/core/db/connection.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/core/db/models/mixins.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/core/db/models/project.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/core/db/models/universe.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/core/db/universe_ingestion.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/core/exceptions.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/src/esgvoc/core/logging_handler.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/tests/Dockerfile +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/tests/__init__.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/tests/api_inputs.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/tests/cli_input_drsgen.txt +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/tests/cli_input_drsvalid.txt +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/tests/test_api_project.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/tests/test_api_universe.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/tests/test_cli_drs.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/tests/test_data_handler.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/tests/test_data_merger.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/tests/test_drs_generator.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/tests/test_drs_validator.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/tests/test_js_generator.py +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/useage_example/.ipynb_checkpoints/Basic-checkpoint.ipynb +0 -0
- {esgvoc-1.1.2 → esgvoc-1.1.3}/useage_example/Basic.ipynb +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: esgvoc
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.3
|
|
4
4
|
Summary: python library and CLI to interact with WCRP CVs
|
|
5
5
|
Project-URL: Repository, https://github.com/ESGF/esgf-vocab
|
|
6
6
|
Author-email: Sébastien Gardoll <sebastien@gardoll.fr>, Guillaume Levavasseur <guillaume.levavasseur@ipsl.fr>, Laurent Troussellier <laurent.troussellier@ipsl.fr>
|
|
@@ -47,6 +47,25 @@ class DrsSpecification(BaseModel):
|
|
|
47
47
|
"""The parts of the DRS specification."""
|
|
48
48
|
|
|
49
49
|
|
|
50
|
+
class AttributeProperty(BaseModel):
|
|
51
|
+
"""
|
|
52
|
+
A NetCDF global attribute property specification.
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
source_collection: str
|
|
56
|
+
"The project collection that originated the property."
|
|
57
|
+
is_required: bool
|
|
58
|
+
"Specifies if the attribute must be present in the NetCDF file."
|
|
59
|
+
value_type: str
|
|
60
|
+
"The type of the attribute value."
|
|
61
|
+
specific_key: str | None = None
|
|
62
|
+
"Specifies a specific key in the collection."
|
|
63
|
+
field_name: str | None = None
|
|
64
|
+
"The name of the attribute field."
|
|
65
|
+
default_value: str | None = None
|
|
66
|
+
"The default value for the attribute."
|
|
67
|
+
|
|
68
|
+
|
|
50
69
|
class CatalogProperty(BaseModel):
|
|
51
70
|
"""
|
|
52
71
|
A dataset property described in a catalog.
|
|
@@ -82,6 +101,9 @@ class CatalogProperties(BaseModel):
|
|
|
82
101
|
"""The extensions of the catalog."""
|
|
83
102
|
|
|
84
103
|
|
|
104
|
+
AttributeSpecification = list[AttributeProperty]
|
|
105
|
+
|
|
106
|
+
|
|
85
107
|
class CatalogSpecification(BaseModel):
|
|
86
108
|
"""
|
|
87
109
|
A catalog specifications.
|
|
@@ -113,4 +135,6 @@ class ProjectSpecs(BaseModel):
|
|
|
113
135
|
# TODO: release = None when all projects have catalog_specs.yaml.
|
|
114
136
|
catalog_specs: CatalogSpecification | None = None
|
|
115
137
|
"""The catalog specifications of the project."""
|
|
138
|
+
attr_specs: AttributeSpecification | None = None
|
|
139
|
+
"""The NetCDF global attribute specifications of the project."""
|
|
116
140
|
model_config = ConfigDict(extra="allow")
|
|
@@ -124,15 +124,17 @@ def _process_composite_term(term: UTerm | PTerm, universe_session: Session,
|
|
|
124
124
|
return property_key, property_values, has_pattern
|
|
125
125
|
|
|
126
126
|
|
|
127
|
-
def _process_col_pattern_terms(collection: PCollection) -> tuple[str, str]:
|
|
128
|
-
# The generation of the value of the field pattern for the collections with more than one term
|
|
129
|
-
# is not specified yet.
|
|
127
|
+
def _process_col_pattern_terms(collection: PCollection) -> tuple[str, str | list[dict]]:
|
|
130
128
|
if len(collection.terms) == 1:
|
|
131
129
|
term = collection.terms[0]
|
|
132
|
-
|
|
130
|
+
property_key, property_value = _process_pattern_term(term)
|
|
133
131
|
else:
|
|
134
|
-
|
|
135
|
-
|
|
132
|
+
property_key = 'anyOf'
|
|
133
|
+
property_value = list()
|
|
134
|
+
for term in collection.terms:
|
|
135
|
+
pkey, pvalue = _process_pattern_term(term)
|
|
136
|
+
property_value.append({pkey: pvalue})
|
|
137
|
+
return property_key, property_value
|
|
136
138
|
|
|
137
139
|
|
|
138
140
|
def _process_pattern_term(term: PTerm) -> tuple[str, str]:
|
|
@@ -290,7 +292,7 @@ def generate_json_schema(project_id: str) -> dict:
|
|
|
290
292
|
result = json.loads(json_raw_str)
|
|
291
293
|
return result
|
|
292
294
|
except Exception as e:
|
|
293
|
-
raise EsgvocException(f'
|
|
295
|
+
raise EsgvocException(f'JSON error: {e}') from e
|
|
294
296
|
else:
|
|
295
297
|
raise EsgvocNotFoundError(f"catalog properties for the project '{project_id}' " +
|
|
296
298
|
"are missing")
|
|
@@ -82,7 +82,7 @@ class CVTester:
|
|
|
82
82
|
|
|
83
83
|
def get_available_projects(self) -> List[str]:
|
|
84
84
|
"""Get list of all available project CVs"""
|
|
85
|
-
return list(ServiceSettings.
|
|
85
|
+
return list(ServiceSettings._get_default_project_configs().keys())
|
|
86
86
|
|
|
87
87
|
def configure_for_testing(
|
|
88
88
|
self,
|
|
@@ -121,7 +121,7 @@ class CVTester:
|
|
|
121
121
|
# Use custom repo/branch if provided, otherwise use defaults
|
|
122
122
|
if repo_url or branch:
|
|
123
123
|
# Custom configuration
|
|
124
|
-
default_config = ServiceSettings.
|
|
124
|
+
default_config = ServiceSettings._get_default_project_configs()[project_name]
|
|
125
125
|
project_config = {
|
|
126
126
|
"project_name": project_name,
|
|
127
127
|
"github_repo": repo_url or default_config["github_repo"],
|
|
@@ -134,7 +134,7 @@ class CVTester:
|
|
|
134
134
|
console.print(f" Branch: {project_config['branch']}")
|
|
135
135
|
else:
|
|
136
136
|
# Default configuration
|
|
137
|
-
project_config = ServiceSettings.
|
|
137
|
+
project_config = ServiceSettings._get_default_project_configs()[project_name].copy()
|
|
138
138
|
console.print(f"[blue]Using default configuration for {project_name}[/blue]")
|
|
139
139
|
|
|
140
140
|
# Create temporary test configuration with universe and single project
|
|
@@ -391,9 +391,12 @@ class CVTester:
|
|
|
391
391
|
"""Test YAML specification files (project_specs.yaml, drs_specs.yaml, catalog_spec.yaml, attr_specs.yaml)"""
|
|
392
392
|
errors = []
|
|
393
393
|
|
|
394
|
+
# Add clear section header
|
|
395
|
+
console.print(f"\n[bold blue]📋 Testing YAML Specification Files[/bold blue]")
|
|
396
|
+
console.print(f"[dim]Repository path: {repo_dir}[/dim]")
|
|
397
|
+
|
|
394
398
|
# Import constants and YAML handling
|
|
395
399
|
try:
|
|
396
|
-
import yaml
|
|
397
400
|
from esgvoc.core.constants import (
|
|
398
401
|
PROJECT_SPECS_FILENAME,
|
|
399
402
|
DRS_SPECS_FILENAME,
|
|
@@ -401,12 +404,24 @@ class CVTester:
|
|
|
401
404
|
ATTRIBUTES_SPECS_FILENAME
|
|
402
405
|
)
|
|
403
406
|
except ImportError as e:
|
|
404
|
-
|
|
407
|
+
error_msg = f"❌ Missing required esgvoc constants: {e}"
|
|
408
|
+
errors.append(error_msg)
|
|
409
|
+
console.print(f"[red]{error_msg}[/red]")
|
|
410
|
+
return errors
|
|
411
|
+
|
|
412
|
+
try:
|
|
413
|
+
import yaml
|
|
414
|
+
except ImportError:
|
|
415
|
+
error_msg = f"❌ PyYAML not installed. Install with: pip install PyYAML"
|
|
416
|
+
errors.append(error_msg)
|
|
417
|
+
console.print(f"[red]{error_msg}[/red]")
|
|
405
418
|
return errors
|
|
406
419
|
|
|
407
420
|
# Get existing collections for validation
|
|
408
421
|
existing_collections = {d.name for d in collection_directories}
|
|
409
422
|
source_collections = set()
|
|
423
|
+
# Track which files contain each collection reference for better error reporting
|
|
424
|
+
collection_file_mapping = {} # collection_name -> set of files that reference it
|
|
410
425
|
files_tested = 0
|
|
411
426
|
|
|
412
427
|
# Test project_specs.yaml
|
|
@@ -419,11 +434,17 @@ class CVTester:
|
|
|
419
434
|
console.print(f" [green]✅ {PROJECT_SPECS_FILENAME} parsed successfully[/green]")
|
|
420
435
|
files_tested += 1
|
|
421
436
|
except yaml.YAMLError as e:
|
|
422
|
-
|
|
437
|
+
error_msg = f"❌ {PROJECT_SPECS_FILENAME}: Invalid YAML syntax - {e}"
|
|
438
|
+
errors.append(error_msg)
|
|
439
|
+
console.print(f" [red]{error_msg}[/red]")
|
|
423
440
|
except Exception as e:
|
|
424
|
-
|
|
441
|
+
error_msg = f"❌ Error reading {PROJECT_SPECS_FILENAME}: {e}"
|
|
442
|
+
errors.append(error_msg)
|
|
443
|
+
console.print(f" [red]{error_msg}[/red]")
|
|
425
444
|
else:
|
|
426
|
-
|
|
445
|
+
error_msg = f"❌ Required file {PROJECT_SPECS_FILENAME} not found"
|
|
446
|
+
errors.append(error_msg)
|
|
447
|
+
console.print(f"📄 [red]{error_msg}[/red]")
|
|
427
448
|
|
|
428
449
|
# Test drs_specs.yaml
|
|
429
450
|
drs_specs_file = repo_dir / DRS_SPECS_FILENAME
|
|
@@ -442,15 +463,24 @@ class CVTester:
|
|
|
442
463
|
collection_ref = part.get("collection_id") or part.get("source_collection")
|
|
443
464
|
if collection_ref:
|
|
444
465
|
source_collections.add(collection_ref)
|
|
466
|
+
if collection_ref not in collection_file_mapping:
|
|
467
|
+
collection_file_mapping[collection_ref] = set()
|
|
468
|
+
collection_file_mapping[collection_ref].add(DRS_SPECS_FILENAME)
|
|
445
469
|
|
|
446
470
|
console.print(f" [green]✅ {DRS_SPECS_FILENAME} parsed successfully[/green]")
|
|
447
471
|
files_tested += 1
|
|
448
472
|
except yaml.YAMLError as e:
|
|
449
|
-
|
|
473
|
+
error_msg = f"❌ {DRS_SPECS_FILENAME}: Invalid YAML syntax - {e}"
|
|
474
|
+
errors.append(error_msg)
|
|
475
|
+
console.print(f" [red]{error_msg}[/red]")
|
|
450
476
|
except Exception as e:
|
|
451
|
-
|
|
477
|
+
error_msg = f"❌ Error reading {DRS_SPECS_FILENAME}: {e}"
|
|
478
|
+
errors.append(error_msg)
|
|
479
|
+
console.print(f" [red]{error_msg}[/red]")
|
|
452
480
|
else:
|
|
453
|
-
|
|
481
|
+
error_msg = f"❌ Required file {DRS_SPECS_FILENAME} not found"
|
|
482
|
+
errors.append(error_msg)
|
|
483
|
+
console.print(f"📄 [red]{error_msg}[/red]")
|
|
454
484
|
|
|
455
485
|
# Test catalog_spec.yaml (optional)
|
|
456
486
|
catalog_specs_file = repo_dir / CATALOG_SPECS_FILENAME
|
|
@@ -467,61 +497,215 @@ class CVTester:
|
|
|
467
497
|
if prop_type in catalog_specs and isinstance(catalog_specs[prop_type], list):
|
|
468
498
|
for prop in catalog_specs[prop_type]:
|
|
469
499
|
if isinstance(prop, dict) and "source_collection" in prop:
|
|
470
|
-
|
|
500
|
+
collection_ref = prop["source_collection"]
|
|
501
|
+
source_collections.add(collection_ref)
|
|
502
|
+
if collection_ref not in collection_file_mapping:
|
|
503
|
+
collection_file_mapping[collection_ref] = set()
|
|
504
|
+
collection_file_mapping[collection_ref].add(CATALOG_SPECS_FILENAME)
|
|
471
505
|
|
|
472
506
|
console.print(f" [green]✅ {CATALOG_SPECS_FILENAME} parsed successfully[/green]")
|
|
473
507
|
files_tested += 1
|
|
474
508
|
except yaml.YAMLError as e:
|
|
475
|
-
|
|
509
|
+
error_msg = f"❌ {CATALOG_SPECS_FILENAME}: Invalid YAML syntax - {e}"
|
|
510
|
+
errors.append(error_msg)
|
|
511
|
+
console.print(f" [red]{error_msg}[/red]")
|
|
476
512
|
except Exception as e:
|
|
477
|
-
|
|
513
|
+
error_msg = f"❌ Error reading {CATALOG_SPECS_FILENAME}: {e}"
|
|
514
|
+
errors.append(error_msg)
|
|
515
|
+
console.print(f" [red]{error_msg}[/red]")
|
|
478
516
|
else:
|
|
479
517
|
console.print(f" [yellow]⚠️ Optional file {CATALOG_SPECS_FILENAME} not found[/yellow]")
|
|
480
518
|
|
|
481
|
-
# Test attr_specs.yaml (
|
|
519
|
+
# Test attr_specs.yaml (now ingested by esgvoc as confirmed by project_ingestion.py updates)
|
|
482
520
|
attr_specs_file = repo_dir / ATTRIBUTES_SPECS_FILENAME
|
|
483
521
|
if attr_specs_file.exists():
|
|
484
|
-
console.print(f"📄 Testing {ATTRIBUTES_SPECS_FILENAME}
|
|
522
|
+
console.print(f"📄 Testing {ATTRIBUTES_SPECS_FILENAME}...")
|
|
485
523
|
try:
|
|
486
524
|
with open(attr_specs_file, "r", encoding="utf-8") as f:
|
|
487
525
|
attr_specs = yaml.safe_load(f)
|
|
488
526
|
|
|
489
|
-
# Extract collection references from attribute specs
|
|
490
|
-
if isinstance(attr_specs,
|
|
491
|
-
#
|
|
527
|
+
# Extract collection references from attribute specs
|
|
528
|
+
if isinstance(attr_specs, list):
|
|
529
|
+
# New format: list of AttributeProperty objects
|
|
530
|
+
for attr_spec in attr_specs:
|
|
531
|
+
if isinstance(attr_spec, dict) and "source_collection" in attr_spec:
|
|
532
|
+
collection_ref = attr_spec["source_collection"]
|
|
533
|
+
source_collections.add(collection_ref)
|
|
534
|
+
if collection_ref not in collection_file_mapping:
|
|
535
|
+
collection_file_mapping[collection_ref] = set()
|
|
536
|
+
collection_file_mapping[collection_ref].add(ATTRIBUTES_SPECS_FILENAME)
|
|
537
|
+
elif isinstance(attr_specs, dict):
|
|
538
|
+
# Legacy format: nested structure with "specs" key
|
|
492
539
|
if "specs" in attr_specs:
|
|
493
540
|
specs = attr_specs["specs"]
|
|
494
541
|
if isinstance(specs, dict):
|
|
495
542
|
for attr_name, attr_spec in specs.items():
|
|
496
543
|
if isinstance(attr_spec, dict) and "source_collection" in attr_spec:
|
|
497
|
-
|
|
544
|
+
collection_ref = attr_spec["source_collection"]
|
|
545
|
+
source_collections.add(collection_ref)
|
|
546
|
+
if collection_ref not in collection_file_mapping:
|
|
547
|
+
collection_file_mapping[collection_ref] = set()
|
|
548
|
+
collection_file_mapping[collection_ref].add(ATTRIBUTES_SPECS_FILENAME)
|
|
549
|
+
elif isinstance(specs, list):
|
|
550
|
+
for attr_spec in specs:
|
|
551
|
+
if isinstance(attr_spec, dict) and "source_collection" in attr_spec:
|
|
552
|
+
collection_ref = attr_spec["source_collection"]
|
|
553
|
+
source_collections.add(collection_ref)
|
|
554
|
+
if collection_ref not in collection_file_mapping:
|
|
555
|
+
collection_file_mapping[collection_ref] = set()
|
|
556
|
+
collection_file_mapping[collection_ref].add(ATTRIBUTES_SPECS_FILENAME)
|
|
498
557
|
|
|
499
558
|
console.print(f" [green]✅ {ATTRIBUTES_SPECS_FILENAME} parsed successfully[/green]")
|
|
500
|
-
console.print(f" [yellow]⚠️ Note: {ATTRIBUTES_SPECS_FILENAME} is not currently ingested by esgvoc[/yellow]")
|
|
501
559
|
files_tested += 1
|
|
502
560
|
except yaml.YAMLError as e:
|
|
503
|
-
|
|
561
|
+
error_msg = f"❌ {ATTRIBUTES_SPECS_FILENAME}: Invalid YAML syntax - {e}"
|
|
562
|
+
errors.append(error_msg)
|
|
563
|
+
console.print(f" [red]{error_msg}[/red]")
|
|
504
564
|
except Exception as e:
|
|
505
|
-
|
|
565
|
+
error_msg = f"❌ Error reading {ATTRIBUTES_SPECS_FILENAME}: {e}"
|
|
566
|
+
errors.append(error_msg)
|
|
567
|
+
console.print(f" [red]{error_msg}[/red]")
|
|
506
568
|
else:
|
|
507
569
|
console.print(f" [yellow]⚠️ Optional file {ATTRIBUTES_SPECS_FILENAME} not found[/yellow]")
|
|
508
570
|
|
|
509
571
|
# Validate collection references
|
|
572
|
+
console.print(f"\n📂 Validating collection references...")
|
|
510
573
|
if source_collections:
|
|
511
574
|
console.print(f" Found {len(source_collections)} source_collection references")
|
|
512
575
|
|
|
513
576
|
for collection in source_collections:
|
|
514
577
|
if collection not in existing_collections:
|
|
515
|
-
|
|
578
|
+
# Enhanced error message showing which files contain the reference
|
|
579
|
+
referencing_files = collection_file_mapping.get(collection, set())
|
|
580
|
+
files_list = ", ".join(sorted(referencing_files))
|
|
581
|
+
error_msg = f"❌ YAML specs reference non-existent collection: '{collection}' (referenced in: {files_list})"
|
|
582
|
+
errors.append(error_msg)
|
|
583
|
+
console.print(f" [red]{error_msg}[/red]")
|
|
516
584
|
else:
|
|
517
585
|
console.print(f" [green]✅ Reference '{collection}' exists[/green]")
|
|
518
586
|
else:
|
|
519
587
|
console.print(" [yellow]⚠️ No collection references found in YAML specs[/yellow]")
|
|
520
588
|
|
|
589
|
+
# Final YAML validation summary
|
|
590
|
+
console.print(f"\n📊 YAML Validation Summary:")
|
|
521
591
|
if files_tested == 0:
|
|
522
|
-
|
|
592
|
+
error_msg = "❌ No YAML specification files found"
|
|
593
|
+
errors.append(error_msg)
|
|
594
|
+
console.print(f" [red]{error_msg}[/red]")
|
|
523
595
|
else:
|
|
524
|
-
|
|
596
|
+
if errors:
|
|
597
|
+
console.print(f" [red]❌ {len(errors)} errors found in YAML files[/red]")
|
|
598
|
+
else:
|
|
599
|
+
console.print(f" [green]✅ All {files_tested} YAML specification files are valid[/green]")
|
|
600
|
+
|
|
601
|
+
console.print(f" [blue]Files tested: {files_tested}[/blue]")
|
|
602
|
+
|
|
603
|
+
return errors
|
|
604
|
+
|
|
605
|
+
def _test_esgvoc_specs_ingestion(self, project_name: str, repo_dir: Path) -> List[str]:
|
|
606
|
+
"""Test that YAML specs are properly ingested into esgvoc and accessible via API"""
|
|
607
|
+
errors = []
|
|
608
|
+
|
|
609
|
+
try:
|
|
610
|
+
# Import esgvoc API and constants
|
|
611
|
+
import esgvoc.api as ev
|
|
612
|
+
from esgvoc.core.constants import ATTRIBUTES_SPECS_FILENAME
|
|
613
|
+
except ImportError as e:
|
|
614
|
+
errors.append(f"❌ Cannot import esgvoc modules for ingestion testing: {e}")
|
|
615
|
+
return errors
|
|
616
|
+
|
|
617
|
+
try:
|
|
618
|
+
import yaml
|
|
619
|
+
except ImportError:
|
|
620
|
+
errors.append(f"❌ PyYAML not installed. Install with: pip install PyYAML")
|
|
621
|
+
return errors
|
|
622
|
+
|
|
623
|
+
console.print(f"🔍 Testing esgvoc ingestion compatibility for {project_name}...")
|
|
624
|
+
|
|
625
|
+
# Get the project specs from esgvoc
|
|
626
|
+
try:
|
|
627
|
+
project = ev.get_project(project_name)
|
|
628
|
+
console.print(f" [green]✅ Project '{project_name}' found in esgvoc[/green]")
|
|
629
|
+
|
|
630
|
+
if hasattr(project, 'attr_specs') and hasattr(project, 'drs_specs'):
|
|
631
|
+
# Project is properly loaded with specs - convert to dict format for compatibility
|
|
632
|
+
specs = {}
|
|
633
|
+
if hasattr(project, 'attr_specs') and project.attr_specs:
|
|
634
|
+
specs["attr_specs"] = project.attr_specs
|
|
635
|
+
if hasattr(project, 'drs_specs') and project.drs_specs:
|
|
636
|
+
specs["drs_specs"] = project.drs_specs
|
|
637
|
+
if hasattr(project, 'catalog_specs') and project.catalog_specs:
|
|
638
|
+
specs["catalog_specs"] = project.catalog_specs
|
|
639
|
+
|
|
640
|
+
console.print(f" [blue]📊 Project specs loaded with keys: {list(specs.keys())}[/blue]")
|
|
641
|
+
|
|
642
|
+
# Test attr_specs ingestion specifically
|
|
643
|
+
attr_specs_file = repo_dir / ATTRIBUTES_SPECS_FILENAME
|
|
644
|
+
if attr_specs_file.exists() and "attr_specs" in specs:
|
|
645
|
+
console.print(f" [green]✅ attr_specs found in ingested project data[/green]")
|
|
646
|
+
|
|
647
|
+
# Load the original YAML for comparison
|
|
648
|
+
with open(attr_specs_file, "r", encoding="utf-8") as f:
|
|
649
|
+
original_attr_specs = yaml.safe_load(f)
|
|
650
|
+
|
|
651
|
+
ingested_attr_specs = specs["attr_specs"]
|
|
652
|
+
|
|
653
|
+
# Validate structure compatibility
|
|
654
|
+
if isinstance(original_attr_specs, list) and isinstance(ingested_attr_specs, list):
|
|
655
|
+
console.print(f" [green]✅ attr_specs structure matches: {len(original_attr_specs)} items in YAML, {len(ingested_attr_specs)} items ingested[/green]")
|
|
656
|
+
|
|
657
|
+
# Check for source_collection fields
|
|
658
|
+
yaml_collections = set()
|
|
659
|
+
ingested_collections = set()
|
|
660
|
+
|
|
661
|
+
for item in original_attr_specs:
|
|
662
|
+
if isinstance(item, dict) and "source_collection" in item:
|
|
663
|
+
yaml_collections.add(item["source_collection"])
|
|
664
|
+
|
|
665
|
+
for item in ingested_attr_specs:
|
|
666
|
+
if isinstance(item, dict) and "source_collection" in item:
|
|
667
|
+
ingested_collections.add(item["source_collection"])
|
|
668
|
+
elif hasattr(item, "source_collection"):
|
|
669
|
+
# Handle Pydantic model objects
|
|
670
|
+
ingested_collections.add(item.source_collection)
|
|
671
|
+
|
|
672
|
+
if yaml_collections == ingested_collections:
|
|
673
|
+
console.print(f" [green]✅ Collection references preserved: {sorted(yaml_collections)}[/green]")
|
|
674
|
+
else:
|
|
675
|
+
errors.append(f"❌ Collection reference mismatch - YAML: {sorted(yaml_collections)}, Ingested: {sorted(ingested_collections)}")
|
|
676
|
+
else:
|
|
677
|
+
console.print(f" [yellow]⚠️ Structure difference: YAML type={type(original_attr_specs)}, Ingested type={type(ingested_attr_specs)}[/yellow]")
|
|
678
|
+
|
|
679
|
+
elif attr_specs_file.exists():
|
|
680
|
+
errors.append(f"❌ attr_specs.yaml exists but not found in ingested project specs")
|
|
681
|
+
|
|
682
|
+
# Test drs_specs ingestion
|
|
683
|
+
if "drs_specs" in specs:
|
|
684
|
+
console.print(f" [green]✅ drs_specs found in ingested project data[/green]")
|
|
685
|
+
else:
|
|
686
|
+
errors.append(f"❌ drs_specs not found in ingested project data")
|
|
687
|
+
|
|
688
|
+
# Test catalog_specs ingestion
|
|
689
|
+
if "catalog_specs" in specs:
|
|
690
|
+
console.print(f" [green]✅ catalog_specs found in ingested project data[/green]")
|
|
691
|
+
else:
|
|
692
|
+
console.print(f" [yellow]⚠️ catalog_specs not found in ingested project data (may be optional)[/yellow]")
|
|
693
|
+
|
|
694
|
+
else:
|
|
695
|
+
# More detailed error message about missing specs
|
|
696
|
+
expected_specs = ["project_specs", "attr_specs", "drs_specs", "catalog_specs (optional)"]
|
|
697
|
+
if hasattr(project, 'attr_specs') or hasattr(project, 'drs_specs'):
|
|
698
|
+
missing_specs = []
|
|
699
|
+
if not hasattr(project, 'attr_specs') or not project.attr_specs:
|
|
700
|
+
missing_specs.append("attr_specs")
|
|
701
|
+
if not hasattr(project, 'drs_specs') or not project.drs_specs:
|
|
702
|
+
missing_specs.append("drs_specs")
|
|
703
|
+
errors.append(f"❌ Project '{project_name}' missing required specs: {missing_specs}. Expected specs: {', '.join(expected_specs)}")
|
|
704
|
+
else:
|
|
705
|
+
errors.append(f"❌ Project '{project_name}' has no specs attributes. Expected specs: {', '.join(expected_specs)}")
|
|
706
|
+
|
|
707
|
+
except Exception as e:
|
|
708
|
+
errors.append(f"❌ Failed to retrieve project '{project_name}' from esgvoc: {e}")
|
|
525
709
|
|
|
526
710
|
return errors
|
|
527
711
|
|
|
@@ -1198,8 +1382,9 @@ class CVTester:
|
|
|
1198
1382
|
try:
|
|
1199
1383
|
from esgvoc.core.service.configuration.setting import ServiceSettings
|
|
1200
1384
|
|
|
1201
|
-
|
|
1202
|
-
|
|
1385
|
+
default_configs = ServiceSettings._get_default_project_configs()
|
|
1386
|
+
if project_name in default_configs:
|
|
1387
|
+
default_local_path = default_configs[project_name]["local_path"]
|
|
1203
1388
|
config_manager = service.get_config_manager()
|
|
1204
1389
|
|
|
1205
1390
|
# Try different path constructions to find where the repository actually is
|
|
@@ -1231,14 +1416,30 @@ class CVTester:
|
|
|
1231
1416
|
console.print("[yellow]⚠️ Could not determine CV repository path, using current directory[/yellow]")
|
|
1232
1417
|
|
|
1233
1418
|
# Step 3: Test repository structure
|
|
1234
|
-
|
|
1419
|
+
console.print(f"[dim]Debug: About to test repository structure with path: {repo_path}[/dim]")
|
|
1420
|
+
try:
|
|
1421
|
+
if not self.test_repository_structure(repo_path):
|
|
1422
|
+
success = False
|
|
1423
|
+
except Exception as e:
|
|
1424
|
+
console.print(f"[red]❌ Repository structure test failed with exception: {e}[/red]")
|
|
1235
1425
|
success = False
|
|
1236
1426
|
|
|
1237
1427
|
# Debug: Check what configuration is active before API test
|
|
1238
1428
|
current_active = service.get_config_manager().get_active_config_name()
|
|
1239
1429
|
console.print(f"[dim]Debug: Active config before API test: {current_active}[/dim]")
|
|
1240
1430
|
|
|
1241
|
-
# Step 4: Test
|
|
1431
|
+
# Step 4: Test YAML specs ingestion compatibility
|
|
1432
|
+
console.print(f"[blue]Testing YAML specs ingestion compatibility...[/blue]")
|
|
1433
|
+
ingestion_errors = self._test_esgvoc_specs_ingestion(project_name, Path(repo_path))
|
|
1434
|
+
if ingestion_errors:
|
|
1435
|
+
console.print(f"[red]❌ YAML specs ingestion test failed with {len(ingestion_errors)} errors:[/red]")
|
|
1436
|
+
for error in ingestion_errors:
|
|
1437
|
+
console.print(f" {error}")
|
|
1438
|
+
success = False
|
|
1439
|
+
else:
|
|
1440
|
+
console.print(f"[green]✅ YAML specs ingestion test passed![/green]")
|
|
1441
|
+
|
|
1442
|
+
# Step 5: Test esgvoc API access
|
|
1242
1443
|
if not self.test_esgvoc_api_access(project_name, repo_path):
|
|
1243
1444
|
success = False
|
|
1244
1445
|
|
|
@@ -1306,7 +1507,7 @@ def main():
|
|
|
1306
1507
|
projects = tester.get_available_projects()
|
|
1307
1508
|
console.print(f"[blue]Available projects ({len(projects)}):[/blue]")
|
|
1308
1509
|
for project in projects:
|
|
1309
|
-
config = ServiceSettings.
|
|
1510
|
+
config = ServiceSettings._get_default_project_configs()[project]
|
|
1310
1511
|
console.print(f" [cyan]{project}[/cyan] - {config['github_repo']} (branch: {config['branch']})")
|
|
1311
1512
|
|
|
1312
1513
|
elif command == "configure":
|