deriva-cli 0.8.2__tar.gz → 0.8.4__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.
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/PKG-INFO +3 -3
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/archimate/manager.py +29 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/archimate/views.py +56 -23
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/archimate/xml_export.py +32 -8
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/database/__init__.py +2 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/database/manager.py +19 -1
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/grafeo/__init__.py +8 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/grafeo/manager.py +51 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/graph/manager.py +5 -3
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/graph/metamodel.py +43 -1
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/graph/models.py +39 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/llm/manager.py +81 -7
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/llm/model_registry.py +20 -3
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/llm/models.py +7 -3
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/llm/retry.py +5 -1
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/nlp/translate.py +1 -2
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/repository/manager.py +160 -32
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/repository/models.py +2 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/treesitter/languages/csharp.py +2 -2
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/treesitter/languages/javascript.py +25 -14
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/treesitter/languages/python.py +5 -3
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/treesitter/manager.py +1 -1
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/cli/cli.py +85 -5
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/cli/commands/config.py +53 -2
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/cli/commands/graph.py +1 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/cli/commands/repo.py +52 -5
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/cli/commands/run.py +7 -2
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/common/file_utils.py +28 -1
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/common/settings.py +8 -1
- deriva_cli-0.8.4/deriva/common/urls.py +15 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/analysis/semantic_matching.py +16 -4
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/derivation/base.py +14 -1
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/derivation/component_services.py +12 -2
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/derivation/detail_level.py +5 -3
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/derivation/element_base.py +22 -7
- deriva_cli-0.8.4/deriva/modules/derivation/levels.py +224 -0
- deriva_cli-0.8.4/deriva/modules/derivation/quality.py +107 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/derivation/refine/__init__.py +2 -0
- deriva_cli-0.8.4/deriva/modules/derivation/refine/abstraction_levels.py +261 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/derivation/refine/joint_consistency.py +28 -16
- deriva_cli-0.8.4/deriva/modules/extraction/build_modules.py +254 -0
- deriva_cli-0.8.4/deriva/modules/extraction/coverage.py +126 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/extraction/data_domains.py +5 -1
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/extraction/data_entities.py +22 -6
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/extraction/deployable_units.py +8 -2
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/extraction/deployments.py +16 -2
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/extraction/directory_classification.py +4 -1
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/extraction/edges.py +2 -1
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/extraction/step_tags.py +4 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/services/__init__.py +8 -2
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/services/config.py +75 -21
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/services/derivation.py +55 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/services/export_bundle.py +5 -0
- deriva_cli-0.8.4/deriva/services/export_levels.py +117 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/services/extraction.py +253 -19
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/services/model_configs.py +70 -2
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/services/ontology.py +30 -16
- deriva_cli-0.8.4/deriva/services/quality.py +47 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/services/session.py +213 -17
- deriva_cli-0.8.4/deriva/services/storage.py +92 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/pyproject.toml +3 -3
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/pyproject.toml.orig +3 -3
- deriva_cli-0.8.2/deriva/services/export_levels.py +0 -71
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/LICENSE +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/archimate/README.md +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/archimate/__init__.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/archimate/archimate_xsd/documentation/Diagram.html +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/archimate/archimate_xsd/documentation/View.html +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/archimate/archimate_xsd/documentation/model.html +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/archimate/archimate_xsd/examples/archimate3_Diagram.xsd +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/archimate/archimate_xsd/examples/archimate3_Model.xsd +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/archimate/archimate_xsd/examples/archimate3_View.xsd +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/archimate/archimate_xsd/schemas/archimate3_Diagram.xsd +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/archimate/archimate_xsd/schemas/archimate3_Model.xsd +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/archimate/archimate_xsd/schemas/archimate3_View.xsd +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/archimate/archimate_xsd/snippets/Interoperability testing snippet-1.xml +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/archimate/archimate_xsd/snippets/Interoperability testing snippet-10.xml +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/archimate/archimate_xsd/snippets/Interoperability testing snippet-2.xml +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/archimate/archimate_xsd/snippets/Interoperability testing snippet-3.xml +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/archimate/archimate_xsd/snippets/Interoperability testing snippet-4.xml +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/archimate/archimate_xsd/snippets/Interoperability testing snippet-5.xml +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/archimate/archimate_xsd/snippets/Interoperability testing snippet-7.xml +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/archimate/archimate_xsd/snippets/Interoperability testing snippet-8.xml +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/archimate/archimate_xsd/snippets/Interoperability testing snippet-9.xml +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/archimate/models.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/archimate/validation.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/database/README.md +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/database/data/derivation_config.json +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/database/data/derivation_patterns.json +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/database/data/extraction_config.json +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/database/data/file_types.json +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/database/data/function_models/pcf-8.0-cross-industry.csv +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/database/data/system_settings.json +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/database/db_tool.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/database/scripts/7_extraction_params.sql +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/database/scripts/schema.sql +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/graph/README.md +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/graph/__init__.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/graph/cache.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/llm/README.md +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/llm/__init__.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/llm/cache.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/llm/rate_limiter.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/llm/schemas.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/nlp/__init__.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/nlp/extract.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/nlp/language.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/nlp/manager.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/nlp/models.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/nlp/phrases.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/nlp/settings.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/nlp/text.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/repository/README.md +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/repository/__init__.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/treesitter/__init__.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/treesitter/cdk.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/treesitter/languages/__init__.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/treesitter/languages/base.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/treesitter/languages/bms.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/treesitter/languages/cobol.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/treesitter/languages/java.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/treesitter/languages/jcl.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/treesitter/languages/sql.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/treesitter/languages/structural.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/adapters/treesitter/models.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/cli/README.md +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/cli/__init__.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/cli/commands/__init__.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/cli/progress.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/common/__init__.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/common/cache_utils.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/common/chunking.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/common/document_reader.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/common/exceptions.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/common/json_utils.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/common/llm_utils.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/common/logging.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/common/naming.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/common/ocel.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/common/paths.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/common/schema_utils.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/common/time_utils.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/common/timing.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/common/types.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/__init__.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/analysis/__init__.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/analysis/consistency.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/analysis/conventions.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/analysis/cross_repo_analysis.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/analysis/deviation.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/analysis/fit_analysis.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/analysis/model_quality.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/analysis/run_consistency.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/analysis/stability_analysis.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/analysis/types.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/derivation/__init__.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/derivation/application_component.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/derivation/application_interface.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/derivation/application_service.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/derivation/business_actor.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/derivation/business_event.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/derivation/business_function.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/derivation/business_object.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/derivation/business_process.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/derivation/data_object.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/derivation/device.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/derivation/function_model.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/derivation/node.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/derivation/prep.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/derivation/refine/base.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/derivation/refine/cross_layer.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/derivation/refine/duplicate_elements.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/derivation/refine/duplicate_relationships.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/derivation/refine/graph_relationships.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/derivation/refine/joint.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/derivation/refine/normalization.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/derivation/refine/orphan_elements.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/derivation/refine/structural_consistency.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/derivation/system_software.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/derivation/technology_service.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/extraction/__init__.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/extraction/base.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/extraction/business_concept.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/extraction/classification.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/extraction/concept_candidates.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/extraction/cooperation.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/extraction/directory.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/extraction/external_dependency.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/extraction/file.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/extraction/lifecycles.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/extraction/method.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/extraction/repository.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/extraction/roles.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/extraction/technologies.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/extraction/technology_candidates.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/extraction/test.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/modules/extraction/type_definition.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/services/about.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/services/config_models.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/services/llm_log.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/services/overfit.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/services/pipeline.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/deriva/services/trace.py +0 -0
- {deriva_cli-0.8.2 → deriva_cli-0.8.4}/packages/README-deriva-cli.md +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: deriva-cli
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.4
|
|
4
4
|
Summary: Deriva is a research project aimed at enabling digital architects by automating the derivation of enterprise architecture models. Deriva uses dual knowledge graphs, heuristics and LLM's.
|
|
5
5
|
Keywords: archimate,enterprise-architecture,code-analysis,llm,grafeo,fastapi
|
|
6
6
|
Author: S. T. Grond
|
|
@@ -38,8 +38,8 @@ Requires-Dist: spacy==3.8.16
|
|
|
38
38
|
Requires-Dist: ctranslate2==4.8.3
|
|
39
39
|
Requires-Dist: sentencepiece==0.2.2
|
|
40
40
|
Requires-Dist: solvor>=0.6.3
|
|
41
|
-
Requires-Dist: pydantic-ai-slim[openai,anthropic]>=2.
|
|
42
|
-
Requires-Dist: httpx2>=2.
|
|
41
|
+
Requires-Dist: pydantic-ai-slim[openai,anthropic]>=2.57
|
|
42
|
+
Requires-Dist: httpx2>=2.14
|
|
43
43
|
Requires-Dist: lmdb>=1.6
|
|
44
44
|
Requires-Dist: typer>=0.27
|
|
45
45
|
Requires-Dist: tree-sitter-typescript==0.23.2
|
|
@@ -577,6 +577,35 @@ class ArchimateManager:
|
|
|
577
577
|
key=lambda e: str(e["identifier"]),
|
|
578
578
|
)
|
|
579
579
|
|
|
580
|
+
def update_element_properties(self, updates: dict[str, dict[str, Any]]) -> int:
|
|
581
|
+
"""Merge keys into the properties of elements (identifier -> keys), leaving their other keys; returns how many changed."""
|
|
582
|
+
if self.db is None:
|
|
583
|
+
raise RuntimeError("Not connected to grafeo. Call connect() first.")
|
|
584
|
+
changed = 0
|
|
585
|
+
for identifier, keys in sorted(updates.items()):
|
|
586
|
+
rows = self.db.execute(f"MATCH (e:`{self.namespace}` {{identifier: $identifier}}) RETURN e.properties AS properties", {"identifier": identifier})
|
|
587
|
+
if not rows:
|
|
588
|
+
continue
|
|
589
|
+
merged = {**(rows[0].get("properties") or {}), **keys}
|
|
590
|
+
self.db.execute_write(f"MATCH (e:`{self.namespace}` {{identifier: $identifier}}) SET e.properties = $properties", {"identifier": identifier, "properties": merged})
|
|
591
|
+
changed += 1
|
|
592
|
+
return changed
|
|
593
|
+
|
|
594
|
+
def update_relationship_properties(self, updates: dict[str, dict[str, Any]]) -> int:
|
|
595
|
+
"""Merge keys into the properties of relationships (identifier -> keys), in place; returns how many changed."""
|
|
596
|
+
if self.db is None:
|
|
597
|
+
raise RuntimeError("Not connected to grafeo. Call connect() first.")
|
|
598
|
+
changed = 0
|
|
599
|
+
for identifier, keys in sorted(updates.items()):
|
|
600
|
+
match = f"MATCH (:`{self.namespace}`)-[r {{identifier: $identifier}}]->(:`{self.namespace}`)"
|
|
601
|
+
rows = self.db.execute(f"{match} RETURN r.properties AS properties", {"identifier": identifier})
|
|
602
|
+
if not rows:
|
|
603
|
+
continue
|
|
604
|
+
merged = {**(rows[0].get("properties") or {}), **keys}
|
|
605
|
+
self.db.execute_write(f"{match} SET r.properties = $properties", {"identifier": identifier, "properties": merged})
|
|
606
|
+
changed += 1
|
|
607
|
+
return changed
|
|
608
|
+
|
|
580
609
|
def delete_relationship(self, identifier: str) -> bool:
|
|
581
610
|
"""Delete a relationship by identifier.
|
|
582
611
|
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"""Generated views (pure): ArchiMate viewpoints per level with a deterministic band layout.
|
|
2
2
|
|
|
3
|
-
Each viewpoint (ArchiMate 3.2 Specification, chapter 13) shows the
|
|
4
|
-
|
|
3
|
+
Each viewpoint (ArchiMate 3.2 Specification, chapter 13) shows the elements of its types on its level (enterprise or
|
|
4
|
+
solution, from the element's ``levels``) and the relationships on that level; a cross-level link is in no view. A
|
|
5
|
+
detail variant shows the solution elements of its types with their detail elements (property ``level`` = detail). The layout puts the
|
|
5
6
|
layers in horizontal bands (business, application, technology) and the elements of a band in a grid, ordered by
|
|
6
7
|
type and name, each detail element right after its parent, so the same model gives the same view in every run.
|
|
7
8
|
"""
|
|
@@ -12,23 +13,32 @@ from dataclasses import dataclass, field
|
|
|
12
13
|
|
|
13
14
|
from .models import Element, Relationship, layer_of
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
("
|
|
20
|
-
("
|
|
21
|
-
("
|
|
16
|
+
ENTERPRISE, SOLUTION, DETAIL = "enterprise", "solution", "detail"
|
|
17
|
+
DEPLOYMENT = frozenset({"ApplicationComponent", "Node", "Device", "SystemSoftware", "TechnologyService"})
|
|
18
|
+
# (view name, viewpoint, element types, level), in export order
|
|
19
|
+
VIEWPOINTS: tuple[tuple[str, str, frozenset[str] | None, str], ...] = (
|
|
20
|
+
("Layered (enterprise)", "Layered", None, ENTERPRISE),
|
|
21
|
+
("Application Cooperation (enterprise)", "Application Cooperation", frozenset({"ApplicationComponent", "ApplicationInterface", "ApplicationService"}), ENTERPRISE),
|
|
22
|
+
("Information Structure (enterprise)", "Information Structure", frozenset({"BusinessObject", "DataObject"}), ENTERPRISE),
|
|
23
|
+
("Implementation and Deployment (enterprise)", "Implementation and Deployment", DEPLOYMENT, ENTERPRISE),
|
|
22
24
|
(
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
frozenset({"
|
|
26
|
-
|
|
25
|
+
"Business (enterprise)",
|
|
26
|
+
"Business Process Cooperation",
|
|
27
|
+
frozenset({"BusinessActor", "BusinessProcess", "BusinessFunction", "BusinessEvent", "ApplicationService"}),
|
|
28
|
+
ENTERPRISE,
|
|
27
29
|
),
|
|
28
|
-
("
|
|
30
|
+
("Application Structure (solution)", "Application Structure", frozenset({"ApplicationComponent"}), SOLUTION),
|
|
31
|
+
("Application Cooperation (solution)", "Application Cooperation", frozenset({"ApplicationComponent", "ApplicationInterface"}), SOLUTION),
|
|
32
|
+
("Information Structure (solution)", "Information Structure", frozenset({"DataObject"}), SOLUTION),
|
|
33
|
+
("Implementation and Deployment (solution)", "Implementation and Deployment", DEPLOYMENT, SOLUTION),
|
|
34
|
+
("Information Structure (detail)", "Information Structure", frozenset({"DataObject"}), DETAIL),
|
|
35
|
+
("Implementation and Deployment (detail)", "Implementation and Deployment", DEPLOYMENT | {"Artifact"}, DETAIL),
|
|
36
|
+
# The interfaces with their members (endpoints, pages, jobs); components are left out, their members are above
|
|
37
|
+
("Application Interfaces (detail)", "Application Cooperation", frozenset({"ApplicationInterface"}), DETAIL),
|
|
29
38
|
)
|
|
39
|
+
|
|
30
40
|
# A view needs an element of its anchor types (the Business view: a business element, not application services alone)
|
|
31
|
-
ANCHORS = {"Business": frozenset({"BusinessActor", "BusinessProcess", "BusinessFunction", "BusinessEvent"})}
|
|
41
|
+
ANCHORS = {"Business (enterprise)": frozenset({"BusinessActor", "BusinessProcess", "BusinessFunction", "BusinessEvent"})}
|
|
32
42
|
BANDS = ("Business", "Application", "Technology", "Other")
|
|
33
43
|
TYPE_ORDER = (
|
|
34
44
|
"BusinessActor", "BusinessFunction", "BusinessProcess", "BusinessEvent", "BusinessObject",
|
|
@@ -61,7 +71,7 @@ class View:
|
|
|
61
71
|
identifier: str
|
|
62
72
|
name: str
|
|
63
73
|
viewpoint: str
|
|
64
|
-
level: str =
|
|
74
|
+
level: str = ENTERPRISE # enterprise, solution or detail (a detail view shows the detail elements too)
|
|
65
75
|
nodes: list[ViewNode] = field(default_factory=list)
|
|
66
76
|
connections: list[ViewConnection] = field(default_factory=list)
|
|
67
77
|
|
|
@@ -70,6 +80,23 @@ def _level(e: Element) -> str:
|
|
|
70
80
|
return str((e.properties or {}).get("level") or "architect")
|
|
71
81
|
|
|
72
82
|
|
|
83
|
+
def _levels(properties: dict | None) -> frozenset[str]:
|
|
84
|
+
"""The levels of an element or relationship: ``level`` detail, else ``levels`` (empty: a cross-level link), else the
|
|
85
|
+
enterprise level (a model from before the levels)."""
|
|
86
|
+
properties = properties or {}
|
|
87
|
+
if properties.get("level") == DETAIL:
|
|
88
|
+
return frozenset({DETAIL})
|
|
89
|
+
levels = properties.get("levels")
|
|
90
|
+
if isinstance(levels, list):
|
|
91
|
+
return frozenset(str(v) for v in levels)
|
|
92
|
+
return frozenset() if properties.get("cross_level") else frozenset({ENTERPRISE})
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _on(level: str, levels: frozenset[str]) -> bool:
|
|
96
|
+
"""Whether something on ``levels`` belongs in a view of ``level`` (a detail view: the solution level and the detail)."""
|
|
97
|
+
return bool(levels & ({SOLUTION, DETAIL} if level == DETAIL else {level}))
|
|
98
|
+
|
|
99
|
+
|
|
73
100
|
def _order(e: Element) -> tuple[int, str, str]:
|
|
74
101
|
return (TYPE_ORDER.index(e.element_type) if e.element_type in TYPE_ORDER else len(TYPE_ORDER), e.name.casefold(), e.identifier)
|
|
75
102
|
|
|
@@ -102,19 +129,25 @@ def _layout(view: View, shown: list[Element]) -> None:
|
|
|
102
129
|
y += BAND_GAP
|
|
103
130
|
|
|
104
131
|
|
|
105
|
-
def build_views(elements: list[Element], relationships: list[Relationship]) -> list[View]:
|
|
106
|
-
"""The views of the model, one per viewpoint that has elements, in VIEWPOINTS order.
|
|
132
|
+
def build_views(elements: list[Element], relationships: list[Relationship], relationship_levels: dict[str, frozenset[str]] | None = None) -> list[View]:
|
|
133
|
+
"""The views of the model, one per viewpoint that has elements on its level, in VIEWPOINTS order.
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
relationship_levels: Relationship id -> its levels, where the relationship's own properties do not say (the
|
|
137
|
+
export's detail relationships); else from the properties
|
|
138
|
+
"""
|
|
107
139
|
views: list[View] = []
|
|
108
|
-
for number, (name, viewpoint, types,
|
|
109
|
-
shown = [e for e in elements if (types is None or e.element_type in types) and (
|
|
140
|
+
for number, (name, viewpoint, types, level) in enumerate(VIEWPOINTS, 1):
|
|
141
|
+
shown = [e for e in elements if (types is None or e.element_type in types) and _on(level, _levels(e.properties))]
|
|
110
142
|
anchors = ANCHORS.get(name)
|
|
111
|
-
if not shown or (anchors and not any(e.element_type in anchors for e in shown)) or (
|
|
143
|
+
if not shown or (anchors and not any(e.element_type in anchors for e in shown)) or (level == DETAIL and not any(_level(e) == DETAIL for e in shown)):
|
|
112
144
|
continue
|
|
113
|
-
view = View(identifier=f"view-{number}", name=name, viewpoint=viewpoint, level=
|
|
145
|
+
view = View(identifier=f"view-{number}", name=name, viewpoint=viewpoint, level=level)
|
|
114
146
|
_layout(view, shown)
|
|
115
147
|
node_of = {n.element: n.identifier for n in view.nodes}
|
|
116
148
|
for r in sorted(relationships, key=lambda r: r.identifier):
|
|
117
|
-
|
|
149
|
+
on = (relationship_levels or {}).get(r.identifier) or _levels(r.properties)
|
|
150
|
+
if r.source in node_of and r.target in node_of and _on(level, on):
|
|
118
151
|
connection = ViewConnection(identifier=f"{view.identifier}-c-{r.identifier}", relationship=r.identifier, source=node_of[r.source], target=node_of[r.target])
|
|
119
152
|
view.connections.append(connection)
|
|
120
153
|
views.append(view)
|
|
@@ -51,6 +51,7 @@ class ArchiMateXMLExporter:
|
|
|
51
51
|
model_id: str = "model-001",
|
|
52
52
|
properties: Mapping[str, Mapping[str, str]] | None = None,
|
|
53
53
|
views: Sequence[View] | None = None,
|
|
54
|
+
relationship_properties: Mapping[str, Mapping[str, str]] | None = None,
|
|
54
55
|
) -> None:
|
|
55
56
|
"""
|
|
56
57
|
Export ArchiMate model to XML file.
|
|
@@ -63,6 +64,7 @@ class ArchiMateXMLExporter:
|
|
|
63
64
|
model_id: Unique identifier for the model
|
|
64
65
|
properties: Exported properties per element id (e.g. ``level``), declared as propertyDefinitions
|
|
65
66
|
views: Diagrams to export (see ``views.build_views``)
|
|
67
|
+
relationship_properties: Exported properties per relationship id, declared with the element ones
|
|
66
68
|
"""
|
|
67
69
|
# Create root model element
|
|
68
70
|
root = etree.Element(
|
|
@@ -83,7 +85,8 @@ class ArchiMateXMLExporter:
|
|
|
83
85
|
self._add_metadata(root)
|
|
84
86
|
|
|
85
87
|
properties = properties or {}
|
|
86
|
-
|
|
88
|
+
relationship_properties = relationship_properties or {}
|
|
89
|
+
keys = sorted({key for values in [*properties.values(), *relationship_properties.values()] for key, value in values.items() if value})
|
|
87
90
|
definition_of = {key: f"propid-{key}" for key in keys}
|
|
88
91
|
|
|
89
92
|
# Add elements section
|
|
@@ -96,7 +99,7 @@ class ArchiMateXMLExporter:
|
|
|
96
99
|
if relationships:
|
|
97
100
|
rels_section = etree.SubElement(root, "relationships")
|
|
98
101
|
for relationship in relationships:
|
|
99
|
-
self._add_relationship(rels_section, relationship)
|
|
102
|
+
self._add_relationship(rels_section, relationship, relationship_properties.get(relationship.identifier, {}), definition_of)
|
|
100
103
|
|
|
101
104
|
# The exchange format's order: elements, relationships, organizations, propertyDefinitions, views
|
|
102
105
|
if keys:
|
|
@@ -105,7 +108,7 @@ class ArchiMateXMLExporter:
|
|
|
105
108
|
definition = etree.SubElement(definitions, "propertyDefinition", attrib={"identifier": definition_of[key], "type": "string"})
|
|
106
109
|
etree.SubElement(definition, "name").text = key
|
|
107
110
|
if views:
|
|
108
|
-
self._add_views(root, views)
|
|
111
|
+
self._add_views(root, views, definition_of.get("level"))
|
|
109
112
|
|
|
110
113
|
# Write to file
|
|
111
114
|
tree = etree.ElementTree(root)
|
|
@@ -190,8 +193,9 @@ class ArchiMateXMLExporter:
|
|
|
190
193
|
value_elem.set(f"{{{self.XML_NS}}}lang", "en")
|
|
191
194
|
value_elem.text = value
|
|
192
195
|
|
|
193
|
-
def _add_views(self, root: etree.Element, views: Sequence[View]) -> None:
|
|
194
|
-
"""Add the diagrams: element nodes with their bounds, relationship connections between them
|
|
196
|
+
def _add_views(self, root: etree.Element, views: Sequence[View], level_definition: str | None = None) -> None:
|
|
197
|
+
"""Add the diagrams: element nodes with their bounds, relationship connections between them, and each view's
|
|
198
|
+
level as a property where the model declares one."""
|
|
195
199
|
diagrams = etree.SubElement(etree.SubElement(root, "views"), "diagrams")
|
|
196
200
|
for view in views:
|
|
197
201
|
diagram = etree.SubElement(
|
|
@@ -202,6 +206,11 @@ class ArchiMateXMLExporter:
|
|
|
202
206
|
name_elem = etree.SubElement(diagram, "name")
|
|
203
207
|
name_elem.set(f"{{{self.XML_NS}}}lang", "en")
|
|
204
208
|
name_elem.text = view.name
|
|
209
|
+
if level_definition:
|
|
210
|
+
prop = etree.SubElement(etree.SubElement(diagram, "properties"), "property", attrib={"propertyDefinitionRef": level_definition})
|
|
211
|
+
value_elem = etree.SubElement(prop, "value")
|
|
212
|
+
value_elem.set(f"{{{self.XML_NS}}}lang", "en")
|
|
213
|
+
value_elem.text = view.level
|
|
205
214
|
for node in view.nodes:
|
|
206
215
|
etree.SubElement(
|
|
207
216
|
diagram,
|
|
@@ -229,13 +238,21 @@ class ArchiMateXMLExporter:
|
|
|
229
238
|
},
|
|
230
239
|
)
|
|
231
240
|
|
|
232
|
-
def _add_relationship(
|
|
241
|
+
def _add_relationship(
|
|
242
|
+
self,
|
|
243
|
+
parent: etree.Element,
|
|
244
|
+
relationship: Relationship,
|
|
245
|
+
properties: Mapping[str, str] | None = None,
|
|
246
|
+
definition_of: Mapping[str, str] | None = None,
|
|
247
|
+
) -> None:
|
|
233
248
|
"""
|
|
234
249
|
Add a relationship to the XML tree.
|
|
235
250
|
|
|
236
251
|
Args:
|
|
237
252
|
parent: Parent XML element
|
|
238
253
|
relationship: ArchiMate relationship to add
|
|
254
|
+
properties: The relationship's exported properties (key -> value)
|
|
255
|
+
definition_of: propertyDefinition identifier per key
|
|
239
256
|
"""
|
|
240
257
|
rel = etree.SubElement(
|
|
241
258
|
parent,
|
|
@@ -258,8 +275,15 @@ class ArchiMateXMLExporter:
|
|
|
258
275
|
doc_elem = etree.SubElement(rel, "documentation")
|
|
259
276
|
doc_elem.text = relationship.documentation
|
|
260
277
|
|
|
261
|
-
#
|
|
262
|
-
|
|
278
|
+
# Only the properties passed in are exported (each needs a model-level propertyDefinition), after name and documentation
|
|
279
|
+
shown = sorted((key, value) for key, value in (properties or {}).items() if value and key in (definition_of or {}))
|
|
280
|
+
if shown and definition_of:
|
|
281
|
+
props = etree.SubElement(rel, "properties")
|
|
282
|
+
for key, value in shown:
|
|
283
|
+
prop = etree.SubElement(props, "property", attrib={"propertyDefinitionRef": definition_of[key]})
|
|
284
|
+
value_elem = etree.SubElement(prop, "value")
|
|
285
|
+
value_elem.set(f"{{{self.XML_NS}}}lang", "en")
|
|
286
|
+
value_elem.text = str(value)
|
|
263
287
|
|
|
264
288
|
def export_to_string(
|
|
265
289
|
self,
|
|
@@ -30,6 +30,7 @@ from .db_tool import export_all as export_database
|
|
|
30
30
|
from .db_tool import import_all as import_database
|
|
31
31
|
from .manager import (
|
|
32
32
|
DB_PATH,
|
|
33
|
+
ConfigDatabaseBusy,
|
|
33
34
|
ensure_database,
|
|
34
35
|
get_connection,
|
|
35
36
|
init_database,
|
|
@@ -43,6 +44,7 @@ FUNCTION_MODELS_DIR = DATA_DIR / "function_models"
|
|
|
43
44
|
|
|
44
45
|
__all__ = [
|
|
45
46
|
"FUNCTION_MODELS_DIR",
|
|
47
|
+
"ConfigDatabaseBusy",
|
|
46
48
|
"ensure_database",
|
|
47
49
|
"get_connection",
|
|
48
50
|
"init_database",
|
|
@@ -28,6 +28,14 @@ SCRIPTS_DIR = Path(__file__).parent / "scripts"
|
|
|
28
28
|
DATA_DIR = Path(__file__).parent / "data"
|
|
29
29
|
|
|
30
30
|
|
|
31
|
+
# How DuckDB reports a database file another process holds (Windows; Linux and macOS)
|
|
32
|
+
_LOCK_MESSAGES = ("being used by another process", "Could not set lock", "Conflicting lock")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class ConfigDatabaseBusy(RuntimeError):
|
|
36
|
+
"""The config database is open in another process (DuckDB allows one writer)."""
|
|
37
|
+
|
|
38
|
+
|
|
31
39
|
def get_connection(read_only: bool = False) -> duckdb.DuckDBPyConnection:
|
|
32
40
|
"""Get a connection to the database.
|
|
33
41
|
|
|
@@ -37,11 +45,21 @@ def get_connection(read_only: bool = False) -> duckdb.DuckDBPyConnection:
|
|
|
37
45
|
|
|
38
46
|
Returns:
|
|
39
47
|
DuckDB connection
|
|
48
|
+
|
|
49
|
+
Raises:
|
|
50
|
+
ConfigDatabaseBusy: Another process (the studio, another CLI run) holds the database
|
|
40
51
|
"""
|
|
41
52
|
# A fresh install has no database yet: create it from the schema and the shipped seed data
|
|
42
53
|
if not DB_PATH.exists():
|
|
43
54
|
ensure_database(DB_PATH)
|
|
44
|
-
|
|
55
|
+
try:
|
|
56
|
+
return duckdb.connect(str(DB_PATH), read_only=read_only)
|
|
57
|
+
except duckdb.IOException as e:
|
|
58
|
+
if any(message in str(e) for message in _LOCK_MESSAGES):
|
|
59
|
+
raise ConfigDatabaseBusy(
|
|
60
|
+
f"The config database {DB_PATH} is open in another Deriva process (the studio or another deriva-cli run). Close it, or wait for it to finish, then try again."
|
|
61
|
+
) from e
|
|
62
|
+
raise
|
|
45
63
|
|
|
46
64
|
|
|
47
65
|
def run_sql_file(filepath: Path, conn: duckdb.DuckDBPyConnection | None = None) -> int:
|
|
@@ -21,9 +21,13 @@ from .manager import (
|
|
|
21
21
|
DEFAULT_DATABASE,
|
|
22
22
|
GrafeoConnection,
|
|
23
23
|
close_database,
|
|
24
|
+
database_bytes,
|
|
25
|
+
database_exists,
|
|
24
26
|
database_file,
|
|
27
|
+
delete_database,
|
|
25
28
|
engine_info,
|
|
26
29
|
get_database,
|
|
30
|
+
list_databases,
|
|
27
31
|
use_database,
|
|
28
32
|
)
|
|
29
33
|
|
|
@@ -31,8 +35,12 @@ __all__ = [
|
|
|
31
35
|
"DEFAULT_DATABASE",
|
|
32
36
|
"GrafeoConnection",
|
|
33
37
|
"close_database",
|
|
38
|
+
"database_bytes",
|
|
39
|
+
"database_exists",
|
|
34
40
|
"database_file",
|
|
41
|
+
"delete_database",
|
|
35
42
|
"engine_info",
|
|
36
43
|
"get_database",
|
|
44
|
+
"list_databases",
|
|
37
45
|
"use_database",
|
|
38
46
|
]
|
|
@@ -27,8 +27,10 @@ Usage:
|
|
|
27
27
|
from __future__ import annotations
|
|
28
28
|
|
|
29
29
|
import logging
|
|
30
|
+
import shutil
|
|
30
31
|
import time
|
|
31
32
|
import weakref
|
|
33
|
+
from pathlib import Path
|
|
32
34
|
from typing import Any
|
|
33
35
|
|
|
34
36
|
from deriva.common.paths import resolve
|
|
@@ -77,6 +79,55 @@ def database_file(key: str) -> str | None:
|
|
|
77
79
|
return str(folder / f"{key}.grafeo")
|
|
78
80
|
|
|
79
81
|
|
|
82
|
+
def _stored_paths(key: str) -> tuple[Path, Path] | None:
|
|
83
|
+
"""The key's database file and its write-ahead log folder, never created (None in memory or for an invalid key)."""
|
|
84
|
+
directory = GrafeoSettings().db_dir
|
|
85
|
+
if not directory or not key or key in (".", "..") or any(sep in key for sep in ("/", "\\")):
|
|
86
|
+
return None
|
|
87
|
+
folder = resolve(directory)
|
|
88
|
+
return folder / f"{key}.grafeo", folder / f"{key}.grafeo.wal"
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def database_exists(key: str) -> bool:
|
|
92
|
+
"""Whether the key's database file exists; never creates it or its folder (False in memory or for an invalid key)."""
|
|
93
|
+
paths = _stored_paths(key)
|
|
94
|
+
return paths is not None and paths[0].exists()
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def list_databases() -> list[str]:
|
|
98
|
+
"""The keys with a database file in GRAFEO_DB_DIR, sorted (none in memory)."""
|
|
99
|
+
directory = GrafeoSettings().db_dir
|
|
100
|
+
folder = resolve(directory) if directory else None
|
|
101
|
+
if folder is None or not folder.is_dir():
|
|
102
|
+
return []
|
|
103
|
+
return sorted(path.name.removesuffix(".grafeo") for path in folder.glob("*.grafeo") if path.is_file())
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def database_bytes(key: str) -> int:
|
|
107
|
+
"""Bytes the key's database takes on disk: its file and its write-ahead log (0 when it has none)."""
|
|
108
|
+
paths = _stored_paths(key)
|
|
109
|
+
if paths is None:
|
|
110
|
+
return 0
|
|
111
|
+
database, log = paths
|
|
112
|
+
size = database.stat().st_size if database.is_file() else 0
|
|
113
|
+
return size + (sum(f.stat().st_size for f in log.rglob("*") if f.is_file()) if log.is_dir() else 0)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def delete_database(key: str) -> bool:
|
|
117
|
+
"""Delete the key's database file and log; False when it had none. The active database is never deleted (switch
|
|
118
|
+
away first): a query after the delete would create a new, empty file."""
|
|
119
|
+
if key == _db_key:
|
|
120
|
+
raise ValueError(f"The database {key!r} is open; switch to another one before deleting it")
|
|
121
|
+
paths = _stored_paths(key)
|
|
122
|
+
if paths is None or not (paths[0].exists() or paths[1].exists()):
|
|
123
|
+
return False
|
|
124
|
+
database, log = paths
|
|
125
|
+
database.unlink(missing_ok=True)
|
|
126
|
+
if log.is_dir():
|
|
127
|
+
shutil.rmtree(log)
|
|
128
|
+
return True
|
|
129
|
+
|
|
130
|
+
|
|
80
131
|
def get_database() -> Any:
|
|
81
132
|
"""Get or open the active GrafeoDB instance (see ``use_database``).
|
|
82
133
|
|
|
@@ -26,7 +26,7 @@ from __future__ import annotations
|
|
|
26
26
|
|
|
27
27
|
import logging
|
|
28
28
|
import re
|
|
29
|
-
from collections.abc import
|
|
29
|
+
from collections.abc import Generator
|
|
30
30
|
from contextlib import contextmanager
|
|
31
31
|
from dataclasses import dataclass, field
|
|
32
32
|
from typing import Any
|
|
@@ -36,6 +36,7 @@ from deriva.common.ocel import create_edge_id
|
|
|
36
36
|
from deriva.common.settings import GraphSettings
|
|
37
37
|
|
|
38
38
|
from .models import (
|
|
39
|
+
BuildModuleNode,
|
|
39
40
|
BusinessConceptNode,
|
|
40
41
|
DataDomainNode,
|
|
41
42
|
DataEntityNode,
|
|
@@ -72,6 +73,7 @@ GraphNode = (
|
|
|
72
73
|
| ServiceNode
|
|
73
74
|
| ExternalDependencyNode
|
|
74
75
|
| DeployableUnitNode
|
|
76
|
+
| BuildModuleNode
|
|
75
77
|
| DataEntityNode
|
|
76
78
|
| DataDomainNode
|
|
77
79
|
| EnvironmentNode
|
|
@@ -278,7 +280,7 @@ class GraphManager:
|
|
|
278
280
|
raise
|
|
279
281
|
|
|
280
282
|
@contextmanager
|
|
281
|
-
def step_tags(self, step: str, version: int | None = None) ->
|
|
283
|
+
def step_tags(self, step: str, version: int | None = None) -> Generator[None]:
|
|
282
284
|
"""Nodes and edges written inside the block carry the extraction step (``extractStep``) and its version."""
|
|
283
285
|
previous = self._step_tags
|
|
284
286
|
self._step_tags = {"extractStep": step, **({"extractVersion": version} if version is not None else {})}
|
|
@@ -288,7 +290,7 @@ class GraphManager:
|
|
|
288
290
|
self._step_tags = previous
|
|
289
291
|
|
|
290
292
|
@contextmanager
|
|
291
|
-
def recording(self) ->
|
|
293
|
+
def recording(self) -> Generator[GraphWrites]:
|
|
292
294
|
"""The nodes and edges written inside the block (for the run log); failed writes are left out."""
|
|
293
295
|
recorder = GraphWrites()
|
|
294
296
|
self._recorders.append(recorder)
|
|
@@ -13,6 +13,10 @@ from typing import Any
|
|
|
13
13
|
from . import models
|
|
14
14
|
|
|
15
15
|
__all__ = [
|
|
16
|
+
"GROUP_ORDER",
|
|
17
|
+
"NODE_GROUPS",
|
|
18
|
+
"node_description",
|
|
19
|
+
"node_group",
|
|
16
20
|
"get_all_node_classes",
|
|
17
21
|
"get_node_properties",
|
|
18
22
|
"get_relationship_types",
|
|
@@ -36,7 +40,45 @@ def get_all_node_classes() -> dict[str, type]:
|
|
|
36
40
|
return node_classes
|
|
37
41
|
|
|
38
42
|
|
|
39
|
-
|
|
43
|
+
# The intermediate ontology's groups (as ArchiMate has its layers): where a node type's evidence comes from
|
|
44
|
+
GROUP_ORDER = ("structure", "code", "business", "deployment")
|
|
45
|
+
NODE_GROUPS = {
|
|
46
|
+
"Repository": "structure",
|
|
47
|
+
"Directory": "structure",
|
|
48
|
+
"File": "structure",
|
|
49
|
+
"Module": "structure",
|
|
50
|
+
"BuildModule": "structure",
|
|
51
|
+
"TypeDefinition": "code",
|
|
52
|
+
"Method": "code",
|
|
53
|
+
"Test": "code",
|
|
54
|
+
"ExternalDependency": "code",
|
|
55
|
+
"Service": "code",
|
|
56
|
+
"BusinessConcept": "business",
|
|
57
|
+
"DataEntity": "business",
|
|
58
|
+
"DataDomain": "business",
|
|
59
|
+
"Lifecycle": "business",
|
|
60
|
+
"LifecycleEvent": "business",
|
|
61
|
+
"Role": "business",
|
|
62
|
+
"DeployableUnit": "deployment",
|
|
63
|
+
"Deployment": "deployment",
|
|
64
|
+
"Environment": "deployment",
|
|
65
|
+
"Software": "deployment",
|
|
66
|
+
"Technology": "deployment",
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def node_group(node_type: str) -> str:
|
|
71
|
+
"""The group of a node type (``other`` for a label the metamodel does not name)."""
|
|
72
|
+
return NODE_GROUPS.get(node_type, "other")
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def node_description(node_class: type) -> str:
|
|
76
|
+
"""The first line of a node class's docstring."""
|
|
77
|
+
lines = (node_class.__doc__ or "").strip().splitlines()
|
|
78
|
+
return lines[0].strip() if lines else ""
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def get_node_properties(node_class: type[Any]) -> list[str]:
|
|
40
82
|
"""Get all property names for a node class.
|
|
41
83
|
|
|
42
84
|
Args:
|
|
@@ -506,6 +506,45 @@ class ExternalDependencyNode:
|
|
|
506
506
|
}
|
|
507
507
|
|
|
508
508
|
|
|
509
|
+
@dataclass
|
|
510
|
+
class BuildModuleNode:
|
|
511
|
+
"""A build module (a folder with a build manifest and code of its own) or a parent system (an aggregator manifest
|
|
512
|
+
that lists modules). Its own files hang below it by ``INCLUDES``, nested modules by ``NESTS``; a deployable unit
|
|
513
|
+
``BUILDS`` the modules in its folder."""
|
|
514
|
+
|
|
515
|
+
name: str
|
|
516
|
+
path: str
|
|
517
|
+
manifest: str
|
|
518
|
+
repository_name: str
|
|
519
|
+
is_module: bool = False
|
|
520
|
+
is_system: bool = False
|
|
521
|
+
members: list[str] = field(default_factory=list) # For a system: the module paths its manifest lists
|
|
522
|
+
same_as: str | None = None # The unit whose folder it is (one unit there, no module inside): one element with it
|
|
523
|
+
excluded: str | None = None # Why it is no application component (infrastructure-as-code, test-only)
|
|
524
|
+
|
|
525
|
+
def generate_id(self) -> str:
|
|
526
|
+
"""Generate a unique ID for this node."""
|
|
527
|
+
return f"BuildModule_{self.path}"
|
|
528
|
+
|
|
529
|
+
def to_dict(self) -> dict:
|
|
530
|
+
"""Convert to dictionary for graph storage."""
|
|
531
|
+
return {
|
|
532
|
+
"name": self.name,
|
|
533
|
+
"path": self.path,
|
|
534
|
+
"manifest": self.manifest,
|
|
535
|
+
"isModule": self.is_module,
|
|
536
|
+
"isSystem": self.is_system,
|
|
537
|
+
"members": list(self.members),
|
|
538
|
+
"sameAs": self.same_as,
|
|
539
|
+
"excluded": self.excluded,
|
|
540
|
+
# Its structural name as the one candidate of the closed-set naming (no naming call)
|
|
541
|
+
"nameCandidates": [self.name],
|
|
542
|
+
# Module ids carry no prefix the graph manager reads the repository from
|
|
543
|
+
"repository_name": self.repository_name,
|
|
544
|
+
"type": "BuildModule",
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
|
|
509
548
|
@dataclass
|
|
510
549
|
class DeployableUnitNode:
|
|
511
550
|
"""A deployable unit: what runs on its own (a container, a declared entry point, a mainframe application).
|