cognite-neat 0.123.2__py3-none-any.whl → 0.127.30__py3-none-any.whl
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.
- cognite/neat/__init__.py +2 -2
- cognite/neat/_client/__init__.py +4 -0
- cognite/neat/_client/api.py +8 -0
- cognite/neat/_client/client.py +21 -0
- cognite/neat/_client/config.py +40 -0
- cognite/neat/_client/containers_api.py +125 -0
- cognite/neat/_client/data_classes.py +44 -0
- cognite/neat/_client/data_model_api.py +115 -0
- cognite/neat/_client/spaces_api.py +115 -0
- cognite/neat/_client/statistics_api.py +24 -0
- cognite/neat/_client/views_api.py +129 -0
- cognite/neat/_config.py +185 -0
- cognite/neat/_data_model/_analysis.py +196 -0
- cognite/neat/_data_model/_constants.py +67 -0
- cognite/neat/_data_model/_identifiers.py +61 -0
- cognite/neat/_data_model/_shared.py +41 -0
- cognite/neat/_data_model/deployer/_differ.py +140 -0
- cognite/neat/_data_model/deployer/_differ_container.py +360 -0
- cognite/neat/_data_model/deployer/_differ_data_model.py +54 -0
- cognite/neat/_data_model/deployer/_differ_space.py +9 -0
- cognite/neat/_data_model/deployer/_differ_view.py +299 -0
- cognite/neat/_data_model/deployer/data_classes.py +529 -0
- cognite/neat/_data_model/deployer/deployer.py +401 -0
- cognite/neat/_data_model/exporters/__init__.py +15 -0
- cognite/neat/_data_model/exporters/_api_exporter.py +37 -0
- cognite/neat/_data_model/exporters/_base.py +24 -0
- cognite/neat/_data_model/exporters/_table_exporter/exporter.py +128 -0
- cognite/neat/_data_model/exporters/_table_exporter/workbook.py +409 -0
- cognite/neat/_data_model/exporters/_table_exporter/writer.py +421 -0
- cognite/neat/_data_model/importers/__init__.py +5 -0
- cognite/neat/_data_model/importers/_api_importer.py +166 -0
- cognite/neat/_data_model/importers/_base.py +16 -0
- cognite/neat/_data_model/importers/_table_importer/data_classes.py +295 -0
- cognite/neat/_data_model/importers/_table_importer/importer.py +192 -0
- cognite/neat/_data_model/importers/_table_importer/reader.py +1063 -0
- cognite/neat/_data_model/importers/_table_importer/source.py +94 -0
- cognite/neat/_data_model/models/conceptual/_base.py +18 -0
- cognite/neat/_data_model/models/conceptual/_concept.py +67 -0
- cognite/neat/_data_model/models/conceptual/_data_model.py +51 -0
- cognite/neat/_data_model/models/conceptual/_properties.py +104 -0
- cognite/neat/_data_model/models/conceptual/_property.py +105 -0
- cognite/neat/_data_model/models/dms/__init__.py +206 -0
- cognite/neat/_data_model/models/dms/_base.py +31 -0
- cognite/neat/_data_model/models/dms/_constants.py +48 -0
- cognite/neat/_data_model/models/dms/_constraints.py +42 -0
- cognite/neat/_data_model/models/dms/_container.py +159 -0
- cognite/neat/_data_model/models/dms/_data_model.py +95 -0
- cognite/neat/_data_model/models/dms/_data_types.py +195 -0
- cognite/neat/_data_model/models/dms/_http.py +28 -0
- cognite/neat/_data_model/models/dms/_indexes.py +30 -0
- cognite/neat/_data_model/models/dms/_limits.py +96 -0
- cognite/neat/_data_model/models/dms/_references.py +135 -0
- cognite/neat/_data_model/models/dms/_schema.py +18 -0
- cognite/neat/_data_model/models/dms/_space.py +48 -0
- cognite/neat/_data_model/models/dms/_types.py +17 -0
- cognite/neat/_data_model/models/dms/_view_filter.py +282 -0
- cognite/neat/_data_model/models/dms/_view_property.py +235 -0
- cognite/neat/_data_model/models/dms/_views.py +210 -0
- cognite/neat/_data_model/models/entities/__init__.py +50 -0
- cognite/neat/_data_model/models/entities/_base.py +101 -0
- cognite/neat/_data_model/models/entities/_constants.py +22 -0
- cognite/neat/_data_model/models/entities/_data_types.py +144 -0
- cognite/neat/_data_model/models/entities/_identifiers.py +61 -0
- cognite/neat/_data_model/models/entities/_parser.py +226 -0
- cognite/neat/_data_model/validation/dms/__init__.py +75 -0
- cognite/neat/_data_model/validation/dms/_ai_readiness.py +364 -0
- cognite/neat/_data_model/validation/dms/_base.py +307 -0
- cognite/neat/_data_model/validation/dms/_connections.py +638 -0
- cognite/neat/_data_model/validation/dms/_consistency.py +57 -0
- cognite/neat/_data_model/validation/dms/_containers.py +174 -0
- cognite/neat/_data_model/validation/dms/_limits.py +420 -0
- cognite/neat/_data_model/validation/dms/_orchestrator.py +222 -0
- cognite/neat/_data_model/validation/dms/_views.py +103 -0
- cognite/neat/_exceptions.py +56 -0
- cognite/neat/_issues.py +68 -0
- cognite/neat/_session/__init__.py +3 -0
- cognite/neat/_session/_html/_render.py +30 -0
- cognite/neat/_session/_html/static/__init__.py +8 -0
- cognite/neat/_session/_html/static/deployment.css +303 -0
- cognite/neat/_session/_html/static/deployment.js +150 -0
- cognite/neat/_session/_html/static/issues.css +211 -0
- cognite/neat/_session/_html/static/issues.js +168 -0
- cognite/neat/_session/_html/static/shared.css +186 -0
- cognite/neat/_session/_html/templates/__init__.py +4 -0
- cognite/neat/_session/_html/templates/deployment.html +75 -0
- cognite/neat/_session/_html/templates/issues.html +45 -0
- cognite/neat/_session/_issues.py +81 -0
- cognite/neat/_session/_opt.py +35 -0
- cognite/neat/_session/_physical.py +261 -0
- cognite/neat/_session/_result.py +236 -0
- cognite/neat/_session/_session.py +88 -0
- cognite/neat/_session/_usage_analytics/__init__.py +0 -0
- cognite/neat/_session/_usage_analytics/_collector.py +131 -0
- cognite/neat/_session/_usage_analytics/_constants.py +23 -0
- cognite/neat/_session/_usage_analytics/_storage.py +240 -0
- cognite/neat/_session/_wrappers.py +82 -0
- cognite/neat/_state_machine/__init__.py +10 -0
- cognite/neat/_state_machine/_base.py +37 -0
- cognite/neat/_state_machine/_states.py +52 -0
- cognite/neat/_store/__init__.py +3 -0
- cognite/neat/_store/_provenance.py +81 -0
- cognite/neat/_store/_store.py +156 -0
- cognite/neat/_utils/__init__.py +0 -0
- cognite/neat/_utils/_reader.py +194 -0
- cognite/neat/_utils/auxiliary.py +39 -0
- cognite/neat/_utils/collection.py +11 -0
- cognite/neat/_utils/http_client/__init__.py +39 -0
- cognite/neat/_utils/http_client/_client.py +245 -0
- cognite/neat/_utils/http_client/_config.py +19 -0
- cognite/neat/_utils/http_client/_data_classes.py +294 -0
- cognite/neat/_utils/http_client/_tracker.py +31 -0
- cognite/neat/_utils/text.py +71 -0
- cognite/neat/_utils/useful_types.py +37 -0
- cognite/neat/_utils/validation.py +154 -0
- cognite/neat/_version.py +1 -1
- cognite/neat/v0/__init__.py +0 -0
- cognite/neat/v0/core/__init__.py +0 -0
- cognite/neat/v0/core/_client/_api/__init__.py +0 -0
- cognite/neat/{core → v0/core}/_client/_api/data_modeling_loaders.py +86 -7
- cognite/neat/{core → v0/core}/_client/_api/neat_instances.py +5 -5
- cognite/neat/{core → v0/core}/_client/_api/schema.py +5 -5
- cognite/neat/{core → v0/core}/_client/_api/statistics.py +3 -3
- cognite/neat/{core → v0/core}/_client/_api_client.py +1 -1
- cognite/neat/v0/core/_client/data_classes/__init__.py +0 -0
- cognite/neat/{core → v0/core}/_client/data_classes/schema.py +4 -4
- cognite/neat/{core → v0/core}/_client/testing.py +1 -1
- cognite/neat/{core → v0/core}/_constants.py +10 -3
- cognite/neat/v0/core/_data_model/__init__.py +0 -0
- cognite/neat/{core → v0/core}/_data_model/_constants.py +9 -6
- cognite/neat/{core → v0/core}/_data_model/_shared.py +5 -5
- cognite/neat/{core → v0/core}/_data_model/analysis/_base.py +12 -8
- cognite/neat/{core → v0/core}/_data_model/exporters/__init__.py +1 -2
- cognite/neat/{core → v0/core}/_data_model/exporters/_base.py +7 -7
- cognite/neat/{core → v0/core}/_data_model/exporters/_data_model2dms.py +9 -9
- cognite/neat/{core → v0/core}/_data_model/exporters/_data_model2excel.py +13 -13
- cognite/neat/{core → v0/core}/_data_model/exporters/_data_model2instance_template.py +4 -4
- cognite/neat/{core/_data_model/exporters/_data_model2ontology.py → v0/core/_data_model/exporters/_data_model2semantic_model.py} +126 -133
- cognite/neat/{core → v0/core}/_data_model/exporters/_data_model2yaml.py +1 -1
- cognite/neat/{core → v0/core}/_data_model/importers/__init__.py +4 -6
- cognite/neat/{core → v0/core}/_data_model/importers/_base.py +5 -5
- cognite/neat/{core → v0/core}/_data_model/importers/_base_file_reader.py +2 -2
- cognite/neat/{core → v0/core}/_data_model/importers/_dict2data_model.py +6 -6
- cognite/neat/{core → v0/core}/_data_model/importers/_dms2data_model.py +19 -16
- cognite/neat/v0/core/_data_model/importers/_graph2data_model.py +299 -0
- cognite/neat/v0/core/_data_model/importers/_rdf/__init__.py +4 -0
- cognite/neat/{core → v0/core}/_data_model/importers/_rdf/_base.py +13 -13
- cognite/neat/{core → v0/core}/_data_model/importers/_rdf/_inference2rdata_model.py +14 -14
- cognite/neat/v0/core/_data_model/importers/_rdf/_owl2data_model.py +144 -0
- cognite/neat/v0/core/_data_model/importers/_rdf/_shared.py +255 -0
- cognite/neat/{core → v0/core}/_data_model/importers/_spreadsheet2data_model.py +94 -13
- cognite/neat/{core → v0/core}/_data_model/models/__init__.py +3 -3
- cognite/neat/{core → v0/core}/_data_model/models/_base_verified.py +5 -5
- cognite/neat/v0/core/_data_model/models/_import_contexts.py +82 -0
- cognite/neat/{core → v0/core}/_data_model/models/_types.py +5 -5
- cognite/neat/{core → v0/core}/_data_model/models/conceptual/_unverified.py +18 -12
- cognite/neat/v0/core/_data_model/models/conceptual/_validation.py +308 -0
- cognite/neat/{core → v0/core}/_data_model/models/conceptual/_verified.py +13 -11
- cognite/neat/{core → v0/core}/_data_model/models/data_types.py +14 -4
- cognite/neat/{core → v0/core}/_data_model/models/entities/__init__.py +6 -0
- cognite/neat/v0/core/_data_model/models/entities/_loaders.py +155 -0
- cognite/neat/{core → v0/core}/_data_model/models/entities/_multi_value.py +2 -2
- cognite/neat/v0/core/_data_model/models/entities/_restrictions.py +230 -0
- cognite/neat/{core → v0/core}/_data_model/models/entities/_single_value.py +121 -16
- cognite/neat/{core → v0/core}/_data_model/models/entities/_types.py +10 -0
- cognite/neat/{core → v0/core}/_data_model/models/mapping/_classic2core.py +5 -5
- cognite/neat/{core → v0/core}/_data_model/models/physical/__init__.py +1 -1
- cognite/neat/{core → v0/core}/_data_model/models/physical/_exporter.py +28 -21
- cognite/neat/{core → v0/core}/_data_model/models/physical/_unverified.py +141 -38
- cognite/neat/{core → v0/core}/_data_model/models/physical/_validation.py +190 -24
- cognite/neat/{core → v0/core}/_data_model/models/physical/_verified.py +135 -15
- cognite/neat/{core → v0/core}/_data_model/transformers/__init__.py +2 -0
- cognite/neat/{core → v0/core}/_data_model/transformers/_base.py +4 -4
- cognite/neat/{core → v0/core}/_data_model/transformers/_converters.py +39 -32
- cognite/neat/{core → v0/core}/_data_model/transformers/_mapping.py +7 -7
- cognite/neat/v0/core/_data_model/transformers/_union_conceptual.py +208 -0
- cognite/neat/{core → v0/core}/_data_model/transformers/_verification.py +7 -7
- cognite/neat/v0/core/_instances/__init__.py +0 -0
- cognite/neat/{core → v0/core}/_instances/_tracking/base.py +1 -1
- cognite/neat/{core → v0/core}/_instances/_tracking/log.py +1 -1
- cognite/neat/{core → v0/core}/_instances/extractors/__init__.py +1 -1
- cognite/neat/{core → v0/core}/_instances/extractors/_base.py +6 -6
- cognite/neat/v0/core/_instances/extractors/_classic_cdf/__init__.py +0 -0
- cognite/neat/{core → v0/core}/_instances/extractors/_classic_cdf/_base.py +7 -7
- cognite/neat/{core → v0/core}/_instances/extractors/_classic_cdf/_classic.py +12 -12
- cognite/neat/{core → v0/core}/_instances/extractors/_classic_cdf/_relationships.py +3 -3
- cognite/neat/{core → v0/core}/_instances/extractors/_classic_cdf/_sequences.py +2 -2
- cognite/neat/{core → v0/core}/_instances/extractors/_dict.py +6 -3
- cognite/neat/{core → v0/core}/_instances/extractors/_dms.py +6 -6
- cognite/neat/{core → v0/core}/_instances/extractors/_dms_graph.py +11 -11
- cognite/neat/{core → v0/core}/_instances/extractors/_mock_graph_generator.py +10 -10
- cognite/neat/{core → v0/core}/_instances/extractors/_raw.py +3 -3
- cognite/neat/{core → v0/core}/_instances/extractors/_rdf_file.py +7 -7
- cognite/neat/{core → v0/core}/_instances/loaders/_base.py +5 -5
- cognite/neat/{core → v0/core}/_instances/loaders/_rdf2dms.py +17 -17
- cognite/neat/{core → v0/core}/_instances/loaders/_rdf_to_instance_space.py +11 -11
- cognite/neat/{core → v0/core}/_instances/queries/_select.py +29 -3
- cognite/neat/{core → v0/core}/_instances/queries/_update.py +1 -1
- cognite/neat/{core → v0/core}/_instances/transformers/_base.py +4 -4
- cognite/neat/{core → v0/core}/_instances/transformers/_classic_cdf.py +6 -6
- cognite/neat/{core → v0/core}/_instances/transformers/_prune_graph.py +4 -4
- cognite/neat/{core → v0/core}/_instances/transformers/_rdfpath.py +1 -1
- cognite/neat/{core → v0/core}/_instances/transformers/_value_type.py +4 -4
- cognite/neat/{core → v0/core}/_issues/_base.py +11 -6
- cognite/neat/{core → v0/core}/_issues/_contextmanagers.py +8 -6
- cognite/neat/{core → v0/core}/_issues/_factory.py +11 -8
- cognite/neat/{core → v0/core}/_issues/errors/__init__.py +3 -1
- cognite/neat/{core → v0/core}/_issues/errors/_external.py +1 -1
- cognite/neat/{core → v0/core}/_issues/errors/_general.py +1 -1
- cognite/neat/{core → v0/core}/_issues/errors/_properties.py +12 -1
- cognite/neat/{core → v0/core}/_issues/errors/_resources.py +2 -2
- cognite/neat/{core → v0/core}/_issues/errors/_wrapper.py +7 -3
- cognite/neat/{core → v0/core}/_issues/warnings/__init__.py +5 -1
- cognite/neat/{core → v0/core}/_issues/warnings/_external.py +1 -1
- cognite/neat/{core → v0/core}/_issues/warnings/_general.py +1 -1
- cognite/neat/{core → v0/core}/_issues/warnings/_models.py +39 -4
- cognite/neat/{core → v0/core}/_issues/warnings/_properties.py +13 -2
- cognite/neat/{core → v0/core}/_issues/warnings/_resources.py +1 -1
- cognite/neat/{core → v0/core}/_issues/warnings/user_modeling.py +1 -1
- cognite/neat/{core → v0/core}/_store/_data_model.py +13 -12
- cognite/neat/{core → v0/core}/_store/_instance.py +45 -12
- cognite/neat/{core → v0/core}/_store/_provenance.py +3 -3
- cognite/neat/{core → v0/core}/_store/exceptions.py +4 -4
- cognite/neat/v0/core/_utils/__init__.py +0 -0
- cognite/neat/{core → v0/core}/_utils/auth.py +1 -1
- cognite/neat/{core → v0/core}/_utils/auxiliary.py +7 -1
- cognite/neat/{core → v0/core}/_utils/collection_.py +2 -2
- cognite/neat/{core → v0/core}/_utils/graph_transformations_report.py +1 -1
- cognite/neat/{core → v0/core}/_utils/rdf_.py +38 -14
- cognite/neat/{core → v0/core}/_utils/reader/_base.py +1 -1
- cognite/neat/{core → v0/core}/_utils/spreadsheet.py +22 -4
- cognite/neat/v0/core/_utils/tarjan.py +44 -0
- cognite/neat/{core → v0/core}/_utils/text.py +1 -1
- cognite/neat/{core → v0/core}/_utils/upload.py +3 -3
- cognite/neat/v0/plugins/__init__.py +4 -0
- cognite/neat/v0/plugins/_base.py +9 -0
- cognite/neat/v0/plugins/_data_model.py +48 -0
- cognite/neat/{plugins → v0/plugins}/_issues.py +1 -1
- cognite/neat/{plugins → v0/plugins}/_manager.py +7 -16
- cognite/neat/{session → v0/session}/_base.py +13 -10
- cognite/neat/{session → v0/session}/_collector.py +1 -1
- cognite/neat/v0/session/_diff.py +51 -0
- cognite/neat/{session → v0/session}/_drop.py +3 -3
- cognite/neat/{session → v0/session}/_explore.py +2 -2
- cognite/neat/{session → v0/session}/_fix.py +2 -2
- cognite/neat/{session → v0/session}/_inspect.py +3 -3
- cognite/neat/{session → v0/session}/_mapping.py +3 -3
- cognite/neat/{session → v0/session}/_plugin.py +4 -5
- cognite/neat/{session → v0/session}/_prepare.py +8 -8
- cognite/neat/{session → v0/session}/_read.py +33 -43
- cognite/neat/{session → v0/session}/_set.py +8 -8
- cognite/neat/{session → v0/session}/_show.py +5 -5
- cognite/neat/{session → v0/session}/_state.py +22 -8
- cognite/neat/{session → v0/session}/_subset.py +4 -4
- cognite/neat/{session → v0/session}/_template.py +11 -11
- cognite/neat/{session → v0/session}/_to.py +12 -12
- cognite/neat/{session → v0/session}/_wizard.py +1 -1
- cognite/neat/{session → v0/session}/engine/_load.py +1 -1
- cognite/neat/{session → v0/session}/exceptions.py +5 -5
- cognite/neat/v1.py +3 -0
- {cognite_neat-0.123.2.dist-info → cognite_neat-0.127.30.dist-info}/METADATA +9 -8
- cognite_neat-0.127.30.dist-info/RECORD +319 -0
- {cognite_neat-0.123.2.dist-info → cognite_neat-0.127.30.dist-info}/WHEEL +1 -1
- cognite/neat/core/_data_model/importers/_dtdl2data_model/__init__.py +0 -3
- cognite/neat/core/_data_model/importers/_dtdl2data_model/_unit_lookup.py +0 -224
- cognite/neat/core/_data_model/importers/_dtdl2data_model/dtdl_converter.py +0 -320
- cognite/neat/core/_data_model/importers/_dtdl2data_model/dtdl_importer.py +0 -155
- cognite/neat/core/_data_model/importers/_dtdl2data_model/spec.py +0 -363
- cognite/neat/core/_data_model/importers/_rdf/__init__.py +0 -5
- cognite/neat/core/_data_model/importers/_rdf/_imf2data_model.py +0 -98
- cognite/neat/core/_data_model/importers/_rdf/_owl2data_model.py +0 -87
- cognite/neat/core/_data_model/importers/_rdf/_shared.py +0 -168
- cognite/neat/core/_data_model/models/conceptual/_validation.py +0 -294
- cognite/neat/core/_data_model/models/entities/_loaders.py +0 -75
- cognite/neat/plugins/__init__.py +0 -3
- cognite/neat/plugins/data_model/importers/__init__.py +0 -5
- cognite/neat/plugins/data_model/importers/_base.py +0 -28
- cognite_neat-0.123.2.dist-info/RECORD +0 -197
- /cognite/neat/{core → _data_model}/__init__.py +0 -0
- /cognite/neat/{core/_client/_api → _data_model/deployer}/__init__.py +0 -0
- /cognite/neat/{core/_client/data_classes → _data_model/exporters/_table_exporter}/__init__.py +0 -0
- /cognite/neat/{core/_data_model → _data_model/importers/_table_importer}/__init__.py +0 -0
- /cognite/neat/{core/_instances → _data_model/models}/__init__.py +0 -0
- /cognite/neat/{core/_instances/extractors/_classic_cdf → _data_model/models/conceptual}/__init__.py +0 -0
- /cognite/neat/{core/_utils → _data_model/validation}/__init__.py +0 -0
- /cognite/neat/{plugins/data_model → _session/_html}/__init__.py +0 -0
- /cognite/neat/{core → v0/core}/_client/__init__.py +0 -0
- /cognite/neat/{core → v0/core}/_client/data_classes/data_modeling.py +0 -0
- /cognite/neat/{core → v0/core}/_client/data_classes/neat_sequence.py +0 -0
- /cognite/neat/{core → v0/core}/_client/data_classes/statistics.py +0 -0
- /cognite/neat/{core → v0/core}/_config.py +0 -0
- /cognite/neat/{core → v0/core}/_data_model/analysis/__init__.py +0 -0
- /cognite/neat/{core → v0/core}/_data_model/catalog/__init__.py +0 -0
- /cognite/neat/{core → v0/core}/_data_model/catalog/classic_model.xlsx +0 -0
- /cognite/neat/{core → v0/core}/_data_model/catalog/conceptual-imf-data-model.xlsx +0 -0
- /cognite/neat/{core → v0/core}/_data_model/catalog/hello_world_pump.xlsx +0 -0
- /cognite/neat/{core → v0/core}/_data_model/models/_base_unverified.py +0 -0
- /cognite/neat/{core → v0/core}/_data_model/models/conceptual/__init__.py +0 -0
- /cognite/neat/{core → v0/core}/_data_model/models/entities/_constants.py +0 -0
- /cognite/neat/{core → v0/core}/_data_model/models/entities/_wrapped.py +0 -0
- /cognite/neat/{core → v0/core}/_data_model/models/mapping/__init__.py +0 -0
- /cognite/neat/{core → v0/core}/_data_model/models/mapping/_classic2core.yaml +0 -0
- /cognite/neat/{core → v0/core}/_instances/_shared.py +0 -0
- /cognite/neat/{core → v0/core}/_instances/_tracking/__init__.py +0 -0
- /cognite/neat/{core → v0/core}/_instances/examples/Knowledge-Graph-Nordic44-dirty.xml +0 -0
- /cognite/neat/{core → v0/core}/_instances/examples/Knowledge-Graph-Nordic44.xml +0 -0
- /cognite/neat/{core → v0/core}/_instances/examples/__init__.py +0 -0
- /cognite/neat/{core → v0/core}/_instances/examples/skos-capturing-sheet-wind-topics.xlsx +0 -0
- /cognite/neat/{core → v0/core}/_instances/extractors/_classic_cdf/_assets.py +0 -0
- /cognite/neat/{core → v0/core}/_instances/extractors/_classic_cdf/_data_sets.py +0 -0
- /cognite/neat/{core → v0/core}/_instances/extractors/_classic_cdf/_events.py +0 -0
- /cognite/neat/{core → v0/core}/_instances/extractors/_classic_cdf/_files.py +0 -0
- /cognite/neat/{core → v0/core}/_instances/extractors/_classic_cdf/_labels.py +0 -0
- /cognite/neat/{core → v0/core}/_instances/extractors/_classic_cdf/_timeseries.py +0 -0
- /cognite/neat/{core → v0/core}/_instances/loaders/__init__.py +0 -0
- /cognite/neat/{core → v0/core}/_instances/queries/__init__.py +0 -0
- /cognite/neat/{core → v0/core}/_instances/queries/_base.py +0 -0
- /cognite/neat/{core → v0/core}/_instances/queries/_queries.py +0 -0
- /cognite/neat/{core → v0/core}/_instances/transformers/__init__.py +0 -0
- /cognite/neat/{core → v0/core}/_issues/__init__.py +0 -0
- /cognite/neat/{core → v0/core}/_issues/formatters.py +0 -0
- /cognite/neat/{core → v0/core}/_shared.py +0 -0
- /cognite/neat/{core → v0/core}/_store/__init__.py +0 -0
- /cognite/neat/{core → v0/core}/_utils/io_.py +0 -0
- /cognite/neat/{core → v0/core}/_utils/reader/__init__.py +0 -0
- /cognite/neat/{core → v0/core}/_utils/time_.py +0 -0
- /cognite/neat/{core → v0/core}/_utils/xml_.py +0 -0
- /cognite/neat/{session → v0/session}/__init__.py +0 -0
- /cognite/neat/{session → v0/session}/_experimental.py +0 -0
- /cognite/neat/{session → v0/session}/_state/README.md +0 -0
- /cognite/neat/{session → v0/session}/engine/__init__.py +0 -0
- /cognite/neat/{session → v0/session}/engine/_import.py +0 -0
- /cognite/neat/{session → v0/session}/engine/_interface.py +0 -0
- {cognite_neat-0.123.2.dist-info → cognite_neat-0.127.30.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
"""Validators for checking if data model is AI-ready."""
|
|
2
|
+
|
|
3
|
+
from cognite.neat._data_model.models.dms._data_types import EnumProperty
|
|
4
|
+
from cognite.neat._data_model.validation.dms._base import DataModelValidator
|
|
5
|
+
from cognite.neat._issues import Recommendation
|
|
6
|
+
|
|
7
|
+
BASE_CODE = "NEAT-DMS-AI-READINESS"
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class DataModelMissingName(DataModelValidator):
|
|
11
|
+
"""Validates that data model has a human-readable name.
|
|
12
|
+
|
|
13
|
+
## What it does
|
|
14
|
+
Validates that the data model has a human-readable name.
|
|
15
|
+
|
|
16
|
+
## Why is this bad?
|
|
17
|
+
Often the data model ids are technical identifiers, abbreviations, etc.
|
|
18
|
+
A missing name makes it harder for users (humans or machines) to understand what the data model represents.
|
|
19
|
+
Providing a clear name improves usability, maintainability, searchability, and AI-readiness.
|
|
20
|
+
|
|
21
|
+
## Example
|
|
22
|
+
A data model has an id IEC61400-25-2 but no name. Users may find it difficult to understand what this data model
|
|
23
|
+
represents. However adding a name "Wind Energy Information Model" would increase clarity and usability.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
code = f"{BASE_CODE}-001"
|
|
27
|
+
issue_type = Recommendation
|
|
28
|
+
|
|
29
|
+
def run(self) -> list[Recommendation]:
|
|
30
|
+
recommendations: list[Recommendation] = []
|
|
31
|
+
|
|
32
|
+
if not self.local_resources.data_model_name:
|
|
33
|
+
recommendations.append(
|
|
34
|
+
Recommendation(
|
|
35
|
+
message="Data model is missing a human-readable name.",
|
|
36
|
+
fix="Add a clear and concise name to the data model.",
|
|
37
|
+
code=self.code,
|
|
38
|
+
)
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
return recommendations
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class DataModelMissingDescription(DataModelValidator):
|
|
45
|
+
"""Validates that data model has a human-readable description.
|
|
46
|
+
|
|
47
|
+
## What it does
|
|
48
|
+
Validates that the data model has a human-readable description.
|
|
49
|
+
|
|
50
|
+
## Why is this bad?
|
|
51
|
+
A missing description makes it harder for users (humans or machines) to understand the purpose and scope
|
|
52
|
+
of the data model. The description provides important context about what domain the data model covers,
|
|
53
|
+
what use cases it supports, and how it should be used.
|
|
54
|
+
|
|
55
|
+
## Example
|
|
56
|
+
A data model has an id CIM, with name Common Information Model, but no description. Users may find it difficult to
|
|
57
|
+
understand what this data model represents, unless extra context is provided. In this particualar case, name
|
|
58
|
+
does not provide sufficient information, as it is too generic, that this data model is focused on the
|
|
59
|
+
electrical power systems domain. However, providing a description such as:
|
|
60
|
+
"The Common Information Model (CIM) is a standard developed by IEC for representing power system
|
|
61
|
+
components and their relationships. It is widely used in the electrical utility industry for data
|
|
62
|
+
exchange and system modeling." would greatly improve clarity and usability.
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
code = f"{BASE_CODE}-002"
|
|
66
|
+
issue_type = Recommendation
|
|
67
|
+
|
|
68
|
+
def run(self) -> list[Recommendation]:
|
|
69
|
+
recommendations: list[Recommendation] = []
|
|
70
|
+
|
|
71
|
+
if not self.local_resources.data_model_description:
|
|
72
|
+
recommendations.append(
|
|
73
|
+
Recommendation(
|
|
74
|
+
message="Data model is missing a description.",
|
|
75
|
+
fix="Add a clear and concise description to the data model.",
|
|
76
|
+
code=self.code,
|
|
77
|
+
)
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
return recommendations
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class ViewMissingName(DataModelValidator):
|
|
84
|
+
"""Validates that a View has a human-readable name.
|
|
85
|
+
|
|
86
|
+
## What it does
|
|
87
|
+
Validates that each view in the data model has a human-readable name.
|
|
88
|
+
|
|
89
|
+
## Why is this bad?
|
|
90
|
+
A missing name makes it harder for users (humans or machines) to understand the purpose of the view.
|
|
91
|
+
This is important as views' external ids are often based on technical identifiers, abbreviations, etc.
|
|
92
|
+
Providing a clear name improves usability, maintainability, searchability, and AI-readiness.
|
|
93
|
+
|
|
94
|
+
## Example
|
|
95
|
+
A view has an id CFIHOS-30000038 but no name. Users may find it difficult to understand what this view represents,
|
|
96
|
+
unless they look up the id in documentation or other resources. Adding name "Pump" would increase clarity and
|
|
97
|
+
usability.
|
|
98
|
+
"""
|
|
99
|
+
|
|
100
|
+
code = f"{BASE_CODE}-003"
|
|
101
|
+
issue_type = Recommendation
|
|
102
|
+
|
|
103
|
+
def run(self) -> list[Recommendation]:
|
|
104
|
+
recommendations: list[Recommendation] = []
|
|
105
|
+
|
|
106
|
+
for view_ref in self.local_resources.data_model_views:
|
|
107
|
+
view = self.local_resources.views_by_reference.get(view_ref)
|
|
108
|
+
|
|
109
|
+
if view is None:
|
|
110
|
+
raise RuntimeError(f"View {view_ref!s} not found in local resources. This is a bug.")
|
|
111
|
+
|
|
112
|
+
if not view.name:
|
|
113
|
+
recommendations.append(
|
|
114
|
+
Recommendation(
|
|
115
|
+
message=f"View {view_ref!s} is missing a human-readable name.",
|
|
116
|
+
fix="Add a clear and concise name to the view.",
|
|
117
|
+
code=self.code,
|
|
118
|
+
)
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
return recommendations
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class ViewMissingDescription(DataModelValidator):
|
|
125
|
+
"""Validates that a View has a human-readable description.
|
|
126
|
+
|
|
127
|
+
## What it does
|
|
128
|
+
Validates that each view in the data model has a human-readable description.
|
|
129
|
+
|
|
130
|
+
## Why is this bad?
|
|
131
|
+
A missing description makes it harder for users (humans or machines) to understand in what context the view
|
|
132
|
+
should be used. The description can provide important information about the view's purpose, scope, and usage.
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
## Example
|
|
136
|
+
A view Site has no description. Users may find it difficult to understand what this view represents, unless
|
|
137
|
+
extra context is provided. Even if we know that Site is used in the context of wind energy developments, a
|
|
138
|
+
description is necessary as it can be used in various context within the same domain such as:
|
|
139
|
+
|
|
140
|
+
Option 1 — Project area
|
|
141
|
+
This view represents a geographical area where wind energy projects are developed and managed.
|
|
142
|
+
|
|
143
|
+
Option 2 — Lease area
|
|
144
|
+
The legally defined lease area allocated for offshore wind development.
|
|
145
|
+
|
|
146
|
+
Option 3 — Measurement site
|
|
147
|
+
A specific location where wind measurements (e.g., LiDAR, met mast) are collected.
|
|
148
|
+
|
|
149
|
+
"""
|
|
150
|
+
|
|
151
|
+
code = f"{BASE_CODE}-004"
|
|
152
|
+
issue_type = Recommendation
|
|
153
|
+
|
|
154
|
+
def run(self) -> list[Recommendation]:
|
|
155
|
+
recommendations: list[Recommendation] = []
|
|
156
|
+
|
|
157
|
+
for view_ref in self.local_resources.data_model_views:
|
|
158
|
+
view = self.local_resources.views_by_reference.get(view_ref)
|
|
159
|
+
|
|
160
|
+
if view is None:
|
|
161
|
+
raise RuntimeError(f"View {view_ref!s} not found in local resources. This is a bug.")
|
|
162
|
+
|
|
163
|
+
if not view.description:
|
|
164
|
+
recommendations.append(
|
|
165
|
+
Recommendation(
|
|
166
|
+
message=f"View {view_ref!s} is missing a description.",
|
|
167
|
+
fix="Add a clear and concise description to the view.",
|
|
168
|
+
code=self.code,
|
|
169
|
+
)
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
return recommendations
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
class ViewPropertyMissingName(DataModelValidator):
|
|
176
|
+
"""Validates that a view property has a human-readable name.
|
|
177
|
+
|
|
178
|
+
## What it does
|
|
179
|
+
Validates that each view property in the data model has a human-readable name.
|
|
180
|
+
|
|
181
|
+
## Why is this bad?
|
|
182
|
+
A missing name makes it harder for users (humans or machines) to understand the purpose of the view property.
|
|
183
|
+
This is important as view property's ids are often based on technical identifiers, abbreviations, etc.
|
|
184
|
+
Providing a clear name improves usability, maintainability, searchability, and AI-readiness.
|
|
185
|
+
|
|
186
|
+
## Example
|
|
187
|
+
A view WindTurbine has a property pc which has no name. Users may find it difficult to understand what this view
|
|
188
|
+
property represents, unless they look up the id in documentation or other resources. Adding name "power curve"
|
|
189
|
+
would increase clarity and usability.
|
|
190
|
+
"""
|
|
191
|
+
|
|
192
|
+
code = f"{BASE_CODE}-005"
|
|
193
|
+
issue_type = Recommendation
|
|
194
|
+
|
|
195
|
+
def run(self) -> list[Recommendation]:
|
|
196
|
+
recommendations: list[Recommendation] = []
|
|
197
|
+
|
|
198
|
+
for view_ref in self.local_resources.data_model_views:
|
|
199
|
+
if properties := self.local_resources.properties_by_view.get(view_ref):
|
|
200
|
+
for prop_ref, definition in properties.items():
|
|
201
|
+
if not definition.name:
|
|
202
|
+
recommendations.append(
|
|
203
|
+
Recommendation(
|
|
204
|
+
message=f"View {view_ref!s} property {prop_ref!s} is missing a human-readable name.",
|
|
205
|
+
fix="Add a clear and concise name to the view property.",
|
|
206
|
+
code=self.code,
|
|
207
|
+
)
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
return recommendations
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
class ViewPropertyMissingDescription(DataModelValidator):
|
|
214
|
+
"""Validates that a View property has a human-readable description.
|
|
215
|
+
|
|
216
|
+
## What it does
|
|
217
|
+
Validates that each view property in the data model has a human-readable description.
|
|
218
|
+
|
|
219
|
+
## Why is this bad?
|
|
220
|
+
A missing description makes it harder for users (humans or machines) to understand in what context the view property
|
|
221
|
+
should be used. The description can provide important information about the view property's purpose,
|
|
222
|
+
scope, and usage.
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
## Example
|
|
226
|
+
A view WindTurbine has a property status with no description. Users may find it difficult to understand what this
|
|
227
|
+
property represents, unless extra context is provided. Even if we know that status is related to wind turbine
|
|
228
|
+
operations, a description is necessary as it can have different meanings in various contexts:
|
|
229
|
+
|
|
230
|
+
Option 1 — Operational status
|
|
231
|
+
Current operational state of the wind turbine (e.g., running, stopped, maintenance, fault).
|
|
232
|
+
|
|
233
|
+
Option 2 — Connection status
|
|
234
|
+
Grid connection status indicating whether the turbine is connected to the electrical grid.
|
|
235
|
+
|
|
236
|
+
Option 3 — Availability status
|
|
237
|
+
Availability state for production indicating whether the turbine is available for power generation.
|
|
238
|
+
|
|
239
|
+
"""
|
|
240
|
+
|
|
241
|
+
code = f"{BASE_CODE}-006"
|
|
242
|
+
issue_type = Recommendation
|
|
243
|
+
|
|
244
|
+
def run(self) -> list[Recommendation]:
|
|
245
|
+
recommendations: list[Recommendation] = []
|
|
246
|
+
|
|
247
|
+
for view_ref in self.local_resources.data_model_views:
|
|
248
|
+
if properties := self.local_resources.properties_by_view.get(view_ref):
|
|
249
|
+
for prop_ref, definition in properties.items():
|
|
250
|
+
if not definition.description:
|
|
251
|
+
recommendations.append(
|
|
252
|
+
Recommendation(
|
|
253
|
+
message=f"View {view_ref!s} property {prop_ref!s} is missing a description.",
|
|
254
|
+
fix="Add a clear and concise description to the view property.",
|
|
255
|
+
code=self.code,
|
|
256
|
+
)
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
return recommendations
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
class EnumerationMissingName(DataModelValidator):
|
|
263
|
+
"""Validates that an enumeration has a human-readable name.
|
|
264
|
+
|
|
265
|
+
## What it does
|
|
266
|
+
Validates that each enumeration value in the data model has a human-readable name.
|
|
267
|
+
|
|
268
|
+
## Why is this bad?
|
|
269
|
+
A missing name makes it harder for users (humans or machines) to understand the purpose of the enumeration value.
|
|
270
|
+
This is important as enumeration values are often technical codes or abbreviations, and a clear name improves
|
|
271
|
+
usability, maintainability, searchability, and AI-readiness.
|
|
272
|
+
|
|
273
|
+
## Example
|
|
274
|
+
An enumeration value with id "NOM" in a wind turbine operational mode property has no name. Users may find it
|
|
275
|
+
difficult to understand what this value represents. Adding name "Normal Operation" would increase clarity
|
|
276
|
+
and usability.
|
|
277
|
+
"""
|
|
278
|
+
|
|
279
|
+
code = f"{BASE_CODE}-007"
|
|
280
|
+
issue_type = Recommendation
|
|
281
|
+
|
|
282
|
+
def run(self) -> list[Recommendation]:
|
|
283
|
+
recommendations: list[Recommendation] = []
|
|
284
|
+
|
|
285
|
+
for container_ref, container in self.local_resources.containers_by_reference.items():
|
|
286
|
+
if container_ref.space != self.local_resources.data_model_reference.space:
|
|
287
|
+
continue
|
|
288
|
+
|
|
289
|
+
for prop_ref, definition in container.properties.items():
|
|
290
|
+
if not isinstance(definition.type, EnumProperty):
|
|
291
|
+
continue
|
|
292
|
+
|
|
293
|
+
for value, enum_def in definition.type.values.items():
|
|
294
|
+
if not enum_def.name:
|
|
295
|
+
recommendations.append(
|
|
296
|
+
Recommendation(
|
|
297
|
+
message=(
|
|
298
|
+
f"Enumeration value {value!r} in property {prop_ref!s} of container "
|
|
299
|
+
f"{container_ref!s} is missing a human-readable name."
|
|
300
|
+
),
|
|
301
|
+
fix="Add a clear and concise name to the enumeration value.",
|
|
302
|
+
code=self.code,
|
|
303
|
+
)
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
return recommendations
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
class EnumerationMissingDescription(DataModelValidator):
|
|
310
|
+
"""Validates that an enumeration value has a human-readable description.
|
|
311
|
+
|
|
312
|
+
## What it does
|
|
313
|
+
Validates that each enumeration value in the data model has a human-readable description.
|
|
314
|
+
|
|
315
|
+
## Why is this bad?
|
|
316
|
+
A missing description makes it harder for users (humans or machines) to understand the meaning and context
|
|
317
|
+
of the enumeration value. The description can provide important information about when and how the value
|
|
318
|
+
should be used, especially when enumeration values are technical codes or abbreviations.
|
|
319
|
+
|
|
320
|
+
## Example
|
|
321
|
+
An enumeration value "NOM" in a wind turbine operational mode property has no description. Users may find it
|
|
322
|
+
difficult to understand what this value represents without additional context. Even with a name like
|
|
323
|
+
"Normal Operation", the description is valuable as it can clarify specifics:
|
|
324
|
+
|
|
325
|
+
Option 1 — Basic definition
|
|
326
|
+
The turbine is operating normally and generating power according to its power curve.
|
|
327
|
+
|
|
328
|
+
Option 2 — Detailed operational context
|
|
329
|
+
The turbine is in normal operation mode, actively generating power with all systems functioning within
|
|
330
|
+
specified parameters and connected to the grid.
|
|
331
|
+
|
|
332
|
+
Option 3 — Contrasting with other modes
|
|
333
|
+
Standard operating mode where the turbine follows the power curve and responds to grid commands,
|
|
334
|
+
as opposed to maintenance mode or fault conditions.
|
|
335
|
+
"""
|
|
336
|
+
|
|
337
|
+
code = f"{BASE_CODE}-008"
|
|
338
|
+
issue_type = Recommendation
|
|
339
|
+
|
|
340
|
+
def run(self) -> list[Recommendation]:
|
|
341
|
+
recommendations: list[Recommendation] = []
|
|
342
|
+
|
|
343
|
+
for container_ref, container in self.local_resources.containers_by_reference.items():
|
|
344
|
+
if container_ref.space != self.local_resources.data_model_reference.space:
|
|
345
|
+
continue
|
|
346
|
+
|
|
347
|
+
for prop_ref, definition in container.properties.items():
|
|
348
|
+
if not isinstance(definition.type, EnumProperty):
|
|
349
|
+
continue
|
|
350
|
+
|
|
351
|
+
for value, enum_def in definition.type.values.items():
|
|
352
|
+
if not enum_def.description:
|
|
353
|
+
recommendations.append(
|
|
354
|
+
Recommendation(
|
|
355
|
+
message=(
|
|
356
|
+
f"Enumeration value {value!r} in property {prop_ref!s} of container "
|
|
357
|
+
f"{container_ref!s} is missing a human-readable description."
|
|
358
|
+
),
|
|
359
|
+
fix="Add a clear and concise description to the enumeration value.",
|
|
360
|
+
code=self.code,
|
|
361
|
+
)
|
|
362
|
+
)
|
|
363
|
+
|
|
364
|
+
return recommendations
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from itertools import chain
|
|
4
|
+
from typing import ClassVar, TypeAlias, cast
|
|
5
|
+
|
|
6
|
+
from pyparsing import cached_property
|
|
7
|
+
|
|
8
|
+
from cognite.neat._data_model.models.dms._container import ContainerRequest
|
|
9
|
+
from cognite.neat._data_model.models.dms._references import (
|
|
10
|
+
ContainerDirectReference,
|
|
11
|
+
ContainerReference,
|
|
12
|
+
DataModelReference,
|
|
13
|
+
ViewDirectReference,
|
|
14
|
+
ViewReference,
|
|
15
|
+
)
|
|
16
|
+
from cognite.neat._data_model.models.dms._view_property import ViewRequestProperty
|
|
17
|
+
from cognite.neat._data_model.models.dms._views import ViewRequest
|
|
18
|
+
from cognite.neat._issues import ConsistencyError, Recommendation
|
|
19
|
+
from cognite.neat._utils.useful_types import ModusOperandi
|
|
20
|
+
|
|
21
|
+
# Type aliases for better readability
|
|
22
|
+
ViewsByReference: TypeAlias = dict[ViewReference, ViewRequest]
|
|
23
|
+
ContainersByReference: TypeAlias = dict[ContainerReference, ContainerRequest]
|
|
24
|
+
AncestorsByReference: TypeAlias = dict[ViewReference, set[ViewReference]]
|
|
25
|
+
ReverseToDirectMapping: TypeAlias = dict[
|
|
26
|
+
tuple[ViewReference, str], tuple[ViewReference, ContainerDirectReference | ViewDirectReference]
|
|
27
|
+
]
|
|
28
|
+
ConnectionEndNodeTypes: TypeAlias = dict[tuple[ViewReference, str], ViewReference | None]
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass
|
|
32
|
+
class LocalResources:
|
|
33
|
+
"""Local data model resources."""
|
|
34
|
+
|
|
35
|
+
data_model_reference: DataModelReference
|
|
36
|
+
data_model_views: set[ViewReference]
|
|
37
|
+
data_model_description: str | None
|
|
38
|
+
data_model_name: str | None
|
|
39
|
+
views_by_reference: ViewsByReference
|
|
40
|
+
properties_by_view: dict[ViewReference, dict[str, ViewRequestProperty]]
|
|
41
|
+
ancestors_by_view_reference: AncestorsByReference
|
|
42
|
+
reverse_to_direct_mapping: ReverseToDirectMapping
|
|
43
|
+
containers_by_reference: ContainersByReference
|
|
44
|
+
connection_end_node_types: ConnectionEndNodeTypes
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dataclass
|
|
48
|
+
class CDFResources:
|
|
49
|
+
"""CDF resources."""
|
|
50
|
+
|
|
51
|
+
views_by_reference: ViewsByReference
|
|
52
|
+
ancestors_by_view_reference: AncestorsByReference
|
|
53
|
+
containers_by_reference: ContainersByReference
|
|
54
|
+
data_model_views: set[ViewReference]
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class DataModelValidator(ABC):
|
|
58
|
+
"""Assessors for fundamental data model principles."""
|
|
59
|
+
|
|
60
|
+
code: ClassVar[str]
|
|
61
|
+
issue_type: ClassVar[type[ConsistencyError] | type[Recommendation]]
|
|
62
|
+
|
|
63
|
+
def __init__(
|
|
64
|
+
self,
|
|
65
|
+
local_resources: LocalResources,
|
|
66
|
+
cdf_resources: CDFResources,
|
|
67
|
+
modus_operandi: ModusOperandi = "additive",
|
|
68
|
+
) -> None:
|
|
69
|
+
self.local_resources = local_resources
|
|
70
|
+
self.cdf_resources = cdf_resources
|
|
71
|
+
self.modus_operandi = modus_operandi
|
|
72
|
+
|
|
73
|
+
@abstractmethod
|
|
74
|
+
def run(self) -> list[ConsistencyError] | list[Recommendation] | list[ConsistencyError | Recommendation]:
|
|
75
|
+
"""Execute the success handler on the data model."""
|
|
76
|
+
# do something with data model
|
|
77
|
+
...
|
|
78
|
+
|
|
79
|
+
def _select_view_with_property(self, view_ref: ViewReference, property_: str) -> ViewRequest | None:
|
|
80
|
+
"""Select the appropriate view (local or CDF) that contains desired property.
|
|
81
|
+
|
|
82
|
+
Prioritizes views that contain the property (first local than CDF),
|
|
83
|
+
then falls back to any available view (even without the property).
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
view_ref: Reference to the view.
|
|
87
|
+
property_: Property name to look for.
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
The selected ViewRequest if found, else None.
|
|
91
|
+
|
|
92
|
+
!! note "Behavior based on modus operandi"
|
|
93
|
+
- In "additive" modus operandi, local and CDF view will be considered irrirespective of their space.
|
|
94
|
+
- In "rebuild" modus operandi, local views will be considered irrispective of their space, while CDF views
|
|
95
|
+
will only be considered if they belong to the different space than the local data model space
|
|
96
|
+
(as they are considered external resources that is managed under other data model/schema space).
|
|
97
|
+
|
|
98
|
+
"""
|
|
99
|
+
|
|
100
|
+
local_view = self.local_resources.views_by_reference.get(view_ref)
|
|
101
|
+
cdf_view = (
|
|
102
|
+
self.cdf_resources.views_by_reference.get(view_ref)
|
|
103
|
+
if view_ref.space != self.local_resources.data_model_reference.space or self.modus_operandi == "additive"
|
|
104
|
+
else None
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
# Try views with the property first, then any available view
|
|
108
|
+
candidates = chain(
|
|
109
|
+
(v for v in (local_view, cdf_view) if v and v.properties and property_ in v.properties),
|
|
110
|
+
(v for v in (local_view, cdf_view) if v),
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
return next(candidates, None)
|
|
114
|
+
|
|
115
|
+
def _select_container_with_property(
|
|
116
|
+
self, container_ref: ContainerReference, property_: str
|
|
117
|
+
) -> ContainerRequest | None:
|
|
118
|
+
"""Select the appropriate container (local or CDF) that contains the desired property.
|
|
119
|
+
|
|
120
|
+
Prioritizes containers that contain the property (first local than CDF),
|
|
121
|
+
then falls back to any available container.
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
container_ref: Reference to the container.
|
|
125
|
+
property_: Property name to look for.
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
The selected ContainerRequest if found, else None.
|
|
129
|
+
|
|
130
|
+
!! note "Behavior based on modus operandi"
|
|
131
|
+
- In "additive" modus operandi, local and CDF containers will be considered irrirespective of their space.
|
|
132
|
+
- In "rebuild" modus operandi, local containers will be considered irrispective of their space, while CDF
|
|
133
|
+
containers will only be considered if they belong to the different space than the local data model space
|
|
134
|
+
(as they are considered external resources that is managed under other data model/schema space).
|
|
135
|
+
|
|
136
|
+
"""
|
|
137
|
+
local_container = self.local_resources.containers_by_reference.get(container_ref)
|
|
138
|
+
cdf_container = self.cdf_resources.containers_by_reference.get(container_ref)
|
|
139
|
+
|
|
140
|
+
cdf_container = (
|
|
141
|
+
self.cdf_resources.containers_by_reference.get(container_ref)
|
|
142
|
+
if container_ref.space != self.local_resources.data_model_reference.space
|
|
143
|
+
or self.modus_operandi == "additive"
|
|
144
|
+
else None
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
# Try containers with the property first, then any available container
|
|
148
|
+
candidates = chain(
|
|
149
|
+
(c for c in (local_container, cdf_container) if c and c.properties and property_ in c.properties),
|
|
150
|
+
(c for c in (local_container, cdf_container) if c),
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
return next(candidates, None)
|
|
154
|
+
|
|
155
|
+
@cached_property
|
|
156
|
+
def data_model_view_references(self) -> set[ViewReference]:
|
|
157
|
+
"""Get all data model view references to validate based on deployment mode.
|
|
158
|
+
|
|
159
|
+
In "rebuild" mode, returns only local data model view references.
|
|
160
|
+
In "additive" mode, returns union of local and CDF data model view references.
|
|
161
|
+
|
|
162
|
+
Returns:
|
|
163
|
+
Set of ViewReference objects representing all data model views to validate.
|
|
164
|
+
"""
|
|
165
|
+
return (
|
|
166
|
+
self.local_resources.data_model_views.union(self.cdf_resources.data_model_views)
|
|
167
|
+
if self.modus_operandi == "additive"
|
|
168
|
+
else self.local_resources.data_model_views
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
@cached_property
|
|
172
|
+
def views_references(self) -> set[ViewReference]:
|
|
173
|
+
"""Get all view references to validate based on deployment mode.
|
|
174
|
+
|
|
175
|
+
In "rebuild" mode, returns only local view references.
|
|
176
|
+
In "additive" mode, returns union of local and CDF view references.
|
|
177
|
+
|
|
178
|
+
Returns:
|
|
179
|
+
Set of ViewReference objects representing all views to validate.
|
|
180
|
+
"""
|
|
181
|
+
|
|
182
|
+
return (
|
|
183
|
+
set(self.local_resources.views_by_reference.keys()).union(set(self.cdf_resources.views_by_reference.keys()))
|
|
184
|
+
if self.modus_operandi == "additive"
|
|
185
|
+
else set(self.local_resources.views_by_reference.keys())
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
@cached_property
|
|
189
|
+
def container_references(self) -> set[ContainerReference]:
|
|
190
|
+
"""Get all container references to validate based on deployment mode.
|
|
191
|
+
|
|
192
|
+
In "rebuild" mode, returns only local container references.
|
|
193
|
+
In "additive" mode, returns union of local and CDF container references.
|
|
194
|
+
|
|
195
|
+
Returns:
|
|
196
|
+
Set of ContainerReference objects representing all containers to validate.
|
|
197
|
+
"""
|
|
198
|
+
|
|
199
|
+
return (
|
|
200
|
+
set(self.local_resources.containers_by_reference.keys()).union(
|
|
201
|
+
set(self.cdf_resources.containers_by_reference.keys())
|
|
202
|
+
)
|
|
203
|
+
if self.modus_operandi == "additive"
|
|
204
|
+
else set(self.local_resources.containers_by_reference.keys())
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
@cached_property
|
|
208
|
+
def merged_views(self) -> dict[ViewReference, ViewRequest]:
|
|
209
|
+
"""Get views with merged properties and implements for accurate limit checking.
|
|
210
|
+
|
|
211
|
+
In "rebuild" mode, returns only local views.
|
|
212
|
+
In "additive" mode, merges local and CDF views by:
|
|
213
|
+
- Combining properties from both versions (local overrides CDF)
|
|
214
|
+
- Combining implements lists (union, no duplicates)
|
|
215
|
+
- Using CDF view as base if it exists, otherwise local view
|
|
216
|
+
|
|
217
|
+
This ensures limit validation accounts for the actual deployed state
|
|
218
|
+
in additive deployments.
|
|
219
|
+
|
|
220
|
+
Returns:
|
|
221
|
+
Dictionary mapping ViewReference to merged ViewRequest objects.
|
|
222
|
+
|
|
223
|
+
Raises:
|
|
224
|
+
RuntimeError: If a referenced view is not found in either local or CDF resources.
|
|
225
|
+
"""
|
|
226
|
+
|
|
227
|
+
if self.modus_operandi != "additive":
|
|
228
|
+
return self.local_resources.views_by_reference
|
|
229
|
+
|
|
230
|
+
merged_views: dict[ViewReference, ViewRequest] = {}
|
|
231
|
+
# Merge local views, combining properties if view exists in both
|
|
232
|
+
for view_ref in self.views_references:
|
|
233
|
+
cdf_view = self.cdf_resources.views_by_reference.get(view_ref)
|
|
234
|
+
local_view = self.local_resources.views_by_reference.get(view_ref)
|
|
235
|
+
|
|
236
|
+
if not cdf_view and not local_view:
|
|
237
|
+
raise RuntimeError(f"View {view_ref!s} not found in either local or CDF resources. This is a bug!")
|
|
238
|
+
|
|
239
|
+
# this will later update of local properties and implements
|
|
240
|
+
merged_views[view_ref] = cast(ViewRequest, (cdf_view or local_view)).model_copy(deep=True)
|
|
241
|
+
|
|
242
|
+
if local_view and local_view.properties:
|
|
243
|
+
if not merged_views[view_ref].properties:
|
|
244
|
+
merged_views[view_ref].properties = local_view.properties
|
|
245
|
+
else:
|
|
246
|
+
merged_views[view_ref].properties.update(local_view.properties)
|
|
247
|
+
|
|
248
|
+
if local_view and local_view.implements:
|
|
249
|
+
if not merged_views[view_ref].implements:
|
|
250
|
+
merged_views[view_ref].implements = local_view.implements
|
|
251
|
+
else: # mypy is complaining here about possible None which is not possible due to the check above
|
|
252
|
+
for impl in local_view.implements:
|
|
253
|
+
if impl not in cast(list[ViewReference], merged_views[view_ref].implements):
|
|
254
|
+
cast(list[ViewReference], merged_views[view_ref].implements).append(impl)
|
|
255
|
+
|
|
256
|
+
return merged_views
|
|
257
|
+
|
|
258
|
+
@cached_property
|
|
259
|
+
def merged_containers(self) -> dict[ContainerReference, ContainerRequest]:
|
|
260
|
+
"""Get containers with merged properties for accurate limit checking.
|
|
261
|
+
|
|
262
|
+
In "rebuild" mode, returns only local containers.
|
|
263
|
+
In "additive" mode, merges local and CDF containers by:
|
|
264
|
+
- Combining properties from both versions (local overrides CDF)
|
|
265
|
+
- Using CDF container as base if it exists, otherwise local container
|
|
266
|
+
|
|
267
|
+
This ensures limit validation accounts for the actual deployed state
|
|
268
|
+
in additive deployments.
|
|
269
|
+
|
|
270
|
+
Returns:
|
|
271
|
+
Dictionary mapping ContainerReference to merged ContainerRequest objects.
|
|
272
|
+
|
|
273
|
+
Raises:
|
|
274
|
+
RuntimeError: If a referenced container is not found in either local or CDF resources.
|
|
275
|
+
"""
|
|
276
|
+
|
|
277
|
+
if self.modus_operandi != "additive":
|
|
278
|
+
return self.local_resources.containers_by_reference
|
|
279
|
+
|
|
280
|
+
merged_containers: dict[ContainerReference, ContainerRequest] = {}
|
|
281
|
+
# Merge local containers, combining properties if container exists in both
|
|
282
|
+
for container_ref in self.container_references:
|
|
283
|
+
cdf_container = self.cdf_resources.containers_by_reference.get(container_ref)
|
|
284
|
+
local_container = self.local_resources.containers_by_reference.get(container_ref)
|
|
285
|
+
|
|
286
|
+
if not cdf_container and not local_container:
|
|
287
|
+
raise RuntimeError(
|
|
288
|
+
f"Container {container_ref!s} not found in either local or CDF resources. This is a bug!"
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
merged_containers[container_ref] = cast(ContainerRequest, (cdf_container or local_container)).model_copy(
|
|
292
|
+
deep=True
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
if local_container and local_container.properties:
|
|
296
|
+
if not merged_containers[container_ref].properties:
|
|
297
|
+
merged_containers[container_ref].properties = local_container.properties
|
|
298
|
+
else:
|
|
299
|
+
merged_containers[container_ref].properties.update(local_container.properties)
|
|
300
|
+
|
|
301
|
+
return merged_containers
|
|
302
|
+
|
|
303
|
+
def _is_ancestor_of_target(self, potential_ancestor: ViewReference, target_view_ref: ViewReference) -> bool:
|
|
304
|
+
"""Check if a view is an ancestor of the target view."""
|
|
305
|
+
return potential_ancestor in self.local_resources.ancestors_by_view_reference.get(
|
|
306
|
+
target_view_ref, set()
|
|
307
|
+
) or potential_ancestor in self.cdf_resources.ancestors_by_view_reference.get(target_view_ref, set())
|