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
cognite/neat/__init__.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
from cognite.neat.core._utils.auth import get_cognite_client
|
|
1
|
+
from cognite.neat._v0.core._utils.auth import get_cognite_client
|
|
2
2
|
|
|
3
|
+
from ._config import NeatConfig, get_neat_config_from_file
|
|
4
|
+
from ._session import NeatSession
|
|
3
5
|
from ._version import __version__
|
|
4
|
-
from .session import NeatSession
|
|
5
6
|
|
|
6
|
-
__all__ = ["NeatSession", "__version__", "get_cognite_client"]
|
|
7
|
+
__all__ = ["NeatConfig", "NeatSession", "__version__", "get_cognite_client", "get_neat_config_from_file"]
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
from cognite.neat._client.config import NeatClientConfig
|
|
2
|
+
from cognite.neat._utils.http_client import HTTPClient
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class NeatAPI:
|
|
6
|
+
def __init__(self, neat_config: NeatClientConfig, http_client: HTTPClient) -> None:
|
|
7
|
+
self._config = neat_config
|
|
8
|
+
self._http_client = http_client
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from cognite.client import ClientConfig, CogniteClient
|
|
2
|
+
|
|
3
|
+
from cognite.neat._utils.http_client import HTTPClient
|
|
4
|
+
|
|
5
|
+
from .config import NeatClientConfig
|
|
6
|
+
from .containers_api import ContainersAPI
|
|
7
|
+
from .data_model_api import DataModelsAPI
|
|
8
|
+
from .spaces_api import SpacesAPI
|
|
9
|
+
from .statistics_api import StatisticsAPI
|
|
10
|
+
from .views_api import ViewsAPI
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class NeatClient:
|
|
14
|
+
def __init__(self, cognite_client_or_config: CogniteClient | ClientConfig) -> None:
|
|
15
|
+
self.config = NeatClientConfig(cognite_client_or_config)
|
|
16
|
+
self.http_client = HTTPClient(self.config)
|
|
17
|
+
self.data_models = DataModelsAPI(self.config, self.http_client)
|
|
18
|
+
self.views = ViewsAPI(self.config, self.http_client)
|
|
19
|
+
self.containers = ContainersAPI(self.config, self.http_client)
|
|
20
|
+
self.spaces = SpacesAPI(self.config, self.http_client)
|
|
21
|
+
self.statistics = StatisticsAPI(self.config, self.http_client)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from cognite.client import ClientConfig, CogniteClient
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class NeatClientConfig(ClientConfig):
|
|
5
|
+
def __init__(self, cognite_client_or_config: CogniteClient | ClientConfig):
|
|
6
|
+
config = (
|
|
7
|
+
cognite_client_or_config.config
|
|
8
|
+
if isinstance(cognite_client_or_config, CogniteClient)
|
|
9
|
+
else cognite_client_or_config
|
|
10
|
+
)
|
|
11
|
+
super().__init__(
|
|
12
|
+
client_name=config.client_name,
|
|
13
|
+
project=config.project,
|
|
14
|
+
credentials=config.credentials,
|
|
15
|
+
api_subversion=config.api_subversion,
|
|
16
|
+
base_url=config.base_url,
|
|
17
|
+
max_workers=config.max_workers,
|
|
18
|
+
headers=config.headers,
|
|
19
|
+
timeout=config.timeout,
|
|
20
|
+
file_transfer_timeout=config.file_transfer_timeout,
|
|
21
|
+
debug=config.debug,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
def create_api_url(self, endpoint: str) -> str:
|
|
25
|
+
"""Create a full API URL for the given endpoint.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
endpoint (str): The API endpoint to append to the base URL.
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
str: The full API URL.
|
|
32
|
+
|
|
33
|
+
Examples:
|
|
34
|
+
>>> config = NeatClientConfig(cluster="bluefield", project="my_project", ...)
|
|
35
|
+
>>> config.create_api_url("/models/instances")
|
|
36
|
+
"https://bluefield.cognitedata.com/api/v1/my_project/models/instances"
|
|
37
|
+
"""
|
|
38
|
+
if not endpoint.startswith("/"):
|
|
39
|
+
endpoint = f"/{endpoint}"
|
|
40
|
+
return f"{self.base_url}/api/v1/projects/{self.project}{endpoint}"
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Sequence
|
|
4
|
+
|
|
5
|
+
from cognite.neat._data_model.models.dms import ContainerReference, ContainerRequest, ContainerResponse, DataModelBody
|
|
6
|
+
from cognite.neat._utils.http_client import ItemIDBody, ItemsRequest, ParametersRequest
|
|
7
|
+
from cognite.neat._utils.useful_types import PrimitiveType
|
|
8
|
+
|
|
9
|
+
from .api import NeatAPI
|
|
10
|
+
from .data_classes import PagedResponse
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ContainersAPI(NeatAPI):
|
|
14
|
+
ENDPOINT = "/models/containers"
|
|
15
|
+
LIST_REQUEST_LIMIT = 1000
|
|
16
|
+
|
|
17
|
+
def apply(self, items: Sequence[ContainerRequest]) -> list[ContainerResponse]:
|
|
18
|
+
"""Apply (create or update) containers in CDF.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
items: List of ContainerRequest objects to apply.
|
|
22
|
+
Returns:
|
|
23
|
+
List of ContainerResponse objects.
|
|
24
|
+
"""
|
|
25
|
+
if not items:
|
|
26
|
+
return []
|
|
27
|
+
if len(items) > 100:
|
|
28
|
+
raise ValueError("Cannot apply more than 100 containers at once.")
|
|
29
|
+
result = self._http_client.request_with_retries(
|
|
30
|
+
ItemsRequest(
|
|
31
|
+
endpoint_url=self._config.create_api_url(self.ENDPOINT),
|
|
32
|
+
method="POST",
|
|
33
|
+
body=DataModelBody(items=items),
|
|
34
|
+
)
|
|
35
|
+
)
|
|
36
|
+
result.raise_for_status()
|
|
37
|
+
result = PagedResponse[ContainerResponse].model_validate_json(result.success_response.body)
|
|
38
|
+
return result.items
|
|
39
|
+
|
|
40
|
+
def retrieve(
|
|
41
|
+
self,
|
|
42
|
+
items: list[ContainerReference],
|
|
43
|
+
) -> list[ContainerResponse]:
|
|
44
|
+
"""Retrieve containers by their identifiers.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
items: List of (space, external_id) tuples identifying the containers to retrieve.
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
List of ContainerResponse objects.
|
|
51
|
+
"""
|
|
52
|
+
if not items:
|
|
53
|
+
return []
|
|
54
|
+
if len(items) > 1000:
|
|
55
|
+
raise ValueError("Cannot retrieve more than 1000 containers at once.")
|
|
56
|
+
|
|
57
|
+
result = self._http_client.request_with_retries(
|
|
58
|
+
ItemsRequest(
|
|
59
|
+
endpoint_url=self._config.create_api_url(f"{self.ENDPOINT}/byids"),
|
|
60
|
+
method="POST",
|
|
61
|
+
body=ItemIDBody(items=items),
|
|
62
|
+
)
|
|
63
|
+
)
|
|
64
|
+
result.raise_for_status()
|
|
65
|
+
result = PagedResponse[ContainerResponse].model_validate_json(result.success_response.body)
|
|
66
|
+
return result.items
|
|
67
|
+
|
|
68
|
+
def delete(self, items: list[ContainerReference]) -> list[ContainerReference]:
|
|
69
|
+
"""Delete containers by their identifiers.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
items: List of ContainerReference objects identifying the containers to delete.
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
List of ContainerReference objects representing the deleted containers.
|
|
76
|
+
"""
|
|
77
|
+
if not items:
|
|
78
|
+
return []
|
|
79
|
+
if len(items) > 100:
|
|
80
|
+
raise ValueError("Cannot delete more than 100 containers at once.")
|
|
81
|
+
|
|
82
|
+
result = self._http_client.request_with_retries(
|
|
83
|
+
ItemsRequest(
|
|
84
|
+
endpoint_url=self._config.create_api_url(f"{self.ENDPOINT}/delete"),
|
|
85
|
+
method="POST",
|
|
86
|
+
body=ItemIDBody(items=items),
|
|
87
|
+
)
|
|
88
|
+
)
|
|
89
|
+
result.raise_for_status()
|
|
90
|
+
result = PagedResponse[ContainerReference].model_validate_json(result.success_response.body)
|
|
91
|
+
return result.items
|
|
92
|
+
|
|
93
|
+
def list(
|
|
94
|
+
self,
|
|
95
|
+
space: str | None = None,
|
|
96
|
+
include_global: bool = False,
|
|
97
|
+
limit: int | None = 10,
|
|
98
|
+
) -> list[ContainerResponse]:
|
|
99
|
+
"""List containers in CDF Project.
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
space: If specified, only containers in this space are returned.
|
|
103
|
+
include_global: If True, include global containers.
|
|
104
|
+
limit: Maximum number of containers to return. If None, return all containers.
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
List of ContainerResponse objects.
|
|
108
|
+
"""
|
|
109
|
+
if limit is not None and limit < 0:
|
|
110
|
+
raise ValueError("Limit must be non-negative.")
|
|
111
|
+
elif limit is not None and limit == 0:
|
|
112
|
+
return []
|
|
113
|
+
parameters: dict[str, PrimitiveType] = {"includeGlobal": include_global}
|
|
114
|
+
if space is not None:
|
|
115
|
+
parameters["space"] = space
|
|
116
|
+
cursor: str | None = None
|
|
117
|
+
container_responses: list[ContainerResponse] = []
|
|
118
|
+
while True:
|
|
119
|
+
if cursor is not None:
|
|
120
|
+
parameters["cursor"] = cursor
|
|
121
|
+
if limit is None:
|
|
122
|
+
parameters["limit"] = self.LIST_REQUEST_LIMIT
|
|
123
|
+
else:
|
|
124
|
+
parameters["limit"] = min(self.LIST_REQUEST_LIMIT, limit - len(container_responses))
|
|
125
|
+
result = self._http_client.request_with_retries(
|
|
126
|
+
ParametersRequest(
|
|
127
|
+
endpoint_url=self._config.create_api_url(self.ENDPOINT),
|
|
128
|
+
method="GET",
|
|
129
|
+
parameters=parameters,
|
|
130
|
+
)
|
|
131
|
+
)
|
|
132
|
+
result.raise_for_status()
|
|
133
|
+
result = PagedResponse[ContainerResponse].model_validate_json(result.success_response.body)
|
|
134
|
+
container_responses.extend(result.items)
|
|
135
|
+
cursor = result.next_cursor
|
|
136
|
+
if cursor is None or (limit is not None and len(container_responses) >= limit):
|
|
137
|
+
break
|
|
138
|
+
return container_responses
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from typing import Generic, TypeVar
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, Field
|
|
4
|
+
|
|
5
|
+
T = TypeVar("T", bound=BaseModel)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class PagedResponse(BaseModel, Generic[T]):
|
|
9
|
+
items: list[T]
|
|
10
|
+
next_cursor: str | None = Field(None, alias="nextCursor")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ResourceLimit(BaseModel):
|
|
14
|
+
"""Model for resources with count and limit."""
|
|
15
|
+
|
|
16
|
+
count: int
|
|
17
|
+
limit: int
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class InstancesDetail(BaseModel, populate_by_name=True):
|
|
21
|
+
"""Model for instances with detailed metrics."""
|
|
22
|
+
|
|
23
|
+
edges: int
|
|
24
|
+
soft_deleted_edges: int = Field(alias="softDeletedEdges")
|
|
25
|
+
nodes: int
|
|
26
|
+
soft_deleted_nodes: int = Field(alias="softDeletedNodes")
|
|
27
|
+
instances: int
|
|
28
|
+
instances_limit: int = Field(alias="instancesLimit")
|
|
29
|
+
soft_deleted_instances: int = Field(alias="softDeletedInstances")
|
|
30
|
+
soft_deleted_instances_limit: int = Field(alias="softDeletedInstancesLimit")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class StatisticsResponse(BaseModel, populate_by_name=True):
|
|
34
|
+
"""Main API response model."""
|
|
35
|
+
|
|
36
|
+
spaces: ResourceLimit
|
|
37
|
+
containers: ResourceLimit
|
|
38
|
+
views: ResourceLimit
|
|
39
|
+
data_models: ResourceLimit = Field(alias="dataModels")
|
|
40
|
+
container_properties: ResourceLimit = Field(alias="containerProperties")
|
|
41
|
+
instances: InstancesDetail
|
|
42
|
+
concurrent_read_limit: int = Field(alias="concurrentReadLimit")
|
|
43
|
+
concurrent_write_limit: int = Field(alias="concurrentWriteLimit")
|
|
44
|
+
concurrent_delete_limit: int = Field(alias="concurrentDeleteLimit")
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
from collections.abc import Sequence
|
|
2
|
+
|
|
3
|
+
from cognite.neat._data_model.models.dms import DataModelBody, DataModelReference, DataModelRequest, DataModelResponse
|
|
4
|
+
from cognite.neat._utils.http_client import ItemIDBody, ItemsRequest, ParametersRequest
|
|
5
|
+
from cognite.neat._utils.useful_types import PrimitiveType
|
|
6
|
+
|
|
7
|
+
from .api import NeatAPI
|
|
8
|
+
from .data_classes import PagedResponse
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class DataModelsAPI(NeatAPI):
|
|
12
|
+
ENDPOINT = "/models/datamodels"
|
|
13
|
+
|
|
14
|
+
def apply(self, data_models: Sequence[DataModelRequest]) -> list[DataModelResponse]:
|
|
15
|
+
"""Apply (create or update) data models in CDF.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
data_models: List of DataModelRequest objects to apply.
|
|
19
|
+
Returns:
|
|
20
|
+
List of DataModelResponse objects.
|
|
21
|
+
"""
|
|
22
|
+
if not data_models:
|
|
23
|
+
return []
|
|
24
|
+
if len(data_models) > 100:
|
|
25
|
+
raise ValueError("Cannot apply more than 100 data models at once.")
|
|
26
|
+
|
|
27
|
+
result = self._http_client.request_with_retries(
|
|
28
|
+
ItemsRequest(
|
|
29
|
+
endpoint_url=self._config.create_api_url(self.ENDPOINT),
|
|
30
|
+
method="POST",
|
|
31
|
+
body=DataModelBody(items=data_models),
|
|
32
|
+
)
|
|
33
|
+
)
|
|
34
|
+
result.raise_for_status()
|
|
35
|
+
result = PagedResponse[DataModelResponse].model_validate_json(result.success_response.body)
|
|
36
|
+
return result.items
|
|
37
|
+
|
|
38
|
+
def retrieve(
|
|
39
|
+
self,
|
|
40
|
+
items: list[DataModelReference],
|
|
41
|
+
) -> list[DataModelResponse]:
|
|
42
|
+
"""Retrieve data models by their identifiers.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
items: List of data models references identifying the data models to retrieve.
|
|
46
|
+
Returns:
|
|
47
|
+
List of DataModelResponse objects.
|
|
48
|
+
"""
|
|
49
|
+
if not items:
|
|
50
|
+
return []
|
|
51
|
+
if len(items) > 100:
|
|
52
|
+
raise ValueError("Cannot retrieve more than 1000 containers at once.")
|
|
53
|
+
|
|
54
|
+
result = self._http_client.request_with_retries(
|
|
55
|
+
ItemsRequest(
|
|
56
|
+
endpoint_url=self._config.create_api_url(f"{self.ENDPOINT}/byids"),
|
|
57
|
+
method="POST",
|
|
58
|
+
body=ItemIDBody(items=items),
|
|
59
|
+
)
|
|
60
|
+
)
|
|
61
|
+
result.raise_for_status()
|
|
62
|
+
result = PagedResponse[DataModelResponse].model_validate_json(result.success_response.body)
|
|
63
|
+
return result.items
|
|
64
|
+
|
|
65
|
+
def delete(self, items: list[DataModelReference]) -> list[DataModelReference]:
|
|
66
|
+
"""Delete data models by their identifiers.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
items: List of data model references identifying the data models to delete.
|
|
70
|
+
Returns:
|
|
71
|
+
List of DataModelReference objects representing the deleted data models.
|
|
72
|
+
"""
|
|
73
|
+
if not items:
|
|
74
|
+
return []
|
|
75
|
+
if len(items) > 100:
|
|
76
|
+
raise ValueError("Cannot delete more than 100 data models at once.")
|
|
77
|
+
|
|
78
|
+
result = self._http_client.request_with_retries(
|
|
79
|
+
ItemsRequest(
|
|
80
|
+
endpoint_url=self._config.create_api_url(f"{self.ENDPOINT}/delete"),
|
|
81
|
+
method="POST",
|
|
82
|
+
body=ItemIDBody(items=items),
|
|
83
|
+
)
|
|
84
|
+
)
|
|
85
|
+
result.raise_for_status()
|
|
86
|
+
result = PagedResponse[DataModelReference].model_validate_json(result.success_response.body)
|
|
87
|
+
return result.items
|
|
88
|
+
|
|
89
|
+
def list(
|
|
90
|
+
self,
|
|
91
|
+
space: str | None = None,
|
|
92
|
+
all_versions: bool = False,
|
|
93
|
+
include_global: bool = False,
|
|
94
|
+
limit: int = 10,
|
|
95
|
+
) -> list[DataModelResponse]:
|
|
96
|
+
"""List data models in CDF Project."""
|
|
97
|
+
if limit > 1000:
|
|
98
|
+
raise ValueError("Pagination is not (yet) supported for listing data models. The maximum limit is 1000.")
|
|
99
|
+
parameters: dict[str, PrimitiveType] = {
|
|
100
|
+
"allVersions": all_versions,
|
|
101
|
+
"includeGlobal": include_global,
|
|
102
|
+
"limit": limit,
|
|
103
|
+
}
|
|
104
|
+
if space is not None:
|
|
105
|
+
parameters["space"] = space
|
|
106
|
+
result = self._http_client.request_with_retries(
|
|
107
|
+
ParametersRequest(
|
|
108
|
+
endpoint_url=self._config.create_api_url(self.ENDPOINT),
|
|
109
|
+
method="GET",
|
|
110
|
+
parameters=parameters,
|
|
111
|
+
)
|
|
112
|
+
)
|
|
113
|
+
result.raise_for_status()
|
|
114
|
+
result = PagedResponse[DataModelResponse].model_validate_json(result.success_response.body)
|
|
115
|
+
return result.items
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
from collections.abc import Callable
|
|
2
|
+
|
|
3
|
+
from cognite.client.credentials import CredentialProvider, OAuthClientCredentials, OAuthInteractive, Token
|
|
4
|
+
|
|
5
|
+
from cognite.neat._utils.text import humanize_collection
|
|
6
|
+
|
|
7
|
+
from .env_vars import ClientEnvironmentVariables, LoginFlow
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def get_credentials(env_vars: ClientEnvironmentVariables) -> CredentialProvider:
|
|
11
|
+
options: dict[LoginFlow, Callable[[ClientEnvironmentVariables], CredentialProvider]] = {
|
|
12
|
+
"client_credentials": create_client_credentials,
|
|
13
|
+
"interactive": create_interactive_credentials,
|
|
14
|
+
"token": create_token_credentials,
|
|
15
|
+
"infer": create_infer_credentials,
|
|
16
|
+
}
|
|
17
|
+
return options[env_vars.LOGIN_FLOW](env_vars)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def create_client_credentials(env_vars: ClientEnvironmentVariables) -> CredentialProvider:
|
|
21
|
+
missing: list[str] = []
|
|
22
|
+
if not env_vars.IDP_CLIENT_ID:
|
|
23
|
+
missing.append("IDP_CLIENT_ID")
|
|
24
|
+
if not env_vars.IDP_CLIENT_SECRET:
|
|
25
|
+
missing.append("IDP_CLIENT_SECRET")
|
|
26
|
+
if env_vars.IDP_CLIENT_ID is None or env_vars.IDP_CLIENT_SECRET is None:
|
|
27
|
+
raise ValueError(
|
|
28
|
+
f"The following environment variables must be set for "
|
|
29
|
+
f"client credentials authentication: {humanize_collection(missing)}"
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
if env_vars.PROVIDER == "cdf":
|
|
33
|
+
return OAuthClientCredentials(
|
|
34
|
+
client_id=env_vars.IDP_CLIENT_ID,
|
|
35
|
+
client_secret=env_vars.IDP_CLIENT_SECRET,
|
|
36
|
+
token_url=env_vars.idp_token_url,
|
|
37
|
+
scopes=None, # type: ignore[arg-type]
|
|
38
|
+
)
|
|
39
|
+
return OAuthClientCredentials(
|
|
40
|
+
client_id=env_vars.IDP_CLIENT_ID,
|
|
41
|
+
client_secret=env_vars.IDP_CLIENT_SECRET,
|
|
42
|
+
token_url=env_vars.idp_token_url,
|
|
43
|
+
audience=env_vars.idp_audience,
|
|
44
|
+
scopes=env_vars.idp_scopes,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def create_interactive_credentials(env_vars: ClientEnvironmentVariables) -> CredentialProvider:
|
|
49
|
+
if not env_vars.IDP_CLIENT_ID:
|
|
50
|
+
raise ValueError("IDP_CLIENT_ID environment variable must be set for interactive authentication.")
|
|
51
|
+
return OAuthInteractive(
|
|
52
|
+
client_id=env_vars.IDP_CLIENT_ID,
|
|
53
|
+
authority_url=env_vars.idp_authority_url,
|
|
54
|
+
scopes=env_vars.idp_scopes,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def create_token_credentials(env_vars: ClientEnvironmentVariables) -> CredentialProvider:
|
|
59
|
+
if not env_vars.CDF_TOKEN:
|
|
60
|
+
raise ValueError("CDF_TOKEN environment variable must be set for token authentication.")
|
|
61
|
+
return Token(env_vars.CDF_TOKEN)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def create_infer_credentials(env_vars: ClientEnvironmentVariables) -> CredentialProvider:
|
|
65
|
+
if env_vars.IDP_CLIENT_SECRET:
|
|
66
|
+
return create_client_credentials(env_vars)
|
|
67
|
+
elif env_vars.CDF_TOKEN:
|
|
68
|
+
return create_token_credentials(env_vars)
|
|
69
|
+
else:
|
|
70
|
+
return create_interactive_credentials(env_vars)
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Any, Literal, TypeAlias, get_args
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel, ConfigDict, ValidationError
|
|
6
|
+
|
|
7
|
+
from cognite.neat._utils.repo import get_repo_root
|
|
8
|
+
from cognite.neat._utils.validation import humanize_validation_error
|
|
9
|
+
|
|
10
|
+
if sys.version_info >= (3, 11):
|
|
11
|
+
from typing import Self
|
|
12
|
+
else:
|
|
13
|
+
from typing_extensions import Self
|
|
14
|
+
|
|
15
|
+
LoginFlow: TypeAlias = Literal["infer", "client_credentials", "interactive", "token"]
|
|
16
|
+
VALID_LOGIN_FLOWS = get_args(LoginFlow)
|
|
17
|
+
Provider: TypeAlias = Literal["entra_id", "auth0", "cdf", "other"]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ClientEnvironmentVariables(BaseModel):
|
|
21
|
+
"""Configuration for environment variables used by the NEAT client."""
|
|
22
|
+
|
|
23
|
+
model_config = ConfigDict(extra="forbid", frozen=True)
|
|
24
|
+
CDF_CLUSTER: str
|
|
25
|
+
CDF_PROJECT: str
|
|
26
|
+
PROVIDER: Provider = "entra_id"
|
|
27
|
+
LOGIN_FLOW: LoginFlow = "infer"
|
|
28
|
+
|
|
29
|
+
IDP_CLIENT_ID: str | None = None
|
|
30
|
+
IDP_CLIENT_SECRET: str | None = None
|
|
31
|
+
CDF_TOKEN: str | None = None
|
|
32
|
+
|
|
33
|
+
IDP_TENANT_ID: str | None = None
|
|
34
|
+
IDP_TOKEN_URL: str | None = None
|
|
35
|
+
|
|
36
|
+
CDF_URL: str | None = None
|
|
37
|
+
IDP_AUDIENCE: str | None = None
|
|
38
|
+
IDP_SCOPES: str | None = None
|
|
39
|
+
IDP_AUTHORITY_URL: str | None = None
|
|
40
|
+
IDP_DISCOVERY_URL: str | None = None
|
|
41
|
+
CDF_MAX_WORKERS: int | None = None
|
|
42
|
+
CDF_CLIENT_TIMEOUT: int | None = None
|
|
43
|
+
CDF_REDIRECT_PORT: int = 53_000
|
|
44
|
+
|
|
45
|
+
@classmethod
|
|
46
|
+
def create_humanize(cls, values: dict[str, Any]) -> Self:
|
|
47
|
+
try:
|
|
48
|
+
return cls.model_validate(values)
|
|
49
|
+
except ValidationError as e:
|
|
50
|
+
errors = [humanize_validation_error(error) for error in e.errors()]
|
|
51
|
+
raise ValueError("Invalid environment variable configuration:\n" + "\n - ".join(errors)) from e
|
|
52
|
+
|
|
53
|
+
@property
|
|
54
|
+
def idp_tenant_id(self) -> str:
|
|
55
|
+
if self.IDP_TENANT_ID:
|
|
56
|
+
return self.IDP_TENANT_ID
|
|
57
|
+
# This line is technically unreachable due to the checks in idp_token_url and idp_authority_url
|
|
58
|
+
raise RuntimeError("IDP_TENANT_ID is missing")
|
|
59
|
+
|
|
60
|
+
@property
|
|
61
|
+
def idp_token_url(self) -> str:
|
|
62
|
+
if self.PROVIDER == "cdf":
|
|
63
|
+
return "https://auth.cognite.com/oauth2/token"
|
|
64
|
+
if self.IDP_TOKEN_URL:
|
|
65
|
+
return self.IDP_TOKEN_URL
|
|
66
|
+
if self.PROVIDER == "entra_id" and self.IDP_TENANT_ID:
|
|
67
|
+
return f"https://login.microsoftonline.com/{self.IDP_TENANT_ID}/oauth2/v2.0/token"
|
|
68
|
+
alternative = " or provide IDP_TENANT_ID" if self.PROVIDER == "entra_id" else ""
|
|
69
|
+
raise ValueError(
|
|
70
|
+
f"IDP_TOKEN_URL is missing. Please provide it{alternative} in the environment variables.",
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
@property
|
|
74
|
+
def cdf_url(self) -> str:
|
|
75
|
+
return self.CDF_URL or f"https://{self.CDF_CLUSTER}.cognitedata.com"
|
|
76
|
+
|
|
77
|
+
@property
|
|
78
|
+
def idp_audience(self) -> str:
|
|
79
|
+
if self.IDP_AUDIENCE:
|
|
80
|
+
return self.IDP_AUDIENCE
|
|
81
|
+
if self.PROVIDER == "auth0":
|
|
82
|
+
return f"https://{self.CDF_PROJECT}.fusion.cognite.com/{self.CDF_PROJECT}"
|
|
83
|
+
else:
|
|
84
|
+
return f"https://{self.CDF_CLUSTER}.cognitedata.com"
|
|
85
|
+
|
|
86
|
+
@property
|
|
87
|
+
def idp_scopes(self) -> list[str]:
|
|
88
|
+
if self.IDP_SCOPES:
|
|
89
|
+
return self.IDP_SCOPES.split(",")
|
|
90
|
+
if self.PROVIDER == "auth0":
|
|
91
|
+
return ["IDENTITY", "user_impersonation"]
|
|
92
|
+
return [f"https://{self.CDF_CLUSTER}.cognitedata.com/.default"]
|
|
93
|
+
|
|
94
|
+
@property
|
|
95
|
+
def idp_authority_url(self) -> str:
|
|
96
|
+
if self.IDP_AUTHORITY_URL:
|
|
97
|
+
return self.IDP_AUTHORITY_URL
|
|
98
|
+
if self.PROVIDER == "entra_id" and self.IDP_TENANT_ID:
|
|
99
|
+
return f"https://login.microsoftonline.com/{self.IDP_TENANT_ID}"
|
|
100
|
+
alternative = " or provide IDP_TENANT_ID" if self.PROVIDER == "entra_id" else ""
|
|
101
|
+
raise ValueError(
|
|
102
|
+
f"IDP_AUTHORITY_URL is missing. Please provide it{alternative} in the environment variables.",
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def get_environment_variables(env_file_name: str) -> ClientEnvironmentVariables:
|
|
107
|
+
to_search: list[tuple[str, Path]] = []
|
|
108
|
+
try:
|
|
109
|
+
repo_root = get_repo_root()
|
|
110
|
+
except RuntimeError:
|
|
111
|
+
...
|
|
112
|
+
else:
|
|
113
|
+
to_search.append(("repository root", repo_root))
|
|
114
|
+
to_search.append(("current working directory", Path.cwd()))
|
|
115
|
+
for location_desc, path in to_search:
|
|
116
|
+
env_path = path / env_file_name
|
|
117
|
+
if env_path.is_file():
|
|
118
|
+
print(f"Found {env_file_name} in {location_desc}.")
|
|
119
|
+
return _parse_env_file(env_path)
|
|
120
|
+
raise FileNotFoundError(f"Could not find {env_file_name} in the repository root or current working directory.")
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _parse_env_file(env_file_path: Path) -> ClientEnvironmentVariables:
|
|
124
|
+
content = env_file_path.read_text()
|
|
125
|
+
variables: dict[str, Any] = {}
|
|
126
|
+
for line in content.splitlines():
|
|
127
|
+
if line.startswith("#") or "=" not in line:
|
|
128
|
+
continue
|
|
129
|
+
key, value = line.strip().split("=", 1)
|
|
130
|
+
variables[key] = value
|
|
131
|
+
return ClientEnvironmentVariables.create_humanize(variables)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
from cognite.client import CogniteClient
|
|
2
|
+
from cognite.client.config import ClientConfig, global_config
|
|
3
|
+
|
|
4
|
+
from cognite.neat import _version
|
|
5
|
+
|
|
6
|
+
from .credentials import get_credentials
|
|
7
|
+
from .env_vars import ClientEnvironmentVariables, get_environment_variables
|
|
8
|
+
|
|
9
|
+
CLIENT_NAME = f"CogniteNeat:{_version.__version__}"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def get_cognite_client(env_file_name: str) -> CogniteClient:
|
|
13
|
+
"""Get a CogniteClient using environment variables from a .env file."
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
env_file_name: The name of the .env file to look for in the repository root / current working directory. If
|
|
17
|
+
the file is found, the variables will be loaded from the file. If the file is not found, the user will
|
|
18
|
+
be prompted to enter the variables and the file will be created.
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
CogniteClient: An instance of CogniteClient configured with the loaded environment variables.
|
|
22
|
+
"""
|
|
23
|
+
try:
|
|
24
|
+
return get_cognite_client_internal(env_file_name)
|
|
25
|
+
except Exception as e:
|
|
26
|
+
raise RuntimeError(f"Failed to create client ❌: {e!s}") from None
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def get_cognite_client_internal(env_file_name: str) -> CogniteClient:
|
|
30
|
+
# This function raises exceptions on failure
|
|
31
|
+
if not env_file_name.endswith(".env"):
|
|
32
|
+
raise ValueError(f"env_file_name must end with '.env'. Got: {env_file_name!r}")
|
|
33
|
+
global_config.disable_pypi_version_check = True
|
|
34
|
+
global_config.silence_feature_preview_warnings = True
|
|
35
|
+
env_vars = get_environment_variables(env_file_name)
|
|
36
|
+
client_config = create_client_config_from_env_vars(env_vars)
|
|
37
|
+
# Todo validate credentials by making a simple call to CDF
|
|
38
|
+
# Offer to store credentials securely if valid
|
|
39
|
+
#
|
|
40
|
+
return CogniteClient(client_config)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def create_client_config_from_env_vars(env_vars: ClientEnvironmentVariables) -> ClientConfig:
|
|
44
|
+
return ClientConfig(
|
|
45
|
+
client_name=CLIENT_NAME,
|
|
46
|
+
project=env_vars.CDF_PROJECT,
|
|
47
|
+
credentials=get_credentials(env_vars),
|
|
48
|
+
max_workers=env_vars.CDF_MAX_WORKERS,
|
|
49
|
+
timeout=env_vars.CDF_CLIENT_TIMEOUT,
|
|
50
|
+
base_url=env_vars.cdf_url,
|
|
51
|
+
)
|