cognite-neat 0.123.26__py3-none-any.whl → 1.0.22__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 +4 -3
- cognite/neat/_client/__init__.py +5 -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 +138 -0
- cognite/neat/_client/data_classes.py +44 -0
- cognite/neat/_client/data_model_api.py +115 -0
- cognite/neat/_client/init/credentials.py +70 -0
- cognite/neat/_client/init/env_vars.py +131 -0
- cognite/neat/_client/init/main.py +51 -0
- cognite/neat/_client/spaces_api.py +115 -0
- cognite/neat/_client/statistics_api.py +24 -0
- cognite/neat/_client/views_api.py +144 -0
- cognite/neat/_config.py +266 -0
- cognite/neat/_data_model/_analysis.py +571 -0
- cognite/neat/_data_model/_constants.py +74 -0
- cognite/neat/_data_model/_identifiers.py +61 -0
- cognite/neat/_data_model/_shared.py +41 -0
- cognite/neat/_data_model/_snapshot.py +134 -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 +644 -0
- cognite/neat/_data_model/deployer/deployer.py +431 -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 +480 -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 +344 -0
- cognite/neat/_data_model/importers/_table_importer/importer.py +192 -0
- cognite/neat/_data_model/importers/_table_importer/reader.py +1102 -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 +141 -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 +310 -0
- cognite/neat/_data_model/models/dms/_view_property.py +235 -0
- cognite/neat/_data_model/models/dms/_views.py +216 -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 +381 -0
- cognite/neat/_data_model/validation/dms/_base.py +25 -0
- cognite/neat/_data_model/validation/dms/_connections.py +681 -0
- cognite/neat/_data_model/validation/dms/_consistency.py +58 -0
- cognite/neat/_data_model/validation/dms/_containers.py +199 -0
- cognite/neat/_data_model/validation/dms/_limits.py +368 -0
- cognite/neat/_data_model/validation/dms/_orchestrator.py +70 -0
- cognite/neat/_data_model/validation/dms/_views.py +164 -0
- cognite/neat/_exceptions.py +68 -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 +476 -0
- cognite/neat/_session/_html/static/deployment.js +181 -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 +80 -0
- cognite/neat/_session/_html/templates/issues.html +45 -0
- cognite/neat/_session/_issues.py +81 -0
- cognite/neat/_session/_physical.py +294 -0
- cognite/neat/_session/_result/__init__.py +3 -0
- cognite/neat/_session/_result/_deployment/__init__.py +0 -0
- cognite/neat/_session/_result/_deployment/_physical/__init__.py +0 -0
- cognite/neat/_session/_result/_deployment/_physical/_changes.py +196 -0
- cognite/neat/_session/_result/_deployment/_physical/_statistics.py +180 -0
- cognite/neat/_session/_result/_deployment/_physical/serializer.py +35 -0
- cognite/neat/_session/_result/_result.py +31 -0
- cognite/neat/_session/_session.py +81 -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 +101 -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 +88 -0
- cognite/neat/_store/_store.py +220 -0
- cognite/neat/_utils/__init__.py +0 -0
- cognite/neat/_utils/_reader.py +194 -0
- cognite/neat/_utils/auxiliary.py +49 -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/repo.py +19 -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/_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 +8 -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 +5 -3
- cognite/neat/_v0/core/_data_model/__init__.py +0 -0
- cognite/neat/{core → _v0/core}/_data_model/_constants.py +7 -0
- cognite/neat/{core → _v0/core}/_data_model/_shared.py +4 -4
- cognite/neat/{core → _v0/core}/_data_model/analysis/_base.py +8 -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 +12 -12
- 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 -116
- cognite/neat/{core → _v0/core}/_data_model/exporters/_data_model2yaml.py +1 -1
- 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 +5 -5
- cognite/neat/{core → _v0/core}/_data_model/importers/_dms2data_model.py +18 -15
- cognite/neat/{core → _v0/core}/_data_model/importers/_graph2data_model.py +12 -12
- cognite/neat/{core → _v0/core}/_data_model/importers/_rdf/_base.py +12 -12
- cognite/neat/{core → _v0/core}/_data_model/importers/_rdf/_inference2rdata_model.py +14 -14
- cognite/neat/{core → _v0/core}/_data_model/importers/_rdf/_owl2data_model.py +41 -21
- cognite/neat/{core → _v0/core}/_data_model/importers/_rdf/_shared.py +9 -9
- cognite/neat/{core → _v0/core}/_data_model/importers/_spreadsheet2data_model.py +92 -12
- 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/{core → _v0/core}/_data_model/models/_import_contexts.py +1 -1
- cognite/neat/{core → _v0/core}/_data_model/models/_types.py +5 -5
- cognite/neat/{core → _v0/core}/_data_model/models/conceptual/_unverified.py +16 -10
- cognite/neat/{core → _v0/core}/_data_model/models/conceptual/_validation.py +12 -12
- cognite/neat/{core → _v0/core}/_data_model/models/conceptual/_verified.py +9 -9
- 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 +26 -19
- cognite/neat/{core → _v0/core}/_data_model/models/physical/_unverified.py +133 -37
- cognite/neat/{core → _v0/core}/_data_model/models/physical/_validation.py +24 -20
- cognite/neat/{core → _v0/core}/_data_model/models/physical/_verified.py +95 -24
- cognite/neat/{core → _v0/core}/_data_model/transformers/_base.py +4 -4
- cognite/neat/{core → _v0/core}/_data_model/transformers/_converters.py +35 -28
- cognite/neat/{core → _v0/core}/_data_model/transformers/_mapping.py +7 -7
- cognite/neat/{core → _v0/core}/_data_model/transformers/_union_conceptual.py +5 -5
- 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 +3 -2
- 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 +5 -5
- cognite/neat/{core → _v0/core}/_issues/_contextmanagers.py +1 -1
- cognite/neat/{core → _v0/core}/_issues/_factory.py +3 -3
- cognite/neat/{core → _v0/core}/_issues/errors/__init__.py +1 -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 +1 -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 +1 -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 +2 -2
- cognite/neat/{core → _v0/core}/_issues/warnings/_properties.py +2 -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 +12 -12
- cognite/neat/{core → _v0/core}/_store/_instance.py +43 -10
- 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 +22 -12
- cognite/neat/{core → _v0/core}/_utils/auxiliary.py +1 -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 +1 -1
- cognite/neat/{core → _v0/core}/_utils/reader/_base.py +1 -1
- cognite/neat/{core → _v0/core}/_utils/spreadsheet.py +18 -4
- cognite/neat/{core → _v0/core}/_utils/text.py +1 -1
- cognite/neat/{core → _v0/core}/_utils/upload.py +3 -3
- cognite/neat/{session → _v0}/engine/_load.py +1 -1
- 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 -15
- 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 +34 -21
- 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 +10 -10
- 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}/exceptions.py +5 -5
- cognite/neat/_version.py +1 -1
- cognite/neat/legacy.py +6 -0
- cognite_neat-1.0.22.dist-info/METADATA +123 -0
- cognite_neat-1.0.22.dist-info/RECORD +329 -0
- cognite_neat-1.0.22.dist-info/WHEEL +4 -0
- 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/session/_session/_data_model/__init__.py +0 -3
- cognite/neat/session/_session/_data_model/_read.py +0 -193
- cognite/neat/session/_session/_data_model/_routes.py +0 -45
- cognite/neat/session/_session/_data_model/_show.py +0 -147
- cognite/neat/session/_session/_data_model/_write.py +0 -335
- cognite_neat-0.123.26.dist-info/METADATA +0 -144
- cognite_neat-0.123.26.dist-info/RECORD +0 -201
- cognite_neat-0.123.26.dist-info/WHEEL +0 -4
- cognite_neat-0.123.26.dist-info/licenses/LICENSE +0 -201
- /cognite/neat/{core → _client/init}/__init__.py +0 -0
- /cognite/neat/{core/_client/_api → _data_model}/__init__.py +0 -0
- /cognite/neat/{core/_client/data_classes → _data_model/deployer}/__init__.py +0 -0
- /cognite/neat/{core/_data_model → _data_model/exporters/_table_exporter}/__init__.py +0 -0
- /cognite/neat/{core/_instances → _data_model/importers/_table_importer}/__init__.py +0 -0
- /cognite/neat/{core/_instances/extractors/_classic_cdf → _data_model/models}/__init__.py +0 -0
- /cognite/neat/{core/_utils → _data_model/models/conceptual}/__init__.py +0 -0
- /cognite/neat/{plugins/data_model → _data_model/validation}/__init__.py +0 -0
- /cognite/neat/{session/_session → _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/importers/__init__.py +0 -0
- /cognite/neat/{core → _v0/core}/_data_model/importers/_rdf/__init__.py +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}/_data_model/transformers/__init__.py +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/tarjan.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}/engine/__init__.py +0 -0
- /cognite/neat/{session → _v0}/engine/_import.py +0 -0
- /cognite/neat/{session → _v0}/engine/_interface.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
|
@@ -0,0 +1,480 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from collections import defaultdict
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from typing import Any, Literal
|
|
5
|
+
|
|
6
|
+
from pydantic import ValidationError
|
|
7
|
+
|
|
8
|
+
from cognite.neat._data_model._constants import DEFAULT_MAX_LIST_SIZE, DEFAULT_MAX_LIST_SIZE_DIRECT_RELATIONS
|
|
9
|
+
from cognite.neat._data_model.importers._table_importer.data_classes import (
|
|
10
|
+
CREATOR_KEY,
|
|
11
|
+
CREATOR_MARKER,
|
|
12
|
+
DMSContainer,
|
|
13
|
+
DMSEnum,
|
|
14
|
+
DMSNode,
|
|
15
|
+
DMSProperty,
|
|
16
|
+
DMSView,
|
|
17
|
+
EntityTableFilter,
|
|
18
|
+
MetadataValue,
|
|
19
|
+
RAWFilterTableFilter,
|
|
20
|
+
TableDMS,
|
|
21
|
+
TableViewFilter,
|
|
22
|
+
)
|
|
23
|
+
from cognite.neat._data_model.models.dms import (
|
|
24
|
+
ContainerPropertyDefinition,
|
|
25
|
+
ContainerReference,
|
|
26
|
+
ContainerRequest,
|
|
27
|
+
DataModelRequest,
|
|
28
|
+
DataType,
|
|
29
|
+
DirectNodeRelation,
|
|
30
|
+
EnumProperty,
|
|
31
|
+
EqualsFilterData,
|
|
32
|
+
Filter,
|
|
33
|
+
FilterAdapter,
|
|
34
|
+
HasDataFilter,
|
|
35
|
+
InFilterData,
|
|
36
|
+
ListablePropertyTypeDefinition,
|
|
37
|
+
NodeReference,
|
|
38
|
+
RequestSchema,
|
|
39
|
+
RequiresConstraintDefinition,
|
|
40
|
+
UniquenessConstraintDefinition,
|
|
41
|
+
ViewCorePropertyRequest,
|
|
42
|
+
ViewReference,
|
|
43
|
+
ViewRequest,
|
|
44
|
+
ViewRequestProperty,
|
|
45
|
+
)
|
|
46
|
+
from cognite.neat._data_model.models.dms._view_property import (
|
|
47
|
+
EdgeProperty,
|
|
48
|
+
MultiEdgeProperty,
|
|
49
|
+
MultiReverseDirectRelationPropertyRequest,
|
|
50
|
+
ReverseDirectRelationProperty,
|
|
51
|
+
SingleEdgeProperty,
|
|
52
|
+
SingleReverseDirectRelationPropertyRequest,
|
|
53
|
+
)
|
|
54
|
+
from cognite.neat._data_model.models.entities import ParsedEntity
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@dataclass
|
|
58
|
+
class ViewProperties:
|
|
59
|
+
properties: list[DMSProperty] = field(default_factory=list)
|
|
60
|
+
nodes: list[DMSNode] = field(default_factory=list)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@dataclass
|
|
64
|
+
class ContainerProperties:
|
|
65
|
+
properties_by_id: dict[tuple[ContainerReference, str], dict] = field(default_factory=dict)
|
|
66
|
+
enum_collections: list[DMSEnum] = field(default_factory=list)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class DMSTableWriter:
|
|
70
|
+
def __init__(self, default_space: str, default_version: str, skip_properties_in_other_spaces: bool) -> None:
|
|
71
|
+
self.default_space = default_space
|
|
72
|
+
self.default_version = default_version
|
|
73
|
+
self.skip_properties_in_other_spaces = skip_properties_in_other_spaces
|
|
74
|
+
|
|
75
|
+
## Main Entry Point ###
|
|
76
|
+
def write_tables(self, schema: RequestSchema) -> TableDMS:
|
|
77
|
+
metadata = self.write_metadata(schema.data_model)
|
|
78
|
+
container_properties = self.write_container_properties(schema.containers)
|
|
79
|
+
view_properties = self.write_view_properties(schema.views, container_properties)
|
|
80
|
+
views = self.write_views(schema.views)
|
|
81
|
+
containers = self.write_containers(schema.containers)
|
|
82
|
+
|
|
83
|
+
return TableDMS(
|
|
84
|
+
metadata=metadata,
|
|
85
|
+
properties=view_properties.properties,
|
|
86
|
+
views=views,
|
|
87
|
+
containers=containers,
|
|
88
|
+
enum=container_properties.enum_collections,
|
|
89
|
+
nodes=view_properties.nodes,
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
### Metadata Sheet ###
|
|
93
|
+
@classmethod
|
|
94
|
+
def write_metadata(cls, data_model: DataModelRequest) -> list[MetadataValue]:
|
|
95
|
+
metadata = [
|
|
96
|
+
MetadataValue(key=key, value=value)
|
|
97
|
+
for key, value in data_model.model_dump(
|
|
98
|
+
mode="json", by_alias=True, exclude_none=True, exclude={"views", "description"}
|
|
99
|
+
).items()
|
|
100
|
+
]
|
|
101
|
+
if data_model.description:
|
|
102
|
+
description, creator = cls._serialize_description(data_model.description)
|
|
103
|
+
if description:
|
|
104
|
+
metadata.append(MetadataValue(key="description", value=description))
|
|
105
|
+
if creator:
|
|
106
|
+
metadata.append(MetadataValue(key=CREATOR_KEY, value=creator))
|
|
107
|
+
return metadata
|
|
108
|
+
|
|
109
|
+
@staticmethod
|
|
110
|
+
def _serialize_description(description: str | None) -> tuple[str | None, str | None]:
|
|
111
|
+
"""DataModelRequest does not have a 'creator' field, this is a special addition that the Neat tables
|
|
112
|
+
format supports (and recommends using). If the data model was created using Neat, the suffix of the
|
|
113
|
+
description will be Creator: <creator>. This function extracts that information."""
|
|
114
|
+
if description is None:
|
|
115
|
+
return None, None
|
|
116
|
+
if CREATOR_MARKER not in description:
|
|
117
|
+
return description, None
|
|
118
|
+
|
|
119
|
+
description, creator = description.rsplit(CREATOR_MARKER, 1)
|
|
120
|
+
return description.rstrip(), creator.strip()
|
|
121
|
+
|
|
122
|
+
### Container Properties Sheet ###
|
|
123
|
+
|
|
124
|
+
def write_containers(self, containers: list[ContainerRequest]) -> list[DMSContainer]:
|
|
125
|
+
return [
|
|
126
|
+
DMSContainer(
|
|
127
|
+
container=self._create_container_entity(container),
|
|
128
|
+
name=container.name,
|
|
129
|
+
description=container.description,
|
|
130
|
+
constraint=self._create_container_constraints(container),
|
|
131
|
+
used_for=container.used_for,
|
|
132
|
+
)
|
|
133
|
+
for container in containers
|
|
134
|
+
]
|
|
135
|
+
|
|
136
|
+
def write_container_properties(self, containers: list[ContainerRequest]) -> ContainerProperties:
|
|
137
|
+
indices_by_container_property = self._write_container_indices(containers)
|
|
138
|
+
constraints_by_container_property = self._write_container_property_constraints(containers)
|
|
139
|
+
|
|
140
|
+
output = ContainerProperties()
|
|
141
|
+
for container in containers:
|
|
142
|
+
for prop_id, prop in container.properties.items():
|
|
143
|
+
container_property = self._write_container_property(
|
|
144
|
+
container.as_reference(),
|
|
145
|
+
prop_id,
|
|
146
|
+
prop,
|
|
147
|
+
indices_by_container_property,
|
|
148
|
+
constraints_by_container_property,
|
|
149
|
+
)
|
|
150
|
+
output.properties_by_id[(container.as_reference(), prop_id)] = container_property
|
|
151
|
+
if isinstance(prop.type, EnumProperty):
|
|
152
|
+
output.enum_collections.extend(
|
|
153
|
+
self._write_enum_collection(container.as_reference(), prop_id, prop.type)
|
|
154
|
+
)
|
|
155
|
+
return output
|
|
156
|
+
|
|
157
|
+
def _write_container_property(
|
|
158
|
+
self,
|
|
159
|
+
container_ref: ContainerReference,
|
|
160
|
+
prop_id: str,
|
|
161
|
+
prop: ContainerPropertyDefinition,
|
|
162
|
+
indices_by_container_property: dict[tuple[ContainerReference, str], list[ParsedEntity]],
|
|
163
|
+
constraints_by_container_property: dict[tuple[ContainerReference, str], list[ParsedEntity]],
|
|
164
|
+
) -> dict[str, Any]:
|
|
165
|
+
return dict(
|
|
166
|
+
connection=self._write_container_property_connection(prop.type),
|
|
167
|
+
value_type=self._write_container_property_value_type(prop, prop_id, container_ref),
|
|
168
|
+
min_count=0 if prop.nullable else 1,
|
|
169
|
+
max_count=self._write_container_property_max_count(prop.type),
|
|
170
|
+
immutable=prop.immutable,
|
|
171
|
+
default=json.dumps(prop.default_value) if isinstance(prop.default_value, dict) else prop.default_value,
|
|
172
|
+
auto_increment=prop.auto_increment,
|
|
173
|
+
container=self._create_container_entity(container_ref),
|
|
174
|
+
container_property=prop_id,
|
|
175
|
+
container_property_name=prop.name,
|
|
176
|
+
container_property_description=prop.description,
|
|
177
|
+
index=indices_by_container_property.get((container_ref, prop_id)),
|
|
178
|
+
constraint=constraints_by_container_property.get((container_ref, prop_id)),
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
def _write_container_property_connection(self, dtype: DataType) -> ParsedEntity | None:
|
|
182
|
+
if not isinstance(dtype, DirectNodeRelation):
|
|
183
|
+
return None
|
|
184
|
+
properties: dict[str, str] = {}
|
|
185
|
+
if dtype.container is not None:
|
|
186
|
+
properties["container"] = str(self._create_container_entity(dtype.container))
|
|
187
|
+
return ParsedEntity("", "direct", properties=properties)
|
|
188
|
+
|
|
189
|
+
def _write_container_property_value_type(
|
|
190
|
+
self, prop: ContainerPropertyDefinition, prop_id: str, container_ref: ContainerReference
|
|
191
|
+
) -> ParsedEntity:
|
|
192
|
+
if isinstance(prop.type, DirectNodeRelation):
|
|
193
|
+
# Will be overwritten if the view property has source set.
|
|
194
|
+
return ParsedEntity("", "#N/A", properties={})
|
|
195
|
+
elif isinstance(prop.type, EnumProperty):
|
|
196
|
+
enum_properties = {"collection": self._enum_collection_name(container_ref, prop_id)}
|
|
197
|
+
if prop.type.unknown_value is not None:
|
|
198
|
+
enum_properties["unknownValue"] = prop.type.unknown_value
|
|
199
|
+
return ParsedEntity("", "enum", properties=enum_properties)
|
|
200
|
+
elif isinstance(prop.type, ListablePropertyTypeDefinition):
|
|
201
|
+
# List and maxListSize are included in the maxCount of the property, so we exclude them here.
|
|
202
|
+
entity_properties = prop.type.model_dump(
|
|
203
|
+
mode="json", by_alias=True, exclude={"list", "maxListSize", "type"}, exclude_none=True
|
|
204
|
+
)
|
|
205
|
+
return ParsedEntity("", prop.type.type, properties=entity_properties)
|
|
206
|
+
else:
|
|
207
|
+
# Should not happen as all types are either ListablePropertyTypeDefinition or EnumProperty.
|
|
208
|
+
return ParsedEntity("", prop.type.type, properties={})
|
|
209
|
+
|
|
210
|
+
@staticmethod
|
|
211
|
+
def _write_container_property_max_count(dtype: DataType) -> int | None:
|
|
212
|
+
if isinstance(dtype, ListablePropertyTypeDefinition) and dtype.list:
|
|
213
|
+
if dtype.max_list_size is not None:
|
|
214
|
+
return dtype.max_list_size
|
|
215
|
+
elif isinstance(dtype, DirectNodeRelation):
|
|
216
|
+
return DEFAULT_MAX_LIST_SIZE_DIRECT_RELATIONS
|
|
217
|
+
else:
|
|
218
|
+
return DEFAULT_MAX_LIST_SIZE
|
|
219
|
+
return 1
|
|
220
|
+
|
|
221
|
+
@staticmethod
|
|
222
|
+
def _write_container_indices(
|
|
223
|
+
containers: list[ContainerRequest],
|
|
224
|
+
) -> dict[tuple[ContainerReference, str], list[ParsedEntity]]:
|
|
225
|
+
"""Writes container indices and groups them by (container_reference, property_id)."""
|
|
226
|
+
indices_by_id: dict[tuple[ContainerReference, str], list[ParsedEntity]] = defaultdict(list)
|
|
227
|
+
for container in containers:
|
|
228
|
+
if not container.indexes:
|
|
229
|
+
continue
|
|
230
|
+
for index_id, index in container.indexes.items():
|
|
231
|
+
for order, prop_id in enumerate(index.properties, 1):
|
|
232
|
+
entity_properties = index.model_dump(
|
|
233
|
+
mode="json", by_alias=True, exclude={"index_type", "properties"}, exclude_none=True
|
|
234
|
+
)
|
|
235
|
+
if len(index.properties) > 1:
|
|
236
|
+
entity_properties["order"] = str(order)
|
|
237
|
+
entity = ParsedEntity(index.index_type, index_id, properties=entity_properties)
|
|
238
|
+
indices_by_id[(container.as_reference(), prop_id)].append(entity)
|
|
239
|
+
return indices_by_id
|
|
240
|
+
|
|
241
|
+
@staticmethod
|
|
242
|
+
def _write_container_property_constraints(
|
|
243
|
+
containers: list[ContainerRequest],
|
|
244
|
+
) -> dict[tuple[ContainerReference, str], list[ParsedEntity]]:
|
|
245
|
+
"""Writes container constraints and groups them by (container_reference, property_id).
|
|
246
|
+
|
|
247
|
+
Note this only includes uniqueness constraints, the require constraints is handled
|
|
248
|
+
in the writing of the container itself.
|
|
249
|
+
"""
|
|
250
|
+
constraints_by_id: dict[tuple[ContainerReference, str], list[ParsedEntity]] = defaultdict(list)
|
|
251
|
+
for container in containers:
|
|
252
|
+
if not container.constraints:
|
|
253
|
+
continue
|
|
254
|
+
for constraint_id, constraint in container.constraints.items():
|
|
255
|
+
if not isinstance(constraint, UniquenessConstraintDefinition):
|
|
256
|
+
continue
|
|
257
|
+
for order, prop_id in enumerate(constraint.properties, 1):
|
|
258
|
+
entity_properties = constraint.model_dump(
|
|
259
|
+
mode="json", by_alias=True, exclude={"constraint_type", "properties"}, exclude_none=True
|
|
260
|
+
)
|
|
261
|
+
if len(constraint.properties) > 1:
|
|
262
|
+
entity_properties["order"] = str(order)
|
|
263
|
+
entity = ParsedEntity(constraint.constraint_type, constraint_id, properties=entity_properties)
|
|
264
|
+
constraints_by_id[(container.as_reference(), prop_id)].append(entity)
|
|
265
|
+
return constraints_by_id
|
|
266
|
+
|
|
267
|
+
def _create_container_constraints(self, container: ContainerRequest) -> list[ParsedEntity] | None:
|
|
268
|
+
if not container.constraints:
|
|
269
|
+
return None
|
|
270
|
+
output: list[ParsedEntity] = []
|
|
271
|
+
for constraint_id, constraint in container.constraints.items():
|
|
272
|
+
if not isinstance(constraint, RequiresConstraintDefinition):
|
|
273
|
+
continue
|
|
274
|
+
entity_properties = {"require": str(self._create_container_entity(constraint.require))}
|
|
275
|
+
output.append(
|
|
276
|
+
ParsedEntity(prefix=constraint.constraint_type, suffix=constraint_id, properties=entity_properties)
|
|
277
|
+
)
|
|
278
|
+
return output or None
|
|
279
|
+
|
|
280
|
+
### Enum Sheet ###
|
|
281
|
+
@staticmethod
|
|
282
|
+
def _enum_collection_name(container_ref: ContainerReference, prop_id: str) -> str:
|
|
283
|
+
return f"{container_ref.external_id}.{prop_id}"
|
|
284
|
+
|
|
285
|
+
def _write_enum_collection(
|
|
286
|
+
self, container_ref: ContainerReference, prop_id: str, enum: EnumProperty
|
|
287
|
+
) -> list[DMSEnum]:
|
|
288
|
+
output: list[DMSEnum] = []
|
|
289
|
+
name = self._enum_collection_name(container_ref, prop_id)
|
|
290
|
+
for value_id, value in enum.values.items():
|
|
291
|
+
output.append(
|
|
292
|
+
DMSEnum(
|
|
293
|
+
collection=name,
|
|
294
|
+
value=value_id,
|
|
295
|
+
name=value.name,
|
|
296
|
+
description=value.description,
|
|
297
|
+
)
|
|
298
|
+
)
|
|
299
|
+
return output
|
|
300
|
+
|
|
301
|
+
### View Sheet ###
|
|
302
|
+
def write_views(self, views: list[ViewRequest]) -> list[DMSView]:
|
|
303
|
+
return [
|
|
304
|
+
DMSView(
|
|
305
|
+
view=self._create_view_entity(view),
|
|
306
|
+
name=view.name,
|
|
307
|
+
description=view.description,
|
|
308
|
+
implements=[self._create_view_entity(parent) for parent in view.implements]
|
|
309
|
+
if view.implements
|
|
310
|
+
else None,
|
|
311
|
+
filter=self.write_view_filter(view.filter),
|
|
312
|
+
)
|
|
313
|
+
for view in views
|
|
314
|
+
]
|
|
315
|
+
|
|
316
|
+
def write_view_filter(self, filter: Filter | None) -> TableViewFilter | None:
|
|
317
|
+
if filter is None:
|
|
318
|
+
return None
|
|
319
|
+
filter_type, entities = self._get_entity_filter(filter)
|
|
320
|
+
if filter_type is not None and entities:
|
|
321
|
+
return EntityTableFilter(type=filter_type, entities=entities)
|
|
322
|
+
else:
|
|
323
|
+
return RAWFilterTableFilter(filter=FilterAdapter.dump_json(filter, by_alias=True).decode(encoding="utf-8"))
|
|
324
|
+
|
|
325
|
+
def _get_entity_filter(self, filter: Filter) -> tuple[Literal["nodeType", "hasData"] | None, list[ParsedEntity]]:
|
|
326
|
+
"""If the filter is an entity-based filter (Equals or In on nodes), return the type and entities.
|
|
327
|
+
Otherwise, return (None, [])."""
|
|
328
|
+
if filter is None or len(filter) != 1:
|
|
329
|
+
return None, []
|
|
330
|
+
filter_name, body = next(iter(filter.items()))
|
|
331
|
+
if (
|
|
332
|
+
isinstance(body, EqualsFilterData)
|
|
333
|
+
and body.property == ["node", "type"]
|
|
334
|
+
and isinstance(body.value, dict)
|
|
335
|
+
and (node_reference := self._try_get_node_reference(body.value))
|
|
336
|
+
):
|
|
337
|
+
return "nodeType", [self._create_node_entity(node_reference)]
|
|
338
|
+
elif (
|
|
339
|
+
isinstance(body, InFilterData)
|
|
340
|
+
and body.property == ["node", "type"]
|
|
341
|
+
and isinstance(body.values, list)
|
|
342
|
+
# All values must be node references
|
|
343
|
+
and len(node_references := [ref for value in body.values if (ref := self._try_get_node_reference(value))])
|
|
344
|
+
== len(body.values)
|
|
345
|
+
):
|
|
346
|
+
return "nodeType", [self._create_node_entity(node) for node in node_references]
|
|
347
|
+
elif (
|
|
348
|
+
isinstance(body, HasDataFilter)
|
|
349
|
+
and
|
|
350
|
+
# All data must be container references, a single view reference makes it a raw filter
|
|
351
|
+
len(container_refs := [item for item in body.data if isinstance(item, ContainerReference)])
|
|
352
|
+
== len(body.data)
|
|
353
|
+
):
|
|
354
|
+
return "hasData", [self._create_container_entity(item) for item in container_refs]
|
|
355
|
+
else:
|
|
356
|
+
return None, []
|
|
357
|
+
|
|
358
|
+
@staticmethod
|
|
359
|
+
def _try_get_node_reference(value: Any) -> NodeReference | None:
|
|
360
|
+
try:
|
|
361
|
+
return NodeReference.model_validate(value)
|
|
362
|
+
except ValidationError:
|
|
363
|
+
return None
|
|
364
|
+
|
|
365
|
+
def write_view_properties(self, views: list[ViewRequest], container: ContainerProperties) -> ViewProperties:
|
|
366
|
+
output = ViewProperties()
|
|
367
|
+
for view in views:
|
|
368
|
+
if not view.properties:
|
|
369
|
+
continue
|
|
370
|
+
if self.skip_properties_in_other_spaces and view.space != self.default_space:
|
|
371
|
+
continue
|
|
372
|
+
for prop_id, prop in view.properties.items():
|
|
373
|
+
output.properties.append(self._write_view_property(view, prop_id, prop, container))
|
|
374
|
+
if isinstance(prop, EdgeProperty):
|
|
375
|
+
output.nodes.append(self._write_node(prop))
|
|
376
|
+
return output
|
|
377
|
+
|
|
378
|
+
def _write_view_property(
|
|
379
|
+
self, view: ViewRequest, prop_id: str, prop: ViewRequestProperty, container: ContainerProperties
|
|
380
|
+
) -> DMSProperty:
|
|
381
|
+
container_properties: dict[str, Any] = {}
|
|
382
|
+
if isinstance(prop, ViewCorePropertyRequest):
|
|
383
|
+
identifier = (prop.container, prop.container_property_identifier)
|
|
384
|
+
if identifier in container.properties_by_id:
|
|
385
|
+
container_properties = container.properties_by_id[identifier]
|
|
386
|
+
view_properties: dict[str, Any] = dict(
|
|
387
|
+
view=self._create_view_entity(view), view_property=prop_id, name=prop.name, description=prop.description
|
|
388
|
+
)
|
|
389
|
+
if connection := self._write_view_property_connection(prop):
|
|
390
|
+
view_properties["connection"] = connection
|
|
391
|
+
if view_value_type := self._write_view_property_value_type(prop):
|
|
392
|
+
view_properties["value_type"] = view_value_type
|
|
393
|
+
view_min_count = self._write_view_property_min_count(prop)
|
|
394
|
+
if view_min_count is not None:
|
|
395
|
+
view_properties["min_count"] = view_min_count
|
|
396
|
+
view_max_count = self._write_view_property_max_count(prop)
|
|
397
|
+
if view_max_count != "container":
|
|
398
|
+
view_properties["max_count"] = view_max_count
|
|
399
|
+
|
|
400
|
+
# Overwrite container properties with view properties where relevant.
|
|
401
|
+
args = container_properties | view_properties
|
|
402
|
+
return DMSProperty(**args)
|
|
403
|
+
|
|
404
|
+
def _write_view_property_connection(self, prop: ViewRequestProperty) -> ParsedEntity | None:
|
|
405
|
+
if isinstance(prop, ViewCorePropertyRequest):
|
|
406
|
+
# Use the container definition for connection
|
|
407
|
+
return None
|
|
408
|
+
elif isinstance(prop, EdgeProperty):
|
|
409
|
+
edge_properties: dict[str, str] = {}
|
|
410
|
+
if prop.direction != "outwards":
|
|
411
|
+
edge_properties["direction"] = prop.direction
|
|
412
|
+
if prop.edge_source is not None:
|
|
413
|
+
edge_properties["edgeSource"] = str(self._create_view_entity(prop.edge_source))
|
|
414
|
+
edge_properties["type"] = str(self._create_node_entity(prop.type))
|
|
415
|
+
return ParsedEntity("", "edge", properties=edge_properties)
|
|
416
|
+
elif isinstance(prop, ReverseDirectRelationProperty):
|
|
417
|
+
return ParsedEntity("", "reverse", properties={"property": prop.through.identifier})
|
|
418
|
+
else:
|
|
419
|
+
raise ValueError(f"Unknown view property type: {type(prop)}")
|
|
420
|
+
|
|
421
|
+
def _write_view_property_value_type(self, prop: ViewRequestProperty) -> ParsedEntity | None:
|
|
422
|
+
if isinstance(prop, ViewCorePropertyRequest):
|
|
423
|
+
if prop.source:
|
|
424
|
+
return self._create_view_entity(prop.source)
|
|
425
|
+
else:
|
|
426
|
+
# Use the container definition for value type
|
|
427
|
+
return None
|
|
428
|
+
elif isinstance(prop, ReverseDirectRelationProperty | EdgeProperty):
|
|
429
|
+
return self._create_view_entity(prop.source)
|
|
430
|
+
else:
|
|
431
|
+
raise ValueError(f"Unknown view property type: {type(prop)}")
|
|
432
|
+
|
|
433
|
+
@staticmethod
|
|
434
|
+
def _write_view_property_min_count(prop: ViewRequestProperty) -> int | None:
|
|
435
|
+
if isinstance(prop, ViewCorePropertyRequest):
|
|
436
|
+
# Use the container definition for min count
|
|
437
|
+
return None
|
|
438
|
+
# Edges and reverse relations cannot be required.
|
|
439
|
+
return 0
|
|
440
|
+
|
|
441
|
+
@staticmethod
|
|
442
|
+
def _write_view_property_max_count(prop: ViewRequestProperty) -> int | None | Literal["container"]:
|
|
443
|
+
if isinstance(prop, ViewCorePropertyRequest):
|
|
444
|
+
# Use the container definition for max count
|
|
445
|
+
return "container"
|
|
446
|
+
elif isinstance(prop, SingleEdgeProperty | SingleReverseDirectRelationPropertyRequest):
|
|
447
|
+
return 1
|
|
448
|
+
elif isinstance(prop, MultiEdgeProperty | MultiReverseDirectRelationPropertyRequest):
|
|
449
|
+
return None
|
|
450
|
+
else:
|
|
451
|
+
raise ValueError(f"Unknown view property type: {type(prop)}")
|
|
452
|
+
|
|
453
|
+
### Node Sheet ###
|
|
454
|
+
|
|
455
|
+
def _write_node(self, prop: EdgeProperty) -> DMSNode:
|
|
456
|
+
return DMSNode(node=self._create_node_entity(prop.type))
|
|
457
|
+
|
|
458
|
+
## Entity Helpers ###
|
|
459
|
+
|
|
460
|
+
def _create_view_entity(self, view: ViewRequest | ViewReference) -> ParsedEntity:
|
|
461
|
+
prefix = view.space
|
|
462
|
+
properties = {"version": view.version}
|
|
463
|
+
if view.space == self.default_space:
|
|
464
|
+
prefix = ""
|
|
465
|
+
if view.version == self.default_version:
|
|
466
|
+
# Only use default version if space is also default.
|
|
467
|
+
properties = {}
|
|
468
|
+
return ParsedEntity(prefix=prefix, suffix=view.external_id, properties=properties)
|
|
469
|
+
|
|
470
|
+
def _create_container_entity(self, container: ContainerRequest | ContainerReference) -> ParsedEntity:
|
|
471
|
+
prefix = container.space
|
|
472
|
+
if container.space == self.default_space:
|
|
473
|
+
prefix = ""
|
|
474
|
+
return ParsedEntity(prefix=prefix, suffix=container.external_id, properties={})
|
|
475
|
+
|
|
476
|
+
def _create_node_entity(self, node: NodeReference) -> ParsedEntity:
|
|
477
|
+
prefix = node.space
|
|
478
|
+
if node.space == self.default_space:
|
|
479
|
+
prefix = ""
|
|
480
|
+
return ParsedEntity(prefix=prefix, suffix=node.external_id, properties={})
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import difflib
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
import yaml
|
|
6
|
+
from pydantic import ValidationError
|
|
7
|
+
|
|
8
|
+
from cognite.neat._client import NeatClient
|
|
9
|
+
from cognite.neat._data_model.importers._base import DMSImporter
|
|
10
|
+
from cognite.neat._data_model.models.dms import (
|
|
11
|
+
DataModelReference,
|
|
12
|
+
RequestSchema,
|
|
13
|
+
SpaceReference,
|
|
14
|
+
)
|
|
15
|
+
from cognite.neat._exceptions import CDFAPIException, DataModelImportException, FileReadException
|
|
16
|
+
from cognite.neat._issues import ModelSyntaxError
|
|
17
|
+
from cognite.neat._utils.http_client import FailedRequestMessage
|
|
18
|
+
from cognite.neat._utils.text import humanize_collection
|
|
19
|
+
from cognite.neat._utils.validation import ValidationContext, humanize_validation_error
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class DMSAPIImporter(DMSImporter):
|
|
23
|
+
"""Imports DMS in the API format."""
|
|
24
|
+
|
|
25
|
+
ENCODING = "utf-8"
|
|
26
|
+
|
|
27
|
+
def __init__(self, schema: RequestSchema | dict[str, Any]) -> None:
|
|
28
|
+
self._schema = schema
|
|
29
|
+
|
|
30
|
+
def to_data_model(self) -> RequestSchema:
|
|
31
|
+
if isinstance(self._schema, RequestSchema):
|
|
32
|
+
return self._schema
|
|
33
|
+
try:
|
|
34
|
+
return RequestSchema.model_validate(self._schema)
|
|
35
|
+
except ValidationError as e:
|
|
36
|
+
context = ValidationContext()
|
|
37
|
+
errors = [
|
|
38
|
+
ModelSyntaxError(message=humanize_validation_error(error, context))
|
|
39
|
+
for error in e.errors(include_input=True, include_url=False)
|
|
40
|
+
]
|
|
41
|
+
raise DataModelImportException(errors) from None
|
|
42
|
+
|
|
43
|
+
@classmethod
|
|
44
|
+
def from_cdf(cls, data_model: DataModelReference, client: NeatClient) -> "DMSAPIImporter":
|
|
45
|
+
"""Create a DMSAPIImporter from a data model in CDF."""
|
|
46
|
+
data_models = client.data_models.retrieve([data_model])
|
|
47
|
+
if not data_models:
|
|
48
|
+
available_data_models = [
|
|
49
|
+
str(model.as_reference()) for model in client.data_models.list(limit=1000, include_global=True)
|
|
50
|
+
]
|
|
51
|
+
close_matches = difflib.get_close_matches(str(data_model), available_data_models, n=1, cutoff=0.9)
|
|
52
|
+
suggestion_msg = ""
|
|
53
|
+
if close_matches:
|
|
54
|
+
suggestion_msg = f" Did you mean: {close_matches[0]!r}?"
|
|
55
|
+
raise CDFAPIException(
|
|
56
|
+
messages=[
|
|
57
|
+
FailedRequestMessage(message=f"Data model '{data_model!s}' not found in CDF.{suggestion_msg}")
|
|
58
|
+
]
|
|
59
|
+
)
|
|
60
|
+
data_model = data_models[0]
|
|
61
|
+
views = client.views.retrieve(data_model.views or [])
|
|
62
|
+
if missing_views := set(data_model.views or []) - {view.as_reference() for view in views}:
|
|
63
|
+
raise CDFAPIException(
|
|
64
|
+
messages=[
|
|
65
|
+
FailedRequestMessage(
|
|
66
|
+
message=f"Views {humanize_collection(missing_views)} not found in CDF "
|
|
67
|
+
f"for data model {data_model}."
|
|
68
|
+
)
|
|
69
|
+
]
|
|
70
|
+
)
|
|
71
|
+
container_ids = list({container for view in views for container in view.mapped_containers})
|
|
72
|
+
containers = client.containers.retrieve(container_ids)
|
|
73
|
+
if missing_containers := set(container_ids) - {container.as_reference() for container in containers}:
|
|
74
|
+
raise CDFAPIException(
|
|
75
|
+
messages=[
|
|
76
|
+
FailedRequestMessage(
|
|
77
|
+
message=f"Containers {humanize_collection(missing_containers)} not found in CDF "
|
|
78
|
+
f"for data model {data_model}."
|
|
79
|
+
)
|
|
80
|
+
]
|
|
81
|
+
)
|
|
82
|
+
node_types = [nt for view in views for nt in view.node_types]
|
|
83
|
+
space_ids = list(
|
|
84
|
+
{data_model.space}
|
|
85
|
+
| {view.space for view in views}
|
|
86
|
+
| {container.space for container in containers}
|
|
87
|
+
| {nt.space for nt in node_types}
|
|
88
|
+
)
|
|
89
|
+
spaces = client.spaces.retrieve([SpaceReference(space=space_id) for space_id in space_ids])
|
|
90
|
+
if missing_spaces := set(space_ids) - {space.space for space in spaces}:
|
|
91
|
+
raise CDFAPIException(
|
|
92
|
+
messages=[
|
|
93
|
+
FailedRequestMessage(
|
|
94
|
+
message=f"Spaces {humanize_collection(missing_spaces)} not found in CDF "
|
|
95
|
+
f"for data model {data_model}."
|
|
96
|
+
)
|
|
97
|
+
]
|
|
98
|
+
)
|
|
99
|
+
return DMSAPIImporter(
|
|
100
|
+
RequestSchema(
|
|
101
|
+
dataModel=data_model.as_request(),
|
|
102
|
+
views=[view.as_request() for view in views],
|
|
103
|
+
containers=[container.as_request() for container in containers],
|
|
104
|
+
nodeTypes=node_types,
|
|
105
|
+
spaces=[space.as_request() for space in spaces],
|
|
106
|
+
)
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
@classmethod
|
|
110
|
+
def from_yaml(cls, yaml_file: Path) -> "DMSAPIImporter":
|
|
111
|
+
"""Create a DMSTableImporter from a YAML file."""
|
|
112
|
+
source = cls._display_name(yaml_file)
|
|
113
|
+
if yaml_file.suffix.lower() in {".yaml", ".yml", ".json"}:
|
|
114
|
+
return cls(yaml.safe_load(yaml_file.read_text(encoding=cls.ENCODING)))
|
|
115
|
+
elif yaml_file.is_dir():
|
|
116
|
+
return cls(cls._read_yaml_files(yaml_file))
|
|
117
|
+
raise FileReadException(source.as_posix(), f"Unsupported file type: {source.suffix}")
|
|
118
|
+
|
|
119
|
+
@classmethod
|
|
120
|
+
def from_json(cls, json_file: Path) -> "DMSAPIImporter":
|
|
121
|
+
"""Create a DMSTableImporter from a JSON file."""
|
|
122
|
+
return cls.from_yaml(json_file)
|
|
123
|
+
|
|
124
|
+
@classmethod
|
|
125
|
+
def _display_name(cls, filepath: Path) -> Path:
|
|
126
|
+
"""Get a display-friendly version of the file path."""
|
|
127
|
+
cwd = Path.cwd()
|
|
128
|
+
source = filepath
|
|
129
|
+
if filepath.is_relative_to(cwd):
|
|
130
|
+
source = filepath.relative_to(cwd)
|
|
131
|
+
return source
|
|
132
|
+
|
|
133
|
+
@classmethod
|
|
134
|
+
def _read_yaml_files(cls, directory: Path) -> dict[str, Any]:
|
|
135
|
+
"""Read all YAML files in a directory and combine them into a single dictionary."""
|
|
136
|
+
schema_data: dict[str, Any] = {}
|
|
137
|
+
data_model: dict[str, Any] | None = None
|
|
138
|
+
for yaml_file in directory.rglob("**/*"):
|
|
139
|
+
if yaml_file.suffix.lower() not in {".yaml", ".yml", ".json"}:
|
|
140
|
+
continue
|
|
141
|
+
stem = yaml_file.stem.casefold()
|
|
142
|
+
if stem.endswith("datamodel") and data_model is not None:
|
|
143
|
+
raise FileReadException(
|
|
144
|
+
cls._display_name(directory).as_posix(),
|
|
145
|
+
"Multiple data model files found in directory.",
|
|
146
|
+
)
|
|
147
|
+
data = yaml.safe_load(yaml_file.read_text(encoding=cls.ENCODING))
|
|
148
|
+
list_data = data if isinstance(data, list) else [data]
|
|
149
|
+
if stem.endswith("datamodel"):
|
|
150
|
+
data_model = data
|
|
151
|
+
elif stem.endswith("container"):
|
|
152
|
+
schema_data.setdefault("containers", []).extend(list_data)
|
|
153
|
+
elif stem.endswith("view"):
|
|
154
|
+
schema_data.setdefault("views", []).extend(list_data)
|
|
155
|
+
elif stem.endswith("space"):
|
|
156
|
+
schema_data.setdefault("spaces", []).extend(list_data)
|
|
157
|
+
elif stem.endswith("node"):
|
|
158
|
+
schema_data.setdefault("nodeTypes", []).extend(list_data)
|
|
159
|
+
# Ignore other files
|
|
160
|
+
if data_model is None:
|
|
161
|
+
raise FileReadException(
|
|
162
|
+
cls._display_name(directory).as_posix(),
|
|
163
|
+
"No data model file found in directory.",
|
|
164
|
+
)
|
|
165
|
+
schema_data["dataModel"] = data_model
|
|
166
|
+
return schema_data
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
|
|
3
|
+
from cognite.neat._data_model.models.dms import RequestSchema
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class DMSImporter(ABC):
|
|
7
|
+
"""This is the base class for all DMS importers."""
|
|
8
|
+
|
|
9
|
+
@abstractmethod
|
|
10
|
+
def to_data_model(self) -> RequestSchema:
|
|
11
|
+
"""Convert the imported data to a RequestSchema.
|
|
12
|
+
|
|
13
|
+
Returns:
|
|
14
|
+
RequestSchema: The data model as a RequestSchema.
|
|
15
|
+
"""
|
|
16
|
+
raise NotImplementedError()
|