cognite-neat 0.70.1__py3-none-any.whl → 0.127.19__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 -1
- 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/_data_model/_analysis.py +186 -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 +399 -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 +291 -0
- cognite/neat/_data_model/importers/_table_importer/importer.py +192 -0
- cognite/neat/_data_model/importers/_table_importer/reader.py +875 -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 +47 -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 +94 -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 +57 -0
- cognite/neat/_data_model/validation/dms/_ai_readiness.py +167 -0
- cognite/neat/_data_model/validation/dms/_base.py +304 -0
- cognite/neat/_data_model/validation/dms/_connections.py +627 -0
- cognite/neat/_data_model/validation/dms/_consistency.py +56 -0
- cognite/neat/_data_model/validation/dms/_containers.py +135 -0
- cognite/neat/_data_model/validation/dms/_limits.py +414 -0
- cognite/neat/_data_model/validation/dms/_orchestrator.py +202 -0
- cognite/neat/_data_model/validation/dms/_views.py +101 -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 +149 -0
- cognite/neat/_session/_html/static/issues.css +211 -0
- cognite/neat/_session/_html/static/issues.js +167 -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 +74 -0
- cognite/neat/_session/_html/templates/issues.html +44 -0
- cognite/neat/_session/_issues.py +76 -0
- cognite/neat/_session/_opt.py +35 -0
- cognite/neat/_session/_physical.py +240 -0
- cognite/neat/_session/_result.py +231 -0
- cognite/neat/_session/_session.py +69 -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 +71 -0
- cognite/neat/_store/_store.py +144 -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 +149 -0
- cognite/neat/_version.py +2 -1
- cognite/neat/v0/core/__init__.py +0 -0
- cognite/neat/v0/core/_client/__init__.py +4 -0
- cognite/neat/v0/core/_client/_api/__init__.py +0 -0
- cognite/neat/v0/core/_client/_api/data_modeling_loaders.py +1032 -0
- cognite/neat/v0/core/_client/_api/neat_instances.py +101 -0
- cognite/neat/v0/core/_client/_api/schema.py +158 -0
- cognite/neat/v0/core/_client/_api/statistics.py +91 -0
- cognite/neat/v0/core/_client/_api_client.py +21 -0
- cognite/neat/v0/core/_client/data_classes/__init__.py +0 -0
- cognite/neat/v0/core/_client/data_classes/data_modeling.py +198 -0
- cognite/neat/v0/core/_client/data_classes/neat_sequence.py +261 -0
- cognite/neat/v0/core/_client/data_classes/schema.py +540 -0
- cognite/neat/v0/core/_client/data_classes/statistics.py +125 -0
- cognite/neat/v0/core/_client/testing.py +39 -0
- cognite/neat/v0/core/_config.py +11 -0
- cognite/neat/v0/core/_constants.py +278 -0
- cognite/neat/v0/core/_data_model/__init__.py +0 -0
- cognite/neat/v0/core/_data_model/_constants.py +210 -0
- cognite/neat/v0/core/_data_model/_shared.py +59 -0
- cognite/neat/v0/core/_data_model/analysis/__init__.py +3 -0
- cognite/neat/v0/core/_data_model/analysis/_base.py +578 -0
- cognite/neat/v0/core/_data_model/catalog/__init__.py +8 -0
- cognite/neat/v0/core/_data_model/catalog/classic_model.xlsx +0 -0
- cognite/neat/v0/core/_data_model/catalog/conceptual-imf-data-model.xlsx +0 -0
- cognite/neat/v0/core/_data_model/catalog/hello_world_pump.xlsx +0 -0
- cognite/neat/v0/core/_data_model/exporters/__init__.py +38 -0
- cognite/neat/v0/core/_data_model/exporters/_base.py +67 -0
- cognite/neat/v0/core/_data_model/exporters/_data_model2dms.py +428 -0
- cognite/neat/v0/core/_data_model/exporters/_data_model2excel.py +612 -0
- cognite/neat/v0/core/_data_model/exporters/_data_model2instance_template.py +158 -0
- cognite/neat/v0/core/_data_model/exporters/_data_model2semantic_model.py +646 -0
- cognite/neat/v0/core/_data_model/exporters/_data_model2yaml.py +84 -0
- cognite/neat/v0/core/_data_model/importers/__init__.py +49 -0
- cognite/neat/v0/core/_data_model/importers/_base.py +64 -0
- cognite/neat/v0/core/_data_model/importers/_base_file_reader.py +56 -0
- cognite/neat/v0/core/_data_model/importers/_dict2data_model.py +131 -0
- cognite/neat/v0/core/_data_model/importers/_dms2data_model.py +705 -0
- 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/v0/core/_data_model/importers/_rdf/_base.py +161 -0
- cognite/neat/v0/core/_data_model/importers/_rdf/_inference2rdata_model.py +618 -0
- cognite/neat/v0/core/_data_model/importers/_rdf/_owl2data_model.py +124 -0
- cognite/neat/v0/core/_data_model/importers/_rdf/_shared.py +255 -0
- cognite/neat/v0/core/_data_model/importers/_spreadsheet2data_model.py +424 -0
- cognite/neat/v0/core/_data_model/models/__init__.py +38 -0
- cognite/neat/v0/core/_data_model/models/_base_unverified.py +181 -0
- cognite/neat/v0/core/_data_model/models/_base_verified.py +449 -0
- cognite/neat/v0/core/_data_model/models/_import_contexts.py +82 -0
- cognite/neat/v0/core/_data_model/models/_types.py +171 -0
- cognite/neat/v0/core/_data_model/models/conceptual/__init__.py +25 -0
- cognite/neat/v0/core/_data_model/models/conceptual/_unverified.py +193 -0
- cognite/neat/v0/core/_data_model/models/conceptual/_validation.py +308 -0
- cognite/neat/v0/core/_data_model/models/conceptual/_verified.py +327 -0
- cognite/neat/v0/core/_data_model/models/data_types.py +422 -0
- cognite/neat/v0/core/_data_model/models/entities/__init__.py +70 -0
- cognite/neat/v0/core/_data_model/models/entities/_constants.py +18 -0
- cognite/neat/v0/core/_data_model/models/entities/_loaders.py +155 -0
- cognite/neat/v0/core/_data_model/models/entities/_multi_value.py +83 -0
- cognite/neat/v0/core/_data_model/models/entities/_restrictions.py +230 -0
- cognite/neat/v0/core/_data_model/models/entities/_single_value.py +676 -0
- cognite/neat/v0/core/_data_model/models/entities/_types.py +103 -0
- cognite/neat/v0/core/_data_model/models/entities/_wrapped.py +217 -0
- cognite/neat/v0/core/_data_model/models/mapping/__init__.py +3 -0
- cognite/neat/v0/core/_data_model/models/mapping/_classic2core.py +39 -0
- cognite/neat/v0/core/_data_model/models/mapping/_classic2core.yaml +608 -0
- cognite/neat/v0/core/_data_model/models/physical/__init__.py +40 -0
- cognite/neat/v0/core/_data_model/models/physical/_exporter.py +658 -0
- cognite/neat/v0/core/_data_model/models/physical/_unverified.py +557 -0
- cognite/neat/v0/core/_data_model/models/physical/_validation.py +887 -0
- cognite/neat/v0/core/_data_model/models/physical/_verified.py +667 -0
- cognite/neat/v0/core/_data_model/transformers/__init__.py +70 -0
- cognite/neat/v0/core/_data_model/transformers/_base.py +79 -0
- cognite/neat/v0/core/_data_model/transformers/_converters.py +2485 -0
- cognite/neat/v0/core/_data_model/transformers/_mapping.py +390 -0
- cognite/neat/v0/core/_data_model/transformers/_union_conceptual.py +208 -0
- cognite/neat/v0/core/_data_model/transformers/_verification.py +120 -0
- cognite/neat/v0/core/_instances/__init__.py +0 -0
- cognite/neat/v0/core/_instances/_shared.py +37 -0
- cognite/neat/v0/core/_instances/_tracking/__init__.py +4 -0
- cognite/neat/v0/core/_instances/_tracking/base.py +30 -0
- cognite/neat/v0/core/_instances/_tracking/log.py +27 -0
- cognite/neat/v0/core/_instances/extractors/__init__.py +76 -0
- cognite/neat/v0/core/_instances/extractors/_base.py +58 -0
- cognite/neat/v0/core/_instances/extractors/_classic_cdf/__init__.py +0 -0
- cognite/neat/v0/core/_instances/extractors/_classic_cdf/_assets.py +37 -0
- cognite/neat/v0/core/_instances/extractors/_classic_cdf/_base.py +443 -0
- cognite/neat/v0/core/_instances/extractors/_classic_cdf/_classic.py +479 -0
- cognite/neat/v0/core/_instances/extractors/_classic_cdf/_data_sets.py +35 -0
- cognite/neat/v0/core/_instances/extractors/_classic_cdf/_events.py +33 -0
- cognite/neat/v0/core/_instances/extractors/_classic_cdf/_files.py +45 -0
- cognite/neat/v0/core/_instances/extractors/_classic_cdf/_labels.py +54 -0
- cognite/neat/v0/core/_instances/extractors/_classic_cdf/_relationships.py +122 -0
- cognite/neat/v0/core/_instances/extractors/_classic_cdf/_sequences.py +280 -0
- cognite/neat/v0/core/_instances/extractors/_classic_cdf/_timeseries.py +48 -0
- cognite/neat/v0/core/_instances/extractors/_dict.py +105 -0
- cognite/neat/v0/core/_instances/extractors/_dms.py +315 -0
- cognite/neat/v0/core/_instances/extractors/_dms_graph.py +238 -0
- cognite/neat/v0/core/_instances/extractors/_mock_graph_generator.py +404 -0
- cognite/neat/v0/core/_instances/extractors/_raw.py +67 -0
- cognite/neat/v0/core/_instances/extractors/_rdf_file.py +80 -0
- cognite/neat/v0/core/_instances/loaders/__init__.py +24 -0
- cognite/neat/v0/core/_instances/loaders/_base.py +116 -0
- cognite/neat/v0/core/_instances/loaders/_rdf2dms.py +649 -0
- cognite/neat/v0/core/_instances/loaders/_rdf_to_instance_space.py +249 -0
- cognite/neat/v0/core/_instances/queries/__init__.py +3 -0
- cognite/neat/v0/core/_instances/queries/_base.py +16 -0
- cognite/neat/v0/core/_instances/queries/_queries.py +16 -0
- cognite/neat/v0/core/_instances/queries/_select.py +478 -0
- cognite/neat/v0/core/_instances/queries/_update.py +37 -0
- cognite/neat/v0/core/_instances/transformers/__init__.py +65 -0
- cognite/neat/v0/core/_instances/transformers/_base.py +133 -0
- cognite/neat/v0/core/_instances/transformers/_classic_cdf.py +589 -0
- cognite/neat/v0/core/_instances/transformers/_prune_graph.py +315 -0
- cognite/neat/v0/core/_instances/transformers/_rdfpath.py +84 -0
- cognite/neat/v0/core/_instances/transformers/_value_type.py +366 -0
- cognite/neat/v0/core/_issues/__init__.py +21 -0
- cognite/neat/v0/core/_issues/_base.py +348 -0
- cognite/neat/v0/core/_issues/_contextmanagers.py +50 -0
- cognite/neat/v0/core/_issues/_factory.py +69 -0
- cognite/neat/v0/core/_issues/errors/__init__.py +90 -0
- cognite/neat/v0/core/_issues/errors/_external.py +91 -0
- cognite/neat/v0/core/_issues/errors/_general.py +51 -0
- cognite/neat/v0/core/_issues/errors/_properties.py +80 -0
- cognite/neat/v0/core/_issues/errors/_resources.py +116 -0
- cognite/neat/v0/core/_issues/errors/_wrapper.py +93 -0
- cognite/neat/{rules/issues → v0/core/_issues}/formatters.py +12 -10
- cognite/neat/v0/core/_issues/warnings/__init__.py +99 -0
- cognite/neat/v0/core/_issues/warnings/_external.py +56 -0
- cognite/neat/v0/core/_issues/warnings/_general.py +46 -0
- cognite/neat/v0/core/_issues/warnings/_models.py +165 -0
- cognite/neat/v0/core/_issues/warnings/_properties.py +108 -0
- cognite/neat/v0/core/_issues/warnings/_resources.py +110 -0
- cognite/neat/v0/core/_issues/warnings/user_modeling.py +125 -0
- cognite/neat/v0/core/_shared.py +77 -0
- cognite/neat/v0/core/_store/__init__.py +4 -0
- cognite/neat/v0/core/_store/_data_model.py +487 -0
- cognite/neat/v0/core/_store/_instance.py +485 -0
- cognite/neat/v0/core/_store/_provenance.py +217 -0
- cognite/neat/v0/core/_store/exceptions.py +59 -0
- cognite/neat/v0/core/_utils/__init__.py +0 -0
- cognite/neat/v0/core/_utils/auth.py +364 -0
- cognite/neat/v0/core/_utils/auxiliary.py +169 -0
- cognite/neat/v0/core/_utils/collection_.py +66 -0
- cognite/neat/v0/core/_utils/graph_transformations_report.py +36 -0
- cognite/neat/v0/core/_utils/io_.py +11 -0
- cognite/neat/v0/core/_utils/rdf_.py +336 -0
- cognite/neat/v0/core/_utils/reader/__init__.py +3 -0
- cognite/neat/v0/core/_utils/reader/_base.py +194 -0
- cognite/neat/v0/core/_utils/spreadsheet.py +182 -0
- cognite/neat/v0/core/_utils/tarjan.py +44 -0
- cognite/neat/v0/core/_utils/text.py +277 -0
- cognite/neat/v0/core/_utils/time_.py +17 -0
- cognite/neat/v0/core/_utils/upload.py +134 -0
- cognite/neat/v0/core/_utils/xml_.py +52 -0
- 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/v0/plugins/_issues.py +35 -0
- cognite/neat/v0/plugins/_manager.py +94 -0
- cognite/neat/v0/session/__init__.py +3 -0
- cognite/neat/v0/session/_base.py +332 -0
- cognite/neat/v0/session/_collector.py +131 -0
- cognite/neat/v0/session/_diff.py +51 -0
- cognite/neat/v0/session/_drop.py +103 -0
- cognite/neat/v0/session/_experimental.py +26 -0
- cognite/neat/v0/session/_explore.py +39 -0
- cognite/neat/v0/session/_fix.py +28 -0
- cognite/neat/v0/session/_inspect.py +285 -0
- cognite/neat/v0/session/_mapping.py +71 -0
- cognite/neat/v0/session/_plugin.py +66 -0
- cognite/neat/v0/session/_prepare.py +286 -0
- cognite/neat/v0/session/_read.py +923 -0
- cognite/neat/v0/session/_set.py +101 -0
- cognite/neat/v0/session/_show.py +298 -0
- cognite/neat/v0/session/_state/README.md +23 -0
- cognite/neat/v0/session/_state.py +161 -0
- cognite/neat/v0/session/_subset.py +88 -0
- cognite/neat/v0/session/_template.py +208 -0
- cognite/neat/v0/session/_to.py +454 -0
- cognite/neat/v0/session/_wizard.py +44 -0
- cognite/neat/v0/session/engine/__init__.py +4 -0
- cognite/neat/v0/session/engine/_import.py +7 -0
- cognite/neat/v0/session/engine/_interface.py +25 -0
- cognite/neat/v0/session/engine/_load.py +131 -0
- cognite/neat/v0/session/exceptions.py +103 -0
- cognite/neat/v1.py +3 -0
- cognite_neat-0.127.19.dist-info/METADATA +145 -0
- cognite_neat-0.127.19.dist-info/RECORD +318 -0
- {cognite_neat-0.70.1.dist-info → cognite_neat-0.127.19.dist-info}/WHEEL +1 -1
- cognite/neat/app/api/asgi/metrics.py +0 -4
- cognite/neat/app/api/configuration.py +0 -110
- cognite/neat/app/api/context_manager/__init__.py +0 -3
- cognite/neat/app/api/context_manager/manager.py +0 -16
- cognite/neat/app/api/data_classes/configuration.py +0 -121
- cognite/neat/app/api/data_classes/rest.py +0 -78
- cognite/neat/app/api/explorer.py +0 -64
- cognite/neat/app/api/routers/configuration.py +0 -24
- cognite/neat/app/api/routers/core.py +0 -51
- cognite/neat/app/api/routers/crud.py +0 -112
- cognite/neat/app/api/routers/data_exploration.py +0 -334
- cognite/neat/app/api/routers/metrics.py +0 -10
- cognite/neat/app/api/routers/rules.py +0 -169
- cognite/neat/app/api/routers/workflows.py +0 -274
- cognite/neat/app/api/utils/data_mapping.py +0 -17
- cognite/neat/app/api/utils/logging.py +0 -26
- cognite/neat/app/api/utils/query_templates.py +0 -92
- cognite/neat/app/main.py +0 -17
- cognite/neat/app/monitoring/metrics.py +0 -69
- cognite/neat/app/ui/index.html +0 -1
- cognite/neat/app/ui/neat-app/.gitignore +0 -23
- cognite/neat/app/ui/neat-app/README.md +0 -70
- cognite/neat/app/ui/neat-app/build/asset-manifest.json +0 -14
- cognite/neat/app/ui/neat-app/build/favicon.ico +0 -0
- cognite/neat/app/ui/neat-app/build/index.html +0 -1
- cognite/neat/app/ui/neat-app/build/logo192.png +0 -0
- cognite/neat/app/ui/neat-app/build/manifest.json +0 -25
- cognite/neat/app/ui/neat-app/build/robots.txt +0 -3
- cognite/neat/app/ui/neat-app/build/static/css/main.38a62222.css +0 -2
- cognite/neat/app/ui/neat-app/build/static/css/main.38a62222.css.map +0 -1
- cognite/neat/app/ui/neat-app/build/static/js/main.ed960141.js +0 -3
- cognite/neat/app/ui/neat-app/build/static/js/main.ed960141.js.LICENSE.txt +0 -97
- cognite/neat/app/ui/neat-app/build/static/js/main.ed960141.js.map +0 -1
- cognite/neat/app/ui/neat-app/build/static/media/logo.8093b84df9ed36a174c629d6fe0b730d.svg +0 -1
- cognite/neat/config.py +0 -46
- cognite/neat/constants.py +0 -36
- cognite/neat/exceptions.py +0 -139
- cognite/neat/graph/__init__.py +0 -3
- cognite/neat/graph/exceptions.py +0 -91
- cognite/neat/graph/extractor/__init__.py +0 -6
- cognite/neat/graph/extractor/_base.py +0 -14
- cognite/neat/graph/extractor/_dexpi.py +0 -306
- cognite/neat/graph/extractor/_graph_capturing_sheet.py +0 -401
- cognite/neat/graph/extractor/_mock_graph_generator.py +0 -359
- cognite/neat/graph/extractors/_base.py +0 -14
- cognite/neat/graph/extractors/_mock_graph_generator.py +0 -359
- cognite/neat/graph/loader/__init__.py +0 -23
- cognite/neat/graph/loader/_asset_loader.py +0 -516
- cognite/neat/graph/loader/_base.py +0 -69
- cognite/neat/graph/loader/_exceptions.py +0 -87
- cognite/neat/graph/loader/core/labels.py +0 -58
- cognite/neat/graph/loader/core/models.py +0 -136
- cognite/neat/graph/loader/core/rdf_to_assets.py +0 -1047
- cognite/neat/graph/loader/core/rdf_to_relationships.py +0 -558
- cognite/neat/graph/loader/rdf_to_dms.py +0 -309
- cognite/neat/graph/loader/validator.py +0 -87
- cognite/neat/graph/models.py +0 -6
- cognite/neat/graph/stores/__init__.py +0 -13
- cognite/neat/graph/stores/_base.py +0 -384
- cognite/neat/graph/stores/_graphdb_store.py +0 -51
- cognite/neat/graph/stores/_memory_store.py +0 -43
- cognite/neat/graph/stores/_oxigraph_store.py +0 -145
- cognite/neat/graph/stores/_oxrdflib.py +0 -247
- cognite/neat/graph/stores/_rdf_to_graph.py +0 -40
- cognite/neat/graph/transformation/entity_matcher.py +0 -101
- cognite/neat/graph/transformation/query_generator/__init__.py +0 -3
- cognite/neat/graph/transformation/query_generator/sparql.py +0 -540
- cognite/neat/graph/transformation/transformer.py +0 -316
- cognite/neat/rules/_analysis/_base.py +0 -25
- cognite/neat/rules/_analysis/_information_rules.py +0 -414
- cognite/neat/rules/_shared.py +0 -5
- cognite/neat/rules/analysis.py +0 -231
- cognite/neat/rules/examples/Rules-Nordic44-to-TNT.xlsx +0 -0
- cognite/neat/rules/examples/Rules-Nordic44-to-graphql.xlsx +0 -0
- cognite/neat/rules/examples/__init__.py +0 -18
- cognite/neat/rules/examples/power-grid-containers.yaml +0 -113
- cognite/neat/rules/examples/power-grid-example.xlsx +0 -0
- cognite/neat/rules/examples/power-grid-model.yaml +0 -213
- cognite/neat/rules/examples/rules-template.xlsx +0 -0
- cognite/neat/rules/examples/sheet2cdf-transformation-rules.xlsx +0 -0
- cognite/neat/rules/examples/skos-rules.xlsx +0 -0
- cognite/neat/rules/examples/source-to-solution-mapping-rules.xlsx +0 -0
- cognite/neat/rules/examples/wind-energy.owl +0 -1511
- cognite/neat/rules/exceptions.py +0 -2972
- cognite/neat/rules/exporter/__init__.py +0 -20
- cognite/neat/rules/exporter/_base.py +0 -45
- cognite/neat/rules/exporter/_core/__init__.py +0 -5
- cognite/neat/rules/exporter/_core/rules2labels.py +0 -24
- cognite/neat/rules/exporter/_rules2dms.py +0 -888
- cognite/neat/rules/exporter/_rules2excel.py +0 -212
- cognite/neat/rules/exporter/_rules2graphql.py +0 -183
- cognite/neat/rules/exporter/_rules2ontology.py +0 -523
- cognite/neat/rules/exporter/_rules2pydantic_models.py +0 -748
- cognite/neat/rules/exporter/_rules2rules.py +0 -104
- cognite/neat/rules/exporter/_rules2triples.py +0 -37
- cognite/neat/rules/exporter/_validation.py +0 -150
- cognite/neat/rules/exporters/__init__.py +0 -15
- cognite/neat/rules/exporters/_base.py +0 -42
- cognite/neat/rules/exporters/_models.py +0 -58
- cognite/neat/rules/exporters/_rules2dms.py +0 -259
- cognite/neat/rules/exporters/_rules2excel.py +0 -196
- cognite/neat/rules/exporters/_rules2ontology.py +0 -561
- cognite/neat/rules/exporters/_rules2yaml.py +0 -78
- cognite/neat/rules/exporters/_validation.py +0 -107
- cognite/neat/rules/importer/__init__.py +0 -22
- cognite/neat/rules/importer/_base.py +0 -70
- cognite/neat/rules/importer/_dict2rules.py +0 -158
- cognite/neat/rules/importer/_dms2rules.py +0 -196
- cognite/neat/rules/importer/_graph2rules.py +0 -304
- cognite/neat/rules/importer/_json2rules.py +0 -39
- cognite/neat/rules/importer/_owl2rules/__init__.py +0 -3
- cognite/neat/rules/importer/_owl2rules/_owl2classes.py +0 -239
- cognite/neat/rules/importer/_owl2rules/_owl2metadata.py +0 -255
- cognite/neat/rules/importer/_owl2rules/_owl2properties.py +0 -217
- cognite/neat/rules/importer/_owl2rules/_owl2rules.py +0 -290
- cognite/neat/rules/importer/_spreadsheet2rules.py +0 -45
- cognite/neat/rules/importer/_xsd2rules.py +0 -20
- cognite/neat/rules/importer/_yaml2rules.py +0 -39
- cognite/neat/rules/importers/__init__.py +0 -16
- cognite/neat/rules/importers/_base.py +0 -124
- cognite/neat/rules/importers/_dms2rules.py +0 -191
- cognite/neat/rules/importers/_dtdl2rules/__init__.py +0 -3
- cognite/neat/rules/importers/_dtdl2rules/_unit_lookup.py +0 -224
- cognite/neat/rules/importers/_dtdl2rules/dtdl_converter.py +0 -318
- cognite/neat/rules/importers/_dtdl2rules/dtdl_importer.py +0 -164
- cognite/neat/rules/importers/_dtdl2rules/spec.py +0 -359
- cognite/neat/rules/importers/_owl2rules/__init__.py +0 -3
- cognite/neat/rules/importers/_owl2rules/_owl2classes.py +0 -219
- cognite/neat/rules/importers/_owl2rules/_owl2metadata.py +0 -217
- cognite/neat/rules/importers/_owl2rules/_owl2properties.py +0 -203
- cognite/neat/rules/importers/_owl2rules/_owl2rules.py +0 -148
- cognite/neat/rules/importers/_spreadsheet2rules.py +0 -266
- cognite/neat/rules/importers/_yaml2rules.py +0 -110
- cognite/neat/rules/issues/__init__.py +0 -27
- cognite/neat/rules/issues/base.py +0 -190
- cognite/neat/rules/issues/dms.py +0 -331
- cognite/neat/rules/issues/fileread.py +0 -156
- cognite/neat/rules/issues/importing.py +0 -220
- cognite/neat/rules/issues/spreadsheet.py +0 -381
- cognite/neat/rules/issues/spreadsheet_file.py +0 -151
- cognite/neat/rules/models/__init__.py +0 -5
- cognite/neat/rules/models/_base.py +0 -151
- cognite/neat/rules/models/_rules/__init__.py +0 -14
- cognite/neat/rules/models/_rules/_types/__init__.py +0 -66
- cognite/neat/rules/models/_rules/_types/_base.py +0 -450
- cognite/neat/rules/models/_rules/_types/_field.py +0 -339
- cognite/neat/rules/models/_rules/_types/_value.py +0 -156
- cognite/neat/rules/models/_rules/base.py +0 -320
- cognite/neat/rules/models/_rules/dms_architect_rules.py +0 -1034
- cognite/neat/rules/models/_rules/dms_schema.py +0 -641
- cognite/neat/rules/models/_rules/domain_rules.py +0 -55
- cognite/neat/rules/models/_rules/information_rules.py +0 -525
- cognite/neat/rules/models/raw_rules.py +0 -304
- cognite/neat/rules/models/rdfpath.py +0 -238
- cognite/neat/rules/models/rules.py +0 -1269
- cognite/neat/rules/models/tables.py +0 -9
- cognite/neat/rules/models/value_types.py +0 -117
- cognite/neat/utils/__init__.py +0 -3
- cognite/neat/utils/auxiliary.py +0 -11
- cognite/neat/utils/cdf.py +0 -24
- cognite/neat/utils/cdf_loaders/__init__.py +0 -25
- cognite/neat/utils/cdf_loaders/_base.py +0 -62
- cognite/neat/utils/cdf_loaders/_data_modeling.py +0 -275
- cognite/neat/utils/cdf_loaders/_ingestion.py +0 -152
- cognite/neat/utils/cdf_loaders/data_classes.py +0 -121
- cognite/neat/utils/exceptions.py +0 -41
- cognite/neat/utils/spreadsheet.py +0 -84
- cognite/neat/utils/text.py +0 -104
- cognite/neat/utils/utils.py +0 -354
- cognite/neat/utils/xml.py +0 -37
- cognite/neat/workflows/__init__.py +0 -12
- cognite/neat/workflows/_exceptions.py +0 -41
- cognite/neat/workflows/base.py +0 -574
- cognite/neat/workflows/cdf_store.py +0 -393
- cognite/neat/workflows/examples/Export DMS/workflow.yaml +0 -89
- cognite/neat/workflows/examples/Export Rules to Ontology/workflow.yaml +0 -152
- cognite/neat/workflows/examples/Extract DEXPI Graph and Export Rules/workflow.yaml +0 -139
- cognite/neat/workflows/examples/Extract RDF Graph and Generate Assets/workflow.yaml +0 -270
- cognite/neat/workflows/examples/Import DMS/workflow.yaml +0 -65
- cognite/neat/workflows/examples/Ontology to Data Model/workflow.yaml +0 -116
- cognite/neat/workflows/examples/Validate Rules/workflow.yaml +0 -67
- cognite/neat/workflows/examples/Validate Solution Model/workflow.yaml +0 -64
- cognite/neat/workflows/examples/Visualize Data Model Using Mock Graph/workflow.yaml +0 -95
- cognite/neat/workflows/examples/Visualize Semantic Data Model/workflow.yaml +0 -111
- cognite/neat/workflows/manager.py +0 -309
- cognite/neat/workflows/migration/steps.py +0 -93
- cognite/neat/workflows/migration/wf_manifests.py +0 -33
- cognite/neat/workflows/model.py +0 -202
- cognite/neat/workflows/steps/data_contracts.py +0 -140
- cognite/neat/workflows/steps/lib/__init__.py +0 -7
- cognite/neat/workflows/steps/lib/graph_extractor.py +0 -123
- cognite/neat/workflows/steps/lib/graph_loader.py +0 -68
- cognite/neat/workflows/steps/lib/graph_store.py +0 -139
- cognite/neat/workflows/steps/lib/io_steps.py +0 -393
- cognite/neat/workflows/steps/lib/rules_exporter.py +0 -453
- cognite/neat/workflows/steps/lib/rules_importer.py +0 -171
- cognite/neat/workflows/steps/lib/rules_validator.py +0 -102
- cognite/neat/workflows/steps/lib/v1/__init__.py +0 -7
- cognite/neat/workflows/steps/lib/v1/graph_contextualization.py +0 -82
- cognite/neat/workflows/steps/lib/v1/graph_extractor.py +0 -644
- cognite/neat/workflows/steps/lib/v1/graph_loader.py +0 -606
- cognite/neat/workflows/steps/lib/v1/graph_store.py +0 -278
- cognite/neat/workflows/steps/lib/v1/graph_transformer.py +0 -58
- cognite/neat/workflows/steps/lib/v1/rules_exporter.py +0 -513
- cognite/neat/workflows/steps/lib/v1/rules_importer.py +0 -612
- cognite/neat/workflows/steps/step_model.py +0 -83
- cognite/neat/workflows/steps_registry.py +0 -212
- cognite/neat/workflows/tasks.py +0 -18
- cognite/neat/workflows/triggers.py +0 -169
- cognite/neat/workflows/utils.py +0 -19
- cognite_neat-0.70.1.dist-info/METADATA +0 -212
- cognite_neat-0.70.1.dist-info/RECORD +0 -234
- cognite_neat-0.70.1.dist-info/entry_points.txt +0 -3
- /cognite/neat/{app/api → _data_model}/__init__.py +0 -0
- /cognite/neat/{app/api/data_classes → _data_model/deployer}/__init__.py +0 -0
- /cognite/neat/{app/api/utils → _data_model/exporters/_table_exporter}/__init__.py +0 -0
- /cognite/neat/{app/monitoring → _data_model/importers/_table_importer}/__init__.py +0 -0
- /cognite/neat/{graph/extractors → _data_model/models}/__init__.py +0 -0
- /cognite/neat/{graph/loader/core → _data_model/models/conceptual}/__init__.py +0 -0
- /cognite/neat/{graph/transformation → _data_model/validation}/__init__.py +0 -0
- /cognite/neat/{rules → _session/_html}/__init__.py +0 -0
- /cognite/neat/{rules/_analysis → _session/_usage_analytics}/__init__.py +0 -0
- /cognite/neat/{workflows/migration → _utils}/__init__.py +0 -0
- /cognite/neat/{workflows/steps → v0}/__init__.py +0 -0
- /cognite/neat/{graph → v0/core/_instances}/examples/Knowledge-Graph-Nordic44-dirty.xml +0 -0
- /cognite/neat/{graph → v0/core/_instances}/examples/Knowledge-Graph-Nordic44.xml +0 -0
- /cognite/neat/{graph → v0/core/_instances}/examples/__init__.py +0 -0
- /cognite/neat/{graph → v0/core/_instances}/examples/skos-capturing-sheet-wind-topics.xlsx +0 -0
- {cognite_neat-0.70.1.dist-info → cognite_neat-0.127.19.dist-info/licenses}/LICENSE +0 -0
|
@@ -0,0 +1,887 @@
|
|
|
1
|
+
import warnings
|
|
2
|
+
from collections import Counter, defaultdict
|
|
3
|
+
from collections.abc import Mapping
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from functools import lru_cache
|
|
6
|
+
from typing import cast
|
|
7
|
+
|
|
8
|
+
from cognite.client import data_modeling as dm
|
|
9
|
+
from cognite.client.data_classes.data_modeling import ContainerList, ViewId, ViewList
|
|
10
|
+
from cognite.client.data_classes.data_modeling.views import (
|
|
11
|
+
ReverseDirectRelation,
|
|
12
|
+
ReverseDirectRelationApply,
|
|
13
|
+
ViewProperty,
|
|
14
|
+
ViewPropertyApply,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
from cognite.neat.v0.core._client import NeatClient
|
|
18
|
+
from cognite.neat.v0.core._client.data_classes.data_modeling import ViewApplyDict
|
|
19
|
+
from cognite.neat.v0.core._client.data_classes.schema import DMSSchema
|
|
20
|
+
from cognite.neat.v0.core._constants import (
|
|
21
|
+
COGNITE_MODELS,
|
|
22
|
+
COGNITE_SPACES,
|
|
23
|
+
DMS_CONTAINER_PROPERTY_SIZE_LIMIT,
|
|
24
|
+
DMS_VIEW_CONTAINER_SIZE_LIMIT,
|
|
25
|
+
get_base_concepts,
|
|
26
|
+
)
|
|
27
|
+
from cognite.neat.v0.core._data_model.models._import_contexts import ImportContext, SpreadsheetContext
|
|
28
|
+
from cognite.neat.v0.core._data_model.models.data_types import DataType
|
|
29
|
+
from cognite.neat.v0.core._data_model.models.entities import ContainerEntity, RawFilter
|
|
30
|
+
from cognite.neat.v0.core._data_model.models.entities._single_value import (
|
|
31
|
+
ContainerIndexEntity,
|
|
32
|
+
ViewEntity,
|
|
33
|
+
)
|
|
34
|
+
from cognite.neat.v0.core._issues import IssueList, NeatError
|
|
35
|
+
from cognite.neat.v0.core._issues.errors import (
|
|
36
|
+
CDFMissingClientError,
|
|
37
|
+
PropertyDefinitionDuplicatedError,
|
|
38
|
+
PropertyInvalidDefinitionError,
|
|
39
|
+
PropertyMappingDuplicatedError,
|
|
40
|
+
PropertyNotFoundError,
|
|
41
|
+
ResourceDuplicatedError,
|
|
42
|
+
ResourceNotFoundError,
|
|
43
|
+
ReversedConnectionNotFeasibleError,
|
|
44
|
+
)
|
|
45
|
+
from cognite.neat.v0.core._issues.errors._external import CDFMissingResourcesError
|
|
46
|
+
from cognite.neat.v0.core._issues.warnings import (
|
|
47
|
+
NotSupportedHasDataFilterLimitWarning,
|
|
48
|
+
NotSupportedViewContainerLimitWarning,
|
|
49
|
+
ReversedConnectionNotFeasibleWarning,
|
|
50
|
+
UndefinedViewWarning,
|
|
51
|
+
user_modeling,
|
|
52
|
+
)
|
|
53
|
+
from cognite.neat.v0.core._issues.warnings._models import ViewWithoutPropertiesWarning
|
|
54
|
+
from cognite.neat.v0.core._issues.warnings.user_modeling import (
|
|
55
|
+
ContainerPropertyLimitWarning,
|
|
56
|
+
DirectRelationMissingSourceWarning,
|
|
57
|
+
NotNeatSupportedFilterWarning,
|
|
58
|
+
)
|
|
59
|
+
from cognite.neat.v0.core._utils.text import humanize_collection
|
|
60
|
+
|
|
61
|
+
from ._verified import PhysicalDataModel, PhysicalProperty
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@dataclass
|
|
65
|
+
class _ContainerPropertyIndex:
|
|
66
|
+
"""This is a helper class used in the indices validation
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
location: The index of the property in the properties list.
|
|
70
|
+
property_: The physical property associated with the container.
|
|
71
|
+
index: The index entity that defines the container property.
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
location: int
|
|
75
|
+
property_: PhysicalProperty
|
|
76
|
+
index: ContainerIndexEntity
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class PhysicalValidation:
|
|
80
|
+
"""This class does all the validation of the physical data model that
|
|
81
|
+
have dependencies between components."""
|
|
82
|
+
|
|
83
|
+
def __init__(
|
|
84
|
+
self,
|
|
85
|
+
data_model: PhysicalDataModel,
|
|
86
|
+
client: NeatClient | None = None,
|
|
87
|
+
context: ImportContext | None = None,
|
|
88
|
+
) -> None:
|
|
89
|
+
# import here to avoid circular import issues
|
|
90
|
+
from cognite.neat.v0.core._data_model.analysis._base import DataModelAnalysis
|
|
91
|
+
|
|
92
|
+
self._data_model = data_model
|
|
93
|
+
self._client = client
|
|
94
|
+
self._metadata = data_model.metadata
|
|
95
|
+
self._properties = data_model.properties
|
|
96
|
+
self._containers = data_model.containers
|
|
97
|
+
self._views = data_model.views
|
|
98
|
+
self._read_info_by_spreadsheet = context if isinstance(context, SpreadsheetContext) else SpreadsheetContext()
|
|
99
|
+
|
|
100
|
+
self.analysis = DataModelAnalysis(physical=self._data_model)
|
|
101
|
+
self._cdf_concepts = {
|
|
102
|
+
ViewEntity.load(concept_as_string) for concept_as_string in get_base_concepts(base_model="CogniteCore")
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
def imported_views_and_containers_ids(
|
|
106
|
+
self, include_views_with_no_properties: bool = True
|
|
107
|
+
) -> tuple[set[ViewEntity], set[ContainerEntity]]:
|
|
108
|
+
existing_views = {view.view for view in self._views}
|
|
109
|
+
imported_views: set[ViewEntity] = set()
|
|
110
|
+
for view in self._views:
|
|
111
|
+
for parent in view.implements or []:
|
|
112
|
+
if parent not in existing_views:
|
|
113
|
+
imported_views.add(parent)
|
|
114
|
+
existing_containers = {container.container for container in self._containers or []}
|
|
115
|
+
imported_containers: set[ContainerEntity] = set()
|
|
116
|
+
view_with_properties: set[ViewEntity] = set()
|
|
117
|
+
for prop in self._properties:
|
|
118
|
+
if prop.container and prop.container not in existing_containers:
|
|
119
|
+
imported_containers.add(prop.container)
|
|
120
|
+
if prop.view not in existing_views:
|
|
121
|
+
imported_views.add(prop.view)
|
|
122
|
+
view_with_properties.add(prop.view)
|
|
123
|
+
|
|
124
|
+
for container in self._containers or []:
|
|
125
|
+
for constraint in container.constraint or []:
|
|
126
|
+
if constraint.require not in existing_containers:
|
|
127
|
+
imported_containers.add(cast(ContainerEntity, constraint.require))
|
|
128
|
+
|
|
129
|
+
if include_views_with_no_properties:
|
|
130
|
+
extra_views = existing_views - view_with_properties
|
|
131
|
+
imported_views.update({view for view in extra_views})
|
|
132
|
+
|
|
133
|
+
return imported_views, imported_containers
|
|
134
|
+
|
|
135
|
+
def validate(self) -> IssueList:
|
|
136
|
+
imported_views, imported_containers = self.imported_views_and_containers_ids(
|
|
137
|
+
include_views_with_no_properties=False
|
|
138
|
+
)
|
|
139
|
+
if (imported_views or imported_containers) and self._client is None:
|
|
140
|
+
raise CDFMissingClientError(
|
|
141
|
+
f"{self._data_model.metadata.as_data_model_id()} has imported views and/or container: "
|
|
142
|
+
f"{imported_views}, {imported_containers}."
|
|
143
|
+
)
|
|
144
|
+
referenced_views = ViewList([])
|
|
145
|
+
referenced_containers = ContainerList([])
|
|
146
|
+
if self._client:
|
|
147
|
+
referenced_views = self._client.loaders.views.retrieve(
|
|
148
|
+
list(imported_views), include_connected=True, include_ancestor=True
|
|
149
|
+
)
|
|
150
|
+
referenced_containers = self._client.loaders.containers.retrieve(
|
|
151
|
+
list(imported_containers), include_connected=True
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
missing_views = {view.as_id() for view in imported_views} - {view.as_id() for view in referenced_views}
|
|
155
|
+
missing_containers = {container.as_id() for container in imported_containers} - {
|
|
156
|
+
container.as_id() for container in referenced_containers
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if missing_views or missing_containers:
|
|
160
|
+
raise CDFMissingResourcesError(containers=tuple(missing_containers), views=tuple(missing_views))
|
|
161
|
+
|
|
162
|
+
# Setup data structures for validation
|
|
163
|
+
dms_schema = self._data_model.as_schema()
|
|
164
|
+
ref_view_by_id = {view.as_id(): view for view in referenced_views}
|
|
165
|
+
ref_container_by_id = {container.as_id(): container for container in referenced_containers}
|
|
166
|
+
# All containers and views are the Containers/Views in the Physical DM + the referenced ones
|
|
167
|
+
all_containers_by_id: dict[dm.ContainerId, dm.ContainerApply | dm.Container] = {
|
|
168
|
+
**dict(dms_schema.containers.items()),
|
|
169
|
+
**ref_container_by_id,
|
|
170
|
+
}
|
|
171
|
+
all_views_by_id: dict[dm.ViewId, dm.ViewApply | dm.View] = {**dict(dms_schema.views.items()), **ref_view_by_id}
|
|
172
|
+
properties_by_ids = self._as_properties_by_ids(dms_schema.views, ref_view_by_id)
|
|
173
|
+
ref_properties_by_ids = self._as_properties_by_ids(ref_view_by_id, {})
|
|
174
|
+
all_properties_by_ids = {**ref_properties_by_ids, **properties_by_ids}
|
|
175
|
+
view_properties_by_id = self._as_view_properties_by_id(properties_by_ids)
|
|
176
|
+
parents_view_ids_by_child_id = self._parent_view_ids_by_child_id(all_views_by_id)
|
|
177
|
+
container_properties_by_id = self._create_container_properties_by_id()
|
|
178
|
+
|
|
179
|
+
issue_list = IssueList()
|
|
180
|
+
|
|
181
|
+
# Validated for duplicated resource
|
|
182
|
+
issue_list.extend(self._duplicated_resources())
|
|
183
|
+
|
|
184
|
+
# Validate if views are defined (i.e. have at least one property defined, or inherited)
|
|
185
|
+
issue_list.extend(self._views_without_properties_exist())
|
|
186
|
+
|
|
187
|
+
# Neat DMS classes Validation
|
|
188
|
+
# These are errors that can only happen due to the format of the Neat DMS classes
|
|
189
|
+
issue_list.extend(self._validate_raw_filter())
|
|
190
|
+
issue_list.extend(self._consistent_container_properties(container_properties_by_id))
|
|
191
|
+
issue_list.extend(self._valid_composite_container_indices(container_properties_by_id))
|
|
192
|
+
issue_list.extend(self._validate_value_type_existence())
|
|
193
|
+
issue_list.extend(
|
|
194
|
+
self._validate_property_referenced_views_and_containers_exists(all_views_by_id, all_containers_by_id)
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
# SDK classes validation
|
|
198
|
+
issue_list.extend(self._containers_are_proper_size(dms_schema))
|
|
199
|
+
issue_list.extend(
|
|
200
|
+
self._validate_reverse_connections(
|
|
201
|
+
properties_by_ids, all_containers_by_id, parents_view_ids_by_child_id, all_properties_by_ids
|
|
202
|
+
)
|
|
203
|
+
)
|
|
204
|
+
issue_list.extend(self._validate_schema(dms_schema, all_views_by_id, all_containers_by_id))
|
|
205
|
+
issue_list.extend(self._validate_referenced_container_limits(dms_schema.views, view_properties_by_id))
|
|
206
|
+
issue_list.extend(self._same_space_views_and_data_model())
|
|
207
|
+
return issue_list
|
|
208
|
+
|
|
209
|
+
def _views_without_properties_exist(self) -> IssueList:
|
|
210
|
+
"""Check if there are views that do not have any properties defined directly or inherited."""
|
|
211
|
+
issue_list = IssueList()
|
|
212
|
+
views = {view.view for view in self._views}
|
|
213
|
+
ancestors_by_view = self.analysis.implements_by_view(include_ancestors=True, include_different_space=True)
|
|
214
|
+
views_with_properties = self.analysis.defined_views().union(self._cdf_concepts)
|
|
215
|
+
|
|
216
|
+
if candidate_views := views.difference(views_with_properties):
|
|
217
|
+
for view in candidate_views:
|
|
218
|
+
# Here we check if at least one of the ancestors of the view has properties
|
|
219
|
+
if (ancestors := ancestors_by_view.get(view)) and ancestors.intersection(views_with_properties):
|
|
220
|
+
continue
|
|
221
|
+
|
|
222
|
+
issue_list.append_if_not_exist(ViewWithoutPropertiesWarning(view_id=view.as_id()))
|
|
223
|
+
|
|
224
|
+
return issue_list
|
|
225
|
+
|
|
226
|
+
def _same_space_views_and_data_model(self) -> IssueList:
|
|
227
|
+
issue_list = IssueList()
|
|
228
|
+
|
|
229
|
+
schema = self._data_model.as_schema(remove_cdf_spaces=True)
|
|
230
|
+
|
|
231
|
+
if schema.data_model and schema.views:
|
|
232
|
+
data_model_space = schema.data_model.space
|
|
233
|
+
views_spaces = {view.space for view in schema.views.values()}
|
|
234
|
+
|
|
235
|
+
if data_model_space not in views_spaces:
|
|
236
|
+
issue_list.append(
|
|
237
|
+
user_modeling.ViewsAndDataModelNotInSameSpaceWarning(
|
|
238
|
+
data_model_space=data_model_space,
|
|
239
|
+
views_spaces=humanize_collection(views_spaces),
|
|
240
|
+
)
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
return issue_list
|
|
244
|
+
|
|
245
|
+
def _duplicated_resources(self) -> IssueList:
|
|
246
|
+
issue_list = IssueList()
|
|
247
|
+
|
|
248
|
+
properties_sheet = self._read_info_by_spreadsheet.get("Properties")
|
|
249
|
+
views_sheet = self._read_info_by_spreadsheet.get("Views")
|
|
250
|
+
containers_sheet = self._read_info_by_spreadsheet.get("Containers")
|
|
251
|
+
|
|
252
|
+
visited = defaultdict(list)
|
|
253
|
+
for row_no, property_ in enumerate(self._properties):
|
|
254
|
+
visited[property_._identifier()].append(
|
|
255
|
+
properties_sheet.adjusted_row_number(row_no) if properties_sheet else row_no + 1
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
for identifier, rows in visited.items():
|
|
259
|
+
if len(rows) == 1:
|
|
260
|
+
continue
|
|
261
|
+
issue_list.append(
|
|
262
|
+
ResourceDuplicatedError(
|
|
263
|
+
identifier[1],
|
|
264
|
+
"property",
|
|
265
|
+
(
|
|
266
|
+
f"the Properties sheet at row {humanize_collection(rows)} "
|
|
267
|
+
"if data model is read from a spreadsheet."
|
|
268
|
+
),
|
|
269
|
+
)
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
visited = defaultdict(list)
|
|
273
|
+
for row_no, view in enumerate(self._views):
|
|
274
|
+
visited[view._identifier()].append(views_sheet.adjusted_row_number(row_no) if views_sheet else row_no + 1)
|
|
275
|
+
|
|
276
|
+
for identifier, rows in visited.items():
|
|
277
|
+
if len(rows) == 1:
|
|
278
|
+
continue
|
|
279
|
+
issue_list.append(
|
|
280
|
+
ResourceDuplicatedError(
|
|
281
|
+
identifier[0],
|
|
282
|
+
"view",
|
|
283
|
+
(f"the Views sheet at row {humanize_collection(rows)} if data model is read from a spreadsheet."),
|
|
284
|
+
)
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
if self._containers:
|
|
288
|
+
visited = defaultdict(list)
|
|
289
|
+
for row_no, container in enumerate(self._containers):
|
|
290
|
+
visited[container._identifier()].append(
|
|
291
|
+
containers_sheet.adjusted_row_number(row_no) if containers_sheet else row_no + 1
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
for identifier, rows in visited.items():
|
|
295
|
+
if len(rows) == 1:
|
|
296
|
+
continue
|
|
297
|
+
issue_list.append(
|
|
298
|
+
ResourceDuplicatedError(
|
|
299
|
+
identifier[0],
|
|
300
|
+
"container",
|
|
301
|
+
(
|
|
302
|
+
f"the Containers sheet at row {humanize_collection(rows)} "
|
|
303
|
+
"if data model is read from a spreadsheet."
|
|
304
|
+
),
|
|
305
|
+
)
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
return issue_list
|
|
309
|
+
|
|
310
|
+
@staticmethod
|
|
311
|
+
def _as_properties_by_ids(
|
|
312
|
+
view_by_id: Mapping[dm.ViewId, dm.ViewApply] | Mapping[dm.ViewId, dm.View],
|
|
313
|
+
ref_view_by_id: dict[dm.ViewId, dm.View],
|
|
314
|
+
) -> dict[tuple[ViewId, str], ViewPropertyApply | ViewProperty]:
|
|
315
|
+
# Priority DMS schema properties.
|
|
316
|
+
# No need to do long lookups in ref_views as these already contain all ancestor properties.
|
|
317
|
+
properties_by_id: dict[tuple[ViewId, str], ViewPropertyApply | ViewProperty] = {}
|
|
318
|
+
for view_id, view in view_by_id.items():
|
|
319
|
+
for prop_id, prop in (view.properties or {}).items():
|
|
320
|
+
properties_by_id[(view_id, prop_id)] = prop
|
|
321
|
+
if view.implements:
|
|
322
|
+
to_check = view.implements.copy()
|
|
323
|
+
while to_check:
|
|
324
|
+
parent_id = to_check.pop()
|
|
325
|
+
if parent_id in view_by_id:
|
|
326
|
+
# Priority of the DMS schema properties
|
|
327
|
+
parent_view = view_by_id[parent_id]
|
|
328
|
+
for prop_id, prop in (parent_view.properties or {}).items():
|
|
329
|
+
if (view_id, prop_id) not in properties_by_id:
|
|
330
|
+
properties_by_id[(view_id, prop_id)] = prop
|
|
331
|
+
to_check.extend(parent_view.implements or [])
|
|
332
|
+
elif parent_id in ref_view_by_id:
|
|
333
|
+
# SDK properties
|
|
334
|
+
parent_read_view = ref_view_by_id[parent_id]
|
|
335
|
+
for prop_id, read_prop in parent_read_view.properties.items():
|
|
336
|
+
if (view_id, prop_id) not in properties_by_id:
|
|
337
|
+
properties_by_id[(view_id, prop_id)] = read_prop
|
|
338
|
+
# Read format of views already includes all ancestor properties
|
|
339
|
+
# so no need to check further
|
|
340
|
+
else:
|
|
341
|
+
# Missing views are caught else where
|
|
342
|
+
continue
|
|
343
|
+
|
|
344
|
+
return properties_by_id
|
|
345
|
+
|
|
346
|
+
@staticmethod
|
|
347
|
+
def _as_view_properties_by_id(
|
|
348
|
+
properties_by_ids: dict[tuple[ViewId, str], ViewPropertyApply | ViewProperty],
|
|
349
|
+
) -> dict[ViewId, list[tuple[str, ViewProperty | ViewPropertyApply]]]:
|
|
350
|
+
view_properties_by_id: dict[dm.ViewId, list[tuple[str, ViewProperty | ViewPropertyApply]]] = defaultdict(list)
|
|
351
|
+
for (view_id, prop_id), prop in properties_by_ids.items():
|
|
352
|
+
view_properties_by_id[view_id].append((prop_id, prop))
|
|
353
|
+
return view_properties_by_id
|
|
354
|
+
|
|
355
|
+
@staticmethod
|
|
356
|
+
def _parent_view_ids_by_child_id(
|
|
357
|
+
all_views_by_id: dict[dm.ViewId, dm.ViewApply | dm.View],
|
|
358
|
+
) -> dict[ViewId, set[ViewId]]:
|
|
359
|
+
@lru_cache
|
|
360
|
+
def get_parents(child_view_id: ViewId) -> set[ViewId]:
|
|
361
|
+
if child_view_id not in all_views_by_id:
|
|
362
|
+
return set()
|
|
363
|
+
child_view = all_views_by_id[child_view_id]
|
|
364
|
+
parents = set(child_view.implements or [])
|
|
365
|
+
for parent_id in child_view.implements or []:
|
|
366
|
+
parents.update(get_parents(parent_id))
|
|
367
|
+
return parents
|
|
368
|
+
|
|
369
|
+
parents_by_view: dict[dm.ViewId, set[dm.ViewId]] = {}
|
|
370
|
+
for view_id in all_views_by_id:
|
|
371
|
+
parents_by_view[view_id] = get_parents(view_id)
|
|
372
|
+
return parents_by_view
|
|
373
|
+
|
|
374
|
+
def _create_container_properties_by_id(
|
|
375
|
+
self,
|
|
376
|
+
) -> dict[tuple[ContainerEntity, str], list[tuple[int, PhysicalProperty]]]:
|
|
377
|
+
"""Create a mapping of container properties with their location in the properties list.
|
|
378
|
+
|
|
379
|
+
Returns:
|
|
380
|
+
dict[tuple[ContainerEntity, str], list[tuple[int, PhysicalProperty]]]: A dictionary where the key is a tuple
|
|
381
|
+
of (ContainerEntity, property name) and the value is a list of tuples of (int, PhysicalProperty) where
|
|
382
|
+
int is the index of the property in the properties list and PhysicalProperty is the property itself.
|
|
383
|
+
"""
|
|
384
|
+
container_properties_by_id: dict[tuple[ContainerEntity, str], list[tuple[int, PhysicalProperty]]] = defaultdict(
|
|
385
|
+
list
|
|
386
|
+
)
|
|
387
|
+
for prop_no, prop in enumerate(self._properties):
|
|
388
|
+
if prop.container and prop.container_property:
|
|
389
|
+
container_properties_by_id[(prop.container, prop.container_property)].append((prop_no, prop))
|
|
390
|
+
return container_properties_by_id
|
|
391
|
+
|
|
392
|
+
def _consistent_container_properties(
|
|
393
|
+
self, container_properties_by_id: dict[tuple[ContainerEntity, str], list[tuple[int, PhysicalProperty]]]
|
|
394
|
+
) -> IssueList:
|
|
395
|
+
properties_sheet = self._read_info_by_spreadsheet.get("Properties")
|
|
396
|
+
errors = IssueList()
|
|
397
|
+
for (container, prop_name), properties in container_properties_by_id.items():
|
|
398
|
+
if len(properties) == 1:
|
|
399
|
+
continue
|
|
400
|
+
container_id = container.as_id()
|
|
401
|
+
|
|
402
|
+
row_numbers = {prop_no for prop_no, _ in properties}
|
|
403
|
+
if properties_sheet:
|
|
404
|
+
row_numbers = {properties_sheet.adjusted_row_number(row_no) for row_no in row_numbers}
|
|
405
|
+
value_types = {prop.value_type for _, prop in properties if prop.value_type}
|
|
406
|
+
# The container type 'direct' is an exception. On a container the type direct can point to any
|
|
407
|
+
# node. The value type is typically set on the view.
|
|
408
|
+
is_all_direct = all(prop.connection == "direct" for _, prop in properties)
|
|
409
|
+
if len(value_types) > 1 and not is_all_direct:
|
|
410
|
+
errors.append(
|
|
411
|
+
PropertyDefinitionDuplicatedError[dm.ContainerId](
|
|
412
|
+
container_id,
|
|
413
|
+
"container",
|
|
414
|
+
prop_name,
|
|
415
|
+
frozenset({v.dms._type if isinstance(v, DataType) else str(v) for v in value_types}),
|
|
416
|
+
tuple(row_numbers),
|
|
417
|
+
"rows",
|
|
418
|
+
)
|
|
419
|
+
)
|
|
420
|
+
list_definitions = {prop.is_list for _, prop in properties if prop.is_list is not None}
|
|
421
|
+
if len(list_definitions) > 1:
|
|
422
|
+
errors.append(
|
|
423
|
+
PropertyDefinitionDuplicatedError[dm.ContainerId](
|
|
424
|
+
container_id,
|
|
425
|
+
"container",
|
|
426
|
+
prop_name,
|
|
427
|
+
frozenset(list_definitions),
|
|
428
|
+
tuple(row_numbers),
|
|
429
|
+
"rows",
|
|
430
|
+
)
|
|
431
|
+
)
|
|
432
|
+
nullable_definitions = {prop.nullable for _, prop in properties if prop.nullable is not None}
|
|
433
|
+
if len(nullable_definitions) > 1:
|
|
434
|
+
errors.append(
|
|
435
|
+
PropertyDefinitionDuplicatedError[dm.ContainerId](
|
|
436
|
+
container_id,
|
|
437
|
+
"container",
|
|
438
|
+
prop_name,
|
|
439
|
+
frozenset(nullable_definitions),
|
|
440
|
+
tuple(row_numbers),
|
|
441
|
+
"rows",
|
|
442
|
+
)
|
|
443
|
+
)
|
|
444
|
+
default_definitions = {prop.default for _, prop in properties if prop.default is not None}
|
|
445
|
+
if len(default_definitions) > 1:
|
|
446
|
+
errors.append(
|
|
447
|
+
PropertyDefinitionDuplicatedError[dm.ContainerId](
|
|
448
|
+
container_id,
|
|
449
|
+
"container",
|
|
450
|
+
prop_name,
|
|
451
|
+
frozenset(
|
|
452
|
+
tuple(f"{k}:{v}" for k, v in def_.items()) if isinstance(def_, dict) else def_
|
|
453
|
+
for def_ in default_definitions
|
|
454
|
+
),
|
|
455
|
+
tuple(row_numbers),
|
|
456
|
+
"rows",
|
|
457
|
+
)
|
|
458
|
+
)
|
|
459
|
+
index_definitions = {
|
|
460
|
+
",".join([str(index) for index in prop.index]) for _, prop in properties if prop.index is not None
|
|
461
|
+
}
|
|
462
|
+
if len(index_definitions) > 1:
|
|
463
|
+
errors.append(
|
|
464
|
+
PropertyDefinitionDuplicatedError(
|
|
465
|
+
container_id,
|
|
466
|
+
"container",
|
|
467
|
+
prop_name,
|
|
468
|
+
frozenset(index_definitions),
|
|
469
|
+
tuple(row_numbers),
|
|
470
|
+
"rows",
|
|
471
|
+
)
|
|
472
|
+
)
|
|
473
|
+
constraint_definitions = {
|
|
474
|
+
",".join([str(constraint) for constraint in prop.constraint])
|
|
475
|
+
for _, prop in properties
|
|
476
|
+
if prop.constraint is not None
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
if len(constraint_definitions) > 1:
|
|
480
|
+
errors.append(
|
|
481
|
+
PropertyDefinitionDuplicatedError[dm.ContainerId](
|
|
482
|
+
container_id,
|
|
483
|
+
"container",
|
|
484
|
+
prop_name,
|
|
485
|
+
frozenset(constraint_definitions),
|
|
486
|
+
tuple(row_numbers),
|
|
487
|
+
"rows",
|
|
488
|
+
)
|
|
489
|
+
)
|
|
490
|
+
|
|
491
|
+
return errors
|
|
492
|
+
|
|
493
|
+
def _valid_composite_container_indices(
|
|
494
|
+
self, container_properties_by_id: dict[tuple[ContainerEntity, str], list[tuple[int, PhysicalProperty]]]
|
|
495
|
+
) -> IssueList:
|
|
496
|
+
"""Validate that the indices on the container properties are valid."""
|
|
497
|
+
index_properties_by_container_index: dict[tuple[ContainerEntity, str], list[_ContainerPropertyIndex]] = (
|
|
498
|
+
defaultdict(list)
|
|
499
|
+
)
|
|
500
|
+
for (container, _), properties in container_properties_by_id.items():
|
|
501
|
+
for row_no, prop in properties:
|
|
502
|
+
for index in prop.index or []:
|
|
503
|
+
index_properties_by_container_index[(container, index.suffix)].append(
|
|
504
|
+
_ContainerPropertyIndex(row_no, prop, index)
|
|
505
|
+
)
|
|
506
|
+
|
|
507
|
+
properties_sheet_info = self._read_info_by_spreadsheet.get("Properties")
|
|
508
|
+
errors = IssueList()
|
|
509
|
+
for (container, _), index_properties in index_properties_by_container_index.items():
|
|
510
|
+
if len(index_properties) <= 1:
|
|
511
|
+
# If there is only one property in the index, this is already validated in the field_validator
|
|
512
|
+
# of the PhysicalProperty class. This validation is only for composite indices.
|
|
513
|
+
continue
|
|
514
|
+
container_id = container.as_id()
|
|
515
|
+
row_numbers = tuple([index_prop.location for index_prop in index_properties])
|
|
516
|
+
if properties_sheet_info:
|
|
517
|
+
row_numbers = tuple([properties_sheet_info.adjusted_row_number(row_no) for row_no in row_numbers])
|
|
518
|
+
|
|
519
|
+
if order_missing_error := self._validate_container_indices_has_order(
|
|
520
|
+
index_properties, row_numbers, container_id
|
|
521
|
+
):
|
|
522
|
+
errors.append(order_missing_error)
|
|
523
|
+
same_order_errors = self._validate_container_indices_same_order(index_properties, row_numbers, container_id)
|
|
524
|
+
errors.extend(same_order_errors)
|
|
525
|
+
return errors
|
|
526
|
+
|
|
527
|
+
@staticmethod
|
|
528
|
+
def _validate_container_indices_has_order(
|
|
529
|
+
index_properties: list[_ContainerPropertyIndex], row_numbers: tuple[int, ...], container_id: dm.ContainerId
|
|
530
|
+
) -> PropertyInvalidDefinitionError | None:
|
|
531
|
+
property_names: list[str] = []
|
|
532
|
+
indices: list[ContainerIndexEntity] = []
|
|
533
|
+
for prop_index in index_properties:
|
|
534
|
+
if prop_index.index.order is None:
|
|
535
|
+
property_names.append(prop_index.property_.view_property)
|
|
536
|
+
indices.append(prop_index.index)
|
|
537
|
+
|
|
538
|
+
if not property_names:
|
|
539
|
+
return None
|
|
540
|
+
|
|
541
|
+
if len(set(property_names)) == 1:
|
|
542
|
+
# If this is the same property, this is not a composite index, but a poorly defined single property
|
|
543
|
+
# index. This will be caught by the PropertyDefinitionDuplicatedError.
|
|
544
|
+
return None
|
|
545
|
+
|
|
546
|
+
properties_str = humanize_collection(property_names)
|
|
547
|
+
fixed_indices = [str(index.model_copy(update={"order": no})) for no, index in enumerate(indices, 1)]
|
|
548
|
+
message = (
|
|
549
|
+
"You must specify the order when using a composite index. "
|
|
550
|
+
f"For example {humanize_collection(fixed_indices)}."
|
|
551
|
+
)
|
|
552
|
+
return PropertyInvalidDefinitionError(
|
|
553
|
+
container_id,
|
|
554
|
+
"container",
|
|
555
|
+
properties_str,
|
|
556
|
+
message,
|
|
557
|
+
row_numbers,
|
|
558
|
+
"rows",
|
|
559
|
+
)
|
|
560
|
+
|
|
561
|
+
@staticmethod
|
|
562
|
+
def _validate_container_indices_same_order(
|
|
563
|
+
index_properties: list[_ContainerPropertyIndex], row_numbers: tuple[int, ...], container_id: dm.ContainerId
|
|
564
|
+
) -> list[PropertyInvalidDefinitionError]:
|
|
565
|
+
"""Checks whether there are multiple properties with the same order in a composite index."""
|
|
566
|
+
properties_by_order: dict[int, list[tuple[int, PhysicalProperty]]] = defaultdict(list)
|
|
567
|
+
for index_prop in index_properties:
|
|
568
|
+
if index_prop.index.order is not None:
|
|
569
|
+
properties_by_order[index_prop.index.order].append((index_prop.location, index_prop.property_))
|
|
570
|
+
same_order_errors: list[PropertyInvalidDefinitionError] = []
|
|
571
|
+
for order, props in properties_by_order.items():
|
|
572
|
+
if len(props) > 1:
|
|
573
|
+
properties_str = humanize_collection([prop.view_property for _, prop in props])
|
|
574
|
+
message = (
|
|
575
|
+
"You cannot have multiple properties with the same order in a composite index. "
|
|
576
|
+
f"Got order={order} for all composite properties."
|
|
577
|
+
"Please ensure that each property has an unique order."
|
|
578
|
+
)
|
|
579
|
+
same_order_errors.append(
|
|
580
|
+
PropertyInvalidDefinitionError(
|
|
581
|
+
container_id,
|
|
582
|
+
"container",
|
|
583
|
+
properties_str,
|
|
584
|
+
message,
|
|
585
|
+
row_numbers,
|
|
586
|
+
"rows",
|
|
587
|
+
)
|
|
588
|
+
)
|
|
589
|
+
return same_order_errors
|
|
590
|
+
|
|
591
|
+
@staticmethod
|
|
592
|
+
def _containers_are_proper_size(dms_schema: DMSSchema) -> IssueList:
|
|
593
|
+
errors = IssueList()
|
|
594
|
+
for container_id, container in dms_schema.containers.items():
|
|
595
|
+
count = len(container.properties or {})
|
|
596
|
+
if count > DMS_CONTAINER_PROPERTY_SIZE_LIMIT:
|
|
597
|
+
errors.append(ContainerPropertyLimitWarning(container_id, count))
|
|
598
|
+
|
|
599
|
+
return errors
|
|
600
|
+
|
|
601
|
+
@staticmethod
|
|
602
|
+
def _validate_referenced_container_limits(
|
|
603
|
+
views: ViewApplyDict, view_properties_by_id: dict[dm.ViewId, list[tuple[str, ViewProperty | ViewPropertyApply]]]
|
|
604
|
+
) -> IssueList:
|
|
605
|
+
issue_list = IssueList()
|
|
606
|
+
for view_id, view in views.items():
|
|
607
|
+
view_properties = view_properties_by_id.get(view_id, [])
|
|
608
|
+
mapped_containers = {
|
|
609
|
+
prop.container
|
|
610
|
+
for _, prop in view_properties
|
|
611
|
+
if isinstance(prop, dm.MappedPropertyApply | dm.MappedProperty)
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
if mapped_containers and len(mapped_containers) > DMS_VIEW_CONTAINER_SIZE_LIMIT:
|
|
615
|
+
issue_list.append(
|
|
616
|
+
NotSupportedViewContainerLimitWarning(
|
|
617
|
+
view_id,
|
|
618
|
+
len(mapped_containers),
|
|
619
|
+
)
|
|
620
|
+
)
|
|
621
|
+
|
|
622
|
+
if view.filter and isinstance(view.filter, dm.filters.HasData) and len(view.filter.dump()["hasData"]) > 10:
|
|
623
|
+
issue_list.append(
|
|
624
|
+
NotSupportedHasDataFilterLimitWarning(
|
|
625
|
+
view_id,
|
|
626
|
+
len(view.filter.dump()["hasData"]),
|
|
627
|
+
)
|
|
628
|
+
)
|
|
629
|
+
return issue_list
|
|
630
|
+
|
|
631
|
+
def _validate_raw_filter(self) -> IssueList:
|
|
632
|
+
issue_list = IssueList()
|
|
633
|
+
for view in self._views:
|
|
634
|
+
if view.filter_ and isinstance(view.filter_, RawFilter) and view.view.space not in COGNITE_SPACES:
|
|
635
|
+
issue_list.append(
|
|
636
|
+
NotNeatSupportedFilterWarning(view.view.as_id()),
|
|
637
|
+
)
|
|
638
|
+
return issue_list
|
|
639
|
+
|
|
640
|
+
def _validate_value_type_existence(self) -> IssueList:
|
|
641
|
+
views = {prop_.view for prop_ in self._properties}.union({view_.view for view_ in self._views})
|
|
642
|
+
issue_list = IssueList()
|
|
643
|
+
for prop_ in self._properties:
|
|
644
|
+
if isinstance(prop_.value_type, ViewEntity) and prop_.value_type not in views:
|
|
645
|
+
issue_list.append(
|
|
646
|
+
UndefinedViewWarning(
|
|
647
|
+
str(prop_.view),
|
|
648
|
+
str(prop_.value_type),
|
|
649
|
+
prop_.view_property,
|
|
650
|
+
)
|
|
651
|
+
)
|
|
652
|
+
return issue_list
|
|
653
|
+
|
|
654
|
+
def _validate_property_referenced_views_and_containers_exists(
|
|
655
|
+
self,
|
|
656
|
+
view_by_id: dict[dm.ViewId, dm.ViewApply | dm.View],
|
|
657
|
+
containers_by_id: dict[dm.ContainerId, dm.ContainerApply | dm.Container],
|
|
658
|
+
) -> IssueList:
|
|
659
|
+
issue_list = IssueList()
|
|
660
|
+
for prop in self._properties:
|
|
661
|
+
if prop.container:
|
|
662
|
+
container_id = prop.container.as_id()
|
|
663
|
+
if container_id not in containers_by_id:
|
|
664
|
+
issue_list.append(
|
|
665
|
+
ResourceNotFoundError(
|
|
666
|
+
container_id,
|
|
667
|
+
"container",
|
|
668
|
+
prop.view,
|
|
669
|
+
"view",
|
|
670
|
+
)
|
|
671
|
+
)
|
|
672
|
+
elif (
|
|
673
|
+
prop.container_property and prop.container_property not in containers_by_id[container_id].properties
|
|
674
|
+
):
|
|
675
|
+
issue_list.append(
|
|
676
|
+
PropertyNotFoundError(
|
|
677
|
+
prop.container,
|
|
678
|
+
"container property",
|
|
679
|
+
prop.container_property,
|
|
680
|
+
dm.PropertyId(prop.view.as_id(), prop.view_property),
|
|
681
|
+
"view property",
|
|
682
|
+
)
|
|
683
|
+
)
|
|
684
|
+
|
|
685
|
+
if prop.view.as_id() not in view_by_id:
|
|
686
|
+
issue_list.append(
|
|
687
|
+
ResourceNotFoundError(
|
|
688
|
+
prop.view,
|
|
689
|
+
"view",
|
|
690
|
+
prop.view_property,
|
|
691
|
+
"property",
|
|
692
|
+
)
|
|
693
|
+
)
|
|
694
|
+
|
|
695
|
+
return issue_list
|
|
696
|
+
|
|
697
|
+
def _validate_reverse_connections(
|
|
698
|
+
self,
|
|
699
|
+
view_property_by_property_id: dict[tuple[dm.ViewId, str], ViewPropertyApply | ViewProperty],
|
|
700
|
+
containers_by_id: dict[dm.ContainerId, dm.ContainerApply | dm.Container],
|
|
701
|
+
parents_by_view: dict[dm.ViewId, set[dm.ViewId]],
|
|
702
|
+
all_view_property_by_property_id: dict[tuple[dm.ViewId, str], ViewPropertyApply | ViewProperty],
|
|
703
|
+
) -> IssueList:
|
|
704
|
+
issue_list = IssueList()
|
|
705
|
+
# do not check for reverse connections in Cognite models
|
|
706
|
+
if self._metadata.as_data_model_id() in COGNITE_MODELS:
|
|
707
|
+
return issue_list
|
|
708
|
+
|
|
709
|
+
for (view_id, prop_id), prop_ in view_property_by_property_id.items():
|
|
710
|
+
if not isinstance(prop_, ReverseDirectRelationApply | ReverseDirectRelation):
|
|
711
|
+
continue
|
|
712
|
+
target_id = prop_.through.source, prop_.through.property
|
|
713
|
+
if target_id not in all_view_property_by_property_id:
|
|
714
|
+
issue_list.append(
|
|
715
|
+
ReversedConnectionNotFeasibleError(
|
|
716
|
+
view_id,
|
|
717
|
+
"reversed connection",
|
|
718
|
+
prop_id,
|
|
719
|
+
f"The {prop_.through.source} {prop_.through.property} does not exist",
|
|
720
|
+
)
|
|
721
|
+
)
|
|
722
|
+
continue
|
|
723
|
+
if isinstance(target_id[0], dm.ContainerId):
|
|
724
|
+
# Todo: How to handle this case? Should not happen if you created the model with Neat
|
|
725
|
+
continue
|
|
726
|
+
|
|
727
|
+
target_property = all_view_property_by_property_id[(target_id[0], target_id[1])]
|
|
728
|
+
# Validate that the target is a direct relation pointing to the view_id
|
|
729
|
+
is_direct_relation = False
|
|
730
|
+
if isinstance(target_property, dm.MappedProperty) and isinstance(target_property.type, dm.DirectRelation):
|
|
731
|
+
is_direct_relation = True
|
|
732
|
+
elif isinstance(target_property, dm.MappedPropertyApply):
|
|
733
|
+
container = containers_by_id[target_property.container]
|
|
734
|
+
if target_property.container_property_identifier in container.properties:
|
|
735
|
+
container_property = container.properties[target_property.container_property_identifier]
|
|
736
|
+
if isinstance(container_property.type, dm.DirectRelation):
|
|
737
|
+
is_direct_relation = True
|
|
738
|
+
if not is_direct_relation:
|
|
739
|
+
issue_list.append(
|
|
740
|
+
ReversedConnectionNotFeasibleError(
|
|
741
|
+
view_id,
|
|
742
|
+
"reversed connection",
|
|
743
|
+
prop_id,
|
|
744
|
+
f"{prop_.through.source} {prop_.through.property} is not a direct relation",
|
|
745
|
+
)
|
|
746
|
+
)
|
|
747
|
+
continue
|
|
748
|
+
if not (
|
|
749
|
+
isinstance(target_property, dm.MappedPropertyApply | dm.MappedProperty)
|
|
750
|
+
# The direct relation is pointing to the view_id or one of its parents
|
|
751
|
+
and (
|
|
752
|
+
(target_property.source == view_id or target_property.source in parents_by_view[view_id])
|
|
753
|
+
# This is a hack that users use to create a multi value direct relations. It works by setting
|
|
754
|
+
# the source of a direct relation to None. Then, you can have multiple reverse direct relations
|
|
755
|
+
# through this property. In Search this will give you a multi value direct relation.
|
|
756
|
+
# Thus, we must allow it here. Note that the missing source in the direct relation will give the
|
|
757
|
+
# user a DirectRelationMissingSourceWarning so they know they are doing a not
|
|
758
|
+
# recommended modeling pattern.
|
|
759
|
+
or target_property.source is None
|
|
760
|
+
)
|
|
761
|
+
):
|
|
762
|
+
issue_list.append(
|
|
763
|
+
ReversedConnectionNotFeasibleWarning(
|
|
764
|
+
view_id,
|
|
765
|
+
"reversed connection",
|
|
766
|
+
prop_id,
|
|
767
|
+
f"{prop_.through.source} {prop_.through.property} is not pointing to {view_id}",
|
|
768
|
+
)
|
|
769
|
+
)
|
|
770
|
+
return issue_list
|
|
771
|
+
|
|
772
|
+
@staticmethod
|
|
773
|
+
def _validate_schema(
|
|
774
|
+
schema: DMSSchema,
|
|
775
|
+
view_by_id: dict[dm.ViewId, dm.ViewApply | dm.View],
|
|
776
|
+
containers_by_id: dict[dm.ContainerId, dm.ContainerApply | dm.Container],
|
|
777
|
+
) -> IssueList:
|
|
778
|
+
errors: set[NeatError] = set()
|
|
779
|
+
defined_spaces = schema.spaces.copy()
|
|
780
|
+
|
|
781
|
+
for container_id, container in schema.containers.items():
|
|
782
|
+
if container.space not in defined_spaces:
|
|
783
|
+
errors.add(ResourceNotFoundError(container.space, "space", container_id, "container"))
|
|
784
|
+
for constraint in container.constraints.values():
|
|
785
|
+
if isinstance(constraint, dm.RequiresConstraint) and constraint.require not in containers_by_id:
|
|
786
|
+
errors.add(ResourceNotFoundError(constraint.require, "container", container_id, "container"))
|
|
787
|
+
|
|
788
|
+
for view_id, view in schema.views.items():
|
|
789
|
+
if view.space not in defined_spaces:
|
|
790
|
+
errors.add(ResourceNotFoundError(view.space, "space", view_id, "view"))
|
|
791
|
+
|
|
792
|
+
for parent in view.implements or []:
|
|
793
|
+
if parent not in view_by_id:
|
|
794
|
+
errors.add(PropertyNotFoundError(parent, "view", "implements", view_id, "view"))
|
|
795
|
+
|
|
796
|
+
for prop_name, prop in (view.properties or {}).items():
|
|
797
|
+
if isinstance(prop, dm.MappedPropertyApply):
|
|
798
|
+
ref_container = containers_by_id.get(prop.container)
|
|
799
|
+
if ref_container is None:
|
|
800
|
+
errors.add(ResourceNotFoundError(prop.container, "container", view_id, "view"))
|
|
801
|
+
elif prop.container_property_identifier not in ref_container.properties:
|
|
802
|
+
errors.add(
|
|
803
|
+
PropertyNotFoundError(
|
|
804
|
+
prop.container,
|
|
805
|
+
"container",
|
|
806
|
+
prop.container_property_identifier,
|
|
807
|
+
view_id,
|
|
808
|
+
"view",
|
|
809
|
+
)
|
|
810
|
+
)
|
|
811
|
+
else:
|
|
812
|
+
container_property = ref_container.properties[prop.container_property_identifier]
|
|
813
|
+
|
|
814
|
+
if isinstance(container_property.type, dm.DirectRelation) and prop.source is None:
|
|
815
|
+
warnings.warn(
|
|
816
|
+
DirectRelationMissingSourceWarning(view_id, prop_name),
|
|
817
|
+
stacklevel=2,
|
|
818
|
+
)
|
|
819
|
+
|
|
820
|
+
if (
|
|
821
|
+
isinstance(prop, dm.EdgeConnectionApply | ReverseDirectRelationApply)
|
|
822
|
+
and prop.source not in view_by_id
|
|
823
|
+
):
|
|
824
|
+
errors.add(PropertyNotFoundError(prop.source, "view", prop_name, view_id, "view"))
|
|
825
|
+
|
|
826
|
+
if (
|
|
827
|
+
isinstance(prop, dm.EdgeConnectionApply)
|
|
828
|
+
and prop.edge_source is not None
|
|
829
|
+
and prop.edge_source not in view_by_id
|
|
830
|
+
):
|
|
831
|
+
errors.add(PropertyNotFoundError(prop.edge_source, "view", prop_name, view_id, "view"))
|
|
832
|
+
|
|
833
|
+
# This allows for multiple view properties to be mapped to the same container property,
|
|
834
|
+
# as long as they have different external_id, otherwise this will lead to raising
|
|
835
|
+
# error ContainerPropertyUsedMultipleTimesError
|
|
836
|
+
property_count = Counter(
|
|
837
|
+
(prop.container, prop.container_property_identifier, view_property_identifier)
|
|
838
|
+
for view_property_identifier, prop in (view.properties or {}).items()
|
|
839
|
+
if isinstance(prop, dm.MappedPropertyApply)
|
|
840
|
+
)
|
|
841
|
+
|
|
842
|
+
for (
|
|
843
|
+
container_id,
|
|
844
|
+
container_property_identifier,
|
|
845
|
+
_,
|
|
846
|
+
), count in property_count.items():
|
|
847
|
+
if count > 1:
|
|
848
|
+
view_properties = [
|
|
849
|
+
prop_name
|
|
850
|
+
for prop_name, prop in (view.properties or {}).items()
|
|
851
|
+
if isinstance(prop, dm.MappedPropertyApply)
|
|
852
|
+
and (prop.container, prop.container_property_identifier)
|
|
853
|
+
== (container_id, container_property_identifier)
|
|
854
|
+
]
|
|
855
|
+
errors.add(
|
|
856
|
+
PropertyMappingDuplicatedError(
|
|
857
|
+
container_id,
|
|
858
|
+
"container",
|
|
859
|
+
container_property_identifier,
|
|
860
|
+
frozenset({dm.PropertyId(view_id, prop_name) for prop_name in view_properties}),
|
|
861
|
+
"view property",
|
|
862
|
+
)
|
|
863
|
+
)
|
|
864
|
+
|
|
865
|
+
if schema.data_model:
|
|
866
|
+
model = schema.data_model
|
|
867
|
+
if model.space not in defined_spaces:
|
|
868
|
+
errors.add(ResourceNotFoundError(model.space, "space", model.as_id(), "data model"))
|
|
869
|
+
|
|
870
|
+
view_counts: dict[dm.ViewId, int] = defaultdict(int)
|
|
871
|
+
for view_id_or_class in model.views or []:
|
|
872
|
+
view_id = view_id_or_class if isinstance(view_id_or_class, dm.ViewId) else view_id_or_class.as_id()
|
|
873
|
+
if view_id not in view_by_id:
|
|
874
|
+
errors.add(ResourceNotFoundError(view_id, "view", model.as_id(), "data model"))
|
|
875
|
+
view_counts[view_id] += 1
|
|
876
|
+
|
|
877
|
+
for view_id, count in view_counts.items():
|
|
878
|
+
if count > 1:
|
|
879
|
+
errors.add(
|
|
880
|
+
ResourceDuplicatedError(
|
|
881
|
+
view_id,
|
|
882
|
+
"view",
|
|
883
|
+
f"DMS {model.as_id()!r}",
|
|
884
|
+
)
|
|
885
|
+
)
|
|
886
|
+
|
|
887
|
+
return IssueList(list(errors))
|