From 0523adbd97ff9687cf7353e22b9ad5c3041ffe31 Mon Sep 17 00:00:00 2001 From: nikokaoja Date: Mon, 22 Jul 2024 17:47:20 +0200 Subject: [PATCH 1/3] remove legacy --- cognite/neat/legacy/__init__.py | 0 cognite/neat/legacy/graph/__init__.py | 3 - .../Knowledge-Graph-Nordic44-dirty.xml | 20182 ---------------- .../examples/Knowledge-Graph-Nordic44.xml | 20163 --------------- .../neat/legacy/graph/examples/__init__.py | 10 - .../skos-capturing-sheet-wind-topics.xlsx | Bin 45798 -> 0 bytes cognite/neat/legacy/graph/exceptions.py | 90 - .../neat/legacy/graph/extractors/__init__.py | 6 - cognite/neat/legacy/graph/extractors/_base.py | 14 - .../neat/legacy/graph/extractors/_dexpi.py | 44 - .../extractors/_graph_capturing_sheet.py | 403 - .../graph/extractors/_mock_graph_generator.py | 361 - cognite/neat/legacy/graph/loaders/__init__.py | 23 - .../legacy/graph/loaders/_asset_loader.py | 511 - cognite/neat/legacy/graph/loaders/_base.py | 67 - .../neat/legacy/graph/loaders/_exceptions.py | 85 - .../legacy/graph/loaders/core/__init__.py | 0 .../neat/legacy/graph/loaders/core/labels.py | 58 - .../neat/legacy/graph/loaders/core/models.py | 136 - .../graph/loaders/core/rdf_to_assets.py | 1046 - .../loaders/core/rdf_to_relationships.py | 559 - .../neat/legacy/graph/loaders/rdf_to_dms.py | 309 - .../neat/legacy/graph/loaders/validator.py | 87 - cognite/neat/legacy/graph/models.py | 6 - cognite/neat/legacy/graph/stores/__init__.py | 13 - cognite/neat/legacy/graph/stores/_base.py | 400 - .../legacy/graph/stores/_graphdb_store.py | 52 - .../neat/legacy/graph/stores/_memory_store.py | 43 - .../legacy/graph/stores/_oxigraph_store.py | 151 - cognite/neat/legacy/graph/stores/_oxrdflib.py | 247 - .../neat/legacy/graph/stores/_rdf_to_graph.py | 42 - .../legacy/graph/transformations/__init__.py | 0 .../graph/transformations/entity_matcher.py | 101 - .../query_generator/__init__.py | 3 - .../transformations/query_generator/sparql.py | 575 - .../graph/transformations/transformer.py | 322 - cognite/neat/legacy/rules/__init__.py | 0 cognite/neat/legacy/rules/analysis.py | 231 - .../examples/Rules-Nordic44-to-graphql.xlsx | Bin 80404 -> 0 bytes .../legacy/rules/examples/Rules-Nordic44.xlsx | Bin 78798 -> 0 bytes .../neat/legacy/rules/examples/__init__.py | 18 - .../rules/examples/power-grid-containers.yaml | 124 - .../rules/examples/power-grid-example.xlsx | Bin 77055 -> 0 bytes .../rules/examples/power-grid-model.yaml | 224 - .../legacy/rules/examples/rules-template.xlsx | Bin 75865 -> 0 bytes .../sheet2cdf-transformation-rules.xlsx | Bin 52433 -> 0 bytes .../legacy/rules/examples/skos-rules.xlsx | Bin 26008 -> 0 bytes .../source-to-solution-mapping-rules.xlsx | Bin 79981 -> 0 bytes .../legacy/rules/examples/wind-energy.owl | 1511 -- cognite/neat/legacy/rules/exceptions.py | 2972 --- .../neat/legacy/rules/exporters/__init__.py | 20 - cognite/neat/legacy/rules/exporters/_base.py | 45 - .../legacy/rules/exporters/_core/__init__.py | 5 - .../rules/exporters/_core/rules2labels.py | 24 - .../neat/legacy/rules/exporters/_rules2dms.py | 885 - .../legacy/rules/exporters/_rules2excel.py | 213 - .../legacy/rules/exporters/_rules2graphql.py | 183 - .../legacy/rules/exporters/_rules2ontology.py | 524 - .../rules/exporters/_rules2pydantic_models.py | 748 - .../legacy/rules/exporters/_rules2rules.py | 105 - .../legacy/rules/exporters/_rules2triples.py | 38 - .../legacy/rules/exporters/_validation.py | 146 - .../neat/legacy/rules/importers/__init__.py | 22 - cognite/neat/legacy/rules/importers/_base.py | 66 - .../legacy/rules/importers/_dict2rules.py | 158 - .../neat/legacy/rules/importers/_dms2rules.py | 194 - .../legacy/rules/importers/_graph2rules.py | 308 - .../legacy/rules/importers/_json2rules.py | 39 - .../rules/importers/_owl2rules/__init__.py | 3 - .../importers/_owl2rules/_owl2classes.py | 239 - .../importers/_owl2rules/_owl2metadata.py | 260 - .../importers/_owl2rules/_owl2properties.py | 217 - .../rules/importers/_owl2rules/_owl2rules.py | 290 - .../rules/importers/_spreadsheet2rules.py | 45 - .../neat/legacy/rules/importers/_xsd2rules.py | 20 - .../legacy/rules/importers/_yaml2rules.py | 39 - cognite/neat/legacy/rules/models/__init__.py | 5 - cognite/neat/legacy/rules/models/_base.py | 151 - cognite/neat/legacy/rules/models/raw_rules.py | 316 - cognite/neat/legacy/rules/models/rdfpath.py | 237 - cognite/neat/legacy/rules/models/rules.py | 1289 - cognite/neat/legacy/rules/models/tables.py | 9 - .../neat/legacy/rules/models/value_types.py | 118 - .../examples/Export_DMS/workflow.yaml | 89 - .../Export_Rules_to_Ontology/workflow.yaml | 152 - .../workflow.yaml | 139 - .../workflow.yaml | 270 - .../examples/Import_DMS/workflow.yaml | 65 - .../Ontology_to_Data_Model/workflow.yaml | 116 - .../examples/Validate_Rules/workflow.yaml | 67 - .../Validate_Solution_Model/workflow.yaml | 64 - .../workflow.yaml | 95 - .../workflow.yaml | 111 - cognite/neat/workflows/migration/__init__.py | 0 cognite/neat/workflows/migration/steps.py | 91 - .../neat/workflows/migration/wf_manifests.py | 33 - .../workflows/steps/lib/legacy/__init__.py | 7 - .../lib/legacy/graph_contextualization.py | 82 - .../steps/lib/legacy/graph_extractor.py | 746 - .../steps/lib/legacy/graph_loader.py | 606 - .../workflows/steps/lib/legacy/graph_store.py | 307 - .../steps/lib/legacy/graph_transformer.py | 58 - .../steps/lib/legacy/rules_exporter.py | 511 - .../steps/lib/legacy/rules_importer.py | 612 - tests/tests_legacy/__init__.py | 0 tests/tests_legacy/conftest.py | 197 - tests/tests_legacy/graph/__init__.py | 0 .../tests_legacy/graph/extractors/__init__.py | 0 .../graph/extractors/test_dexpi.py | 9 - .../graph/extractors/test_mock_graph.py | 42 - .../extractors/test_rules2graph_sheet.py | 19 - .../graph/extractors/test_sheet2graph.py | 30 - tests/tests_legacy/graph/loaders/__init__.py | 0 .../graph/loaders/core/__init__.py | 0 .../graph/loaders/core/test_rdf_to_assets.py | 193 - .../loaders/core/test_rdf_to_relationships.py | 97 - .../graph/loaders/test_asset_loader.py | 68 - .../graph/loaders/test_rdf_to_dms.py | 76 - .../graph/loaders/test_validator.py | 29 - .../graph/transformations/__init__.py | 0 .../query_generator/__init__.py | 0 .../query_generator/test_sparql.py | 24 - .../test_alternative_property.py | 72 - .../graph/transformations/test_transformer.py | 57 - tests/tests_legacy/test_loader.py | 27 - tests/tests_legacy/tests_rules/__init__.py | 0 .../tests_rules/exporter/__init__.py | 0 .../tests_rules/exporter/test_rules2dms.py | 223 - .../tests_rules/exporter/test_rules2excel.py | 16 - .../exporter/test_rules2graphql.py | 18 - .../exporter/test_rules2ontology.py | 51 - .../exporter/test_rules2pydantic_models.py | 68 - .../exporter/test_rules2triples.py | 5 - .../tests_rules/importer/__init__.py | 0 .../tests_rules/importer/test_dms2tables.py | 39 - .../tests_rules/importer/test_excel2rules.py | 37 - .../tests_rules/importer/test_graph2tables.py | 10 - .../tests_rules/importer/test_json2tables.py | 24 - .../importer/test_ontology2rules.py | 12 - .../tests_rules/test_analysis_rules_v1.py | 29 - .../tests_rules/test_models_old.py | 43 - .../tests_legacy/tests_rules/test_rdfpath.py | 348 - .../tests_import_export/__init__.py | 0 .../tests_import_export/test_export_to_dms.py | 48 - tests/tests_legacy/workflows/__init__.py | 0 .../tests_legacy/workflows/steps/__init__.py | 0 .../workflows/steps/test_graph_loader.py | 115 - 147 files changed, 64400 deletions(-) delete mode 100644 cognite/neat/legacy/__init__.py delete mode 100644 cognite/neat/legacy/graph/__init__.py delete mode 100644 cognite/neat/legacy/graph/examples/Knowledge-Graph-Nordic44-dirty.xml delete mode 100644 cognite/neat/legacy/graph/examples/Knowledge-Graph-Nordic44.xml delete mode 100644 cognite/neat/legacy/graph/examples/__init__.py delete mode 100644 cognite/neat/legacy/graph/examples/skos-capturing-sheet-wind-topics.xlsx delete mode 100644 cognite/neat/legacy/graph/exceptions.py delete mode 100644 cognite/neat/legacy/graph/extractors/__init__.py delete mode 100644 cognite/neat/legacy/graph/extractors/_base.py delete mode 100644 cognite/neat/legacy/graph/extractors/_dexpi.py delete mode 100644 cognite/neat/legacy/graph/extractors/_graph_capturing_sheet.py delete mode 100644 cognite/neat/legacy/graph/extractors/_mock_graph_generator.py delete mode 100644 cognite/neat/legacy/graph/loaders/__init__.py delete mode 100644 cognite/neat/legacy/graph/loaders/_asset_loader.py delete mode 100644 cognite/neat/legacy/graph/loaders/_base.py delete mode 100644 cognite/neat/legacy/graph/loaders/_exceptions.py delete mode 100644 cognite/neat/legacy/graph/loaders/core/__init__.py delete mode 100644 cognite/neat/legacy/graph/loaders/core/labels.py delete mode 100644 cognite/neat/legacy/graph/loaders/core/models.py delete mode 100644 cognite/neat/legacy/graph/loaders/core/rdf_to_assets.py delete mode 100644 cognite/neat/legacy/graph/loaders/core/rdf_to_relationships.py delete mode 100644 cognite/neat/legacy/graph/loaders/rdf_to_dms.py delete mode 100644 cognite/neat/legacy/graph/loaders/validator.py delete mode 100644 cognite/neat/legacy/graph/models.py delete mode 100644 cognite/neat/legacy/graph/stores/__init__.py delete mode 100644 cognite/neat/legacy/graph/stores/_base.py delete mode 100644 cognite/neat/legacy/graph/stores/_graphdb_store.py delete mode 100644 cognite/neat/legacy/graph/stores/_memory_store.py delete mode 100644 cognite/neat/legacy/graph/stores/_oxigraph_store.py delete mode 100644 cognite/neat/legacy/graph/stores/_oxrdflib.py delete mode 100644 cognite/neat/legacy/graph/stores/_rdf_to_graph.py delete mode 100644 cognite/neat/legacy/graph/transformations/__init__.py delete mode 100644 cognite/neat/legacy/graph/transformations/entity_matcher.py delete mode 100644 cognite/neat/legacy/graph/transformations/query_generator/__init__.py delete mode 100644 cognite/neat/legacy/graph/transformations/query_generator/sparql.py delete mode 100644 cognite/neat/legacy/graph/transformations/transformer.py delete mode 100644 cognite/neat/legacy/rules/__init__.py delete mode 100644 cognite/neat/legacy/rules/analysis.py delete mode 100644 cognite/neat/legacy/rules/examples/Rules-Nordic44-to-graphql.xlsx delete mode 100644 cognite/neat/legacy/rules/examples/Rules-Nordic44.xlsx delete mode 100644 cognite/neat/legacy/rules/examples/__init__.py delete mode 100644 cognite/neat/legacy/rules/examples/power-grid-containers.yaml delete mode 100644 cognite/neat/legacy/rules/examples/power-grid-example.xlsx delete mode 100644 cognite/neat/legacy/rules/examples/power-grid-model.yaml delete mode 100644 cognite/neat/legacy/rules/examples/rules-template.xlsx delete mode 100644 cognite/neat/legacy/rules/examples/sheet2cdf-transformation-rules.xlsx delete mode 100644 cognite/neat/legacy/rules/examples/skos-rules.xlsx delete mode 100644 cognite/neat/legacy/rules/examples/source-to-solution-mapping-rules.xlsx delete mode 100644 cognite/neat/legacy/rules/examples/wind-energy.owl delete mode 100644 cognite/neat/legacy/rules/exceptions.py delete mode 100644 cognite/neat/legacy/rules/exporters/__init__.py delete mode 100644 cognite/neat/legacy/rules/exporters/_base.py delete mode 100644 cognite/neat/legacy/rules/exporters/_core/__init__.py delete mode 100644 cognite/neat/legacy/rules/exporters/_core/rules2labels.py delete mode 100644 cognite/neat/legacy/rules/exporters/_rules2dms.py delete mode 100644 cognite/neat/legacy/rules/exporters/_rules2excel.py delete mode 100644 cognite/neat/legacy/rules/exporters/_rules2graphql.py delete mode 100644 cognite/neat/legacy/rules/exporters/_rules2ontology.py delete mode 100644 cognite/neat/legacy/rules/exporters/_rules2pydantic_models.py delete mode 100644 cognite/neat/legacy/rules/exporters/_rules2rules.py delete mode 100644 cognite/neat/legacy/rules/exporters/_rules2triples.py delete mode 100644 cognite/neat/legacy/rules/exporters/_validation.py delete mode 100644 cognite/neat/legacy/rules/importers/__init__.py delete mode 100644 cognite/neat/legacy/rules/importers/_base.py delete mode 100644 cognite/neat/legacy/rules/importers/_dict2rules.py delete mode 100644 cognite/neat/legacy/rules/importers/_dms2rules.py delete mode 100644 cognite/neat/legacy/rules/importers/_graph2rules.py delete mode 100644 cognite/neat/legacy/rules/importers/_json2rules.py delete mode 100644 cognite/neat/legacy/rules/importers/_owl2rules/__init__.py delete mode 100644 cognite/neat/legacy/rules/importers/_owl2rules/_owl2classes.py delete mode 100644 cognite/neat/legacy/rules/importers/_owl2rules/_owl2metadata.py delete mode 100644 cognite/neat/legacy/rules/importers/_owl2rules/_owl2properties.py delete mode 100644 cognite/neat/legacy/rules/importers/_owl2rules/_owl2rules.py delete mode 100644 cognite/neat/legacy/rules/importers/_spreadsheet2rules.py delete mode 100644 cognite/neat/legacy/rules/importers/_xsd2rules.py delete mode 100644 cognite/neat/legacy/rules/importers/_yaml2rules.py delete mode 100644 cognite/neat/legacy/rules/models/__init__.py delete mode 100644 cognite/neat/legacy/rules/models/_base.py delete mode 100644 cognite/neat/legacy/rules/models/raw_rules.py delete mode 100644 cognite/neat/legacy/rules/models/rdfpath.py delete mode 100644 cognite/neat/legacy/rules/models/rules.py delete mode 100644 cognite/neat/legacy/rules/models/tables.py delete mode 100644 cognite/neat/legacy/rules/models/value_types.py delete mode 100644 cognite/neat/legacy/workflows/examples/Export_DMS/workflow.yaml delete mode 100644 cognite/neat/legacy/workflows/examples/Export_Rules_to_Ontology/workflow.yaml delete mode 100644 cognite/neat/legacy/workflows/examples/Extract_DEXPI_Graph_and_Export_Rules/workflow.yaml delete mode 100644 cognite/neat/legacy/workflows/examples/Extract_RDF_Graph_and_Generate_Assets/workflow.yaml delete mode 100644 cognite/neat/legacy/workflows/examples/Import_DMS/workflow.yaml delete mode 100644 cognite/neat/legacy/workflows/examples/Ontology_to_Data_Model/workflow.yaml delete mode 100644 cognite/neat/legacy/workflows/examples/Validate_Rules/workflow.yaml delete mode 100644 cognite/neat/legacy/workflows/examples/Validate_Solution_Model/workflow.yaml delete mode 100644 cognite/neat/legacy/workflows/examples/Visualize_Data_Model_Using_Mock_Graph/workflow.yaml delete mode 100644 cognite/neat/legacy/workflows/examples/Visualize_Semantic_Data_Model/workflow.yaml delete mode 100644 cognite/neat/workflows/migration/__init__.py delete mode 100644 cognite/neat/workflows/migration/steps.py delete mode 100644 cognite/neat/workflows/migration/wf_manifests.py delete mode 100644 cognite/neat/workflows/steps/lib/legacy/__init__.py delete mode 100644 cognite/neat/workflows/steps/lib/legacy/graph_contextualization.py delete mode 100644 cognite/neat/workflows/steps/lib/legacy/graph_extractor.py delete mode 100644 cognite/neat/workflows/steps/lib/legacy/graph_loader.py delete mode 100644 cognite/neat/workflows/steps/lib/legacy/graph_store.py delete mode 100644 cognite/neat/workflows/steps/lib/legacy/graph_transformer.py delete mode 100644 cognite/neat/workflows/steps/lib/legacy/rules_exporter.py delete mode 100644 cognite/neat/workflows/steps/lib/legacy/rules_importer.py delete mode 100644 tests/tests_legacy/__init__.py delete mode 100644 tests/tests_legacy/conftest.py delete mode 100644 tests/tests_legacy/graph/__init__.py delete mode 100644 tests/tests_legacy/graph/extractors/__init__.py delete mode 100644 tests/tests_legacy/graph/extractors/test_dexpi.py delete mode 100644 tests/tests_legacy/graph/extractors/test_mock_graph.py delete mode 100644 tests/tests_legacy/graph/extractors/test_rules2graph_sheet.py delete mode 100644 tests/tests_legacy/graph/extractors/test_sheet2graph.py delete mode 100644 tests/tests_legacy/graph/loaders/__init__.py delete mode 100644 tests/tests_legacy/graph/loaders/core/__init__.py delete mode 100644 tests/tests_legacy/graph/loaders/core/test_rdf_to_assets.py delete mode 100644 tests/tests_legacy/graph/loaders/core/test_rdf_to_relationships.py delete mode 100644 tests/tests_legacy/graph/loaders/test_asset_loader.py delete mode 100644 tests/tests_legacy/graph/loaders/test_rdf_to_dms.py delete mode 100644 tests/tests_legacy/graph/loaders/test_validator.py delete mode 100644 tests/tests_legacy/graph/transformations/__init__.py delete mode 100644 tests/tests_legacy/graph/transformations/query_generator/__init__.py delete mode 100644 tests/tests_legacy/graph/transformations/query_generator/test_sparql.py delete mode 100644 tests/tests_legacy/graph/transformations/test_alternative_property.py delete mode 100644 tests/tests_legacy/graph/transformations/test_transformer.py delete mode 100644 tests/tests_legacy/test_loader.py delete mode 100644 tests/tests_legacy/tests_rules/__init__.py delete mode 100644 tests/tests_legacy/tests_rules/exporter/__init__.py delete mode 100644 tests/tests_legacy/tests_rules/exporter/test_rules2dms.py delete mode 100644 tests/tests_legacy/tests_rules/exporter/test_rules2excel.py delete mode 100644 tests/tests_legacy/tests_rules/exporter/test_rules2graphql.py delete mode 100644 tests/tests_legacy/tests_rules/exporter/test_rules2ontology.py delete mode 100644 tests/tests_legacy/tests_rules/exporter/test_rules2pydantic_models.py delete mode 100644 tests/tests_legacy/tests_rules/exporter/test_rules2triples.py delete mode 100644 tests/tests_legacy/tests_rules/importer/__init__.py delete mode 100644 tests/tests_legacy/tests_rules/importer/test_dms2tables.py delete mode 100644 tests/tests_legacy/tests_rules/importer/test_excel2rules.py delete mode 100644 tests/tests_legacy/tests_rules/importer/test_graph2tables.py delete mode 100644 tests/tests_legacy/tests_rules/importer/test_json2tables.py delete mode 100644 tests/tests_legacy/tests_rules/importer/test_ontology2rules.py delete mode 100644 tests/tests_legacy/tests_rules/test_analysis_rules_v1.py delete mode 100644 tests/tests_legacy/tests_rules/test_models_old.py delete mode 100644 tests/tests_legacy/tests_rules/test_rdfpath.py delete mode 100644 tests/tests_legacy/tests_rules/tests_import_export/__init__.py delete mode 100644 tests/tests_legacy/tests_rules/tests_import_export/test_export_to_dms.py delete mode 100644 tests/tests_legacy/workflows/__init__.py delete mode 100644 tests/tests_legacy/workflows/steps/__init__.py delete mode 100644 tests/tests_legacy/workflows/steps/test_graph_loader.py diff --git a/cognite/neat/legacy/__init__.py b/cognite/neat/legacy/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/cognite/neat/legacy/graph/__init__.py b/cognite/neat/legacy/graph/__init__.py deleted file mode 100644 index f83e37fb7..000000000 --- a/cognite/neat/legacy/graph/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .stores import NeatGraphStoreBase - -__all__ = ["NeatGraphStoreBase"] diff --git a/cognite/neat/legacy/graph/examples/Knowledge-Graph-Nordic44-dirty.xml b/cognite/neat/legacy/graph/examples/Knowledge-Graph-Nordic44-dirty.xml deleted file mode 100644 index 9d3a797dc..000000000 --- a/cognite/neat/legacy/graph/examples/Knowledge-Graph-Nordic44-dirty.xml +++ /dev/null @@ -1,20182 +0,0 @@ - - - - - - 2018-08-31T13:47:55 - 2015-03-06T00:30:00 - Statnett SF - 38 - CGM Test model developed by Statnett SF. Nordic 44 bus system for the Nordic region - http://www.Statnett.no/IGM/Nordic44_CGM - http://entsoe.eu/CIM/EquipmentCore/3/1 - http://entsoe.eu/CIM/EquipmentOperation/3/1 - http://entsoe.eu/CIM/EquipmentCore/3/2 - http://entsoe.eu/CIM/EquipmentOperation/3/2 - - - 0.0003333333 - 22.5 - 180 - 0 - - false - true - 3701 6700 '1 ' - 300AJAURE-MO - - - - 0 - 0 - 0.9 - 0 - - false - true - 5603 5610 '1 ' - 300ARENDAL-KRISTA_HVDC - - 1 - - - 0.0005555556 - 4.5 - 54 - 0 - - false - true - 5500 5603 '1 ' - 300ASKER-ARENDAL - - 1 - - - 0.0006666667 - 0.9 - 18 - 0 - - false - true - 3244 6500 '1 ' - 300HOGASEN-TRONDHEIM - - - - 0.0002222222 - 1.8 - 19.8 - 0 - - false - true - 5600 5603 '1 ' - 300KRISTIAN-ARENDAL - - 1 - - - 0 - 0 - 0.9 - 0 - - false - true - 5600 5620 '1 ' - 300KRISTIAN-FEDA - - 1 - - - 0.0007777778 - 1.8 - 18 - 0 - - false - true - 5600 6000 '1 ' - 300KRISTIAN-KVILLDAL - - - - 0.0002222222 - 2.7 - 30.6 - 0 - - false - true - 5600 5601 '1 ' - 300KRISTIAN-STAVANGE - - - - 0.0003333333 - 3.06 - 37.8 - 0 - - false - true - 6000 6100 '1 ' - 300KVILLDAL-BLAFALLI - - - - 0.0005555556 - 0.81 - 8.46 - 0 - - false - true - 5400 5500 '1 ' - 300OSLO-ASKER - - - - 0.0002777778 - 2.97 - 32.4 - 0 - - false - true - 5400 6000 '1 ' - 300OSLO-KVILLDAL - - - - 0.0001111111 - 1.89 - 19.8 - 0 - - false - true - 5300 6100 '1 ' - 300SIMA-BLAFALLI - - - - 0.0004888889 - 2.43 - 23.4 - 0 - - false - true - 5100 5500 '1 ' - 300TRETTEN-ASKER - - - - 0.0006666667 - 7.2 - 81 - 0 - - false - true - 5100 6500 '1 ' - 300TRETTEN-TRONDHEIM - - - - 0.0011111111 - 15.3 - 162 - 0 - - false - true - 6500 6700 '1 ' - 300TRONDHEIM-MO 1 - - - - 0.0013333333 - 9 - 117 - 0 - - false - true - 6500 6700 '2 ' - 300TRONDHEIM-MO 2 - - - - 0 - 0 - 0 - - false - 420ARENDAL-SANDEFJORD - - - - 0.000175737 - 1.2348 - 21.168 - 0 - - false - true - 5301 5305 '1 ' - 420AURLAND-EIDFJORD - - 1 - - - 0.0003401361 - 1.764 - 35.28 - 0 - - false - true - 5301 5304 '1 ' - 420AURLAND-GEILO - - 1 - - - 0.0002834467 - 2.2932 - 35.28 - 0 - - false - true - 5301 6001 '1 ' - 420AURLAND-HAGAFOSS - - 1 - - - 0.0003968254 - 2.9988 - 42.336 - 0 - - false - true - 5102 5304 '1 ' - 420DAGALI-GEILO - - - - 0.0007369615 - 5.292 - 81.144 - 0 - - false - true - 5102 6001 '1 ' - 420DAGALI-HAGAFOSS - - - - 0.000170068 - 0.7056 - 12.348 - 0 - - false - true - 5102 5103 '1 ' - 420DAGALI-KONGSBER - - - - 0 - 0 - 1.764 - 0 - - false - true - 3000 3020 '1 ' - 420FORSMARK-DANNEBO - - - - 0.0002834467 - 1.4112 - 21.168 - 0 - - false - true - 3000 3245 '1 ' - 420FORSMARK-JARPSTRO 1 - - - - 0.0002834467 - 3.1752 - 35.28 - 0 - - false - true - 3000 3245 '2 ' - 420FORSMARK-JARPSTRO 2 - - - - 0.000170068 - 1.0584 - 14.112 - 0 - - false - true - 3000 3300 '1 ' - 420FORSMARK-OSKARHA 1 - - - - 0.0001417234 - 1.5876 - 17.64 - 0 - - false - true - 3000 3300 '2 ' - 420FORSMARK-OSKARHA 2 - - - - 0.0028344671 - 13.23 - 158.76 - 0 - - false - true - 3000 3115 '1 ' - 420FORSMARK-PORJUS - - - - 0.0002834467 - 1.764 - 26.46 - 0 - - false - true - 5304 5305 '1 ' - 420GEILO-EIDFJORD 1 - - - - 0.0002267574 - 2.2932 - 2.9988 - 0 - - false - true - 5304 5305 '2 ' - 420GEILO-EIDFJORD 2 - - - - 0.0004421769 - 3.528 - 13.23 - 0 - - false - true - 3249 7100 '1 ' - 420GRUNDFOR-OULU - - - - 0.0005102041 - 1.4112 - 17.64 - 0 - - false - true - 5101 5102 '1 ' - 420HALDEN-DAGALI - - - - 0.0002267574 - 1.764 - 24.696 - 0 - - false - true - 5101 5103 '1 ' - 420HALDEN-KONGSBER - - - - 0.0031179138 - 1.764 - 26.46 - 0 - - false - true - 5101 5501 '1 ' - 420HALDEN-SKIEN - - - - 0 - 0 - 1.764 - 0 - - false - true - 7000 7020 '1 ' - 420HELSINKI-ESTLINK - - - - 0.0007369615 - 7.056 - 21.168 - 0 - - false - true - 7000 7100 '1 ' - 420HELSINKI-OULU 1 - - - - 0.0007369615 - 7.056 - 21.168 - 0 - - false - true - 7000 7100 '2 ' - 420HELSINKI-OULU 2 - - - - 0.0007369615 - 7.056 - 24.696 - 0 - - false - true - 7000 7100 '3 ' - 420HELSINKI-OULU 3 - - - - 0 - 0 - 1.764 - 0 - - false - true - 7000 7010 '1 ' - 420HELSINKI-VYBORG - - - - 0.0011337868 - 2.646 - 37.926 - 0 - - false - true - 3100 3249 '1 ' - 420HJALTA-GRUNDFOR - - - - 0.0011337868 - 2.646 - 35.28 - 0 - - false - true - 3100 3115 '1 ' - 420HJALTA-PORJUS - - - - 0.0014172336 - 14.112 - 88.2 - 0 - - false - true - 3100 3359 '1 ' - 420HJALTA-RINGHALS 1 - - - - 0.0013605442 - 7.056 - 40.572 - 0 - - false - true - 3100 3359 '2 ' - 420HJALTA-RINGHALS 2 - - - - 0.0011337868 - 7.056 - 42.336 - 0 - - false - true - 3100 3200 '1 ' - 420HJALTA-TENHULT 1 - - - - 0.0011337868 - 7.056 - 42.336 - 0 - - false - true - 3100 3200 '2 ' - 420HJALTA-TENHULT 2 - - - - 0.0011337868 - 7.056 - 42.336 - 0 - - false - true - 3100 3200 '3 ' - 420HJALTA-TENHULT 3 - - - - 0.0003968254 - 3.528 - 44.1 - 0 - - false - true - 5103 5304 '1 ' - 420KONGSBER-GEILO 1 - - - - 0.0003401361 - 2.2932 - 35.28 - 0 - - false - true - 5103 5304 '2 ' - 420KONGSBER-GEILO 2 - - - - 0 - 0 - 1.764 - 0 - - false - true - 8500 8600 '1 ' - 420MALMO-ARRIE - - - - 0 - 0 - 1.764 - 0 - - false - true - 8500 8700 '1 ' - 420MALMO-KARLSH - - - - 0.0003401361 - 3.528 - 40.572 - 0 - - false - true - 3300 8500 '1 ' - 420OSKARHA-MALMO 1 - - - - 0.0005668934 - 2.1168 - 47.628 - 0 - - false - true - 3300 8500 '2 ' - 420OSKARHA-MALMO 2 - - - - 0.0003401361 - 3.528 - 35.28 - 0 - - false - true - 3200 3300 '1 ' - 420OSKARHA-TENHULT - - - - 0.0004535147 - 2.646 - 35.28 - 0 - - false - true - 3115 3249 '1 ' - 420PORJUS-GRUNDFOR - - - - 0.0007936508 - 7.938 - 88.2 - 0 - - false - true - 3115 3245 '1 ' - 420PORJUS-JARPSTRO - - - - 0.0005668934 - 7.056 - 70.56 - 0 - - false - true - 3115 6701 '1 ' - 420PORJUS-NARVIK - - - - 0.0007369615 - 7.056 - 22.932 - 0 - - false - true - 3115 7100 '1 ' - 420PORJUS-OULU - - - - 0.0005102041 - 2.8224 - 45.864 - 0 - - false - true - 3359 5101 '1 ' - 420RINGHALS-HALDEN 1 - - - - - 0.0003401361 - 3.528 - 38.808 - 0 - - false - true - 3359 5101 '2 ' - 420RINGHALS-HALDEN 2 - - - - - 0.0005668934 - 2.1168 - 47.628 - 0 - - false - true - 3359 8500 '1 ' - 420RINGHALS-MALMO 1 - - - - 0.0005102041 - 4.41 - 56.448 - 0 - - false - true - 3359 8500 '2 ' - 420RINGHALS-MALMO 2 - - - - 0.0001587302 - 1.12896 - 17.64 - 0 - - false - true - 5401 6001 '1 ' - 420SYLLING-HAGAFOSS - - - - 0.0005102041 - 2.8224 - 44.982 - 0 - - false - true - 5401 5602 '1 ' - 420SYLLING-SANDEFJORD - - - - 0.0004535147 - 3.087 - 47.628 - 0 - - false - true - 5401 5501 '1 ' - 420SYLLING-SKIEN - - - - 1.70068E-05 - 0.12348 - 1.764 - 0 - - false - true - 5402 6001 '1 ' - 420SYSLEHAGAFOSS - - - - 0.0003401361 - 1.764 - 29.988 - 0 - - false - true - 3200 8500 '1 ' - 420TENHULT-MALMO - - - - 0.0003968254 - 1.764 - 35.28 - 0 - - false - true - 3200 3359 '1 ' - 420TENHULT-RINGHALS - - - - 2130 - 2130 - - - APL High WV - - - 1775 - 1775 - - - APL PATL at 20degree - - - 1605 - 1605 - - - APL PATL at 30degree - - - 1600 - 1600 - - - APL STABILITY - - - 2130 - 2130 - - - APL TATL at 20degree - - - 1925 - 1925 - - - APL TATL at 30degree - - - NO-BZ-1 HYDA P - NO1 BiddingZone Hydro Actual Active Power - - ThreePhaseActivePower - B11 - - - - - NO-BZ-1 HYDF P - NO1 BiddingZone Hydro Forecast Active Power - - ThreePhaseActivePower - B11 - - - - - NO-BZ-1 HYRAF P - NO1 BiddingZone Hydro Reservoir Availability Forecast Active Power - - ThreePhaseActivePower - B12 - - - - - NO-BZ-1 IGENC P - NO1 BiddingZone Installed Generating Capacity Active Power - - ThreePhaseActivePower - A68 - - - - - NO-BZ-1 IHYPC P - NO1 BiddingZone Installed Hydro Pump Capacity Active Power - - ThreePhaseActivePower - B10 - - - - - NO-BZ-1 IHYRC P - NO1 BiddingZone Installed Hydro Reservoir Capacity Active Power - - ThreePhaseActivePower - B12 - - - - - NO-BZ-1 IRORC P - NO1 BiddingZone Installed Run-of-river Capacity Active Power - - ThreePhaseActivePower - B11 - - - - - NO-BZ-1 IWINC P - NO1 BiddingZone Installed Wind Capacity Active Power - - ThreePhaseActivePower - B19 - - - - - NO-BZ-1 LA P - NO1 BiddingZone Load Actual Active Power - - ThreePhaseActivePower - A04 - - - - - NO-BZ-1 LF P - NO1 BiddingZone Load Forecast Active Power - - ThreePhaseActivePower - A04 - - - - - NO-BZ-1 NEXA P - NO1 BiddingZone Net Exchange Actual Active Power - - ThreePhaseActivePower - xx - - - - - NO-BZ-1 NEXF P - NO1 BiddingZone Net Exchange Forecast Active Power - - ThreePhaseActivePower - xx - - - - - NO-BZ-1 NUCA P - NO1 BiddingZone Nuclear Actual Active Power - - ThreePhaseActivePower - B14 - - - - - NO-BZ-1 NUCF P - NO1 BiddingZone Nuclear Forecast Active Power - - ThreePhaseActivePower - B14 - - - - - NO-BZ-1 OTHA P - NO1 BiddingZone Other Actual Active Power - - ThreePhaseActivePower - B20 - - - - - NO-BZ-1 OTHF P - NO1 BiddingZone Other Forecast Active Power - - ThreePhaseActivePower - B20 - - - - - NO-BZ-1 PROA P - NO1 BiddingZone Production Actual Active Power - - ThreePhaseActivePower - A38 - - - - - NO-BZ-1 PROF P - NO1 BiddingZone Production Forecast Active Power - - ThreePhaseActivePower - A38 - - - - - NO-BZ-1 PUMAF P - NO1 BiddingZone Hydro Pump Availability Forecast Active Power - - ThreePhaseActivePower - B10 - - - - - NO-BZ-1 RCF P - NO1 BiddingZone Remaining Capacity Forecast Active Power - - ThreePhaseActivePower - A38 - - - - - NO-BZ-1 RORA P - NO1 BiddingZone Run-of-river Actual Active Power - - ThreePhaseActivePower - B11 - - - - - NO-BZ-1 RORF P - NO1 BiddingZone Run-of-river Forecast Active Power - - ThreePhaseActivePower - B11 - - - - - NO-BZ-1 THRA P - NO1 BiddingZone Thermal Actual Active Power - - ThreePhaseActivePower - B04 - - - - - NO-BZ-1 THRF P - NO1 BiddingZone Thermal Forecast Active Power - - ThreePhaseActivePower - B04 - - - - - NO-BZ-1 WINA P - NO1 BiddingZone Wind Actual Active Power - - ThreePhaseActivePower - B19 - - - - - NO-BZ-1 WINF P - NO1 BiddingZone Wind Forecast Active Power - - ThreePhaseActivePower - B19 - - - - - NO-BZ-2 LA P - NO2 BiddingZone Load Actual Active Power - - ThreePhaseActivePower - A04 - - - - - NO-BZ-2 LF P - NO2 BiddingZone Load Forecast Active Power - - ThreePhaseActivePower - A04 - - - - - NO-BZ-3 LA P - NO3 BiddingZone Load Actual Active Power - - ThreePhaseActivePower - A04 - - - - - NO-BZ-3 LF P - NO3 BiddingZone Load Forecast Active Power - - ThreePhaseActivePower - A04 - - - - - - NO-BZ-1 HYD 1M P_V - NO1 BiddingZone Hydro Forecast Month-ahead - - 4050 - - - - NO-BZ-1 HYD 1Y P_V - NO1 BiddingZone Hydro Forecast Year-ahead - - 4050 - - - - NO-BZ-1 HYD 2D P_V - NO1 BiddingZone Hydro Forecast Two Day-ahead - - 4050 - - - - NO-BZ-1 HYD 3D P_V - NO1 BiddingZone Hydro Forecast Three Day-ahead - - 4050 - - - - NO-BZ-1 HYD 4D P_V - NO1 BiddingZone Hydro Forecast Four Day-ahead - - 4050 - - - - NO-BZ-1 HYD 5D P_V - NO1 BiddingZone Hydro Forecast Five Day-ahead - - 4050 - - - - NO-BZ-1 HYD 6D P_V - NO1 BiddingZone Hydro Forecast Six Day-ahead - - 4050 - - - - NO-BZ-1 HYD 7D P_V - NO1 BiddingZone Hydro Forecast Seven Day-ahead - - 4050 - - - - NO-BZ-1 HYDO P_V - NO1 BiddingZone Hydro Actual Observed - - 4000 - - - - NO-BZ-1 HYDS P_V - NO1 BiddingZone Hydro Actual Scheduled - - 4050 - - - - NO-BZ-1 HYP 1D P_V - NO1 BiddingZone Hydro Pump Availability Forecast Day-ahead - - 3720 - - - - NO-BZ-1 HYP 1M P_V - NO1 BiddingZone Hydro Pump Availability Forecast Month-ahead - - 3720 - - - - NO-BZ-1 HYP 1Y P_V - NO1 BiddingZone Hydro Pump Availability Forecast Year-ahead - - 3720 - - - - NO-BZ-1 HYP 2D P_V - NO1 BiddingZone Hydro Pump Availability Forecast Two Day-ahead - - 3720 - - - - NO-BZ-1 HYP 3D P_V - NO1 BiddingZone Hydro Pump Availability Forecast Three Day-ahead - - 3720 - - - - NO-BZ-1 HYP 4D P_V - NO1 BiddingZone Hydro Pump Availability Forecast Four Day-ahead - - 3720 - - - - NO-BZ-1 HYP 5D P_V - NO1 BiddingZone Hydro Pump Availability Forecast Five Day-ahead - - 3720 - - - - NO-BZ-1 HYP 6D P_V - NO1 BiddingZone Hydro Pump Availability Forecast Six Day-ahead - - 3720 - - - - NO-BZ-1 HYP 7D P_V - NO1 BiddingZone Hydro Pump Availability Forecast Seven Day-ahead - - 3720 - - - - NO-BZ-1 HYP ID P_V - NO1 BiddingZone Hydro Pump Availability Forecast Intraday - - 3720 - - - - NO-BZ-1 HYRA 1D P_V - NO1 BiddingZone Hydro Reservoir Availability Forecast Day-ahead - - 3720 - - - - NO-BZ-1 HYRA 1M P_V - NO1 BiddingZone Hydro Reservoir Availability Forecast Month-ahead - - 3720 - - - - NO-BZ-1 HYRA 1Y P_V - NO1 BiddingZone Hydro Reservoir Availability Forecast Year-ahead - - 3720 - - - - NO-BZ-1 HYRA 2D P_V - NO1 BiddingZone Hydro Reservoir Availability Forecast Two Day-ahead - - 3720 - - - - NO-BZ-1 HYRA 3D P_V - NO1 BiddingZone Hydro Reservoir Availability Forecast Three Day-ahead - - 3720 - - - - NO-BZ-1 HYRA 4D P_V - NO1 BiddingZone Hydro Reservoir Availability Forecast Four Day-ahead - - 3720 - - - - NO-BZ-1 HYRA 5D P_V - NO1 BiddingZone Hydro Reservoir Availability Forecast Five Day-ahead - - 3720 - - - - NO-BZ-1 HYRA 6D P_V - NO1 BiddingZone Hydro Reservoir Availability Forecast Six Day-ahead - - 3720 - - - - NO-BZ-1 HYRA 7D P_V - NO1 BiddingZone Hydro Reservoir Availability Forecast Seven Day-ahead - - 3720 - - - - NO-BZ-1 HYRA ID P_V - NO1 BiddingZone Hydro Reservoir Availability Forecast Intraday - - 3720 - - - - NO-BZ-1 IGENC P_V - NO1 BiddingZone Installed Generating Capacity - - 2370 - - - - NO-BZ-1 IHYPC P_V - NO1 BiddingZone Installed Hydro Pump Capacity - - 2370 - - - - NO-BZ-1 IHYRC P_V - NO1 BiddingZone Installed Hydro Reservoir Capacity - - 2370 - - - - NO-BZ-1 IRORC P_V - NO1 BiddingZone Installed Run-of-river Capacity - - 2370 - - - - NO-BZ-1 IWINCF P_V - NO1 BiddingZone Installed Wind Capacity - - 2370 - - - - NO-BZ-1 LAO P_V - NO1 BiddingZone Load Actual Observed - - 3700 - - - - NO-BZ-1 LAS P_V - NO1 BiddingZone Load Actual Scheduled - - 3750 - - - - NO-BZ-1 LF 1D P_V - NO1 BiddingZone Load Forecast Day-ahead - - 3720 - - - - NO-BZ-1 LF 1M P_V - NO1 BiddingZone Load Forecast Month-ahead - - 3831 - - - - NO-BZ-1 LF 1Y P_V - NO1 BiddingZone Load Forecast Year-ahead - - 3831 - - - - NO-BZ-1 LF 2D P_V - NO1 BiddingZone Load Forecast Two Day-ahead - - 3888 - - - - NO-BZ-1 LF 3D P_V - NO1 BiddingZone Load Forecast Three Day-ahead - - 3878 - - - - NO-BZ-1 LF 4D P_V - NO1 BiddingZone Load Forecast Four Day-ahead - - 3870 - - - - NO-BZ-1 LF 5D P_V - NO1 BiddingZone Load Forecast Five Day-ahead - - 3850 - - - - NO-BZ-1 LF 6D P_V - NO1 BiddingZone Load Forecast Six Day-ahead - - 3855 - - - - NO-BZ-1 LF 7D P_V - NO1 BiddingZone Load Forecast Seven Day-ahead - - 3831 - - - - NO-BZ-1 LF ID P_V - NO1 BiddingZone Load Forecast Intraday - - 3720 - - - - NO-BZ-1 NEX 1M P_V - NO1 BiddingZone Net Exchange Forecast Month-ahead - - 4050 - - - - NO-BZ-1 NEX 1Y P_V - NO1 BiddingZone Net Exchange Forecast Year-ahead - - 4050 - - - - NO-BZ-1 NEX 2D P_V - NO1 BiddingZone Net Exchange Forecast Two Day-ahead - - 4050 - - - - NO-BZ-1 NEX 3D P_V - NO1 BiddingZone Net Exchange Forecast Three Day-ahead - - 4050 - - - - NO-BZ-1 NEX 4D P_V - NO1 BiddingZone Net Exchange Forecast Four Day-ahead - - 4050 - - - - NO-BZ-1 NEX 5D P_V - NO1 BiddingZone Net Exchange Forecast Five Day-ahead - - 4050 - - - - NO-BZ-1 NEX 6D P_V - NO1 BiddingZone Net Exchange Forecast Six Day-ahead - - 4050 - - - - NO-BZ-1 NEX 7D P_V - NO1 BiddingZone Net Exchange Forecast Seven Day-ahead - - 4050 - - - - NO-BZ-1 NEXO P_V - NO1 BiddingZone Net Exchange Actual Observed - - 4000 - - - - NO-BZ-1 NEXS P_V - NO1 BiddingZone Net Exchange Actual Scheduled - - 4050 - - - - NO-BZ-1 NUC 1M P_V - NO1 BiddingZone Nuclear Forecast Month-ahead - - 4050 - - - - NO-BZ-1 NUC 1Y P_V - NO1 BiddingZone Nuclear Forecast Year-ahead - - 4050 - - - - NO-BZ-1 NUC 2D P_V - NO1 BiddingZone Nuclear Forecast Two Day-ahead - - 4050 - - - - NO-BZ-1 NUC 3D P_V - NO1 BiddingZone Nuclear Forecast Three Day-ahead - - 4050 - - - - NO-BZ-1 NUC 4D P_V - NO1 BiddingZone Nuclear Forecast Four Day-ahead - - 4050 - - - - NO-BZ-1 NUC 5D P_V - NO1 BiddingZone Nuclear Forecast Five Day-ahead - - 4050 - - - - NO-BZ-1 NUC 6D P_V - NO1 BiddingZone Nuclear Forecast Six Day-ahead - - 4050 - - - - NO-BZ-1 NUC 7D P_V - NO1 BiddingZone Nuclear Forecast Seven Day-ahead - - 4050 - - - - NO-BZ-1 NUCO P_V - NO1 BiddingZone Nuclear Actual Observed - - 4000 - - - - NO-BZ-1 NUCS P_V - NO1 BiddingZone Nuclear Actual Scheduled - - 4050 - - - - NO-BZ-1 OTH 1M P_V - NO1 BiddingZone Other Production Forecast Month-ahead - - 4050 - - - - NO-BZ-1 OTH 1Y P_V - NO1 BiddingZone Other Production Forecast Year-ahead - - 4050 - - - - NO-BZ-1 OTH 2D P_V - NO1 BiddingZone Other Production Forecast Two Day-ahead - - 4050 - - - - NO-BZ-1 OTH 3D P_V - NO1 BiddingZone Other Production Forecast Three Day-ahead - - 4050 - - - - NO-BZ-1 OTH 4D P_V - NO1 BiddingZone Other Production Forecast Four Day-ahead - - 4050 - - - - NO-BZ-1 OTH 5D P_V - NO1 BiddingZone Other Production Forecast Five Day-ahead - - 4050 - - - - NO-BZ-1 OTH 6D P_V - NO1 BiddingZone Other Production Forecast Six Day-ahead - - 4050 - - - - NO-BZ-1 OTH 7D P_V - NO1 BiddingZone Other Production Forecast Seven Day-ahead - - 4050 - - - - NO-BZ-1 OTHO P_V - NO1 BiddingZone Other Production Actual Observed - - 4000 - - - - NO-BZ-1 OTHS P_V - NO1 BiddingZone Other Production Actual Scheduled - - 4050 - - - - NO-BZ-1 PRO 1M P_V - NO1 BiddingZone Production Forecast Month-ahead - - 4050 - - - - NO-BZ-1 PRO 1Y P_V - NO1 BiddingZone Production Forecast Year-ahead - - 4050 - - - - NO-BZ-1 PRO 2D P_V - NO1 BiddingZone Production Forecast Two Day-ahead - - 4050 - - - - NO-BZ-1 PRO 3D P_V - NO1 BiddingZone Production Forecast Three Day-ahead - - 4050 - - - - NO-BZ-1 PRO 4D P_V - NO1 BiddingZone Production Forecast Four Day-ahead - - 4050 - - - - NO-BZ-1 PRO 5D P_V - NO1 BiddingZone Production Forecast Five Day-ahead - - 4050 - - - - NO-BZ-1 PRO 6D P_V - NO1 BiddingZone Production Forecast Six Day-ahead - - 4050 - - - - NO-BZ-1 PRO 7D P_V - NO1 BiddingZone Production Forecast Seven Day-ahead - - 4050 - - - - NO-BZ-1 PROO P_V - NO1 BiddingZone Production Actual Observed - - 4000 - - - - NO-BZ-1 PROS P_V - NO1 BiddingZone Production Actual Scheduled - - 4050 - - - - NO-BZ-1 RCA 1D P_V - NO1 BiddingZone Remaining Capacity Forecast Day-ahead - - 3720 - - - - NO-BZ-1 RCA 1M P_V - NO1 BiddingZone Remaining Capacity Forecast Month-ahead - - 3720 - - - - NO-BZ-1 RCA 1Y P_V - NO1 BiddingZone Remaining Capacity Forecast Year-ahead - - 3720 - - - - NO-BZ-1 RCA 2D P_V - NO1 BiddingZone Remaining Capacity Forecast Two Day-ahead - - 3720 - - - - NO-BZ-1 RCA 3D P_V - NO1 BiddingZone Remaining Capacity Forecast Three Day-ahead - - 3720 - - - - NO-BZ-1 RCA 4D P_V - NO1 BiddingZone Remaining Capacity Forecast Four Day-ahead - - 3720 - - - - NO-BZ-1 RCA 5D P_V - NO1 BiddingZone Remaining Capacity Forecast Five Day-ahead - - 3720 - - - - NO-BZ-1 RCA 6D P_V - NO1 BiddingZone Remaining Capacity Forecast Six Day-ahead - - 3720 - - - - NO-BZ-1 RCA 7D P_V - NO1 BiddingZone Remaining Capacity Forecast Seven Day-ahead - - 3720 - - - - NO-BZ-1 RCA ID P_V - NO1 BiddingZone Remaining Capacity Forecast Intraday - - 3720 - - - - NO-BZ-1 ROR 1M P_V - NO1 BiddingZone Run-of-river Forecast Month-ahead - - 1199 - - - - NO-BZ-1 ROR 1Y P_V - NO1 BiddingZone Run-of-river Forecast Year-ahead - - 1202 - - - - NO-BZ-1 ROR 2D P_V - NO1 BiddingZone Run-of-river Forecast Two Day-ahead - - 1200 - - - - NO-BZ-1 ROR 3D P_V - NO1 BiddingZone Run-of-river Forecast Three Day-ahead - - 1234 - - - - NO-BZ-1 ROR 4D P_V - NO1 BiddingZone Run-of-river Forecast Four Day-ahead - - 1188 - - - - NO-BZ-1 ROR 5D P_V - NO1 BiddingZone Run-of-river Forecast Five Day-ahead - - 1300 - - - - NO-BZ-1 ROR 6D P_V - NO1 BiddingZone Run-of-river Forecast Six Day-ahead - - 1221 - - - - NO-BZ-1 ROR 7D P_V - NO1 BiddingZone Run-of-river Forecast Seven Day-ahead - - 1200 - - - - NO-BZ-1 THR 1M P_V - NO1 BiddingZone Thermal Forecast Month-ahead - - 4050 - - - - NO-BZ-1 THR 1Y P_V - NO1 BiddingZone Thermal Forecast Year-ahead - - 4050 - - - - NO-BZ-1 THR 2D P_V - NO1 BiddingZone Thermal Forecast Two Day-ahead - - 4050 - - - - NO-BZ-1 THR 3D P_V - NO1 BiddingZone Thermal Forecast Three Day-ahead - - 4050 - - - - NO-BZ-1 THR 4D P_V - NO1 BiddingZone Thermal Forecast Four Day-ahead - - 4050 - - - - NO-BZ-1 THR 5D P_V - NO1 BiddingZone Thermal Forecast Five Day-ahead - - 4050 - - - - NO-BZ-1 THR 6D P_V - NO1 BiddingZone Thermal Forecast Six Day-ahead - - 4050 - - - - NO-BZ-1 THR 7D P_V - NO1 BiddingZone Thermal Forecast Seven Day-ahead - - 4050 - - - - NO-BZ-1 THRO P_V - NO1 BiddingZone Thermal Actual Observed - - 4000 - - - - NO-BZ-1 THRS P_V - NO1 BiddingZone Thermal Actual Scheduled - - 4050 - - - - NO-BZ-1 WIN 1M P_V - NO1 BiddingZone Wind Forecast Month-ahead - - 4050 - - - - NO-BZ-1 WIN 1Y P_V - NO1 BiddingZone Wind Forecast Year-ahead - - 4050 - - - - NO-BZ-1 WIN 2D P_V - NO1 BiddingZone Wind Forecast Two Day-ahead - - 4050 - - - - NO-BZ-1 WIN 3D P_V - NO1 BiddingZone Wind Forecast Three Day-ahead - - 4050 - - - - NO-BZ-1 WIN 4D P_V - NO1 BiddingZone Wind Forecast Four Day-ahead - - 4050 - - - - NO-BZ-1 WIN 5D P_V - NO1 BiddingZone Wind Forecast Five Day-ahead - - 4050 - - - - NO-BZ-1 WIN 6D P_V - NO1 BiddingZone Wind Forecast Six Day-ahead - - 4050 - - - - NO-BZ-1 WIN 7D P_V - NO1 BiddingZone Wind Forecast Seven Day-ahead - - 4050 - - - - NO-BZ-1 WINO P_V - NO1 BiddingZone Wind Actual Observed - - 4000 - - - - NO-BZ-1 WINS P_V - NO1 BiddingZone Wind Actual Scheduled - - 4050 - - - - NO-BZ-2 LAO P_V - NO2 BiddingZone Load Actual Observed - - 6000 - - - - NO-BZ-2 LAS P_V - NO2 BiddingZone Load Actual Scheduled - - 6010 - - - - NO-BZ-2 LF 1D P_V - NO2 BiddingZone Load Forecast Day-ahead - - 6100 - - - - NO-BZ-2 LF 2D P_V - NO2 BiddingZone Load Forecast Two Day-ahead - - 6060 - - - - NO-BZ-3 LAO P_V - NO3 BiddingZone Load Actual Observed - - 2350 - - - - NO-BZ-3 LAS P_V - NO3 BiddingZone Load Actual Scheduled - - 2360 - - - - NO-BZ-3 LF 1D P_V - NO3 BiddingZone Load Forecast Day-ahead - - 2370 - - - - NO-BZ-3 LF 2D P_V - NO3 BiddingZone Load Forecast Two Day-ahead - - 2349 - - - 1000 - - - 135 - 135kV - - - 300 - 300kV - - - 420 - 420kV - - - ARENDAL 300AS1 - - Arendal 300 Asker 1 Bay - - - ARENDAL 300KR1 - - Arendal 300 Kristiansand 1 Bay - - - ARENDAL 300KR2 - - Arendal 300 Kristiansand HVDC 2 Bay - - - ARENDAL 300SC1 - - Arendal 300 Shunt Compensator 1 Bay - - - ARENDAL 300T1 - - Arendal 300 Transformer 1 Bay - - - ARENDAL 420T1 - - Arendal 420 Transformer 1 Bay - - - KRISTIAN300AR1 - - Kristiansand 300 Arendal 1 Bay - - - KRISTIAN300FE1 - - Kristiansand 300 Feda 1 Bay - - - KRISTIAN300G1 - - Kristiansand 300 Gen 1 Bay - - - KRISTIAN300G2 - - Kristiansand 300 Gen 2 Bay - - - KRISTIAN300G3 - - Kristiansand 300 Gen 3 Bay - - - KRISTIAN300G4 - - Kristiansand 300 Gen 4 Bay - - - KRISTIAN300KV1 - - Kristiansand 300 Kvilldal 1 Bay - - - KRISTIAN300L1 - - Kristiansand 300 Load 1 Bay - - - KRISTIAN300L2 - - Kristiansand 300 Load 2 Bay - - - KRISTIAN300ST1 - - Kristiansand 300 Stavanger 1 Bay - - - ARENDAL 300AS1 AB_S - - false - false - - false - Arendal 300 Asker 1 A Breaker - - - ARENDAL 300AS1 BB_S - - false - true - - false - Arendal 300 Asker 1 B Breaker - - - ARENDAL 300KR1 AB_S - - false - false - - false - Arendal 300 Kristiansand 1 A Breaker - - - ARENDAL 300KR1 BB_S - - false - true - - false - Arendal 300 Kristiansand 1 B Breaker - - - ARENDAL 300KR2 AB_S - - false - false - - false - Arendal 300 Kristiansand 2 A Breaker - - - ARENDAL 300KR2 BB_S - - false - true - - false - Arendal 300 Kristiansand 2 B Breaker - - - ARENDAL 300SC1 AB_S - - false - false - - false - Arendal 300 Shunt Compensator 1 A Breaker - - - ARENDAL 300T1 AB_S - - false - false - - false - Arendal 300 Transformer 1 A Breaker - - - ARENDAL 420T1 AB_S - - false - false - - false - Arendal 420 Transformer 1 A Breaker - - - KRISTIAN300AR1 AB_S - - false - false - - false - Kristiansand 300 Arendal 1 A Breaker - - - KRISTIAN300AR1 BB_S - - false - true - - false - Kristiansand 300 Arendal 1 B Breaker - - - KRISTIAN300FE1 AB_S - - false - false - - false - Kristiansand 300 Feda 1 A Breaker - - - KRISTIAN300FE1 BB_S - - false - true - - false - Kristiansand 300 Feda 1 B Breaker - - - KRISTIAN300G1 AB_S - - false - false - - false - Kristiansand 300 Gen 1 A Breaker - - - KRISTIAN300G1 BB_S - - false - true - - false - Kristiansand 300 Gen 1 B Breaker - - - KRISTIAN300G2 AB_S - - false - false - - false - Kristiansand 300 Gen 2 A Breaker - - - KRISTIAN300G2 BB_S - - false - true - - false - Kristiansand 300 Gen 2 B Breaker - - - KRISTIAN300G3 AB_S - - false - false - - false - Kristiansand 300 Gen 3 A Breaker - - - KRISTIAN300G3 BB_S - - false - true - - false - Kristiansand 300 Gen 3 B Breaker - - - KRISTIAN300G4 AB_S - - false - false - - false - Kristiansand 300 Gen 4 A Breaker - - - KRISTIAN300G4 BB_S - - false - true - - false - Kristiansand 300 Gen 4 B Breaker - - - KRISTIAN300KV1 AB_S - - false - false - - false - Kristiansand 300 Kvilldal 1 A Breaker - - - KRISTIAN300KV1 BB_S - - false - true - - false - Kristiansand 300 Kvilldal 1 B Breaker - - - KRISTIAN300L1 AB_S - - false - false - - false - Kristiansand 300 Load 1 A Breaker - - - KRISTIAN300L1 BB_S - - false - true - - false - Kristiansand 300 Load 1 B Breaker - - - KRISTIAN300L2 AB_S - - false - false - - false - Kristiansand 300 Load 2 A Breaker - - - KRISTIAN300L2 BB_S - - false - true - - false - Kristiansand 300 Load 2 B Breaker - - - KRISTIAN300ST1 AB_S - - false - false - - false - Kristiansand 300 Stavanger 1 A Breaker - - - KRISTIAN300ST1 BB_S - - false - true - - false - Kristiansand 300 Stavanger 1 B Breaker - - - - AJAURE 300 A - - false - - - - ARENDAL 300 A - - false - - - - ARENDAL 300 B - - false - - - - ARRIE 420 A - - false - - - - ASKER 300 A - - false - - - - AURLAND 420 A - - false - - - - BLAFALLI300 A - - false - - - - DAGALI 420 A - - false - - - - DANNEBO 420 A - - false - - - - EIDFJORD420 A - - false - - - - ESTLINK 420 A - - false - - - - FEDA 300 A - - false - - - - FORSMARK420 A - - false - - - - GEILO 420 A - - false - - - - GRUNDFOR420 A - - false - - - - HAGAFOSS420 A - - false - - - - HALDEN 420 A - - false - - - - HELSINKI420 A - - false - - - - HJALTA 420 A - - false - - - - HOGASEN 300 A - - false - - - - JARPSTRO420 A - - false - - - - KARLSH 420 A - - false - - - - KONGSBER420 A - - false - - - - KRISTIA 300 A - - false - - - - KRISTIAN300 A - - false - - - - KRISTIAN300 B - - false - - - - KVILLDAL300 A - - false - - - - MALMO 420 A - - false - - - - MO 300 A - - false - - - - NARVIK 420 A - - false - - - - OSKARSHA420 A - - false - - - - OSLO 300 A - - false - - - - OULU 420 A - - false - - - - PORJUS 420 A - - false - - - - RINGHALS420 A - - false - - - - SANDEFJO420 A - - false - - - - SIMA 300 A - - false - - - - SKIEN 420 A - - false - - - - STAVANGE300 A - - false - - - - STENKU 135 A - - false - - - - SYLLING 420 A - - false - - - - SYSLE 420 A - - false - - - - TENHULT 420 A - - false - - - - TRETTEN 300 A - - false - - - - TRONDHEI300 A - - false - - - - VYBORG 420 A - - false - - - - - 1030.381 - 430.058 - - - false - true - 5500 '1 ' - ASKER 300 L1 - - - - - - 1030.381 - 430.058 - - - false - true - 5500 '2 ' - ASKER 300 L2 - - - - - - 670.271 - 245.824 - - - false - true - 6100 '1 ' - BLAFALLI300 L1 - - - - - - 670.271 - 245.824 - - - false - true - 6100 '2 ' - BLAFALLI300 L2 - - - - - - 272.451 - 92.183 - - - false - true - 3000 '1 ' - FORSMARK420 L1 - - - - - - 272.451 - 92.183 - - - false - true - 3000 '2 ' - FORSMARK420 L2 - - - - - - 272.451 - 92.183 - - - false - true - 3000 '3 ' - FORSMARK420 L3 - - - - - - 1418.454 - 417.731 - - - false - true - 3249 '1 ' - GRUNDFOR420 L1 - - - - - - 1319.43 - 275.111 - - - false - true - 7000 '1 ' - HELSINKI420 L1 - - - - - - 1319.43 - 275.111 - - - false - true - 7000 '2 ' - HELSINKI420 L2 - - - - - - 1319.43 - 275.111 - - - false - true - 7000 '3 ' - HELSINKI420 L3 - - - - - - 1319.43 - 275.111 - - - false - true - 7000 '4 ' - HELSINKI420 L4 - - - - - - 1319.43 - 275.111 - - - false - true - 7000 '5 ' - HELSINKI420 L5 - - - - - - 67.546 - 70.693 - - - false - true - 3100 '1 ' - HJALTA 420 L1 - - - - - - 1312.229 - 267.37 - - - false - true - 5600 '1 ' - KRISTIAN300 L1 - - - - - - 1312.229 - 267.37 - - - false - true - 5600 '2 ' - KRISTIAN300 L2 - - - - - - 764 - 251.115 - - - false - true - 8500 '1 ' - MALMO 420 L1 - - - - - - 764 - 251.115 - - - false - true - 8500 '2 ' - MALMO 420 L2 - - - - - - 764 - 251.115 - - - false - true - 8500 '3 ' - MALMO 420 L3 - - - - - - 2070 - 680.376 - - - false - true - 6700 '1 ' - MO 300 L1 - - - - - - 1192.154 - 332.079 - - - false - true - 3300 '1 ' - OSKARSHA420 L1 - - - - - - 1192.154 - 332.079 - - - false - true - 3300 '2 ' - OSKARSHA420 L2 - - - - - - 1128.514 - 215.027 - - - false - true - 5400 '1 ' - OSLO 300 L1 - - - - - - 1185.425 - 786.043 - - - false - true - 7100 '1 ' - OULU 420 L1 - - - - - - 1185.425 - 786.043 - - - false - true - 7100 '2 ' - OULU 420 L2 - - - - - - 1041 - 342.16 - - - false - true - 3115 '1 ' - PORJUS 420 L1 - - - - - - 1430.584 - 498.117 - - - false - true - 3359 '1 ' - RINGHALS420 L1 - - - - - - 1430.584 - 498.117 - - - false - true - 3359 '2 ' - RINGHALS420 L2 - - - - - - 1430.584 - 498.117 - - - false - true - 3359 '3 ' - RINGHALS420 L3 - - - - - - 1430.584 - 498.117 - - - false - true - 3359 '4 ' - RINGHALS420 L4 - - - - - - 2195 - 721.461 - - - false - true - 5300 '2 ' - SIMA 300 L2 - - - - - - 539.723 - 150.52 - - - false - true - 5100 '1 ' - TRETTEN 300 L1 - - - - - - 779.667 - 256.264 - - - false - true - 6500 '1 ' - TRONDHEI300 L1 - - - - - - 779.667 - 256.264 - - - false - true - 6500 '2 ' - TRONDHEI300 L2 - - - - - - 779.667 - 256.264 - - - false - true - 6500 '3 ' - TRONDHEI300 L3 - - - - - ASKER 300 LG1 - 5500_1 - - - - ASKER 300 LG2 - 5500_2 - - - - BLAFALLI300 LG1 - 6100_1 - - - - BLAFALLI300 LG1 - 6100_2 - - - - FORSMARK420 LG1 - 3000_1 - - - - FORSMARK420 LG2 - 3000_2 - - - - FORSMARK420 LG3 - 3000_3 - - - - GRUNDFOR420 LG1 - 3249_1 - - - - HELSINKI420 LG1 - 7000_1 - - - - HELSINKI420 LG2 - 7000_2 - - - - HELSINKI420 LG3 - 7000_3 - - - - HELSINKI420 LG4 - 7000_4 - - - - HELSINKI420 LG5 - 7000_5 - - - - HJALTA 420 LG1 - 3100_1 - - - - KRISTIANCLG1 - KRISTIAN Conf Load Group 1 - - - - KRISTIANCLG2 - KRISTIAN Conf Load Group 2 - - - - MALMO 420 LG1 - 8500_1 - - - - MALMO 420 LG2 - 8500_2 - - - - MALMO 420 LG3 - 8500_3 - - - - MO 300 LG1 - 6700_1 - - - - OSKARSHA420 LG1 - 3300_1 - - - - OSKARSHA420 LG2 - 3300_2 - - - - OSLO 300 LG1 - 5400_1 - - - - OULU 420 LG1 - 7100_1 - - - - OULU 420 LG1 - 7100_2 - - - - PORJUS 420 LG1 - 3115_1 - - - - RINGHALS420 LG1 - 3359_1 - - - - RINGHALS420 LG2 - 3359_2 - - - - RINGHALS420 LG3 - 3359_3 - - - - RINGHALS420 LG4 - 3359_4 - - - - SIMA 300 LG2 - 5300_2 - - - - TRETTEN 300 LG1 - 5100_1 - - - - TRONDHEI300 LG1 - 6500_1 - - - - TRONDHEI300 LG2 - 6500_2 - - - - TRONDHEI300 LG3 - 6500_3 - - - - AJAURE - - - - AJAURE test - - - - ARENDAL CN 001 - - - - ARENDAL CN 002 - - - - ARENDAL CN 003 - - - - ARENDAL CN 004 - - - - ARENDAL CN 005 - - - - ARENDAL CN 006 - - - - ARENDAL CN 007 - - - - ARENDAL CN 008 - - - - ARENDAL CN 009 - - - - ARENDAL CN 010 - - - - ARENDAL CN 011 - - - - ARENDAL CN 012 - - - - ARENDAL CN 013 - - - - ARENDAL CN 014 - - - - ARENDAL CN 015 - - - - ARRIE_HVDC - - - - AURLAND - - - - BLAFALLI - - - - DAGALI - - - - DANNEBO_HVDC - - - - EIDFJORD - - - - ESTLINK_HVDC - - - - FEDA_HVDC - - - - FORSMARK - - - - GEILO - - - - GRUNDFORS - - - - HAGAFOSS - - - - HASLE - - - - HELSINKI - - - - HJALTA - - - - HOGASEN - - - - JARPSTROMMEN - - - - KAGGEFOSS - - - - KARLSH_HVDC - - - - KONGSBERG - - - - KRISTIAN CN 001 - - - - KRISTIAN CN 002 - - - - KRISTIAN CN 003 - - - - KRISTIAN CN 004 - - - - KRISTIAN CN 005 - - - - KRISTIAN CN 006 - - - - KRISTIAN CN 007 - - - - KRISTIAN CN 008 - - - - KRISTIAN CN 009 - - - - KRISTIAN CN 010 - - - - KRISTIAN CN 011 - - - - KRISTIAN CN 012 - - - - KRISTIAN CN 013 - - - - KRISTIAN CN 014 - - - - KRISTIAN CN 015 - - - - KRISTIAN CN 016 - - - - KRISTIAN CN 017 - - - - KRISTIAN CN 018 - - - - KRISTIAN CN 019 - - - - KRISTIAN CN 020 - - - - KRISTIAN CN 021 - - - - KRISTIAN CN 022 - - - - KRISTIAN CN 023 - - - - KRISTIAN CN 024 - - - - KRISTIAN CN 025 - - - - KRISTIAN CN 026 - - - - KRISTIAN CN 027 - - - - KRISTIAN CN 028 - - - - KRISTIAN CN 029 - - - - KRISTIAN CN 030 - - - - KRISTIAN CN 031 - - - - KRISTIAN CN 032 - - - - KRISTIA_HVDC - - - - KVILLDAL - - - - MALMO - - - - OFOTEN - - - - OSKARSHAMN - - - - OSLO1 - - - - OSLO2 - - - - OULU - - - - PORJUS - - - - RINGHALS - - - - ROSSAGA - - - - SANDEFJORD - - - - SIMA - - - - SKIEN - - - - STAVANGER - - - - STENKU_HVDC - - - - SYLLING - - - - TENHULT - - - - TRETTEN - - - - TRONDHEIM - - - - VYBORG_HVDC - - - - - FI CA - - - - - NO CA - - - - - SE CA - - - - - CAGU_FI_HELSINKIG1 - SMPF_7000_1 - - - - - - CAGU_NO1_ASKER G1 - SMPF_5500_1 - - - - - - CAGU_NO2_KRISTING1 - SMPF_5600_1 - - - - - - CAGU_NO3_TRONDHEIG1 - SMPF_6500_1 - - - - - - CAGU_NO4_MO G1 - SMPF_6700_1 - - - - - - CAGU_NO5_SIMA G1 - SMPF_5300_1 - - - - - - CAGU_SE1_PORJUS G1 - SMPF_3115_1 - - - - - - CAGU_SE2_GROUNDFORG1 - SMPF_3249_1 - - - - - - CAGU_SE4_MALMO G1 - SMPF_8500_1 - - - - - - SMPF_SE3_OSKARSHAG1 - SMPF_3300_1 - - - - 1810 - - - CL_PATL_-10degreeCelsius - CurrentLimit PATL Negative 10 degree Celsius - 1810 - - - 4010 - - - CL_PATL_-10degreeCelsius - CurrentLimit PATL Negative 10 degree Celsius - 4010 - - - 1815 - - - CL_PATL_-15degreeCelsius - CurrentLimit PATL Negative 15 degree Celsius - 1815 - - - 4115 - - - CL_PATL_-15degreeCelsius - CurrentLimit PATL Negative 15 degree Celsius - 4115 - - - 4220 - - - CL_PATL_-20degreeCelsius - CurrentLimit PATL Negative 20 degree Celsius - 4220 - - - 1820 - - - CL_PATL_-20degreeCelsius - CurrentLimit PATL Negative 20 degree Celsius - 1820 - - - 4325 - - - CL_PATL_-25degreeCelsius - CurrentLimit PATL Negative 25 degree Celsius - 4325 - - - 1825 - - - CL_PATL_-25degreeCelsius - CurrentLimit PATL Negative 25 degree Celsius - 1825 - - - 4430 - - - CL_PATL_-30degreeCelsius - CurrentLimit PATL Negative 30 degree Celsius - 4430 - - - 1830 - - - CL_PATL_-30degreeCelsius - CurrentLimit PATL Negative 30 degree Celsius - 1830 - - - 6736 - - - CL_PATL_-30degreeCelsius - CurrentLimit PATL Negative 30 degree Celsius - 6736 - - - 1805 - - - CL_PATL_-5degreeCelsius - CurrentLimit PATL Negative 5 degree Celsius - 1805 - - - 3905 - - - CL_PATL_-5degreeCelsius - CurrentLimit PATL Negative 5 degree Celsius - 3905 - - - 1800 - - - CL_PATL_0degreeCelsius - CurrentLimit PATL 0 degree Celsius - 1800 - - - 3800 - - - CL_PATL_0degreeCelsius - CurrentLimit PATL 0 degree Celsius - 3800 - - - 3610 - - - CL_PATL_10degreeCelsius - CurrentLimit PATL 10 degree Celsius - 3610 - - - 1790 - - - CL_PATL_10degreeCelsius - CurrentLimit PATL 10 degree Celsius - 1790 - - - 3515 - - - CL_PATL_15degreeCelsius - CurrentLimit PATL 15 degree Celsius - 3515 - - - 1785 - - - CL_PATL_15degreeCelsius - CurrentLimit PATL 15 degree Celsius - 1785 - - - 1780 - - - CL_PATL_20degreeCelsius - CurrentLimit PATL 20 degree Celsius - 1780 - - - 3420 - - - CL_PATL_20degreeCelsius - CurrentLimit PATL 20 degree Celsius - 3420 - - - 4812 - - - CL_PATL_20degreeCelsius - CurrentLimit PATL 20 degree Celsius - 4812 - - - 3325 - - - CL_PATL_25degreeCelsius - CurrentLimit PATL 25 degree Celsius - 3325 - - - 1775 - - - CL_PATL_25degreeCelsius - CurrentLimit PATL 25 degree Celsius - 1775 - - - 1770 - - - CL_PATL_30degreeCelsius - CurrentLimit PATL 30 degree Celsius - 1770 - - - 3230 - - - CL_PATL_30degreeCelsius - CurrentLimit PATL 30 degree Celsius - 3230 - - - 1795 - - - CL_PATL_5degreeCelsius - CurrentLimit PATL 5 degree Celsius - 1795 - - - 3705 - - - CL_PATL_5degreeCelsius - CurrentLimit PATL 5 degree Celsius - 3705 - - - 7910 - - - CL_TATL_-10degreeCelsius - CurrentLimit TATL Negative 10 degree Celsius - 7910 - - - 2510 - - - CL_TATL_-10degreeCelsius - CurrentLimit TATL Negative 10 degree Celsius - 2510 - - - 2515 - - - CL_TATL_-15degreeCelsius - CurrentLimit TATL Negative 15 degree Celsius - 2515 - - - 8015 - - - CL_TATL_-15degreeCelsius - CurrentLimit TATL Negative 15 degree Celsius - 8015 - - - 8120 - - - CL_TATL_-20degreeCelsius - CurrentLimit TATL Negative 20 degree Celsius - 8120 - - - 2520 - - - CL_TATL_-20degreeCelsius - CurrentLimit TATL Negative 20 degree Celsius - 2520 - - - 8225 - - - CL_TATL_-25degreeCelsius - CurrentLimit TATL Negative 25 degree Celsius - 8225 - - - 2525 - - - CL_TATL_-25degreeCelsius - CurrentLimit TATL Negative 25 degree Celsius - 2525 - - - 8330 - - - CL_TATL_-30degreeCelsius - CurrentLimit TATL Negative 30 degree Celsius - 8330 - - - 2530 - - - CL_TATL_-30degreeCelsius - CurrentLimit TATL Negative 30 degree Celsius - 2530 - - - 7880 - - - CL_TATL_-30degreeCelsius - CurrentLimit TATL Negative 30 degree Celsius - 7880 - - - 7805 - - - CL_TATL_-5degreeCelsius - CurrentLimit TATL Negative 5 degree Celsius - 7805 - - - 2505 - - - CL_TATL_-5degreeCelsius - CurrentLimit TATL Negative 5 degree Celsius - 2505 - - - 2500 - - - CL_TATL_0degreeCelsius - CurrentLimit TATL 0 degree Celsius - 2500 - - - 7700 - - - CL_TATL_0degreeCelsius - CurrentLimit TATL 0 degree Celsius - 7700 - - - 7510 - - - CL_TATL_10degreeCelsius - CurrentLimit TATL 10 degree Celsius - 7510 - - - 2490 - - - CL_TATL_10degreeCelsius - CurrentLimit TATL 10 degree Celsius - 2490 - - - 7415 - - - CL_TATL_15degreeCelsius - CurrentLimit TATL 15 degree Celsius - 7415 - - - 2485 - - - CL_TATL_15degreeCelsius - CurrentLimit TATL 15 degree Celsius - 2485 - - - 2480 - - - CL_TATL_20degreeCelsius - CurrentLimit TATL 20 degree Celsius - 2480 - - - 7320 - - - CL_TATL_20degreeCelsius - CurrentLimit TATL 20 degree Celsius - 7320 - - - 5774 - - - CL_TATL_20degreeCelsius - CurrentLimit TATL 20 degree Celsius - 5774 - - - 2475 - - - CL_TATL_25degreeCelsius - CurrentLimit TATL 25 degree Celsius - 2475 - - - 7225 - - - CL_TATL_25degreeCelsius - CurrentLimit TATL 25 degree Celsius - 7225 - - - 2470 - - - CL_TATL_30degreeCelsius - CurrentLimit TATL 30 degree Celsius - 2470 - - - 7130 - - - CL_TATL_30degreeCelsius - CurrentLimit TATL 30 degree Celsius - 7130 - - - 2495 - - - CL_TATL_5degreeCelsius - CurrentLimit TATL 5 degree Celsius - 2495 - - - 7605 - - - CL_TATL_5degreeCelsius - CurrentLimit TATL 5 degree Celsius - 7605 - - - 2061.965247 - - - RATEA - 2061.965247 - - - 2061.965247 - - - RATEA - 2061.965247 - - - 2749.286996 - - - RATEA - 2749.286996 - - - 2749.286996 - - - RATEA - 2749.286996 - - - 1649.572198 - - - RATEA - 1649.572198 - - - 1649.572198 - - - RATEA - 1649.572198 - - - 1099.714798 - - - RATEA - 1099.714798 - - - 1099.714798 - - - RATEA - 1099.714798 - - - 1512.107848 - - - RATEA - 1512.107848 - - - 1512.107848 - - - RATEA - 1512.107848 - - - 1512.107848 - - - RATEA - 1512.107848 - - - 1512.107848 - - - RATEA - 1512.107848 - - - 2749.286996 - - - RATEA - 2749.286996 - - - 2749.286996 - - - RATEA - 2749.286996 - - - 1649.572198 - - - RATEA - 1649.572198 - - - 1649.572198 - - - RATEA - 1649.572198 - - - 1649.572198 - - - RATEA - 1649.572198 - - - 1649.572198 - - - RATEA - 1649.572198 - - - 2749.286996 - - - RATEA - 2749.286996 - - - 2749.286996 - - - RATEA - 2749.286996 - - - 1237.179148 - - - RATEA - 1237.179148 - - - 1237.179148 - - - RATEA - 1237.179148 - - - 1649.572198 - - - RATEA - 1649.572198 - - - 1649.572198 - - - RATEA - 1649.572198 - - - 1787.036547 - - - RATEA - 1787.036547 - - - 1787.036547 - - - RATEA - 1787.036547 - - - 1512.107848 - - - RATEA - 1512.107848 - - - 1512.107848 - - - RATEA - 1512.107848 - - - 1168.446973 - - - RATEA - 1168.446973 - - - 1168.446973 - - - RATEA - 1168.446973 - - - 1787.036547 - - - RATEA - 1787.036547 - - - 1787.036547 - - - RATEA - 1787.036547 - - - 1099.714798 - - - RATEA - 1099.714798 - - - 1099.714798 - - - RATEA - 1099.714798 - - - 1787.036547 - - - RATEA - 1787.036547 - - - 1787.036547 - - - RATEA - 1787.036547 - - - 2611.822646 - - - RATEA - 2611.822646 - - - 2611.822646 - - - RATEA - 2611.822646 - - - 3464.101615 - - - RATEA - 3464.101615 - - - 3464.101615 - - - RATEA - 3464.101615 - - - 2611.822646 - - - RATEA - 2611.822646 - - - 2611.822646 - - - RATEA - 2611.822646 - - - 2611.822646 - - - RATEA - 2611.822646 - - - 2611.822646 - - - RATEA - 2611.822646 - - - 2611.822646 - - - RATEA - 2611.822646 - - - 2611.822646 - - - RATEA - 2611.822646 - - - 2611.822646 - - - RATEA - 2611.822646 - - - 2611.822646 - - - RATEA - 2611.822646 - - - 2199.429597 - - - RATEA - 2199.429597 - - - 2199.429597 - - - RATEA - 2199.429597 - - - 2061.965247 - - - RATEA - 2061.965247 - - - 2061.965247 - - - RATEA - 2061.965247 - - - 1512.107848 - - - RATEA - 1512.107848 - - - 1512.107848 - - - RATEA - 1512.107848 - - - 577.350269 - - - RATEA - 577.350269 - - - 577.350269 - - - RATEA - 577.350269 - - - 1347.150628 - - - RATEA - 1347.150628 - - - 1347.150628 - - - RATEA - 1347.150628 - - - 1539.600718 - - - RATEA - 1539.600718 - - - 1539.600718 - - - RATEA - 1539.600718 - - - 2336.893947 - - - RATEA - 2336.893947 - - - 2336.893947 - - - RATEA - 2336.893947 - - - 1855.768722 - - - RATEA - 1855.768722 - - - 1855.768722 - - - RATEA - 1855.768722 - - - 2749.286996 - - - RATEA - 2749.286996 - - - 2749.286996 - - - RATEA - 2749.286996 - - - 2749.286996 - - - RATEA - 2749.286996 - - - 2749.286996 - - - RATEA - 2749.286996 - - - 2061.965247 - - - RATEA - 2061.965247 - - - 2061.965247 - - - RATEA - 2061.965247 - - - 1993.233072 - - - RATEA - 1993.233072 - - - 1993.233072 - - - RATEA - 1993.233072 - - - 2061.965247 - - - RATEA - 2061.965247 - - - 2061.965247 - - - RATEA - 2061.965247 - - - 2061.965247 - - - RATEA - 2061.965247 - - - 2061.965247 - - - RATEA - 2061.965247 - - - 3656.551705 - - - RATEA - 3656.551705 - - - 3656.551705 - - - RATEA - 3656.551705 - - - 1718.304373 - - - RATEA - 1718.304373 - - - 1718.304373 - - - RATEA - 1718.304373 - - - 2749.286996 - - - RATEA - 2749.286996 - - - 2749.286996 - - - RATEA - 2749.286996 - - - 2474.358297 - - - RATEA - 2474.358297 - - - 2474.358297 - - - RATEA - 2474.358297 - - - 2680.554821 - - - RATEA - 2680.554821 - - - 2680.554821 - - - RATEA - 2680.554821 - - - 1855.768722 - - - RATEA - 1855.768722 - - - 1855.768722 - - - RATEA - 1855.768722 - - - 3656.551705 - - - RATEA - 3656.551705 - - - 3656.551705 - - - RATEA - 3656.551705 - - - 1539.600718 - - - RATEA - 1539.600718 - - - 1539.600718 - - - RATEA - 1539.600718 - - - 2061.965247 - - - RATEA - 2061.965247 - - - 2061.965247 - - - RATEA - 2061.965247 - - - 3024.215696 - - - RATEA - 3024.215696 - - - 3024.215696 - - - RATEA - 3024.215696 - - - 1718.304373 - - - RATEA - 1718.304373 - - - 1718.304373 - - - RATEA - 1718.304373 - - - 2886.751346 - - - RATEA - 2886.751346 - - - 2886.751346 - - - RATEA - 2886.751346 - - - 1539.600718 - - - RATEA - 1539.600718 - - - 1539.600718 - - - RATEA - 1539.600718 - - - 2309.401077 - - - RATEA - 2309.401077 - - - 2309.401077 - - - RATEA - 2309.401077 - - - 4811.252243 - - - RATEA - 4811.252243 - - - 4811.252243 - - - RATEA - 4811.252243 - - - 1924.500897 - - - RATEA - 1924.500897 - - - 1924.500897 - - - RATEA - 1924.500897 - - - 4041.451884 - - - RATEA - 4041.451884 - - - 4041.451884 - - - RATEA - 4041.451884 - - - 4811.252243 - - - RATEA - 4811.252243 - - - 4811.252243 - - - RATEA - 4811.252243 - - - 3752.77675 - - - RATEA - 3752.77675 - - - 3752.77675 - - - RATEA - 3752.77675 - - - 1539.600718 - - - RATEA - 1539.600718 - - - 1539.600718 - - - RATEA - 1539.600718 - - - 1924.500897 - - - RATEA - 1924.500897 - - - 1924.500897 - - - RATEA - 1924.500897 - - - 3436.608745 - - - RATEA - 3436.608745 - - - 3436.608745 - - - RATEA - 3436.608745 - - - 1374.643498 - - - RATEA - 1374.643498 - - - 1374.643498 - - - RATEA - 1374.643498 - - - 1429.629238 - - - RATEA - 1429.629238 - - - 1429.629238 - - - RATEA - 1429.629238 - - - 1429.629238 - - - RATEA - 1429.629238 - - - 1429.629238 - - - RATEA - 1429.629238 - - - 1649.572198 - - - RATEA - 1649.572198 - - - 1649.572198 - - - RATEA - 1649.572198 - - - 1374.643498 - - - RATEA - 1374.643498 - - - 1374.643498 - - - RATEA - 1374.643498 - - - 1374.643498 - - - RATEA - 1374.643498 - - - 1374.643498 - - - RATEA - 1374.643498 - - - 3849.001795 - - - RATEA - 3849.001795 - - - 2749.286996 - - - RATEA - 2749.286996 - - - 3849.001795 - - - RATEA - 3849.001795 - - - 2749.286996 - - - RATEA - 2749.286996 - - - 2749.286996 - - - RATEA - 2749.286996 - - - 8553.337321 - - - RATEA - 8553.337321 - - - 2749.286996 - - - RATEA - 2749.286996 - - - 3849.001795 - - - RATEA - 3849.001795 - - - 7698.003589 - - - RATEA - 7698.003589 - - - 5498.573992 - - - RATEA - 5498.573992 - - - 3849.001795 - - - RATEA - 3849.001795 - - - 2749.286996 - - - RATEA - 2749.286996 - - - 4233.901974 - - - RATEA - 4233.901974 - - - 3024.215696 - - - RATEA - 3024.215696 - - - 3849.001795 - - - RATEA - 3849.001795 - - - 2749.286996 - - - RATEA - 2749.286996 - - - 3849.001795 - - - RATEA - 3849.001795 - - - 2749.286996 - - - RATEA - 2749.286996 - - - 3849.001795 - - - RATEA - 3849.001795 - - - 2749.286996 - - - RATEA - 2749.286996 - - - 4233.901974 - - - RATEA - 4233.901974 - - - 3024.215696 - - - RATEA - 3024.215696 - - - 3849.001795 - - - RATEA - 3849.001795 - - - 2749.286996 - - - RATEA - 2749.286996 - - - 1649.572198 - - - RATEA - 1649.572198 - - - 1649.572198 - - - RATEA - 1649.572198 - - - 2474.358297 - - - RATEB - 2474.358297 - - - 2474.358297 - - - RATEB - 2474.358297 - - - 3436.608745 - - - RATEB - 3436.608745 - - - 3436.608745 - - - RATEB - 3436.608745 - - - 2199.429597 - - - RATEB - 2199.429597 - - - 2199.429597 - - - RATEB - 2199.429597 - - - 1787.036547 - - - RATEB - 1787.036547 - - - 1787.036547 - - - RATEB - 1787.036547 - - - 1787.036547 - - - RATEB - 1787.036547 - - - 1787.036547 - - - RATEB - 1787.036547 - - - 1787.036547 - - - RATEB - 1787.036547 - - - 1787.036547 - - - RATEB - 1787.036547 - - - 3436.608745 - - - RATEB - 3436.608745 - - - 3436.608745 - - - RATEB - 3436.608745 - - - 2749.286996 - - - RATEB - 2749.286996 - - - 2749.286996 - - - RATEB - 2749.286996 - - - 2749.286996 - - - RATEB - 2749.286996 - - - 2749.286996 - - - RATEB - 2749.286996 - - - 3436.608745 - - - RATEB - 3436.608745 - - - 3436.608745 - - - RATEB - 3436.608745 - - - 1787.036547 - - - RATEB - 1787.036547 - - - 1787.036547 - - - RATEB - 1787.036547 - - - 2749.286996 - - - RATEB - 2749.286996 - - - 2749.286996 - - - RATEB - 2749.286996 - - - 2474.358297 - - - RATEB - 2474.358297 - - - 2474.358297 - - - RATEB - 2474.358297 - - - 1787.036547 - - - RATEB - 1787.036547 - - - 1787.036547 - - - RATEB - 1787.036547 - - - 1374.643498 - - - RATEB - 1374.643498 - - - 1374.643498 - - - RATEB - 1374.643498 - - - 2061.965247 - - - RATEB - 2061.965247 - - - 2061.965247 - - - RATEB - 2061.965247 - - - 1512.107848 - - - RATEB - 1512.107848 - - - 1512.107848 - - - RATEB - 1512.107848 - - - 2474.358297 - - - RATEB - 2474.358297 - - - 2474.358297 - - - RATEB - 2474.358297 - - - 3024.215696 - - - RATEB - 3024.215696 - - - 3024.215696 - - - RATEB - 3024.215696 - - - 4426.352064 - - - RATEB - 4426.352064 - - - 4426.352064 - - - RATEB - 4426.352064 - - - 3024.215696 - - - RATEB - 3024.215696 - - - 3024.215696 - - - RATEB - 3024.215696 - - - 3024.215696 - - - RATEB - 3024.215696 - - - 3024.215696 - - - RATEB - 3024.215696 - - - 3024.215696 - - - RATEB - 3024.215696 - - - 3024.215696 - - - RATEB - 3024.215696 - - - 3024.215696 - - - RATEB - 3024.215696 - - - 3024.215696 - - - RATEB - 3024.215696 - - - 2749.286996 - - - RATEB - 2749.286996 - - - 2749.286996 - - - RATEB - 2749.286996 - - - 2749.286996 - - - RATEB - 2749.286996 - - - 2749.286996 - - - RATEB - 2749.286996 - - - 1787.036547 - - - RATEB - 1787.036547 - - - 1787.036547 - - - RATEB - 1787.036547 - - - 769.800359 - - - RATEB - 769.800359 - - - 769.800359 - - - RATEB - 769.800359 - - - 1539.600718 - - - RATEB - 1539.600718 - - - 1539.600718 - - - RATEB - 1539.600718 - - - 1732.050808 - - - RATEB - 1732.050808 - - - 1732.050808 - - - RATEB - 1732.050808 - - - 2474.358297 - - - RATEB - 2474.358297 - - - 2474.358297 - - - RATEB - 2474.358297 - - - 2199.429597 - - - RATEB - 2199.429597 - - - 2199.429597 - - - RATEB - 2199.429597 - - - 3024.215696 - - - RATEB - 3024.215696 - - - 3024.215696 - - - RATEB - 3024.215696 - - - 3024.215696 - - - RATEB - 3024.215696 - - - 3024.215696 - - - RATEB - 3024.215696 - - - 2474.358297 - - - RATEB - 2474.358297 - - - 2474.358297 - - - RATEB - 2474.358297 - - - 2336.893947 - - - RATEB - 2336.893947 - - - 2336.893947 - - - RATEB - 2336.893947 - - - 2405.626122 - - - RATEB - 2405.626122 - - - 2405.626122 - - - RATEB - 2405.626122 - - - 2474.358297 - - - RATEB - 2474.358297 - - - 2474.358297 - - - RATEB - 2474.358297 - - - 4233.901974 - - - RATEB - 4233.901974 - - - 4233.901974 - - - RATEB - 4233.901974 - - - 2061.965247 - - - RATEB - 2061.965247 - - - 2061.965247 - - - RATEB - 2061.965247 - - - 3024.215696 - - - RATEB - 3024.215696 - - - 3024.215696 - - - RATEB - 3024.215696 - - - 2749.286996 - - - RATEB - 2749.286996 - - - 2749.286996 - - - RATEB - 2749.286996 - - - 3024.215696 - - - RATEB - 3024.215696 - - - 3024.215696 - - - RATEB - 3024.215696 - - - 1924.500897 - - - RATEB - 1924.500897 - - - 1924.500897 - - - RATEB - 1924.500897 - - - 4233.901974 - - - RATEB - 4233.901974 - - - 4233.901974 - - - RATEB - 4233.901974 - - - 1732.050808 - - - RATEB - 1732.050808 - - - 1732.050808 - - - RATEB - 1732.050808 - - - 2474.358297 - - - RATEB - 2474.358297 - - - 2474.358297 - - - RATEB - 2474.358297 - - - 3299.144395 - - - RATEB - 3299.144395 - - - 3299.144395 - - - RATEB - 3299.144395 - - - 2061.965247 - - - RATEB - 2061.965247 - - - 2061.965247 - - - RATEB - 2061.965247 - - - 3436.608745 - - - RATEB - 3436.608745 - - - 3436.608745 - - - RATEB - 3436.608745 - - - 1732.050808 - - - RATEB - 1732.050808 - - - 1732.050808 - - - RATEB - 1732.050808 - - - 2886.751346 - - - RATEB - 2886.751346 - - - 2886.751346 - - - RATEB - 2886.751346 - - - 5773.502692 - - - RATEB - 5773.502692 - - - 5773.502692 - - - RATEB - 5773.502692 - - - 2309.401077 - - - RATEB - 2309.401077 - - - 2309.401077 - - - RATEB - 2309.401077 - - - 4811.252243 - - - RATEB - 4811.252243 - - - 4811.252243 - - - RATEB - 4811.252243 - - - 5773.502692 - - - RATEB - 5773.502692 - - - 5773.502692 - - - RATEB - 5773.502692 - - - 4233.901974 - - - RATEB - 4233.901974 - - - 4233.901974 - - - RATEB - 4233.901974 - - - 1732.050808 - - - RATEB - 1732.050808 - - - 1732.050808 - - - RATEB - 1732.050808 - - - 2309.401077 - - - RATEB - 2309.401077 - - - 2309.401077 - - - RATEB - 2309.401077 - - - 4123.930494 - - - RATEB - 4123.930494 - - - 4123.930494 - - - RATEB - 4123.930494 - - - 1649.572198 - - - RATEB - 1649.572198 - - - 1649.572198 - - - RATEB - 1649.572198 - - - 1649.572198 - - - RATEB - 1649.572198 - - - 1649.572198 - - - RATEB - 1649.572198 - - - 1649.572198 - - - RATEB - 1649.572198 - - - 1649.572198 - - - RATEB - 1649.572198 - - - 2061.965247 - - - RATEB - 2061.965247 - - - 2061.965247 - - - RATEB - 2061.965247 - - - 1649.572198 - - - RATEB - 1649.572198 - - - 1649.572198 - - - RATEB - 1649.572198 - - - 1649.572198 - - - RATEB - 1649.572198 - - - 1649.572198 - - - RATEB - 1649.572198 - - - 5773.502692 - - - RATEB - 5773.502692 - - - 4123.930494 - - - RATEB - 4123.930494 - - - 5773.502692 - - - RATEB - 5773.502692 - - - 4123.930494 - - - RATEB - 4123.930494 - - - 4123.930494 - - - RATEB - 4123.930494 - - - 12830.005982 - - - RATEB - 12830.005982 - - - 4123.930494 - - - RATEB - 4123.930494 - - - 5773.502692 - - - RATEB - 5773.502692 - - - 8660.254038 - - - RATEB - 8660.254038 - - - 6185.895741 - - - RATEB - 6185.895741 - - - 5773.502692 - - - RATEB - 5773.502692 - - - 4123.930494 - - - RATEB - 4123.930494 - - - 5773.502692 - - - RATEB - 5773.502692 - - - 4123.930494 - - - RATEB - 4123.930494 - - - 5773.502692 - - - RATEB - 5773.502692 - - - 4123.930494 - - - RATEB - 4123.930494 - - - 5773.502692 - - - RATEB - 5773.502692 - - - 4123.930494 - - - RATEB - 4123.930494 - - - 5773.502692 - - - RATEB - 5773.502692 - - - 4123.930494 - - - RATEB - 4123.930494 - - - 5773.502692 - - - RATEB - 5773.502692 - - - 4123.930494 - - - RATEB - 4123.930494 - - - 5773.502692 - - - RATEB - 5773.502692 - - - 4123.930494 - - - RATEB - 4123.930494 - - - 2749.286996 - - - RATEB - 2749.286996 - - - 2749.286996 - - - RATEB - 2749.286996 - - - 2886.751346 - - - RATEC - 2886.751346 - - - 2886.751346 - - - RATEC - 2886.751346 - - - 4123.930494 - - - RATEC - 4123.930494 - - - 4123.930494 - - - RATEC - 4123.930494 - - - 2474.358297 - - - RATEC - 2474.358297 - - - 2474.358297 - - - RATEC - 2474.358297 - - - 2199.429597 - - - RATEC - 2199.429597 - - - 2199.429597 - - - RATEC - 2199.429597 - - - 1924.500897 - - - RATEC - 1924.500897 - - - 1924.500897 - - - RATEC - 1924.500897 - - - 1924.500897 - - - RATEC - 1924.500897 - - - 1924.500897 - - - RATEC - 1924.500897 - - - 4123.930494 - - - RATEC - 4123.930494 - - - 4123.930494 - - - RATEC - 4123.930494 - - - 3436.608745 - - - RATEC - 3436.608745 - - - 3436.608745 - - - RATEC - 3436.608745 - - - 3436.608745 - - - RATEC - 3436.608745 - - - 3436.608745 - - - RATEC - 3436.608745 - - - 4123.930494 - - - RATEC - 4123.930494 - - - 4123.930494 - - - RATEC - 4123.930494 - - - 2199.429597 - - - RATEC - 2199.429597 - - - 2199.429597 - - - RATEC - 2199.429597 - - - 3436.608745 - - - RATEC - 3436.608745 - - - 3436.608745 - - - RATEC - 3436.608745 - - - 2749.286996 - - - RATEC - 2749.286996 - - - 2749.286996 - - - RATEC - 2749.286996 - - - 1924.500897 - - - RATEC - 1924.500897 - - - 1924.500897 - - - RATEC - 1924.500897 - - - 1512.107848 - - - RATEC - 1512.107848 - - - 1512.107848 - - - RATEC - 1512.107848 - - - 2336.893947 - - - RATEC - 2336.893947 - - - 2336.893947 - - - RATEC - 2336.893947 - - - 1787.036547 - - - RATEC - 1787.036547 - - - 1787.036547 - - - RATEC - 1787.036547 - - - 2749.286996 - - - RATEC - 2749.286996 - - - 2749.286996 - - - RATEC - 2749.286996 - - - 3574.073095 - - - RATEC - 3574.073095 - - - 3574.073095 - - - RATEC - 3574.073095 - - - 4811.252243 - - - RATEC - 4811.252243 - - - 4811.252243 - - - RATEC - 4811.252243 - - - 3574.073095 - - - RATEC - 3574.073095 - - - 3574.073095 - - - RATEC - 3574.073095 - - - 3574.073095 - - - RATEC - 3574.073095 - - - 3574.073095 - - - RATEC - 3574.073095 - - - 3574.073095 - - - RATEC - 3574.073095 - - - 3574.073095 - - - RATEC - 3574.073095 - - - 3574.073095 - - - RATEC - 3574.073095 - - - 3574.073095 - - - RATEC - 3574.073095 - - - 3436.608745 - - - RATEC - 3436.608745 - - - 3436.608745 - - - RATEC - 3436.608745 - - - 3436.608745 - - - RATEC - 3436.608745 - - - 3436.608745 - - - RATEC - 3436.608745 - - - 1924.500897 - - - RATEC - 1924.500897 - - - 1924.500897 - - - RATEC - 1924.500897 - - - 962.250449 - - - RATEC - 962.250449 - - - 962.250449 - - - RATEC - 962.250449 - - - 1732.050808 - - - RATEC - 1732.050808 - - - 1732.050808 - - - RATEC - 1732.050808 - - - 1828.275852 - - - RATEC - 1828.275852 - - - 1828.275852 - - - RATEC - 1828.275852 - - - 2611.822646 - - - RATEC - 2611.822646 - - - 2611.822646 - - - RATEC - 2611.822646 - - - 2474.358297 - - - RATEC - 2474.358297 - - - 2474.358297 - - - RATEC - 2474.358297 - - - 3436.608745 - - - RATEC - 3436.608745 - - - 3436.608745 - - - RATEC - 3436.608745 - - - 3299.144395 - - - RATEC - 3299.144395 - - - 3299.144395 - - - RATEC - 3299.144395 - - - 2749.286996 - - - RATEC - 2749.286996 - - - 2749.286996 - - - RATEC - 2749.286996 - - - 2749.286996 - - - RATEC - 2749.286996 - - - 2749.286996 - - - RATEC - 2749.286996 - - - 2749.286996 - - - RATEC - 2749.286996 - - - 2749.286996 - - - RATEC - 2749.286996 - - - 2749.286996 - - - RATEC - 2749.286996 - - - 2749.286996 - - - RATEC - 2749.286996 - - - 5003.702333 - - - RATEC - 5003.702333 - - - 5003.702333 - - - RATEC - 5003.702333 - - - 2336.893947 - - - RATEC - 2336.893947 - - - 2336.893947 - - - RATEC - 2336.893947 - - - 3436.608745 - - - RATEC - 3436.608745 - - - 3436.608745 - - - RATEC - 3436.608745 - - - 2955.483521 - - - RATEC - 2955.483521 - - - 2955.483521 - - - RATEC - 2955.483521 - - - 3299.144395 - - - RATEC - 3299.144395 - - - 3299.144395 - - - RATEC - 3299.144395 - - - 2061.965247 - - - RATEC - 2061.965247 - - - 2061.965247 - - - RATEC - 2061.965247 - - - 5003.702333 - - - RATEC - 5003.702333 - - - 5003.702333 - - - RATEC - 5003.702333 - - - 1828.275852 - - - RATEC - 1828.275852 - - - 1828.275852 - - - RATEC - 1828.275852 - - - 2749.286996 - - - RATEC - 2749.286996 - - - 2749.286996 - - - RATEC - 2749.286996 - - - 3574.073095 - - - RATEC - 3574.073095 - - - 3574.073095 - - - RATEC - 3574.073095 - - - 2336.893947 - - - RATEC - 2336.893947 - - - 2336.893947 - - - RATEC - 2336.893947 - - - 4123.930494 - - - RATEC - 4123.930494 - - - 4123.930494 - - - RATEC - 4123.930494 - - - 1828.275852 - - - RATEC - 1828.275852 - - - 1828.275852 - - - RATEC - 1828.275852 - - - 3464.101615 - - - RATEC - 3464.101615 - - - 3464.101615 - - - RATEC - 3464.101615 - - - 6735.753141 - - - RATEC - 6735.753141 - - - 6735.753141 - - - RATEC - 6735.753141 - - - 2886.751346 - - - RATEC - 2886.751346 - - - 2886.751346 - - - RATEC - 2886.751346 - - - 5581.052602 - - - RATEC - 5581.052602 - - - 5581.052602 - - - RATEC - 5581.052602 - - - 6735.753141 - - - RATEC - 6735.753141 - - - 6735.753141 - - - RATEC - 6735.753141 - - - 4618.802154 - - - RATEC - 4618.802154 - - - 4618.802154 - - - RATEC - 4618.802154 - - - 1828.275852 - - - RATEC - 1828.275852 - - - 1828.275852 - - - RATEC - 1828.275852 - - - 2501.851166 - - - RATEC - 2501.851166 - - - 2501.851166 - - - RATEC - 2501.851166 - - - 4811.252243 - - - RATEC - 4811.252243 - - - 4811.252243 - - - RATEC - 4811.252243 - - - 2061.965247 - - - RATEC - 2061.965247 - - - 2061.965247 - - - RATEC - 2061.965247 - - - 2061.965247 - - - RATEC - 2061.965247 - - - 2061.965247 - - - RATEC - 2061.965247 - - - 2061.965247 - - - RATEC - 2061.965247 - - - 2061.965247 - - - RATEC - 2061.965247 - - - 2336.893947 - - - RATEC - 2336.893947 - - - 2336.893947 - - - RATEC - 2336.893947 - - - 2061.965247 - - - RATEC - 2061.965247 - - - 2061.965247 - - - RATEC - 2061.965247 - - - 2061.965247 - - - RATEC - 2061.965247 - - - 2061.965247 - - - RATEC - 2061.965247 - - - 7698.003589 - - - RATEC - 7698.003589 - - - 5498.573992 - - - RATEC - 5498.573992 - - - 7698.003589 - - - RATEC - 7698.003589 - - - 5498.573992 - - - RATEC - 5498.573992 - - - 5498.573992 - - - RATEC - 5498.573992 - - - 17106.674643 - - - RATEC - 17106.674643 - - - 5498.573992 - - - RATEC - 5498.573992 - - - 7698.003589 - - - RATEC - 7698.003589 - - - 9622.504486 - - - RATEC - 9622.504486 - - - 6873.21749 - - - RATEC - 6873.21749 - - - 7698.003589 - - - RATEC - 7698.003589 - - - 5498.573992 - - - RATEC - 5498.573992 - - - 7698.003589 - - - RATEC - 7698.003589 - - - 5498.573992 - - - RATEC - 5498.573992 - - - 7698.003589 - - - RATEC - 7698.003589 - - - 5498.573992 - - - RATEC - 5498.573992 - - - 7698.003589 - - - RATEC - 7698.003589 - - - 5498.573992 - - - RATEC - 5498.573992 - - - 7698.003589 - - - RATEC - 7698.003589 - - - 5498.573992 - - - RATEC - 5498.573992 - - - 7698.003589 - - - RATEC - 7698.003589 - - - 5498.573992 - - - RATEC - 5498.573992 - - - 7698.003589 - - - RATEC - 7698.003589 - - - 5498.573992 - - - RATEC - 5498.573992 - - - 3436.608745 - - - RATEC - 3436.608745 - - - 3436.608745 - - - RATEC - 3436.608745 - - - ARENDAL 300AS1 AD_S - - false - false - - Arendal 300 Asker 1 A Disconnector - - - ARENDAL 300AS1 BD_S - - false - true - - Arendal 300 Asker 1 B Disconnector - - - ARENDAL 300KR1 AD_S - - false - false - - Arendal 300 Kristiansand 1 A Disconnector - - - ARENDAL 300KR1 BD_S - - false - true - - Arendal 300 Kristiansand 1 B Disconnector - - - ARENDAL 300KR2 AD_S - - false - false - - Arendal 300 Kristiansand 2 A Disconnector - - - ARENDAL 300KR2 BD_S - - false - true - - Arendal 300 Kristiansand 2 B Disconnector - - - KRISTIAN300AR1 AD_S - - false - false - - Kristiansand 300 Arendal 1 A Disconnector - false - - - KRISTIAN300AR1 BD_S - - false - true - - Kristiansand 300 Feda 1 B Disconnector - false - - - KRISTIAN300FE1 AD_S - - false - false - - Kristiansand 300 Feda 1 A Disconnector - false - - - KRISTIAN300FE1 BD_S - - false - true - - Kristiansand 300 Feda 1 B Disconnector - false - - - KRISTIAN300G1 AD_S - - false - false - - Kristiansand 300 Gen 1 A Disconnector - false - - - KRISTIAN300G1 BD_S - - false - true - - Kristiansand 300 Gen 1 B Disconnector - false - - - KRISTIAN300G2 AD_S - - false - false - - Kristiansand 300 Gen 2 A Disconnector - false - - - KRISTIAN300G2 BD_S - - false - true - - Kristiansand 300 Gen 2 B Disconnector - false - - - KRISTIAN300G3 AD_S - - false - false - - Kristiansand 300 Gen 3 A Disconnector - false - - - KRISTIAN300G3 BD_S - - false - true - - Kristiansand 300 Gen 3 B Disconnector - false - - - KRISTIAN300G4 AD_S - - false - false - - Kristiansand 300 Gen 4 A Disconnector - false - - - KRISTIAN300G4 BD_S - - false - true - - Kristiansand 300 Gen 4 B Disconnector - false - - - KRISTIAN300KV1 AD_S - - false - false - - Kristiansand 300 Kvilldal 1 A Disconnector - false - - - KRISTIAN300KV1 BD_S - - false - true - - Kristiansand 300 Kvilldal 1 B Disconnector - false - - - KRISTIAN300L1 AD_S - - false - false - - Kristiansand 300 Load 1 A Disconnector - false - - - KRISTIAN300L1 BD_S - - false - true - - Kristiansand 300 Load 1 B Disconnector - false - - - KRISTIAN300L2 AD_S - - false - false - - Kristiansand 300 Load 2 A Disconnector - false - - - KRISTIAN300L2 BD_S - - false - true - - Kristiansand 300 Load 2 B Disconnector - false - - - KRISTIAN300ST1 AD_S - - false - false - - Kristiansand 300 Stavanger 1 A Disconnector - false - - - KRISTIAN300ST1 BD_S - - false - true - - Kristiansand 300 Stavanger 1 B Disconnector - false - - - 1280 - 248.65 - 0 - 1280 - 1280 - 0 - 0 - 248.65 - 1280 - 1280 - 100 - 0 - 0 - 1 - - false - true - 5500 '1 ' - ASKER G1 - false - 1 - - - - 1280 - 248.65 - 0 - 1280 - 1280 - 0 - 0 - 248.65 - 1280 - 1280 - 100 - 0 - 0 - 1 - - false - true - 5500 '2 ' - ASKER G2 - false - 2 - - - - 1120 - 681.833 - 0 - 1120 - 1120 - 0 - 0 - 681.833 - 1120 - 1120 - 20 - 0 - 0 - 1 - - false - true - 6100 '1 ' - BLAFALLIG1 - true - 1 - - - - 1120 - 681.833 - 0 - 1120 - 1120 - 0 - 0 - 681.833 - 1120 - 1120 - 20 - 0 - 0 - 1 - - false - true - 6100 '2 ' - BLAFALLIG2 - true - 2 - - - - 1120 - 681.833 - 0 - 1120 - 1120 - 0 - 0 - 681.833 - 1120 - 1120 - 20 - 0 - 0 - 1 - - false - true - 6100 '3 ' - BLAFALLIG3 - true - 3 - - - - 1120 - 681.833 - 0 - 1120 - 1120 - 0 - 0 - 681.833 - 1120 - 1120 - 20 - 0 - 0 - 1 - - false - true - 6100 '4 ' - BLAFALLIG4 - true - 4 - - - - 1120 - 681.833 - 0 - 1120 - 1120 - 0 - 0 - 681.833 - 1120 - 1120 - 20 - 0 - 0 - 1 - - false - true - 6100 '5 ' - BLAFALLIG5 - true - 5 - - - - 1167 - 392.863 - 0 - 1167 - 1167 - 0 - 0 - 392.863 - 1167 - 1167 - 33.3 - 0 - 0 - 1 - - false - true - 3000 '1 ' - FORSMARK G1 - true - 1 - - - - 1167 - 392.863 - 0 - 1167 - 1167 - 0 - 0 - 392.863 - 1167 - 1167 - 33.3 - 0 - 0 - 1 - - false - true - 3000 '2 ' - FORSMARK G2 - true - 2 - - - - 1167 - 392.863 - 0 - 1167 - 1167 - 0 - 0 - 392.863 - 1167 - 1167 - 33.3 - 0 - 0 - 1 - - false - true - 3000 '3 ' - FORSMARK G3 - false - 3 - - - - 1230 - 440.397 - 0 - 1230 - 1230 - 0 - 0 - 440.397 - 1230 - 1230 - 14.3 - 0 - 0 - 1 - - false - true - 3249 '1 ' - GRUNDFORG1 - false - 1 - - - - 1230 - 440.397 - 0 - 1230 - 1230 - 0 - 0 - 440.397 - 1230 - 1230 - 14.3 - 0 - 0 - 1 - - false - true - 3249 '2 ' - GRUNDFORG2 - true - 2 - - - - 1230 - 440.397 - 0 - 1230 - 1230 - 0 - 0 - 440.397 - 1230 - 1230 - 14.3 - 0 - 0 - 1 - - false - true - 3249 '3 ' - GRUNDFORG3 - true - 3 - - - - 1230 - 440.397 - 0 - 1230 - 1230 - 0 - 0 - 440.397 - 1230 - 1230 - 14.3 - 0 - 0 - 1 - - false - true - 3249 '4 ' - GRUNDFORG4 - true - 4 - - - - 1230 - 440.397 - 0 - 1230 - 1230 - 0 - 0 - 440.397 - 1230 - 1230 - 14.3 - 0 - 0 - 1 - - false - true - 3249 '5 ' - GRUNDFORG5 - true - 5 - - - - 1230 - 440.397 - 0 - 1230 - 1230 - 0 - 0 - 440.397 - 1230 - 1230 - 14.3 - 0 - 0 - 1 - - false - true - 3249 '6 ' - GRUNDFORG6 - true - 6 - - - - 1230 - 440.397 - 0 - 1230 - 1230 - 0 - 0 - 440.397 - 1230 - 1230 - 14.3 - 0 - 0 - 1 - - false - true - 3249 '7 ' - GRUNDFORG7 - true - 7 - - - - 1230 - 440.397 - 0 - 1230 - 1230 - 0 - 0 - 440.397 - 1230 - 1230 - 14.3 - 0 - 0 - 1 - - false - true - 3249 '8 ' - GRUNDFORG8 - true - 8 - - - - 1167 - 685.687 - 0 - 1167 - 1167 - 0 - 0 - 685.687 - 1167 - 1167 - 11.1 - 0 - 0 - 1 - - false - true - 7000 '1 ' - HELSINKIG1 - true - 1 - - - - 1167 - 685.687 - 0 - 1167 - 1167 - 0 - 0 - 685.687 - 1167 - 1167 - 11.1 - 0 - 0 - 1 - - false - true - 7000 '2 ' - HELSINKIG2 - true - 2 - - - - 1167 - 685.687 - 0 - 1167 - 1167 - 0 - 0 - 685.687 - 1167 - 1167 - 11.1 - 0 - 0 - 1 - - false - true - 7000 '3 ' - HELSINKIG3 - true - 3 - - - - 1167 - 685.687 - 0 - 1167 - 1167 - 0 - 0 - 685.687 - 1167 - 1167 - 11.1 - 0 - 0 - 1 - - false - true - 7000 '4 ' - HELSINKIG4 - true - 4 - - - - 1167 - 685.687 - 0 - 1167 - 1167 - 0 - 0 - 685.687 - 1167 - 1167 - 11.1 - 0 - 0 - 1 - - false - true - 7000 '5 ' - HELSINKIG5 - true - 5 - - - - 1167 - 685.687 - 0 - 1167 - 1167 - 0 - 0 - 685.687 - 1167 - 1167 - 11.1 - 0 - 0 - 1 - - false - true - 7000 '6 ' - HELSINKIG6 - true - 6 - - - - 1167 - 685.687 - 0 - 1167 - 1167 - 0 - 0 - 685.687 - 1167 - 1167 - 11.1 - 0 - 0 - 1 - - false - true - 7000 '7 ' - HELSINKIG7 - true - 7 - - - - 1167 - 685.687 - 0 - 1167 - 1167 - 0 - 0 - 685.687 - 1167 - 1167 - 11.1 - 0 - 0 - 1 - - false - true - 7000 '8 ' - HELSINKIG8 - false - 8 - - - - 1167 - 685.687 - 0 - 1167 - 1167 - 0 - 0 - 685.687 - 1167 - 1167 - 11.1 - 0 - 0 - 1 - - false - true - 7000 '9 ' - HELSINKIG9 - false - 9 - - - - 850 - 199.925 - 0 - 850 - 850 - 0 - 0 - 199.925 - 850 - 850 - 100 - 0 - 0 - 1 - - false - true - 3245 '1 ' - JARPSTROG1 - false - 1 - - - - 1050 - 323.652 - 0 - 1050 - 1050 - 0 - 0 - 323.652 - 1050 - 1050 - 50 - 0 - 0 - 0.999 - - false - true - 5600 '1 ' - KRISTIANG1 - true - 1 - - - - 1050 - 323.652 - 0 - 1050 - 1050 - 0 - 0 - 323.652 - 1050 - 1050 - 50 - 0 - 0 - 0.999 - - false - true - 5600 '2 ' - KRISTIANG2 - true - 2 - - - - 1050 - 323.652 - 0 - 1050 - 1050 - 0 - 0 - 323.652 - 1050 - 1050 - 50 - 0 - 0 - 0.999 - - false - true - 5600 '3 ' - KRISTIANG3 - true - 3 - - - - 1050 - 323.652 - 0 - 1050 - 1050 - 0 - 0 - 323.652 - 1050 - 1050 - 50 - 0 - 0 - 0.999 - - false - true - 5600 '4 ' - KRISTIANG4 - false - 4 - - - - 620 - 466.081 - 0 - 620 - 620 - 0 - 0 - 466.081 - 620 - 620 - 100 - 0 - 0 - 1 - - false - true - 6000 '1 ' - KVILLDALG1 - true - 1 - - - - 620 - 466.081 - 0 - 620 - 620 - 0 - 0 - 466.081 - 620 - 620 - 100 - 0 - 0 - 1 - - false - true - 6000 '2 ' - KVILLDALG2 - true - 2 - - - - 620 - 466.081 - 0 - 620 - 620 - 0 - 0 - 466.081 - 620 - 620 - 100 - 0 - 0 - 1 - - false - true - 6000 '3 ' - KVILLDALG3 - true - 3 - - - - 620 - 466.081 - 0 - 620 - 620 - 0 - 0 - 466.081 - 620 - 620 - 100 - 0 - 0 - 1 - - false - true - 6000 '4 ' - KVILLDALG4 - true - 4 - - - - 1183 - 139.462 - 0 - 1183 - 1183 - 0 - 0 - 139.462 - 1183 - 1183 - 16.7 - 0 - 0 - 1 - - false - true - 8500 '1 ' - MALMO G1 - true - 1 - - - - 1183 - 139.462 - 0 - 1183 - 1183 - 0 - 0 - 139.462 - 1183 - 1183 - 16.7 - 0 - 0 - 1 - - false - true - 8500 '2 ' - MALMO G2 - true - 2 - - - - 1183 - 139.462 - 0 - 1183 - 1183 - 0 - 0 - 139.462 - 1183 - 1183 - 16.7 - 0 - 0 - 1 - - false - true - 8500 '3 ' - MALMO G3 - true - 3 - - - - 1183 - 139.462 - 0 - 1183 - 1183 - 0 - 0 - 139.462 - 1183 - 1183 - 16.7 - 0 - 0 - 1 - - false - true - 8500 '4 ' - MALMO G4 - true - 4 - - - - 1183 - 139.462 - 0 - 1183 - 1183 - 0 - 0 - 139.462 - 1183 - 1183 - 16.7 - 0 - 0 - 1 - - false - true - 8500 '5 ' - MALMO G5 - true - 5 - - - - 1183 - 139.462 - 0 - 1183 - 1183 - 0 - 0 - 139.462 - 1183 - 1183 - 16.7 - 0 - 0 - 1 - - false - true - 8500 '6 ' - MALMO G6 - true - 6 - - - - 1050 - 612.145 - 0 - 1050 - 1050 - 0 - 0 - 612.145 - 1050 - 1050 - 50 - 0 - 0 - 1 - - false - true - 6700 '1 ' - MO G1 - true - 1 - - - - 1050 - 612.145 - 0 - 1050 - 1050 - 0 - 0 - 612.145 - 1050 - 1050 - 50 - 0 - 0 - 1 - - false - true - 6700 '2 ' - MO G2 - true - 2 - - - - 1050 - 612.145 - 0 - 1050 - 1050 - 0 - 0 - 612.145 - 1050 - 1050 - 50 - 0 - 0 - 1 - - false - true - 6700 '3 ' - MO G3 - true - 3 - - - - 1050 - 612.145 - 0 - 1050 - 1050 - 0 - 0 - 612.145 - 1050 - 1050 - 50 - 0 - 0 - 1 - - false - true - 6700 '4 ' - MO G4 - true - 4 - - - - 1000 - 750.456 - 0 - 1000 - 1000 - 0 - 0 - 750.456 - 1000 - 1000 - 33.3 - 0 - 0 - 1 - - false - true - 3300 '1 ' - OSKARSHAG1 - true - 1 - - - - 1000 - 750.456 - 0 - 1000 - 1000 - 0 - 0 - 750.456 - 1000 - 1000 - 33.3 - 0 - 0 - 1 - - false - true - 3300 '2 ' - OSKARSHAG2 - true - 2 - - - - 1000 - 750.456 - 0 - 1000 - 1000 - 0 - 0 - 750.456 - 1000 - 1000 - 33.3 - 0 - 0 - 1 - - false - true - 3300 '3 ' - OSKARSHAG3 - true - 3 - - - - 1000 - 750.456 - 0 - 1000 - 1000 - 0 - 0 - 750.456 - 1000 - 1000 - 33.3 - 0 - 0 - 1 - - false - true - 3300 '4 ' - OSKARSHAG4 - true - 4 - - - - 1000 - 750.456 - 0 - 1000 - 1000 - 0 - 0 - 750.456 - 1000 - 1000 - 33.3 - 0 - 0 - 1 - - false - true - 3300 '5 ' - OSKARSHAG5 - true - 5 - - - - 1000 - 750.456 - 0 - 1000 - 1000 - 0 - 0 - 750.456 - 1000 - 1000 - 33.3 - 0 - 0 - 1 - - false - true - 3300 '6 ' - OSKARSHAG6 - true - 6 - - - - 1100 - 260.104 - 0 - 1100 - 1100 - 0 - 0 - 260.104 - 1100 - 1100 - 50 - 0 - 0 - 1 - - false - true - 5400 '1 ' - OSLO G1 - true - 1 - - - - 1100 - 260.104 - 0 - 1100 - 1100 - 0 - 0 - 260.104 - 1100 - 1100 - 50 - 0 - 0 - 1 - - false - true - 5400 '2 ' - OSLO G2 - true - 2 - - - - 900 - 435.385 - 0 - 900 - 900 - 0 - 0 - 435.385 - 900 - 900 - 33.3 - 0 - 0 - 1 - - false - true - 7100 '1 ' - OULU G1 - true - 1 - - - - 900 - 435.385 - 0 - 900 - 900 - 0 - 0 - 435.385 - 900 - 900 - 33.3 - 0 - 0 - 1 - - false - true - 7100 '2 ' - OULU G2 - true - 2 - - - - 900 - 435.385 - 0 - 900 - 900 - 0 - 0 - 435.385 - 900 - 900 - 33.3 - 0 - 0 - 1 - - false - true - 7100 '3 ' - OULU G3 - true - 3 - - - - 1000 - 335.404 - 0 - 1000 - 1000 - 0 - 0 - 335.404 - 1000 - 1000 - 33.3 - 0 - 0 - 1 - - false - true - 3115 '1 ' - PORJUS G1 - true - 1 - - - - 1000 - 335.404 - 0 - 1000 - 1000 - 0 - 0 - 335.404 - 1000 - 1000 - 33.3 - 0 - 0 - 1 - - false - true - 3115 '2 ' - PORJUS G2 - true - 2 - - - - 1000 - 335.404 - 0 - 1000 - 1000 - 0 - 0 - 335.404 - 1000 - 1000 - 33.3 - 0 - 0 - 1 - - false - true - 3115 '3 ' - PORJUS G3 - true - 3 - - - - 1000 - 335.404 - 0 - 1000 - 1000 - 0 - 0 - 335.404 - 1000 - 1000 - 33.3 - 0 - 0 - 1 - - false - true - 3115 '4 ' - PORJUS G4 - true - 4 - - - - 1000 - 335.404 - 0 - 1000 - 1000 - 0 - 0 - 335.404 - 1000 - 1000 - 33.3 - 0 - 0 - 1 - - false - true - 3115 '5 ' - PORJUS G5 - false - 5 - - - - 1217 - 739.674 - 0 - 1217 - 1217 - 0 - 0 - 739.674 - 1217 - 1217 - 16.7 - 0 - 0 - 1 - - false - true - 3359 '1 ' - RINGHALSG1 - true - 1 - - - - 1217 - 739.674 - 0 - 1217 - 1217 - 0 - 0 - 739.674 - 1217 - 1217 - 16.7 - 0 - 0 - 1 - - false - true - 3359 '2 ' - RINGHALSG2 - true - 2 - - - - 1217 - 739.674 - 0 - 1217 - 1217 - 0 - 0 - 739.674 - 1217 - 1217 - 16.7 - 0 - 0 - 1 - - false - true - 3359 '3 ' - RINGHALSG3 - true - 3 - - - - 1217 - 739.674 - 0 - 1217 - 1217 - 0 - 0 - 739.674 - 1217 - 1217 - 16.7 - 0 - 0 - 1 - - false - true - 3359 '4 ' - RINGHALSG4 - true - 4 - - - - 1217 - 739.674 - 0 - 1217 - 1217 - 0 - 0 - 739.674 - 1217 - 1217 - 16.7 - 0 - 0 - 1 - - false - true - 3359 '5 ' - RINGHALSG5 - false - 5 - - - - 1217 - 739.674 - 0 - 1217 - 1217 - 0 - 0 - 739.674 - 1217 - 1217 - 16.7 - 0 - 0 - 1 - - false - true - 3359 '6 ' - RINGHALSG6 - false - 6 - - - - 1075 - 807.547 - 0 - 1075 - 1075 - 0 - 0 - 807.547 - 1075 - 1075 - 50 - 0 - 0 - 1 - - false - true - 5300 '1 ' - SIMA G1 - true - 1 - - - - 1075 - 807.547 - 0 - 1075 - 1075 - 0 - 0 - 807.547 - 1075 - 1075 - 50 - 0 - 0 - 1 - - false - true - 5300 '2 ' - SIMA G2 - true - 2 - - - - 1075 - 807.547 - 0 - 1075 - 1075 - 0 - 0 - 807.547 - 1075 - 1075 - 50 - 0 - 0 - 1 - - false - true - 5300 '3 ' - SIMA G3 - true - 3 - - - - 1075 - 807.547 - 0 - 1075 - 1075 - 0 - 0 - 807.547 - 1075 - 1075 - 50 - 0 - 0 - 1 - - false - true - 5300 '4 ' - SIMA G4 - true - 4 - - - - 1075 - 807.547 - 0 - 1075 - 1075 - 0 - 0 - 807.547 - 1075 - 1075 - 50 - 0 - 0 - 1 - - false - true - 5300 '5 ' - SIMA G5 - true - 5 - - - - 1075 - 807.547 - 0 - 1075 - 1075 - 0 - 0 - 807.547 - 1075 - 1075 - 50 - 0 - 0 - 1 - - false - true - 5300 '6 ' - SIMA G6 - true - 6 - - - - 1100 - 274.554 - 0 - 1100 - 1100 - 0 - 0 - 274.554 - 1100 - 1100 - 100 - 0 - 0 - 1 - - false - true - 5100 '1 ' - TRETTEN G1 - false - 1 - - - - 1100 - 274.554 - 0 - 1100 - 1100 - 0 - 0 - 274.554 - 1100 - 1100 - 100 - 0 - 0 - 1 - - false - true - 5100 '2 ' - TRETTEN G2 - false - 2 - - - - 1000 - 269.964 - 0 - 1000 - 1000 - 0 - 0 - 269.964 - 1000 - 1000 - 25 - 0 - 0 - 1 - - false - true - 6500 '1 ' - TRONDHEIG1 - true - 1 - - - - 1000 - 269.964 - 0 - 1000 - 1000 - 0 - 0 - 269.964 - 1000 - 1000 - 25 - 0 - 0 - 1 - - false - true - 6500 '2 ' - TRONDHEIG2 - true - 2 - - - - 1000 - 269.964 - 0 - 1000 - 1000 - 0 - 0 - 269.964 - 1000 - 1000 - 25 - 0 - 0 - 1 - - false - true - 6500 '3 ' - TRONDHEIG3 - true - 3 - - - - 1000 - 269.964 - 0 - 1000 - 1000 - 0 - 0 - 269.964 - 1000 - 1000 - 25 - 0 - 0 - 1 - - false - true - 6500 '4 ' - TRONDHEIG4 - true - 4 - - - - NO - Nordic - - - LA - Serbia - - - - LC 300AJAURE-MO - - - - LC 300ARENDAL-KRISTA_HVDC - - - - LC 300ASKER-ARENDAL - - - - LC 300HOGASEN-TRONDHEIM - - - - LC 300KRISTIAN-ARENDAL - - - - LC 300KRISTIAN-FEDA - - - - LC 300KRISTIAN-KVILLDAL - - - - LC 300KRISTIAN-STAVANGE - - - - LC 300KVILLDAL-BLAFALLI - - - - LC 300OSLO-ASKER - - - - LC 300OSLO-KVILLDAL - - - - LC 300SIMA-BLAFALLI - - - - LC 300TRETTEN-ASKER - - - - LC 300TRETTEN-TRONDHEIM - - - - LC 300TRONDHEIM-MO 1 - - - - LC 300TRONDHEIM-MO 2 - - - - LC 420ARENDAL-SANDEFJORD - - - - LC 420AURLAND-EIDFJORD - - - - LC 420AURLAND-GEILO - - - - LC 420AURLAND-HAGAFOSS - - - - LC 420DAGALI-GEILO - - - - LC 420DAGALI-HAGAFOSS - - - - LC 420DAGALI-KONGSBER - - - - LC 420FORSMARK-DANNEBO - - - - LC 420FORSMARK-JARPSTRO 1 - - - - LC 420FORSMARK-JARPSTRO 2 - - - - LC 420FORSMARK-OSKARHA 1 - - - - LC 420FORSMARK-OSKARHA 2 - - - - LC 420FORSMARK-PORJUS - - - - LC 420GEILO-EIDFJORD 1 - - - - LC 420GEILO-EIDFJORD 2 - - - - LC 420GRUNDFOR-OULU - - - - LC 420HALDEN-DAGALI - - - - LC 420HALDEN-KONGSBER - - - - LC 420HALDEN-SKIEN - - - - LC 420HELSINKI-ESTLINK - - - - LC 420HELSINKI-OULU 1 - - - - LC 420HELSINKI-OULU 2 - - - - LC 420HELSINKI-OULU 3 - - - - LC 420HELSINKI-VYBORG - - - - LC 420HJALTA-GRUNDFOR - - - - LC 420HJALTA-PORJUS - - - - LC 420HJALTA-RINGHALS 1 - - - - LC 420HJALTA-RINGHALS 2 - - - - LC 420HJALTA-TENHULT 1 - - - - LC 420HJALTA-TENHULT 2 - - - - LC 420HJALTA-TENHULT 3 - - - - LC 420KONGSBER-GEILO 1 - - - - LC 420KONGSBER-GEILO 2 - - - - LC 420MALMO-ARRIE - - - - LC 420MALMO-KARLSH - - - - LC 420OSKARHA-MALMO 1 - - - - LC 420OSKARHA-MALMO 2 - - - - LC 420OSKARHA-TENHULT - - - - LC 420PORJUS-GRUNDFOR - - - - LC 420PORJUS-JARPSTRO - - - - LC 420PORJUS-NARVIK - - - - LC 420PORJUS-OULU - - - - LC 420RINGHALS-HALDEN 1 - - - - LC 420RINGHALS-HALDEN 2 - - - - LC 420RINGHALS-MALMO 1 - - - - LC 420RINGHALS-MALMO 2 - - - - LC 420SYLLING-HAGAFOSS - - - - LC 420SYLLING-SANDEFJORD - - - - LC 420SYLLING-SKIEN - - - - LC 420SYSLE-HAGAFOSS - - - - LC 420TENHULT-MALMO - - - - LC 420TENHULT-RINGHALS - - - -1.44444E-05 - -3.3333E-06 - 0 - 1 - 300 - 1 - - - - false - true - Branch Shunt 5500_5603_1 - ARENDAL 300 LSC1 - 1 - - - 1.44444E-05 - 3.3333E-06 - 0 - 1 - 300 - 1 - - - - false - true - Branch Shunt 5500_5603_1 - ASKER 300 LSC1 - - - 5.669E-07 - 1.1338E-06 - 0 - 1 - 420 - 1 - - - - false - true - Branch Shunt 5102_6001_1 - DAGALI 420 LSC1 - - - -5.669E-07 - 1.1338E-06 - 0 - 1 - 420 - 1 - - - - false - true - Branch Shunt 5102_6001_1 - HAGAFOSS420 LSC1 - - - 2.8345E-06 - 1.1338E-06 - 0 - 1 - 420 - 1 - - - - false - true - Branch Shunt 5401_6001_1 - HAGAFOSS420 LSC1 - - - -0.0055238095 - 0.0001264172 - 0 - 1 - 420 - 1 - - - - false - true - Branch Shunt 5101_5501_1 - HALDEN 420 LSC1 - - - 0.0055238095 - -0.000122449 - 0 - 1 - 420 - 1 - - - - false - true - Branch Shunt 5101_5501_1 - SKIEN 420 LSC1 - - - -2.8345E-06 - -1.1338E-06 - 0 - 1 - 420 - 1 - - - - false - true - Branch Shunt 5401_6001_1 - SYLLING 420 LSC1 - - - FI LA - - - NO LA - - - SE LA - - - false - 0 - 0 - 1 - 0 - 0 - 1 - CONST_P_Q LRC - Constant q and q power load response characteristic - - - Allocated - MMSv334 - Market Management System v3.3.3 - - - Calculated - Cac - Calculation based on other analog values - - - Forecasted - LFTYv101 - Load Forecasting Tool Year v.1.0.1 - - - Forecasted - LFTDv109 - Load Forecasting Tool Day v.1.0.9 - - - Forecasted - LFTMv520 - Load Forecasting Tool Month v.5.2.0 - - - Forecasted - WFv101 - Wind Forecast Tool v.1.0.1 - - - Forecasted - RoRFv222 - Run-of-river Forecast Tool v.2.2.2 - - - Forecasted - LFTWv234 - Load Forecasting Tool Week v.2.3.4 - - - ICCP - ICCP - From Elcom, ICCP from partner - - - Operator - Operator - User entry - - - PowerFlow - SEv267 - State Estimator v.2.6.7 - - - SCADA - SCADA - From RTU or 61850 - - - - - 493 - 162.041 - - - false - true - 8600 '1 ' - ARRIE 420 L1 - - - - - 608 - 199.84 - - - false - true - 3020 '1 ' - DANNEBO 420 L1 - - - - - 519 - 170.587 - - - false - true - 7020 '1 ' - ESTLINK 420 L1 - - - - - 729 - 239.611 - - - false - true - 5620 '1 ' - FEDA 300 L1 - - - - - -608 - -199.84 - - - false - true - 7000 '6 ' - HELSINKI420 L6 - - - - - 3 - 0.986 - - - false - true - 8700 '1 ' - KARLSH 420 L1 - - - - - 1230 - 175.266 - - - false - true - 5610 '1 ' - KRISTIA 300 L1 - - - - - 946 - 310.935 - - - false - true - 8500 '4 ' - MALMO 420 L4 - - - - - 12 - 3.944 - - - false - true - 6701 '1 ' - NARVIK 420 L1 - - - - - -19 - -6.245 - - - false - true - 6701 '3 ' - NARVIK 420 L3 - - - - - -12 - -3.944 - - - false - true - 7100 '3 ' - OULU 420 L3 - - - - - -80 - -26.295 - - - false - true - 3360 '1 ' - STEENKU 135 L1 - - - - - -903 - -296.802 - - - false - true - 7010 '1 ' - VYBORG 420 L1 - - - - ARRIE 420 LG1 - 8600_1 - - - - DANNEBO 420 LG1 - 3020_1 - - - - ESTLINK 420 LG1 - 7020_1 - - - - FEDA 300 LG1 - 5620_1 - - - - HELSINKI420 LG6 - 7000_6 - - - - KARLSH 420 LG1 - 8700_1 - - - - KRISTIA 300 LG1 - 5610_1 - - - - MALMO 420 LG4 - 8500_4 - - - - NARVIK 420 LG1 - 6701_1 - - - - NARVIK 420 LG3 - 6701_3 - - - - OULU 420 LG3 - 7100_3 - - - - STEENKU 135 LG1 - 3360_1 - - - - VYBORG 420 LG1 - 7010_1 - - - Grid Operator1 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - PTC 420RINGHALS-HALDEN LimitSet for direction Halden to Ringhals - LS PTC 420RING-HALDEN F HA - - - - - PTC 420RINGHALS-HALDEN LimitSet for PATL 20 direction Ringhals to Halden - LS PTC 420RING-HALDEN PATL10 RI - - - - - - PTC 420RINGHALS-HALDEN LimitSet for PATL 30 direction Ringhals to Halden - LS PTC 420RING-HALDEN PATL30 RI - - - - - - PTC 420RINGHALS-HALDEN LimitSet for TATL 20 direction Ringhals to Halden - LS PTC 420RING-HALDEN TATL20 RI - - - - - - PTC 420RINGHALS-HALDEN LimitSet for TATL 30 direction Ringhals to Halden - LS PTC 420RING-HALDEN TATL30 RI - - - - - - PTC 420RINGHALS-HALDEN LimitSet for warning voltage direction Ringhals to Halden - LS PTC 420RING-HALDEN WV RI - - - - - Branch 3000_3020_1 Limits <Default> - Limits <Default> - - - - Branch 3000_3020_1 Limits <Default> - Limits <Default> - - - - Branch 3000_3115_1 Limits <Default> - Limits <Default> - - - - Branch 3000_3115_1 Limits <Default> - Limits <Default> - - - - Branch 3000_3245_1 Limits <Default> - Limits <Default> - - - - Branch 3000_3245_1 Limits <Default> - Limits <Default> - - - - Branch 3000_3245_2 Limits <Default> - Limits <Default> - - - - Branch 3000_3245_2 Limits <Default> - Limits <Default> - - - - Branch 3000_3300_1 Limits <Default> - Limits <Default> - - - - Branch 3000_3300_1 Limits <Default> - Limits <Default> - - - - Branch 3000_3300_2 Limits <Default> - Limits <Default> - - - - Branch 3000_3300_2 Limits <Default> - Limits <Default> - - - - Branch 3100_3115_1 Limits <Default> - Limits <Default> - - - - Branch 3100_3115_1 Limits <Default> - Limits <Default> - - - - Branch 3100_3200_1 Limits <Default> - Limits <Default> - - - - Branch 3100_3200_1 Limits <Default> - Limits <Default> - - - - Branch 3100_3200_3 Limits <Default> - Limits <Default> - - - - Branch 3100_3200_3 Limits <Default> - Limits <Default> - - - - Branch 3100_3249_1 Limits <Default> - Limits <Default> - - - - Branch 3100_3249_1 Limits <Default> - Limits <Default> - - - - Branch 3100_3359_1 Limits <Default> - Limits <Default> - - - - Branch 3100_3359_1 Limits <Default> - Limits <Default> - - - - Branch 3100_3359_2 Limits <Default> - Limits <Default> - - - - Branch 3100_3359_2 Limits <Default> - Limits <Default> - - - - Branch 3115_3245_1 Limits <Default> - Limits <Default> - - - - Branch 3115_3245_1 Limits <Default> - Limits <Default> - - - - Branch 3115_3249_1 Limits <Default> - Limits <Default> - - - - Branch 3115_3249_1 Limits <Default> - Limits <Default> - - - - Branch 3115_6701_1 Limits <Default> - Limits <Default> - - - - Branch 3115_6701_1 Limits <Default> - Limits <Default> - - - - Branch 3115_7100_1 Limits <Default> - Limits <Default> - - - - Branch 3115_7100_1 Limits <Default> - Limits <Default> - - - - Branch 3200_3300_1 Limits <Default> - Limits <Default> - - - - Branch 3200_3300_1 Limits <Default> - Limits <Default> - - - - Branch 3200_3359_1 Limits <Default> - Limits <Default> - - - - Branch 3200_3359_1 Limits <Default> - Limits <Default> - - - - Branch 3200_8500_1 Limits <Default> - Limits <Default> - - - - Branch 3200_8500_1 Limits <Default> - Limits <Default> - - - - Branch 3244_6500_1 Limits <Default> - Limits <Default> - - - - Branch 3244_6500_1 Limits <Default> - Limits <Default> - - - - Branch 3249_7100_1 Limits <Default> - Limits <Default> - - - - Branch 3249_7100_1 Limits <Default> - Limits <Default> - - - - Branch 3300_8500_1 Limits <Default> - Limits <Default> - - - - Branch 3300_8500_1 Limits <Default> - Limits <Default> - - - - Branch 3300_8500_2 Limits <Default> - Limits <Default> - - - - Branch 3300_8500_2 Limits <Default> - Limits <Default> - - - - Branch 3359_5101_1 Limits <Default> - Limits <Default> - - - - Branch 3359_5101_1 Limits <Default> - Limits <Default> - - - - Branch 3359_5101_2 Limits <Default> - Limits <Default> - - - - Branch 3359_5101_2 Limits <Default> - Limits <Default> - - - - Branch 3359_8500_1 Limits <Default> - Limits <Default> - - - - Branch 3359_8500_1 Limits <Default> - Limits <Default> - - - - Branch 3359_8500_2 Limits <Default> - Limits <Default> - - - - Branch 3359_8500_2 Limits <Default> - Limits <Default> - - - - Branch 3701_6700_1 Limits <Default> - Limits <Default> - - - - Branch 3701_6700_1 Limits <Default> - Limits <Default> - - - - Branch 5100_5500_1 Limits <Default> - Limits <Default> - - - - Branch 5100_5500_1 Limits <Default> - Limits <Default> - - - - Branch 5100_6500_1 Limits <Default> - Limits <Default> - - - - Branch 5100_6500_1 Limits <Default> - Limits <Default> - - - - Branch 5101_5102_1 Limits <Default> - Limits <Default> - - - - Branch 5101_5102_1 Limits <Default> - Limits <Default> - - - - Branch 5101_5103_1 Limits <Default> - Limits <Default> - - - - Branch 5101_5103_1 Limits <Default> - Limits <Default> - - - - Branch 5101_5501_1 Limits <Default> - Limits <Default> - - - - Branch 5101_5501_1 Limits <Default> - Limits <Default> - - - - Branch 5102_5103_1 Limits <Default> - Limits <Default> - - - - Branch 5102_5103_1 Limits <Default> - Limits <Default> - - - - Branch 5102_5304_1 Limits <Default> - Limits <Default> - - - - Branch 5102_5304_1 Limits <Default> - Limits <Default> - - - - Branch 5102_6001_1 Limits <Default> - Limits <Default> - - - - Branch 5102_6001_1 Limits <Default> - Limits <Default> - - - - Branch 5103_5304_1 Limits <Default> - Limits <Default> - - - - Branch 5103_5304_1 Limits <Default> - Limits <Default> - - - - Branch 5103_5304_2 Limits <Default> - Limits <Default> - - - - Branch 5103_5304_2 Limits <Default> - Limits <Default> - - - - Branch 5300_6100_1 Limits <Default> - Limits <Default> - - - - Branch 5300_6100_1 Limits <Default> - Limits <Default> - - - - Branch 5301_5304_1 Limits <Default> - Limits <Default> - - - - Branch 5301_5304_1 Limits <Default> - Limits <Default> - - - - Branch 5301_5305_1 Limits <Default> - Limits <Default> - - - - Branch 5301_5305_1 Limits <Default> - Limits <Default> - - - - Branch 5301_6001_1 Limits <Default> - Limits <Default> - - - - Branch 5301_6001_1 Limits <Default> - Limits <Default> - - - - Branch 5304_5305_1 Limits <Default> - Limits <Default> - - - - Branch 5304_5305_1 Limits <Default> - Limits <Default> - - - - Branch 5304_5305_2 Limits <Default> - Limits <Default> - - - - Branch 5304_5305_2 Limits <Default> - Limits <Default> - - - - Branch 5400_5500_1 Limits <Default> - Limits <Default> - - - - Branch 5400_5500_1 Limits <Default> - Limits <Default> - - - - Branch 5400_6000_1 Limits <Default> - Limits <Default> - - - - Branch 5400_6000_1 Limits <Default> - Limits <Default> - - - - Branch 5401_5501_1 Limits <Default> - Limits <Default> - - - - Branch 5401_5501_1 Limits <Default> - Limits <Default> - - - - Branch 5401_5602_1 Limits <Default> - Limits <Default> - - - - Branch 5401_5602_1 Limits <Default> - Limits <Default> - - - - Branch 5401_6001_1 Limits <Default> - Limits <Default> - - - - Branch 5401_6001_1 Limits <Default> - Limits <Default> - - - - Branch 5402_6001_1 Limits <Default> - Limits <Default> - - - - Branch 5402_6001_1 Limits <Default> - Limits <Default> - - - - Branch 5500_5603_1 Limits <Default> - Limits <Default> - - - - Branch 5500_5603_1 Limits <Default> - Limits <Default> - - - - Branch 5600_5601_1 Limits <Default> - Limits <Default> - - - - Branch 5600_5601_1 Limits <Default> - Limits <Default> - - - - Branch 5600_5603_1 Limits <Default> - Limits <Default> - - - - Branch 5600_5603_1 Limits <Default> - Limits <Default> - - - - Branch 5600_5620_1 Limits <Default> - Limits <Default> - - - - Branch 5600_5620_1 Limits <Default> - Limits <Default> - - - - Branch 5600_6000_1 Limits <Default> - Limits <Default> - - - - Branch 5600_6000_1 Limits <Default> - Limits <Default> - - - - Branch 5603_5610_1 Limits <Default> - Limits <Default> - - - - Branch 5603_5610_1 Limits <Default> - Limits <Default> - - - - Branch 6000_6100_1 Limits <Default> - Limits <Default> - - - - Branch 6000_6100_1 Limits <Default> - Limits <Default> - - - - Branch 6500_6700_1 Limits <Default> - Limits <Default> - - - - Branch 6500_6700_1 Limits <Default> - Limits <Default> - - - - Branch 6500_6700_2 Limits <Default> - Limits <Default> - - - - Branch 6500_6700_2 Limits <Default> - Limits <Default> - - - - Branch 7000_7010_1 Limits <Default> - Limits <Default> - - - - Branch 7000_7010_1 Limits <Default> - Limits <Default> - - - - Branch 7000_7020_1 Limits <Default> - Limits <Default> - - - - Branch 7000_7020_1 Limits <Default> - Limits <Default> - - - - Branch 7000_7100_1 Limits <Default> - Limits <Default> - - - - Branch 7000_7100_1 Limits <Default> - Limits <Default> - - - - Branch 7000_7100_2 Limits <Default> - Limits <Default> - - - - Branch 7000_7100_2 Limits <Default> - Limits <Default> - - - - Branch 7000_7100_3 Limits <Default> - Limits <Default> - - - - Branch 7000_7100_3 Limits <Default> - Limits <Default> - - - - Branch 8500_8600_1 Limits <Default> - Limits <Default> - - - - Branch 8500_8600_1 Limits <Default> - Limits <Default> - - - - Branch 8500_8700_1 Limits <Default> - Limits <Default> - - - - Branch 8500_8700_1 Limits <Default> - Limits <Default> - - - - Branch 3244_3245_0_1 Limits <Default> - Limits <Default> - - - - Branch 3244_3245_0_1 Limits <Default> - Limits <Default> - - - - Branch 3701_3249_0_1 Limits <Default> - Limits <Default> - - - - Branch 3701_3249_0_1 Limits <Default> - Limits <Default> - - - - Branch 3359_3360_0_1 Limits <Default> - Limits <Default> - - - - Branch 3359_3360_0_1 Limits <Default> - Limits <Default> - - - - Branch 5101_5100_0_1 Limits <Default> - Limits <Default> - - - - Branch 5101_5100_0_1 Limits <Default> - Limits <Default> - - - - Branch 5300_5301_0_1 Limits <Default> - Limits <Default> - - - - Branch 5300_5301_0_1 Limits <Default> - Limits <Default> - - - - Branch 5400_5401_0_1 Limits <Default> - Limits <Default> - - - - Branch 5400_5401_0_1 Limits <Default> - Limits <Default> - - - - Branch 5400_5402_0_1 Limits <Default> - Limits <Default> - - - - Branch 5400_5402_0_1 Limits <Default> - Limits <Default> - - - - Branch 5500_5501_0_1 Limits <Default> - Limits <Default> - - - - Branch 5500_5501_0_1 Limits <Default> - Limits <Default> - - - - Branch 5601_6001_0_1 Limits <Default> - Limits <Default> - - - - Branch 5601_6001_0_1 Limits <Default> - Limits <Default> - - - - Branch 5603_5602_0_1 Limits <Default> - Limits <Default> - - - - Branch 5603_5602_0_1 Limits <Default> - Limits <Default> - - - - Branch 6000_6001_0_1 Limits <Default> - Limits <Default> - - - - Branch 6000_6001_0_1 Limits <Default> - Limits <Default> - - - - Branch 6700_6701_0_1 Limits <Default> - Limits <Default> - - - - Branch 6700_6701_0_1 Limits <Default> - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Branch 3100_3200_2 Limits <Default> - Limits <Default> - - - - Branch 3100_3200_2 Limits <Default> - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - OperationalLimitSet Negative 10 degree Celsius - OLS_-10dg6700_6701_0_1 - - - - OperationalLimitSet Negative 10 degree Celsius - OLS_-10dg_5101_5103_1 - - - - - - OperationalLimitSet Negative 15 degree Celsius - OLS_-15dg6700_6701_0_1 - - - - OperationalLimitSet Negative 15 degree Celsius - OLS_-15dg_5101_5103_1 - - - - - - OperationalLimitSet Negative 20 degree Celsius - OLS_-20dg6700_6701_0_1 - - - - OperationalLimitSet Negative 20 degree Celsius - OLS_-20dg_5101_5103_1 - - - - - - OperationalLimitSet Negative 25 degree Celsius - OLS_-25dg6700_6701_0_1 - - - - OperationalLimitSet Negative 25 degree Celsius - OLS_-25dg_5101_5103_1 - - - - - - OperationalLimitSet Negative 30 degree Celsius - OLS_-30dg6700_6701_0_1 - - - - - OperationalLimitSet Negative 30 degree Celsius - OLS_-30dg_300KRISTIAN-ARENDAL - - - - OperationalLimitSet Negative 30 degree Celsius - OLS_-30dg_5101_5103_1 - - - - - - OperationalLimitSet Negative 5 degree Celsius - OLS_-5dg6700_6701_0_1 - - - - OperationalLimitSet Negative 5 degree Celsius - OLS_-5dg_5101_5103_1 - - - - - - OperationalLimitSet 0 degree Celsius - OLS_0dg6700_6701_0_1 - - - - OperationalLimitSet 0 degree Celsius - OLS_0dg_5101_5103_1 - - - - - - OperationalLimitSet 10 degree Celsius - OLS_10dg6700_6701_0_1 - - - - OperationalLimitSet 10 degree Celsius - OLS_10dg_5101_5103_1 - - - - - - OperationalLimitSet 15 degree Celsius - OLS_15dg6700_6701_0_1 - - - - OperationalLimitSet 15 degree Celsius - OLS_15dg_5101_5103_1 - - - - - - OperationalLimitSet 20 degree Celsius - OLS_20dg6700_6701_0_1 - - - - - OperationalLimitSet 20 degree Celsius - OLS_20dg_300KRISTIAN-ARENDAL - - - - OperationalLimitSet 20 degree Celsius - OLS_20dg_5101_5103_1 - - - - - - OperationalLimitSet 25 degree Celsius - OLS_25dg6700_6701_0_1 - - - - OperationalLimitSet 25 degree Celsius - OLS_25dg_5101_5103_1 - - - - - - OperationalLimitSet 30 degree Celsius - OLS_30dg6700_6701_0_1 - - - - OperationalLimitSet 30 degree Celsius - OLS_30dg_5101_5103_1 - - - - - - OperationalLimitSet 5 degree Celsius - OLS_5dg6700_6701_0_1 - - - - OperationalLimitSet 5 degree Celsius - OLS_5dg_5101_5103_1 - - - - - 900 - - - HighVoltage - - - 0 - - - HighVoltage - - - 900 - - - LowVoltage - - - 0 - - - LowVoltage - - - 0 - - - OLT High PATL at 20degree - - - - 0 - - - OLT High PATL at 30degree - - - - 0 - - - OLT High warning Voltage - - - - 0 - - - OLT Low Stability - - - - 0 - - - OLT TATL at 20degree - - - - 0 - - - OLT TATL at 30degree - - - - 0 - - - PATL - - - 7200 - - - TATL - - - 900 - - - TATL - - - 1 - 1 - 1 - 0 - 0 - 1.4 - 0.6 - 2 - - true - 3701 3249 0 '1 ' - AJAURE T1 - - - 1 - 1 - 1 - 0 - 0 - 1.4 - 0.6 - 2 - - true - 5603 5602 0 '1 ' - ARENDAL T1 - 1 - - - 1 - 1 - 1 - 0 - 0 - 1.4 - 0.6 - 2 - - true - 5500 5501 0 '1 ' - ASKER T1 - - - 1 - 1 - 1 - 0 - 0 - 1.4 - 0.6 - 2 - - true - 5101 5100 0 '1 ' - HALDEN T1 - - - 1 - 1 - 1 - 0 - 0 - 1.4 - 0.6 - 2 - - true - 3244 3245 0 '1 ' - HOGASEN T1 - - - 1 - 1 - 1 - 0 - 0 - 1.4 - 0.6 - 2 - - true - 6000 6001 0 '1 ' - KVILLDALT1 - - - 1 - 1 - 1 - 0 - 0 - 1.4 - 0.6 - 2 - - true - 6700 6701 0 '1 ' - MO T1 - - - 1 - 1 - 1 - 0 - 0 - 1.4 - 0.6 - 2 - - true - 5400 5401 0 '1 ' - OSLO T1 - - - 1 - 1 - 1 - 0 - 0 - 1.4 - 0.6 - 2 - - true - 5400 5402 0 '1 ' - OSLO T2 - - - 1 - 1 - 1 - 0 - 0 - 1.4 - 0.6 - 2 - - true - 3359 3360 0 '1 ' - RINGHALST1 - - - 1 - 1 - 1 - 0 - 0 - 1.4 - 0.6 - 2 - - true - 5300 5301 0 '1 ' - SIMA T1 - 1 - - - 1 - 1 - 1 - 0 - 0 - 1.4 - 0.6 - 2 - - true - 5601 6001 0 '1 ' - STAVANGET1 - - - - 0 - - 0 - 0 - 0 - 1000 - 300 - 0 - 300 - - - 2 - false - 3701_3249_0_1 Primary Winding - AJAURE T1 P - 0 - 1000 - - - - 0 - - 0 - 0 - 3.528 - 1000 - 420 - 88.2 - 420 - - - 1 - false - 3701_3249_0_1 Secondary Winding - AJAURE T1 S - - - - 0 - - 0 - 0 - 0 - 1000 - 300 - 0 - 300 - - - 2 - false - 5603_5602_0_1 Primary Winding - ARENDALST1 P - 0 - 1000 - - - - 0 - - 0 - 0 - 0.14112 - 1000 - 420 - 5.3802 - 420 - - - 1 - false - 5603_5602_0_1 Secondary Winding - ARENDALST1 S - - - - 0 - - 0 - 0 - 0 - 1000 - 300 - 0 - 300 - - - 2 - false - 5500_5501_0_1 Primary Winding - ASKER T1 P - 0 - 1000 - - - - 0 - - 0 - 0 - 0 - 1000 - 300 - 0 - 300 - - - 2 - false - 5101_5100_0_1 Secondary Winding - HALDEN T1 S - - - - 0 - - 0 - 0 - 0 - 1000 - 300 - 0 - 300 - - - 2 - false - 3244_3245_0_1 Primary Winding - HOGASEN T1 P - 0 - 1000 - - - - 0 - - 0 - 0 - 0.882 - 1000 - 420 - 3.528 - 420 - - - 1 - false - 3244_3245_0_1 Secondary Winding - HOGASEN T1 S - - - - 0 - - 0 - 0 - 0 - 1000 - 300 - 0 - 300 - - - 2 - false - 6000_6001_0_1 Primary Winding - KVILLDALT1 P - 0 - 1000 - - - - 0 - - 0 - 0 - 0.07056 - 1000 - 420 - 2.646 - 420 - - - 1 - false - 6000_6001_0_1 Secondary Winding - KVILLDALT1 S - - - - 0 - - 0 - 0 - 0 - 1000 - 300 - 0 - 300 - - - 2 - false - 6700_6701_0_1 Primary Winding - MO T1 P - 0 - 1000 - - - - 0 - - 0 - 0 - 0.882 - 1000 - 420 - 3.528 - 420 - - - 1 - false - 6700_6701_0_1 Secondary Winding - MO T1 S - - - - 0 - - 0 - 0 - 0 - 1000 - 300 - 0 - 300 - - - 2 - false - 5400_5401_0_1 Primary Winding - OSLO T1 P - 0 - 1000 - - - - 0 - - 0 - 0 - 0.56448 - 1000 - 420 - 21.168 - 420 - - - 1 - false - 5400_5401_0_1 Secondary Winding - OSLO T1 S - - - - 0 - - 0 - 0 - 0 - 1000 - 300 - 0 - 300 - - - 2 - false - 5400_5402_0_1 Primary Winding - OSLO T2 P - 0 - 1000 - - - - 0 - - 0 - 0 - 0.07056 - 1000 - 420 - 2.646 - 420 - - - 1 - false - 5400_5402_0_1 Secondary Winding - OSLO T2 S - - - - 0 - - 0 - 0 - 0.07056 - 1000 - 420 - 2.646 - 420 - - - 1 - false - 5500_5501_0_1 Secondary Winding - OSLO T2 S - - - - 0 - - 0 - 0 - 0.882 - 1000 - 420 - 3.528 - 420 - - - 1 - false - 3359_3360_0_1 Primary Winding - RINGHALST1 P - 0 - 1000 - - - - 0 - - 0 - 0 - 0.14112 - 1000 - 420 - 5.3802 - 420 - - - 1 - false - 5101_5100_0_1 Primary Winding - RINGHALST1 P - 0 - 1000 - - - - 0 - - 0 - 0 - 0 - 1000 - 135 - 0 - 135 - - - 2 - false - 3359_3360_0_1 Secondary Winding - RINGHALST1 S - - - - 0 - - 0 - 0 - 0 - 1000 - 300 - 0 - 300 - - - 2 - false - 5300_5301_0_1 Primary Winding - SIMA T1 P - 0 - 1000 - - - - 0 - - 0 - 0 - 0.28224 - 1000 - 420 - 10.76 - 420 - - - 1 - false - 5300_5301_0_1 Secondary Winding - SIMA T1 S - - - - 0 - - 0 - 0 - 0 - 1000 - 300 - 0 - 300 - - - 2 - false - 5601_6001_0_1 Primary Winding - STAVANGET1 P - 0 - 1000 - - - - 0 - - 0 - 0 - 0.03528 - 1000 - 420 - 1.3406 - 420 - - - 1 - false - 5601_6001_0_1 Secondary Winding - STAVANGET1 S - - - - 2.5 - - - 33 - 1 - true - 17 - 300 - 17 - Primary Tap - AJAURE T1 P RTC1 - - - - 2.5 - - - 33 - 1 - true - 17 - 300 - 17 - Primary Tap - ARENDALST1 P RTC1 - - - - 2.5 - - - 33 - 1 - true - 17 - 300 - 18 - Primary Tap - ASKER T1 P RTC1 - - - - 2.5 - - - 33 - 1 - true - 17 - 300 - 17 - Primary Tap - HOGASEN T1 P RTC1 - - - - 2.5 - - - 33 - 1 - true - 17 - 300 - 17 - Primary Tap - KVILLDALT1 P RTC1 - - - - 2.5 - - - 33 - 1 - true - 17 - 300 - 18 - Primary Tap - MO T1 P RTC1 - - - - 2.5 - - - 33 - 1 - true - 17 - 300 - 17 - Primary Tap - OSLO T1 P RTC1 - - - - 2.5 - - - 33 - 1 - true - 17 - 300 - 17 - Primary Tap - OSLO T2 P RTC1 - - - - 2.5 - - - 33 - 1 - true - 17 - 420 - 18 - Primary Tap - RINGHALST1 P RTC1 - - - - 2.5 - - - 33 - 1 - true - 17 - 420 - 17 - Primary Tap - RINGHALST1 P RTC1 - - - - 2.5 - - - 33 - 1 - true - 17 - 300 - 16 - Primary Tap - SIMA T1 P RTC1 - - - - 2.5 - - - 33 - 1 - true - 17 - 300 - 17 - Primary Tap - STAVANGET1 P RTC1 - - - - - ASKER 300 RC1 - 5500 - - - - - BLAFALLI300 RC1 - 6100 - - - - - FORSMARK420 RC1 - 3000 - - - - - GRUNDFOR420 RC1 - 3249 - - - - - HELSINKI420 RC1 - 7000 - - - - - JARPSTRO420 RC1 - 3245 - - - - - KRISTIAN300 RC1 - - - - - KVILLDAL300 RC1 - 6000 - - - - - MALMO 420 RC1 - 8500 - - - - - MO 300 RC1 - 6700 - - - - - OSKARSHA420 RC1 - 3300 - - - - - OSLO 300 RC1 - 5400 - - - - - OULU 420 RC1 - 7100 - - - - - PORJUS 420 RC1 - 3115 - - - - - RINGHALS420 RC1 - 3359 - - - - - SIMA 300 RC1 - 5300 - - - - - TRETTEN 300 RC1 - 5100 - - - - - TRONDHEI300 RC1 - 6500 - - - - FI1 SGR - - - - - NO1 SGR - - - - NO2 SGR - - - - NO3 SGR - - - - NO4 SGR - - - - NO5 SGR - - - - SE1 SGR - - - - SE2 SGR - - - - SE3 SGR - - - - SE4 SGR - - - - FI1 SLA - - - - NO1 SLA - - - - NO2 SLA - - - - NO3 SLA - - - - NO4 SLA - - - - NO5 SLA - - - - SE1 SLA - - - - SE2 SLA - - - - SE3 SLA - - - - SE4 SLA - - - - AJAURE-orphan-test - - - - - AJAURE - - - - - ARENDAL - - - - - ARRIE_HVDC - - - - - ASKER - - - - - AURLAND - - - - - BLAFALLI - - - - - DAGALI - - - - - DANNEBO_HVDC - - - - - EIDFJORD - - - - - ESTLINK_HVDC - - - - - FEDA_HVDC - - - - - FORSMARK - - - - - GEILO - - - - - GRUNDFORS - - - - - HAGAFOSS - - - - - HALDEN - - - - - HELSINKI - - - - - HJALTA - - - - - HOGASEN - - - - - JARPSTROMMEN - - - - - KARLSH_HVDC - - - - - KONGSBERG - - - - - KRISTIANSAND - - - - - KRISTIA_HVDC - - - - - KVILLDAL - - - - - MALMO - - - - - MO - - - - - NARVIK - - - - - OSKARSHAMN - - - - - OSLO - - - - - OULU - - - - - PORJUS - - - - - RINGHALS - - - - - SANDEFJORD - - - - - SIMA - - - - - SKIEN - - - - - STAVANGER - - - - - STENKU_HVDC - - - - - SYLLING - - - - - SYSLE - - - - - TENHULT - - - - - TRETTEN - - - - - TRONDHEIM - - - - - VYBORG_HVDC - - - - 1200 - 301.2 - -1200 - 301.2 - 100 - 0 - - 14.1672413793 - - 1450 - - - 5500 '1 ' - ASKER 300 M1 - - - 1200 - 301.2 - -1200 - 301.2 - 100 - 0 - - 14.1672413793 - - 1450 - - - 5500 '2 ' - ASKER 300 M2 - - - 900 - 300 - -900 - 300 - 20 - 0 - - 13.064516129 - - 1240 - - - 6100 '1 ' - BLAFALLI300 M1 - - - 900 - 300 - -900 - 300 - 20 - 0 - - 13.064516129 - - 1240 - - - 6100 '2 ' - BLAFALLI300 M2 - - - 900 - 300 - -900 - 300 - 20 - 0 - - 13.064516129 - - 1240 - - - 6100 '3 ' - BLAFALLI300 M3 - - - 900 - 300 - -900 - 300 - 20 - 0 - - 13.064516129 - - 1240 - - - 6100 '4 ' - BLAFALLI300 M4 - - - 900 - 300 - -900 - 300 - 20 - 0 - - 13.064516129 - - 1240 - - - 6100 '5 ' - BLAFALLI300 M5 - - - 967 - 420 - -967 - 420 - 33.3 - 0 - - 30.5307692308 - - 1300 - - - 3000 '1 ' - FORSMARK420 M1 - - - 967 - 420 - -967 - 420 - 33.3 - 0 - - 30.5307692308 - - 1300 - - - 3000 '2 ' - FORSMARK420 M2 - - - 967 - 420 - -967 - 420 - 33.3 - 0 - - 30.5307692308 - - 1300 - - - 3000 '3 ' - FORSMARK420 M3 - - - 986 - 420 - -986 - 420 - 14.3 - 0 - - 27.2984524687 - - 1357 - - - 3249 '1 ' - GRUNDFOR420 M1 - - - 986 - 420 - -986 - 420 - 14.3 - 0 - - 27.2984524687 - - 1357 - - - 3249 '2 ' - GRUNDFOR420 M2 - - - 986 - 420 - -986 - 420 - 14.3 - 0 - - 27.2984524687 - - 1357 - - - 3249 '3 ' - GRUNDFOR420 M3 - - - 986 - 420 - -986 - 420 - 14.3 - 0 - - 27.2984524687 - - 1357 - - - 3249 '4 ' - GRUNDFOR420 M4 - - - 986 - 420 - -986 - 420 - 14.3 - 0 - - 27.2984524687 - - 1357 - - - 3249 '5 ' - GRUNDFOR420 M5 - - - 986 - 420 - -986 - 420 - 14.3 - 0 - - 27.2984524687 - - 1357 - - - 3249 '6 ' - GRUNDFOR420 M6 - - - 986 - 420 - -986 - 420 - 14.3 - 0 - - 27.2984524687 - - 1357 - - - 3249 '7 ' - GRUNDFOR420 M7 - - - 986 - 420 - -986 - 420 - 14.3 - 0 - - 27.2984524687 - - 1357 - - - 3249 '8 ' - GRUNDFOR420 M8 - - - 911 - 420 - -911 - 420 - 11.1 - 0 - - 31.0563380282 - - 1278 - - - 7000 '1 ' - HELSINKI420 M1 - - - 911 - 420 - -911 - 420 - 11.1 - 0 - - 31.0563380282 - - 1278 - - - 7000 '2 ' - HELSINKI420 M2 - - - 911 - 420 - -911 - 420 - 11.1 - 0 - - 31.0563380282 - - 1278 - - - 7000 '3 ' - HELSINKI420 M3 - - - 911 - 420 - -911 - 420 - 11.1 - 0 - - 31.0563380282 - - 1278 - - - 7000 '4 ' - HELSINKI420 M4 - - - 911 - 420 - -911 - 420 - 11.1 - 0 - - 31.0563380282 - - 1278 - - - 7000 '5 ' - HELSINKI420 M5 - - - 911 - 420 - -911 - 420 - 11.1 - 0 - - 31.0563380282 - - 1278 - - - 7000 '6 ' - HELSINKI420 M6 - - - 911 - 420 - -911 - 420 - 11.1 - 0 - - 31.0563380282 - - 1278 - - - 7000 '7 ' - HELSINKI420 M7 - - - 911 - 420 - -911 - 420 - 11.1 - 0 - - 31.0563380282 - - 1278 - - - 7000 '8 ' - HELSINKI420 M8 - - - 911 - 420 - -911 - 420 - 11.1 - 0 - - 31.0563380282 - - 1278 - - - 7000 '9 ' - HELSINKI420 M9 - - - 670 - 420 - -670 - 420 - 100 - 0 - - 28.5675157895 - - 950 - - - 3245 '1 ' - JARPSTRO420 M1 - - - 850 - 303 - -850 - 303 - 50 - 0 - - 15.2727272727 - - 1650 - - - 5600 '1 ' - KRISTIAN300 M1 - - false - - - 850 - 303 - -850 - 303 - 50 - 0 - - 15.2727272727 - - 1650 - - - 5600 '2 ' - KRISTIAN300 M2 - - false - - - 850 - 303 - -850 - 303 - 50 - 0 - - 15.2727272727 - - 1650 - - - 5600 '3 ' - KRISTIAN300 M3 - - false - - - 850 - 303 - -850 - 303 - 50 - 0 - - 15.2727272727 - - 1650 - - - 5600 '4 ' - KRISTIAN300 M4 - - false - - - 500 - 301.5 - -500 - 301.5 - 100 - 0 - - 37.0588235294 - - 680 - - - 6000 '1 ' - KVILLDAL300 M1 - - - 500 - 301.5 - -500 - 301.5 - 100 - 0 - - 37.0588235294 - - 680 - - - 6000 '2 ' - KVILLDAL300 M2 - - - 500 - 301.5 - -500 - 301.5 - 100 - 0 - - 37.0588235294 - - 680 - - - 6000 '3 ' - KVILLDAL300 M3 - - - 500 - 301.5 - -500 - 301.5 - 100 - 0 - - 37.0588235294 - - 680 - - - 6000 '4 ' - KVILLDAL300 M4 - - - 917 - 428.4 - -917 - 428.4 - 16.7 - 0 - - 23.1518215385 - - 1300 - - - 8500 '1 ' - MALMO 420 M1 - - - 917 - 428.4 - -917 - 428.4 - 16.7 - 0 - - 23.1518215385 - - 1300 - - - 8500 '2 ' - MALMO 420 M2 - - - 917 - 428.4 - -917 - 428.4 - 16.7 - 0 - - 23.1518215385 - - 1300 - - - 8500 '3 ' - MALMO 420 M3 - - - 917 - 428.4 - -917 - 428.4 - 16.7 - 0 - - 23.1518215385 - - 1300 - - - 8500 '4 ' - MALMO 420 M4 - - - 917 - 428.4 - -917 - 428.4 - 16.7 - 0 - - 23.1518215385 - - 1300 - - - 8500 '5 ' - MALMO 420 M5 - - - 917 - 428.4 - -917 - 428.4 - 16.7 - 0 - - 23.1518215385 - - 1300 - - - 8500 '6 ' - MALMO 420 M6 - - - 900 - 306 - -900 - 306 - 50 - 0 - - 12.7965 - - 1200 - - - 6700 '1 ' - MO 300 M1 - - - 900 - 306 - -900 - 306 - 50 - 0 - - 12.7965 - - 1200 - - - 6700 '2 ' - MO 300 M2 - - - 900 - 306 - -900 - 306 - 50 - 0 - - 12.7965 - - 1200 - - - 6700 '3 ' - MO 300 M3 - - - 900 - 306 - -900 - 306 - 50 - 0 - - 12.7965 - - 1200 - - - 6700 '4 ' - MO 300 M4 - - - 767 - 420 - -767 - 420 - 33.3 - 0 - - 25.6581818182 - - 1100 - - - 3300 '1 ' - OSKARSHA420 M1 - - - 767 - 420 - -767 - 420 - 33.3 - 0 - - 25.6581818182 - - 1100 - - - 3300 '2 ' - OSKARSHA420 M2 - - - 767 - 420 - -767 - 420 - 33.3 - 0 - - 25.6581818182 - - 1100 - - - 3300 '3 ' - OSKARSHA420 M3 - - - 767 - 420 - -767 - 420 - 33.3 - 0 - - 25.6581818182 - - 1100 - - - 3300 '4 ' - OSKARSHA420 M4 - - - 767 - 420 - -767 - 420 - 33.3 - 0 - - 25.6581818182 - - 1100 - - - 3300 '5 ' - OSKARSHA420 M5 - - - 767 - 420 - -767 - 420 - 33.3 - 0 - - 25.6581818182 - - 1100 - - - 3300 '6 ' - OSKARSHA420 M6 - - - 900 - 302.1 - -900 - 302.1 - 50 - 0 - - 11.7551020408 - - 1225 - - - 5400 '1 ' - OSLO 300 M1 - - - 900 - 302.1 - -900 - 302.1 - 50 - 0 - - 11.7551020408 - - 1225 - - - 5400 '2 ' - OSLO 300 M2 - - - 700 - 420 - -700 - 420 - 33.3 - 0 - - 27.13914 - - 1000 - - - 7100 '1 ' - OULU 420 M1 - - - 700 - 420 - -700 - 420 - 33.3 - 0 - - 27.13914 - - 1000 - - - 7100 '2 ' - OULU 420 M2 - - - 700 - 420 - -700 - 420 - 33.3 - 0 - - 27.13914 - - 1000 - - - 7100 '3 ' - OULU 420 M3 - - - 933 - 420 - -933 - 420 - 33.3 - 0 - - 36.8836363636 - - 1100 - - - 3115 '1 ' - PORJUS 420 M1 - - - 933 - 420 - -933 - 420 - 33.3 - 0 - - 36.8836363636 - - 1100 - - - 3115 '2 ' - PORJUS 420 M2 - - - 933 - 420 - -933 - 420 - 33.3 - 0 - - 36.8836363636 - - 1100 - - - 3115 '3 ' - PORJUS 420 M3 - - - 933 - 420 - -933 - 420 - 33.3 - 0 - - 36.8836363636 - - 1100 - - - 3115 '4 ' - PORJUS 420 M4 - - - 933 - 420 - -933 - 420 - 33.3 - 0 - - 36.8836363636 - - 1100 - - - 3115 '5 ' - PORJUS 420 M5 - - - 983 - 420 - -983 - 420 - 16.7 - 0 - - 25.3166666667 - - 1350 - - - 3359 '1 ' - RINGHALS420 M1 - - - 983 - 420 - -983 - 420 - 16.7 - 0 - - 25.3166666667 - - 1350 - - - 3359 '2 ' - RINGHALS420 M2 - - - 983 - 420 - -983 - 420 - 16.7 - 0 - - 25.3166666667 - - 1350 - - - 3359 '3 ' - RINGHALS420 M3 - - - 983 - 420 - -983 - 420 - 16.7 - 0 - - 25.3166666667 - - 1350 - - - 3359 '4 ' - RINGHALS420 M4 - - - 983 - 420 - -983 - 420 - 16.7 - 0 - - 25.3166666667 - - 1350 - - - 3359 '5 ' - RINGHALS420 M5 - - - 983 - 420 - -983 - 420 - 16.7 - 0 - - 25.3166666667 - - 1350 - - - 3359 '6 ' - RINGHALS420 M6 - - - 850 - 300 - -850 - 300 - 50 - 0 - - 19.5 - - 1200 - - - 5300 '1 ' - SIMA 300 M1 - - - 850 - 300 - -850 - 300 - 50 - 0 - - 19.5 - - 1200 - - - 5300 '2 ' - SIMA 300 M2 - - - 850 - 300 - -850 - 300 - 50 - 0 - - 19.5 - - 1200 - - - 5300 '3 ' - SIMA 300 M3 - - - 850 - 300 - -850 - 300 - 50 - 0 - - 19.5 - - 1200 - - - 5300 '4 ' - SIMA 300 M4 - - - 850 - 300 - -850 - 300 - 50 - 0 - - 19.5 - - 1200 - - - 5300 '5 ' - SIMA 300 M5 - - - 850 - 300 - -850 - 300 - 50 - 0 - - 19.5 - - 1200 - - - 5300 '6 ' - SIMA 300 M6 - - - 850 - 300 - -850 - 300 - 100 - 0 - - 11.35125 - - 1200 - - - 5100 '1 ' - TRETTEN 300 M1 - - - 850 - 300 - -850 - 300 - 100 - 0 - - 11.35125 - - 1200 - - - 5100 '2 ' - TRETTEN 300 M2 - - - 800 - 300 - -800 - 300 - 25 - 0 - - 12.9289090909 - - 1100 - - - 6500 '1 ' - TRONDHEI300 M1 - - - 800 - 300 - -800 - 300 - 25 - 0 - - 12.9289090909 - - 1100 - - - 6500 '2 ' - TRONDHEI300 M2 - - - 800 - 300 - -800 - 300 - 25 - 0 - - 12.9289090909 - - 1100 - - - 6500 '3 ' - TRONDHEI300 M3 - - - 800 - 300 - -800 - 300 - 25 - 0 - - 12.9289090909 - - 1100 - - - 6500 '4 ' - TRONDHEI300 M4 - - - - - AJAURE T1 P C1 - 3701 - - - - - ARENDALST1 P C1 - 5602 - - - - - ASKER T1 P C1 - 5501 - - - - - HOGASEN T1 P C1 - 3245 - - - - - KVILLDALT1 P C1 - 6001 - - - - - MO T1 P C1 - 6701 - - - - - OSLO T1 P C1 - 5401 - - - - - OSLO T2 P C1 - 5402 - - - - - RINGHALST1 P C1 - 3360 - - - - - RINGHALST1 P C1 - 5101 - - - - - SIMA T1 P C1 - 5301 - - - - - STAVANGET1 P C1 - 5601 - - - - - 300ASKER-ARENDAL T1 - 5500 5603 '1 ' - - 2 - - - - - 300KRISTIAN-ARENDAL T1 - 5603 5610 '1 ' - 1 - - - - - 300KRISTIAN-ARENDAL T2 - 5600 5603 '1 ' - 2 - - - - - 300KRISTIAN-ARENDAL_T1 - 5600 5603 '1 ' - 1 - - - - - 300KRISTIAN-FEDA_T1 - 5600 5620 '1 ' - 1 - - - - - 300KRISTIAN-KVILLDAL_T1 - 5600 6000 '1 ' - 1 - - - - - 300KRISTIAN-STAVANGE_T1 - 5600 5601 '1 ' - 1 - - - - - 420ARENDAL-SANDEFJORD T1 - 1 - - - - - ARENDAL 300 A T1 - 1 - - - - - ARENDAL 300 B T1 - 1 - - - - - ARENDAL 300 LSC1 T1 - Branch Shunt 5500_5603_1 - 1 - - - - - ARENDAL 300AS1 AB_S T1 - 1 - - - - - ARENDAL 300AS1 AB_S T2 - 2 - - - - - ARENDAL 300AS1 AD_S T1 - 1 - - - - - ARENDAL 300AS1 AD_S T2 - 2 - - - - - ARENDAL 300AS1 BB_S T1 - 1 - - - - - ARENDAL 300AS1 BB_S T2 - 2 - - - - - ARENDAL 300AS1 BD_S T1 - 1 - - - - - ARENDAL 300AS1 BD_S T2 - 2 - - - - - ARENDAL 300KR1 AB_S T1 - 1 - - - - - ARENDAL 300KR1 AB_S T2 - 2 - - - - - ARENDAL 300KR1 AD_S T1 - 1 - - - - - ARENDAL 300KR1 AD_S T2 - 2 - - - - - ARENDAL 300KR1 BB_S T1 - 1 - - - - - ARENDAL 300KR1 BB_S T2 - 2 - - - - - ARENDAL 300KR1 BD_S T1 - 1 - - - - - ARENDAL 300KR1 BD_S T2 - 2 - - - - - ARENDAL 300KR2 AB_S T1 - 1 - - - - - ARENDAL 300KR2 AB_S T2 - 2 - - - - - ARENDAL 300KR2 AD_S T1 - 1 - - - - - ARENDAL 300KR2 AD_S T2 - 2 - - - - - ARENDAL 300KR2 BB_S T1 - 1 - - - - - ARENDAL 300KR2 BB_S T2 - 2 - - - - - ARENDAL 300KR2 BD_S T1 - 1 - - - - - ARENDAL 300KR2 BD_S T2 - 2 - - - - - ARENDAL 300SC1 AB_S T1 - 1 - - - - - ARENDAL 300SC1 AB_S T2 - 2 - - - - - ARENDAL 300T1 AB_S T1 - 1 - - - - - ARENDAL 300T1 AB_S T2 - 2 - - - - - ARENDAL 420T1 AB_S T1 - 1 - - - - - ARENDAL 420T1 AB_S T2 - 2 - - - - - ARENDAL T1 T1 - 5603_5602_0_1 Primary Winding - 1 - - - - - ARENDAL T1 T2 - 5603_5602_0_1 Secondary Winding - 2 - - - - - KRISTIAN300 B BS T1 - 1 - - - - - KRISTIAN300 BS1 T1 - 1 - - - - - KRISTIAN300 L1 T1 - 5600 '1 ' - 1 - - - - - KRISTIAN300 L2 T1 - 5600 '2 ' - 1 - - - - - KRISTIAN300 M1 T1 - 5600 '1 ' - 1 - - - - - KRISTIAN300 M2 T1 - 5600 '2 ' - 1 - - - - - KRISTIAN300 M3 T1 - 5600 '3 ' - 1 - - - - - KRISTIAN300 M4 T1 - 5600 '4 ' - 1 - - - - - KRISTIAN300AR1 AB_S T1 - 1 - - - - - KRISTIAN300AR1 AB_S T2 - 2 - - - - - KRISTIAN300AR1 AD_S T1 - 1 - - - - - KRISTIAN300AR1 AD_S T2 - 2 - - - - - KRISTIAN300AR1 BB_S T1 - 1 - - - - - KRISTIAN300AR1 BB_S T2 - 2 - - - - - KRISTIAN300AR1 BD_S T1 - 1 - - - - - KRISTIAN300AR1 BD_S T2 - 2 - - - - - KRISTIAN300FE1 AB_S T1 - 1 - - - - - KRISTIAN300FE1 AB_S T2 - 2 - - - - - KRISTIAN300FE1 AD_S T1 - 1 - - - - - KRISTIAN300FE1 AD_S T2 - 2 - - - - - KRISTIAN300FE1 BB_S T1 - 1 - - - - - KRISTIAN300FE1 BB_S T2 - 2 - - - - - KRISTIAN300FE1 BD_S T1 - 1 - - - - - KRISTIAN300FE1 BD_S T2 - 2 - - - - - KRISTIAN300G1 AB_S T1 - 1 - - - - - KRISTIAN300G1 AB_S T2 - 2 - - - - - KRISTIAN300G1 AD_S T1 - 1 - - - - - KRISTIAN300G1 AD_S T2 - 2 - - - - - KRISTIAN300G1 BB_S T1 - 1 - - - - - KRISTIAN300G1 BB_S T2 - 2 - - - - - KRISTIAN300G1 BD_S T1 - 1 - - - - - KRISTIAN300G1 BD_S T2 - 2 - - - - - KRISTIAN300G2 AB_S T1 - 1 - - - - - KRISTIAN300G2 AB_S T2 - 2 - - - - - KRISTIAN300G2 AD_S T1 - 1 - - - - - KRISTIAN300G2 AD_S T2 - 2 - - - - - KRISTIAN300G2 BB_S T1 - 1 - - - - - KRISTIAN300G2 BB_S T2 - 2 - - - - - KRISTIAN300G2 BD_S T1 - 1 - - - - - KRISTIAN300G2 BD_S T2 - 2 - - - - - KRISTIAN300G3 AB_S T1 - 1 - - - - - KRISTIAN300G3 AB_S T2 - 2 - - - - - KRISTIAN300G3 AD_S T1 - 1 - - - - - KRISTIAN300G3 AD_S T2 - 2 - - - - - KRISTIAN300G3 BB_S T1 - 1 - - - - - KRISTIAN300G3 BB_S T2 - 2 - - - - - KRISTIAN300G3 BD_S T1 - 1 - - - - - KRISTIAN300G3 BD_S T2 - 2 - - - - - KRISTIAN300G4 AB_S T1 - 1 - - - - - KRISTIAN300G4 AB_S T2 - 2 - - - - - KRISTIAN300G4 AD_S T1 - 1 - - - - - KRISTIAN300G4 AD_S T2 - 2 - - - - - KRISTIAN300G4 BB_S T1 - 1 - - - - - KRISTIAN300G4 BB_S T2 - 2 - - - - - KRISTIAN300G4 BD_S T1 - 1 - - - - - KRISTIAN300G4 BD_S T2 - 2 - - - - - KRISTIAN300KV1 AB_S T1 - 1 - - - - - KRISTIAN300KV1 AB_S T2 - 2 - - - - - KRISTIAN300KV1 AD_S T1 - 1 - - - - - KRISTIAN300KV1 AD_S T2 - 2 - - - - - KRISTIAN300KV1 BB_S T1 - 1 - - - - - KRISTIAN300KV1 BB_S T1 - 1 - - - - - KRISTIAN300KV1 BB_S T2 - 2 - - - - - KRISTIAN300KV1 BB_S T2 - 2 - - - - - KRISTIAN300KV1 BD_S T1 - 1 - - - - - KRISTIAN300KV1 BD_S T2 - 2 - - - - - KRISTIAN300L1 AB_S T1 - 1 - - - - - KRISTIAN300L1 AB_S T2 - 2 - - - - - KRISTIAN300L1 AD_S T1 - 1 - - - - - KRISTIAN300L1 AD_S T2 - 2 - - - - - KRISTIAN300L1 BB_S T1 - 1 - - - - - KRISTIAN300L1 BB_S T2 - 2 - - - - - KRISTIAN300L1 BD_S T1 - 1 - - - - - KRISTIAN300L1 BD_S T2 - 2 - - - - - KRISTIAN300L2 AB_S T1 - 1 - - - - - KRISTIAN300L2 AB_S T2 - 2 - - - - - KRISTIAN300L2 AD_S T1 - 1 - - - - - KRISTIAN300L2 AD_S T2 - 2 - - - - - KRISTIAN300L2 BB_S T1 - 1 - - - - - KRISTIAN300L2 BB_S T2 - 2 - - - - - KRISTIAN300L2 BD_S T1 - 1 - - - - - KRISTIAN300L2 BD_S T2 - 2 - - - - - KRISTIAN300ST1 AB_S T1 - 1 - - - - - KRISTIAN300ST1 AB_S T2 - 2 - - - - - KRISTIAN300ST1 AD_S T1 - 1 - - - - - KRISTIAN300ST1 AD_S T2 - 2 - - - - - KRISTIAN300ST1 BD_S T1 - 1 - - - - - KRISTIAN300ST1 BD_S T2 - 2 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1-test - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - Some Alias Name Which is not used since name exist - T1 - - - - - Alias Name - - - - - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - 3000 '1 ' - - - - - T1 - 3000 '2 ' - - - - - T1 - 3000 '3 ' - - - - - T1 - 3020 '1 ' - - - - - T1 - 3100 '1 ' - - - - - T1 - 3115 '1 ' - - - - - T1 - 3249 '1 ' - - - - - T1 - 3300 '1 ' - - - - - T1 - 3300 '2 ' - - - - - T1 - 3359 '1 ' - - - - - T1 - 3359 '2 ' - - - - - T1 - 3359 '3 ' - - - - - T1 - 3359 '4 ' - - - - - T1 - 3360 '1 ' - - - - - T1 - 5100 '1 ' - - - - - T1 - 5300 '2 ' - - - - - T1 - 5400 '1 ' - - - - - T1 - 5500 '1 ' - - - - - T1 - 5500 '2 ' - - - - - T1 - - - - - T1 - 5610 '1 ' - - - - - T1 - 5620 '1 ' - - - - - T1 - 6100 '1 ' - - - - - T1 - 6100 '2 ' - - - - - T1 - 6500 '1 ' - - - - - T1 - 6500 '2 ' - - - - - T1 - 6500 '3 ' - - - - - T1 - 6700 '1 ' - - - - - T1 - 6701 '1 ' - - - - - T1 - 6701 '3 ' - - - - - T1 - 7000 '1 ' - - - - - T1 - 7000 '2 ' - - - - - T1 - 7000 '3 ' - - - - - T1 - 7000 '4 ' - - - - - T1 - 7000 '5 ' - - - - - T1 - 7000 '6 ' - - - - - T1 - 7010 '1 ' - - - - - T1 - 7020 '1 ' - - - - - T1 - 7100 '1 ' - - - - - T1 - 7100 '2 ' - - - - - T1 - 7100 '3 ' - - - - - T1 - 8500 '1 ' - - - - - T1 - 8500 '2 ' - - - - - T1 - 8500 '3 ' - - - - - T1 - 8500 '4 ' - - - - - T1 - 8600 '1 ' - - - - - T1 - 8700 '1 ' - - - - - T1 - 3000 '1 ' - - - - - T1 - 3000 '2 ' - - - - - T1 - 3000 '3 ' - - - - - T1 - 3115 '1 ' - - - - - T1 - 3115 '2 ' - - - - - T1 - 3115 '3 ' - - - - - T1 - 3115 '4 ' - - - - - T1 - 3115 '5 ' - - - - - T1 - 3245 '1 ' - - - - - T1 - 3249 '1 ' - - - - - T1 - 3249 '2 ' - - - - - T1 - 3249 '3 ' - - - - - T1 - 3249 '4 ' - - - - - T1 - 3249 '5 ' - - - - - T1 - 3249 '6 ' - - - - - T1 - 3249 '7 ' - - - - - T1 - 3249 '8 ' - - - - - T1 - 3300 '1 ' - - - - - T1 - 3300 '2 ' - - - - - T1 - 3300 '3 ' - - - - - T1 - 3300 '4 ' - - - - - T1 - 3300 '5 ' - - - - - T1 - 3300 '6 ' - - - - - T1 - 3359 '1 ' - - - - - T1 - 3359 '2 ' - - - - - T1 - 3359 '3 ' - - - - - T1 - 3359 '4 ' - - - - - T1 - 3359 '5 ' - - - - - T1 - 3359 '6 ' - - - - - T1 - 5100 '1 ' - - - - - T1 - 5100 '2 ' - - - - - T1 - 5300 '1 ' - - - - - T1 - 5300 '2 ' - - - - - T1 - 5300 '3 ' - - - - - T1 - 5300 '4 ' - - - - - T1 - 5300 '5 ' - - - - - T1 - 5300 '6 ' - - - - - T1 - 5400 '1 ' - - - - - T1 - 5400 '2 ' - - - - - T1 - 5500 '1 ' - - - - - T1 - 5500 '2 ' - - - - - T1 - 6000 '1 ' - - - - - T1 - 6000 '2 ' - - - - - T1 - 6000 '3 ' - - - - - T1 - 6000 '4 ' - - - - - T1 - 6100 '1 ' - - - - - T1 - 6100 '2 ' - - - - - T1 - 6100 '3 ' - - - - - T1 - 6100 '4 ' - - - - - T1 - 6100 '5 ' - - - - - T1 - 6500 '1 ' - - - - - T1 - 6500 '2 ' - - - - - T1 - 6500 '3 ' - - - - - T1 - 6500 '4 ' - - - - - T1 - 6700 '1 ' - - - - - T1 - 6700 '2 ' - - - - - T1 - 6700 '3 ' - - - - - T1 - 6700 '4 ' - - - - - T1 - 7000 '1 ' - - - - - T1 - 7000 '2 ' - - - - - T1 - 7000 '3 ' - - - - - T1 - 7000 '4 ' - - - - - T1 - 7000 '5 ' - - - - - T1 - 7000 '6 ' - - - - - T1 - 7000 '7 ' - - - - - T1 - 7000 '8 ' - - - - - T1 - 7000 '9 ' - - - - - T1 - 7100 '1 ' - - - - - T1 - 7100 '2 ' - - - - - T1 - 7100 '3 ' - - - - - T1 - 8500 '1 ' - - - - - T1 - 8500 '2 ' - - - - - T1 - 8500 '3 ' - - - - - T1 - 8500 '4 ' - - - - - T1 - 8500 '5 ' - - - - - T1 - 8500 '6 ' - - - - - T1 - 3000 3020 '1 ' - 1 - - - - - T1 - 3000 3115 '1 ' - 1 - - - - - T1 - 3000 3245 '1 ' - 1 - - - - - T1 - 3000 3245 '2 ' - 1 - - - - - T1 - 3000 3300 '1 ' - 1 - - - - - T1 - 3000 3300 '2 ' - 1 - - - - - T1 - 3100 3115 '1 ' - 1 - - - - - T1 - 3100 3200 '1 ' - 1 - - - - - T1 - 3100 3200 '2 ' - 1 - - - - - T1 - 3100 3200 '3 ' - 1 - - - - - T1 - 3100 3249 '1 ' - 1 - - - - - T1 - 3100 3359 '1 ' - 1 - - - - - T1 - 3100 3359 '2 ' - 1 - - - - - T1 - 3115 3245 '1 ' - 1 - - - - - T1 - 3115 3249 '1 ' - 1 - - - - - T1 - 3115 6701 '1 ' - 1 - - - - - T1 - 3115 7100 '1 ' - 1 - - - - - T1 - 3200 3300 '1 ' - 1 - - - - - T1 - 3200 3359 '1 ' - 1 - - - - - T1 - 3200 8500 '1 ' - 1 - - - - - T1 - 3244 6500 '1 ' - 1 - - - - - T1 - 3249 7100 '1 ' - 1 - - - - - T1 - 3300 8500 '1 ' - 1 - - - - - T1 - 3300 8500 '2 ' - 1 - - - - - T1 - 3359 5101 '1 ' - 1 - - - - - T1 - 3359 5101 '2 ' - 1 - - - - - T1 - 3359 8500 '1 ' - 1 - - - - - T1 - 3359 8500 '2 ' - 1 - - - - - T1 - 3701 6700 '1 ' - 1 - - - - - T1 - 5100 5500 '1 ' - 1 - - - - - T1 - 5100 6500 '1 ' - 1 - - - - - T1 - 5101 5102 '1 ' - 1 - - - - - T1 - 5101 5103 '1 ' - 1 - - - - - T1 - 5101 5501 '1 ' - 1 - - - - - T1 - Branch Shunt 5101_5501_1 - - - - - T1 - Branch Shunt 5101_5501_1 - - - - - T1 - 5102 5103 '1 ' - 1 - - - - - T1 - 5102 5304 '1 ' - 1 - - - - - T1 - 5102 6001 '1 ' - 1 - - - - - T1 - Branch Shunt 5102_6001_1 - - - - - T1 - Branch Shunt 5102_6001_1 - - - - - T1 - 5103 5304 '1 ' - 1 - - - - - T1 - 5103 5304 '2 ' - 1 - - - - - T1 - 5300 6100 '1 ' - 1 - - - - - T1 - 5301 5304 '1 ' - 1 - - - - - T1 - 5301 5305 '1 ' - 1 - - - - - T1 - 5301 6001 '1 ' - 1 - - - - - T1 - 5304 5305 '1 ' - 1 - - - - - T1 - 5304 5305 '2 ' - 1 - - - - - T1 - 5400 5500 '1 ' - 1 - - - - - T1 - 5400 6000 '1 ' - 1 - - - - - T1 - 5401 5501 '1 ' - 1 - - - - - T1 - 5401 5602 '1 ' - 1 - - - - - T1 - 5401 6001 '1 ' - 1 - - - - - T1 - Branch Shunt 5401_6001_1 - - - - - T1 - Branch Shunt 5401_6001_1 - - - - - T1 - 5402 6001 '1 ' - 1 - - - - - T1 - 5500 5603 '1 ' - 1 - - - - - T1 - Branch Shunt 5500_5603_1 - - - - - T1 - 6000 6100 '1 ' - 1 - - - - - T1 - 6500 6700 '1 ' - 1 - - - - - T1 - 6500 6700 '2 ' - 1 - - - - - T1 - 7000 7010 '1 ' - 1 - - - - - T1 - 7000 7020 '1 ' - 1 - - - - - T1 - 7000 7100 '1 ' - 1 - - - - - T1 - 7000 7100 '2 ' - 1 - - - - - T1 - 7000 7100 '3 ' - 1 - - - - - T1 - 8500 8600 '1 ' - 1 - - - - - T1 - 8500 8700 '1 ' - 1 - - - - - T1 - 3244_3245_0_1 Primary Winding - 1 - - - - - T1 - 3701_3249_0_1 Primary Winding - 1 - - - - - T1 - 3359_3360_0_1 Primary Winding - 1 - - - - - T1 - 5101_5100_0_1 Primary Winding - 1 - - - - - T1 - 5300_5301_0_1 Primary Winding - 1 - - - - - T1 - 5400_5401_0_1 Primary Winding - 1 - - - - - T1 - 5400_5402_0_1 Primary Winding - 1 - - - - - T1 - 5500_5501_0_1 Primary Winding - 1 - - - - - T1 - 5601_6001_0_1 Primary Winding - 1 - - - - - T1 - 6000_6001_0_1 Primary Winding - 1 - - - - - T1 - 6700_6701_0_1 Primary Winding - 1 - - - - - T2 - 3000 3020 '1 ' - 2 - - - - - T2 - 3000 3115 '1 ' - 2 - - - - - T2 - 3000 3245 '1 ' - 2 - - - - - T2 - 3000 3245 '2 ' - 2 - - - - - T2 - 3000 3300 '1 ' - 2 - - - - - T2 - 3000 3300 '2 ' - 2 - - - - - T2 - 3100 3115 '1 ' - 2 - - - - - T2 - 3100 3200 '1 ' - 2 - - - - - T2 - 3100 3200 '2 ' - 2 - - - - - T2 - 3100 3200 '3 ' - 2 - - - - - T2 - 3100 3249 '1 ' - 2 - - - - - T2 - 3100 3359 '1 ' - 2 - - - - - T2 - 3100 3359 '2 ' - 2 - - - - - T2 - 3115 3245 '1 ' - 2 - - - - - T2 - 3115 3249 '1 ' - 2 - - - - - T2 - 3115 6701 '1 ' - 2 - - - - - T2 - 3115 7100 '1 ' - 2 - - - - - T2 - 3200 3300 '1 ' - 2 - - - - - T2 - 3200 3359 '1 ' - 2 - - - - - T2 - 3200 8500 '1 ' - 2 - - - - - T2 - 3244 6500 '1 ' - 2 - - - - - T2 - 3249 7100 '1 ' - 2 - - - - - T2 - 3300 8500 '1 ' - 2 - - - - - T2 - 3300 8500 '2 ' - 2 - - - - - T2 - 3359 5101 '1 ' - 2 - - - - - T2 - 3359 5101 '2 ' - 2 - - - - - T2 - 3359 8500 '1 ' - 2 - - - - - T2 - 3359 8500 '2 ' - 2 - - - - - T2 - 3701 6700 '1 ' - 2 - - - - - T2 - 5100 5500 '1 ' - 2 - - - - - T2 - 5100 6500 '1 ' - 2 - - - - - T2 - 5101 5102 '1 ' - 2 - - - - - T2 - 5101 5103 '1 ' - 2 - - - - - T2 - 5101 5501 '1 ' - 2 - - - - - T2 - 5102 5103 '1 ' - 2 - - - - - T2 - 5102 5304 '1 ' - 2 - - - - - T2 - 5102 6001 '1 ' - 2 - - - - - T2 - 5103 5304 '1 ' - 2 - - - - - T2 - 5103 5304 '2 ' - 2 - - - - - T2 - 5300 6100 '1 ' - 2 - - - - - T2 - 5301 5304 '1 ' - 2 - - - - - T2 - 5301 5305 '1 ' - 2 - - - - - T2 - 5301 6001 '1 ' - 2 - - - - - T2 - 5304 5305 '1 ' - 2 - - - - - T2 - 5304 5305 '2 ' - 2 - - - - - T2 - 5400 5500 '1 ' - 2 - - - - - T2 - 5400 6000 '1 ' - 2 - - - - - T2 - 5401 5501 '1 ' - 2 - - - - - T2 - 5401 5602 '1 ' - 2 - - - - - T2 - 5401 6001 '1 ' - 2 - - - - - T2 - 5402 6001 '1 ' - 2 - - - - - T2 - 5600 5601 '1 ' - 2 - - - - - T2 - 5600 5620 '1 ' - 2 - - - - - T2 - 5600 6000 '1 ' - 2 - - - - - T2 - 5603 5610 '1 ' - 2 - - - - - T2 - 6000 6100 '1 ' - 2 - - - - - T2 - 6500 6700 '1 ' - 2 - - - - - T2 - 6500 6700 '2 ' - 2 - - - - - T2 - 7000 7010 '1 ' - 2 - - - - - T2 - 7000 7020 '1 ' - 2 - - - - - T2 - 7000 7100 '1 ' - 2 - - - - - T2 - 7000 7100 '2 ' - 2 - - - - - T2 - 7000 7100 '3 ' - 2 - - - - - T2 - 8500 8600 '1 ' - 2 - - - - - T2 - 8500 8700 '1 ' - 2 - - - - - T2 - 3244_3245_0_1 Secondary Winding - 2 - - - - - T2 - 3701_3249_0_1 Secondary Winding - 2 - - - - - T2 - 3359_3360_0_1 Secondary Winding - 2 - - - - - T2 - 5101_5100_0_1 Secondary Winding - 2 - - - - - T2 - 5300_5301_0_1 Secondary Winding - 2 - - - - - T2 - 5400_5401_0_1 Secondary Winding - 2 - - - - - T2 - 5400_5402_0_1 Secondary Winding - 2 - - - - - T2 - 5500_5501_0_1 Secondary Winding - 2 - - - - - T2 - 5601_6001_0_1 Secondary Winding - 2 - - - - - T2 - 6000_6001_0_1 Secondary Winding - 2 - - - - - T2 - 6700_6701_0_1 Secondary Winding - 2 - - - - - T2 - 2 - - - - - AJAURE 300 - - - - - AJAURE 300 - o test - - - - - ARENDAL 300 - - - - - ARENDAL 420 - - - - - ARRIE 420 - - - - - ASKER 300 - - - - - AURLAND 420 - - - - - BLAFALLI300 - - - - - DAGALI 420 - - - - - DANNEBO 420 - - - - - EIDFJORD420 - - - - - ESTLINK 420 - - - - - FEDA 300 - - - - - FORSMARK420 - - - - - GEILO 420 - - - - - GRUNDFOR420 - - - - - HAGAFOSS420 - - - - - HALDEN 420 - - - - - HELSINKI420 - - - - - HJALTA 420 - - - - - HOGASEN 300 - - - - - JARPSTRO420 - - - - - KARLSH 420 - - - - - KONGSBER420 - - - - - KRISTIA 300 - - - - - KRISTIAN300 - - - - - KVILLDAL300 - - - - - MALMO 420 - - - - - MO 300 - - - - - NARVIK 420 - - - - - OSKARSHA420 - - - - - OSLO 300 - - - - - OULU 420 - - - - - PORJUS 420 - - - - - RINGHALS420 - - - - - SANDEFJO420 - - - - - SIMA 300 - - - - - SKIEN 420 - - - - - STAVANGE300 - - - - - STEENKU 135 - - - - - SYLLING 420 - - - - - SYSLE 420 - - - - - TENHULT 420 - - - - - TRETTEN 300 - - - - - TRONDHEI300 - - - - - VYBORG 420 - - - 462 - - - EVHI - 462 - - - 462 - - - EVHI - 462 - - - 462 - - - EVHI - 462 - - - 462 - - - EVHI - 462 - - - 462 - - - EVHI - 462 - - - 330 - - - EVHI - 330 - - - 462 - - - EVHI - 462 - - - 462 - - - EVHI - 462 - - - 462 - - - EVHI - 462 - - - 462 - - - EVHI - 462 - - - 148.5 - - - EVHI - 148.5 - - - 330 - - - EVHI - 330 - - - 330 - - - EVHI - 330 - - - 462 - - - EVHI - 462 - - - 462 - - - EVHI - 462 - - - 462 - - - EVHI - 462 - - - 330 - - - EVHI - 330 - - - 462 - - - EVHI - 462 - - - 462 - - - EVHI - 462 - - - 462 - - - EVHI - 462 - - - 330 - - - EVHI - 330 - - - 462 - - - EVHI - 462 - - - 462 - - - EVHI - 462 - - - 330 - - - EVHI - 330 - - - 462 - - - EVHI - 462 - - - 330 - - - EVHI - 330 - - - 330 - - - EVHI - 330 - - - 462 - - - EVHI - 462 - - - 330 - - - EVHI - 330 - - - 330 - - - EVHI - 330 - - - 330 - - - EVHI - 330 - - - 330 - - - EVHI - 330 - - - 462 - - - EVHI - 462 - - - 330 - - - EVHI - 330 - - - 330 - - - EVHI - 330 - - - 330 - - - EVHI - 330 - - - 462 - - - EVHI - 462 - - - 462 - - - EVHI - 462 - - - 462 - - - EVHI - 462 - - - 462 - - - EVHI - 462 - - - 462 - - - EVHI - 462 - - - 462 - - - EVHI - 462 - - - 462 - - - EVHI - 462 - - - 462 - - - EVHI - 462 - - - 330 - - - EVHI - 330 - - - 330 - - - EVHI - 330 - - - 378 - - - EVLO - 378 - - - 378 - - - EVLO - 378 - - - 378 - - - EVLO - 378 - - - 378 - - - EVLO - 378 - - - 378 - - - EVLO - 378 - - - 270 - - - EVLO - 270 - - - 378 - - - EVLO - 378 - - - 378 - - - EVLO - 378 - - - 378 - - - EVLO - 378 - - - 378 - - - EVLO - 378 - - - 121.5 - - - EVLO - 121.5 - - - 270 - - - EVLO - 270 - - - 270 - - - EVLO - 270 - - - 378 - - - EVLO - 378 - - - 378 - - - EVLO - 378 - - - 378 - - - EVLO - 378 - - - 270 - - - EVLO - 270 - - - 378 - - - EVLO - 378 - - - 378 - - - EVLO - 378 - - - 378 - - - EVLO - 378 - - - 270 - - - EVLO - 270 - - - 378 - - - EVLO - 378 - - - 378 - - - EVLO - 378 - - - 270 - - - EVLO - 270 - - - 378 - - - EVLO - 378 - - - 270 - - - EVLO - 270 - - - 270 - - - EVLO - 270 - - - 378 - - - EVLO - 378 - - - 270 - - - EVLO - 270 - - - 270 - - - EVLO - 270 - - - 270 - - - EVLO - 270 - - - 270 - - - EVLO - 270 - - - 378 - - - EVLO - 378 - - - 270 - - - EVLO - 270 - - - 270 - - - EVLO - 270 - - - 270 - - - EVLO - 270 - - - 378 - - - EVLO - 378 - - - 378 - - - EVLO - 378 - - - 378 - - - EVLO - 378 - - - 378 - - - EVLO - 378 - - - 378 - - - EVLO - 378 - - - 378 - - - EVLO - 378 - - - 378 - - - EVLO - 378 - - - 378 - - - EVLO - 378 - - - 270 - - - EVLO - 270 - - - 270 - - - EVLO - 270 - - - 462 - - - NVHI - 462 - - - 462 - - - NVHI - 462 - - - 462 - - - NVHI - 462 - - - 462 - - - NVHI - 462 - - - 462 - - - NVHI - 462 - - - 330 - - - NVHI - 330 - - - 462 - - - NVHI - 462 - - - 462 - - - NVHI - 462 - - - 462 - - - NVHI - 462 - - - 462 - - - NVHI - 462 - - - 148.5 - - - NVHI - 148.5 - - - 330 - - - NVHI - 330 - - - 330 - - - NVHI - 330 - - - 462 - - - NVHI - 462 - - - 462 - - - NVHI - 462 - - - 462 - - - NVHI - 462 - - - 330 - - - NVHI - 330 - - - 462 - - - NVHI - 462 - - - 462 - - - NVHI - 462 - - - 462 - - - NVHI - 462 - - - 330 - - - NVHI - 330 - - - 462 - - - NVHI - 462 - - - 462 - - - NVHI - 462 - - - 330 - - - NVHI - 330 - - - 462 - - - NVHI - 462 - - - 330 - - - NVHI - 330 - - - 330 - - - NVHI - 330 - - - 462 - - - NVHI - 462 - - - 330 - - - NVHI - 330 - - - 330 - - - NVHI - 330 - - - 330 - - - NVHI - 330 - - - 330 - - - NVHI - 330 - - - 462 - - - NVHI - 462 - - - 330 - - - NVHI - 330 - - - 330 - - - NVHI - 330 - - - 330 - - - NVHI - 330 - - - 462 - - - NVHI - 462 - - - 462 - - - NVHI - 462 - - - 462 - - - NVHI - 462 - - - 462 - - - NVHI - 462 - - - 462 - - - NVHI - 462 - - - 462 - - - NVHI - 462 - - - 462 - - - NVHI - 462 - - - 462 - - - NVHI - 462 - - - 330 - - - NVHI - 330 - - - 330 - - - NVHI - 330 - - - 378 - - - NVLO - 378 - - - 378 - - - NVLO - 378 - - - 378 - - - NVLO - 378 - - - 378 - - - NVLO - 378 - - - 378 - - - NVLO - 378 - - - 270 - - - NVLO - 270 - - - 378 - - - NVLO - 378 - - - 378 - - - NVLO - 378 - - - 378 - - - NVLO - 378 - - - 378 - - - NVLO - 378 - - - 121.5 - - - NVLO - 121.5 - - - 270 - - - NVLO - 270 - - - 270 - - - NVLO - 270 - - - 378 - - - NVLO - 378 - - - 378 - - - NVLO - 378 - - - 378 - - - NVLO - 378 - - - 270 - - - NVLO - 270 - - - 378 - - - NVLO - 378 - - - 378 - - - NVLO - 378 - - - 378 - - - NVLO - 378 - - - 270 - - - NVLO - 270 - - - 378 - - - NVLO - 378 - - - 378 - - - NVLO - 378 - - - 270 - - - NVLO - 270 - - - 378 - - - NVLO - 378 - - - 270 - - - NVLO - 270 - - - 270 - - - NVLO - 270 - - - 378 - - - NVLO - 378 - - - 270 - - - NVLO - 270 - - - 270 - - - NVLO - 270 - - - 270 - - - NVLO - 270 - - - 270 - - - NVLO - 270 - - - 378 - - - NVLO - 378 - - - 270 - - - NVLO - 270 - - - 270 - - - NVLO - 270 - - - 270 - - - NVLO - 270 - - - 378 - - - NVLO - 378 - - - 378 - - - NVLO - 378 - - - 378 - - - NVLO - 378 - - - 378 - - - NVLO - 378 - - - 378 - - - NVLO - 378 - - - 378 - - - NVLO - 378 - - - 378 - - - NVLO - 378 - - - 378 - - - NVLO - 378 - - - 270 - - - NVLO - 270 - - - 270 - - - NVLO - 270 - - - CS 420RINGHALS-HALDEN 1 - Circuit contribution share of CC 420RINGHALS-HALDEN 1 on PTC 420RINGHALS-HALDEN - 1 - - - - CS 420RINGHALS-HALDEN 2 - Circuit contribution share of CC 420RINGHALS-HALDEN 2 on PTC 420RINGHALS-HALDEN - 1 - - - - FI-ELSP-1 - - Finland - FI-ELSP-1 - - - NO-ELSP-1 - Elspot NO1 - NO1 - 2011-05-16: Eastern Norway. South of Vågåmo (300kV) and Litjfo. East of Rød 420kV), Rjukan (420kV), Vemorktopp00kV), Hof (300kV) and Fardal (300kV). - NO1 - - - NO-ELSP-2 - Elspot NO2 - NO2 - 2011-05-16: Southern Norway. West of Hasle (420kV), Sylling (4kV). East of Sauda (300kV) - NO2 - - - NO-ELSP-3 - Elspot NO3 - NO3 - 2011-05-16: Mid Part of Norway, n.o.Ø.Vinstra (300kV), n.o.Litjf(132 kV bus bars), s.o.Tunnsjødal-Verdal (300kV), s (300kV) - NO3 - - - NO-ELSP-4 - Elspot NO4 - NO4 - 2011-05-16: Mid Part of Norway, n.o.Tunnsjødal-Verdal (300kV),os (300kV) Northern Norway - NO4 - - - NO-ELSP-5 - Elspot NO5 - NO5 - 2011-05-16: Western Norway. West of Hylen (300kv), Nesflaten (300kv) - NO5 - - - SE-ELSP-1 - Elspot SE1 - Sverige 1 - SE-ELSP-1 - - - SE-ELSP-2 - Elspot SE2 - Sverige 2 - SE-ELSP-2 - - - SE-ELSP-3 - Sverige 3 - SE-ELSP-3 - - - SE-ELSP-4 - Elspot SE4 - Sverige 4 - SE-ELSP-4 - - - FI-SCAR-1 - SA FI - Finland - - - - - NO-SCAR-1 - SA NO1 - Viken North - - - - - NO-SCAR-2 - SA NO2 - NO2 - 2011-05-16: Southern Norway. West of Hasle (420kV), Sylling (4kV). East of Sauda (300kV) - - - - - NO-SCAR-3 - SA NO3 - NO3 - 2011-05-16: Mid Part of Norway, n.o.Ø.Vinstra (300kV), n.o.Litjf(132 kV bus bars), s.o.Tunnsjødal-Verdal (300kV), s (300kV) - - - - - NO-SCAR-4 - SA NO4 - NO4 - 2011-05-16: Mid Part of Norway, n.o.Tunnsjødal-Verdal (300kV),os (300kV) Northern Norway - - - - - NO-SCAR-5 - SA NO5 - NO5 - 2011-05-16: Western Norway. West of Hylen (300kv), Nesflaten (300kv) - - - - - SE-SCAR-1 - SA SE1 - Sverige 1 - - - - - SE-SCAR-2 - SA SE2 - Sverige 2 - - - - - SE-SCAR-3 - SA SE3 - Sverige 3 - - - - - SE-SCAR-4 - SA SE4 - Sverige 4 - - - - - NO-SCAR-6 - SA Ostfold - Energy Scheduling Area Ostfold - - - - - CC 420RINGHALS-HALDEN 1 - Line Circuit container for 420RINGHALS-HALDEN 1. - - - - - CC 420RINGHALS-HALDEN 2 - Line Circuit container for 420RINGHALS-HALDEN 2. - - - - - PTC 420RINGHALS-HALDEN - PowerTransferCorridor 420RINGHALS-HALDEN - - - -10 - Negative 10 degree Celsius - -10degreeCelsius - - - -15 - Negative 15 degree Celsius - -15degreeCelsius - - - -20 - Negative 20 degree Celsius - -20degreeCelsius - - - -25 - Negative 25 degree Celsius - -25degreeCelsius - - - -30 - Negative 30 degree Celsius - -30degreeCelsius - - - -5 - Negative 5 degree Celsius - -5degreeCelsius - - - 0 - 0 degree Celsius - 0degreeCelsius - - - 10 - 10 degree Celsius - 10degreeCelsius - - - 15 - 15 degree Celsius - 15degreeCelsius - - - 20 - 20 degree Celsius - 20degreeCelsius - - - 25 - 25 degree Celsius - 25degreeCelsius - - - 30 - 30 degree Celsius - 30degreeCelsius - - - 5 - 5 degree Celsius - 5degreeCelsius - - - AND G 300KRISTIAN-ARENDAL - - - - AND G 300KRISTIAN-ARENDAL F DIR - - Check the flow and direction on 300KRISTIAN-ARENDAL. - - - AND G 300KRISTIAN-KVILLDAL F DIR - - Check the flow and direction on 300KRISTIAN-KVILLDAL. - - - OR G KRISTIAN300 VL - - - - OR G NorNed - - - - GP 300KRISTIAN-ARENDAL FLOW - One out of two check for monitoring for flow on the line. This check for the amount. - - - - 1.15 - - - - GP 300KRISTIAN-ARENDAL IC - One out of two check for monitoring for tripping. This check that the line not connected - - - - true - - - GP 300KRISTIAN-ARENDAL IS - One out of two check for monitoring for tripping. This check that the line is in service - - - - - - GP 300KRISTIAN-KVILLDAL FLOW - One out of two check for monitoring for flow on the line. This check for the amount. - - - - 1.15 - - - - GP 300KRISTIAN-ARENDAL FLOW DIR - Output from the flow and direction gate for 300KRISTIAN-ARENDAL. - - - - - GP 300KRISTIAN-KVILLDAL FLOW DIR - Output from the flow and direction gate for 300KRISTIAN-KVILLDAL. - - - - - GP 300KRISTIAN-ARENDAL DIR - One out of two check for monitoring for flow on the line. This check for the direction. - - - 0 - - - - - GP 300KRISTIAN-KVILLDAL DIR - One out of two check for monitoring for flow on the line. This check for the direction. - - - 0 - - - - - GP KRISTIAN300 A VL - Check the voltage level on the terminal of the busbarsection. - - - 270 - - - - - GP KRISTIAN300 B VL - Check the voltage level on the terminal of the busbarsection. - - - 270 - - - - - PAA FEDA 300 L1 - true - - 150 - - true - false - - - - PAA KVI KRISTIAN G1 - true - - 25 - - true - false - - - - PAA KVI KRISTIAN G2 - true - - 30 - - true - false - - - - PAC FEDA PAA - - - PAC KRISTIAN-ARENDAL PAA - - - PAC KRISTIAN300 L2 PAA - Isolating the load KRISTIAN300 L2. - - - PAA KRISTIAN300 L2 - true - - false - - - - PAA KRISTIAN300L1 AD_S - true - - false - - - - PAA KRISTIAN300L2 AB_S - true - - false - - - - PAA KRISTIAN300L2 BB_S - true - - false - - - - PAA KRISTIAN300L2 BD_S - true - - false - - - - SIPS 300KRISTIAN-ARENDAL - true - - - - SIPS KRISTIAN300 VL - true - - - - SIPS NorNed - - true - - - S1 KRI-ARE - - 1 - - - S1 KRISTIAN300 VL - - 1 - - - S1 NorNed - - 1 - - - ST1 S1 KRI-ARE - - - true - - - - ST1 S1 KRISTIAN300 VL - - - true - - - - ST1 S1 NorNed - - - - true - - - Blafalli_SR - Blafalli_SR - NOKG11 - - - Forsmark_SR - Forsmark_SR - NOKG01 - - - Helsinki_SR - Helsinki_SR - NOKG14 - - - Jarpstrommen_Grundfors_SR - Jarpstrommen_Grundfors_SR - NOKG03 - - - Kristiansand_SR - Kristiansand_SR - NOKG09 - - - Kvilldal_SR - Kvilldal_SR - NOKG10 - - - Malmo_SR - Malmo_SR - NOKG16 - - - Oskarshamn_SR - Oskarshamn_SR - NOKG04 - - - Oslo_SR - Oslo_SR - NOKG08 - - - Oulu_SR - Oulu_SR - NOKG15 - - - Porjus_SR - Porjus_SR - NOKG02 - - - Ringhals_SR - Ringhals_SR - NOKG05 - - - Rossaga_SR - Rossaga_SR - NOKG13 - - - Sima_SR - Sima_SR - NOKG07 - - - Tretten_SR - Tretten_SR - NOKG06 - - - Trondheim_SR - Trondheim_SR - NOKG12 - - diff --git a/cognite/neat/legacy/graph/examples/Knowledge-Graph-Nordic44.xml b/cognite/neat/legacy/graph/examples/Knowledge-Graph-Nordic44.xml deleted file mode 100644 index 6b1cef484..000000000 --- a/cognite/neat/legacy/graph/examples/Knowledge-Graph-Nordic44.xml +++ /dev/null @@ -1,20163 +0,0 @@ - - - - - - 2018-08-31T13:47:55 - 2015-03-06T00:30:00 - Statnett SF - 38 - CGM Test model developed by Statnett SF. Nordic 44 bus system for the Nordic region - http://www.Statnett.no/IGM/Nordic44_CGM - http://entsoe.eu/CIM/EquipmentCore/3/1 - http://entsoe.eu/CIM/EquipmentOperation/3/1 - http://entsoe.eu/CIM/EquipmentCore/3/2 - http://entsoe.eu/CIM/EquipmentOperation/3/2 - - - 0.0003333333 - 22.5 - 180 - 0 - - false - true - 3701 6700 '1 ' - 300AJAURE-MO - - - - 0 - 0 - 0.9 - 0 - - false - true - 5603 5610 '1 ' - 300ARENDAL-KRISTA_HVDC - - 1 - - - 0.0005555556 - 4.5 - 54 - 0 - - false - true - 5500 5603 '1 ' - 300ASKER-ARENDAL - - 1 - - - 0.0006666667 - 0.9 - 18 - 0 - - false - true - 3244 6500 '1 ' - 300HOGASEN-TRONDHEIM - - - - 0.0002222222 - 1.8 - 19.8 - 0 - - false - true - 5600 5603 '1 ' - 300KRISTIAN-ARENDAL - - 1 - - - 0 - 0 - 0.9 - 0 - - false - true - 5600 5620 '1 ' - 300KRISTIAN-FEDA - - 1 - - - 0.0007777778 - 1.8 - 18 - 0 - - false - true - 5600 6000 '1 ' - 300KRISTIAN-KVILLDAL - - - - 0.0002222222 - 2.7 - 30.6 - 0 - - false - true - 5600 5601 '1 ' - 300KRISTIAN-STAVANGE - - - - 0.0003333333 - 3.06 - 37.8 - 0 - - false - true - 6000 6100 '1 ' - 300KVILLDAL-BLAFALLI - - - - 0.0005555556 - 0.81 - 8.46 - 0 - - false - true - 5400 5500 '1 ' - 300OSLO-ASKER - - - - 0.0002777778 - 2.97 - 32.4 - 0 - - false - true - 5400 6000 '1 ' - 300OSLO-KVILLDAL - - - - 0.0001111111 - 1.89 - 19.8 - 0 - - false - true - 5300 6100 '1 ' - 300SIMA-BLAFALLI - - - - 0.0004888889 - 2.43 - 23.4 - 0 - - false - true - 5100 5500 '1 ' - 300TRETTEN-ASKER - - - - 0.0006666667 - 7.2 - 81 - 0 - - false - true - 5100 6500 '1 ' - 300TRETTEN-TRONDHEIM - - - - 0.0011111111 - 15.3 - 162 - 0 - - false - true - 6500 6700 '1 ' - 300TRONDHEIM-MO 1 - - - - 0.0013333333 - 9 - 117 - 0 - - false - true - 6500 6700 '2 ' - 300TRONDHEIM-MO 2 - - - - 0 - 0 - 0 - - false - 420ARENDAL-SANDEFJORD - - - - 0.000175737 - 1.2348 - 21.168 - 0 - - false - true - 5301 5305 '1 ' - 420AURLAND-EIDFJORD - - 1 - - - 0.0003401361 - 1.764 - 35.28 - 0 - - false - true - 5301 5304 '1 ' - 420AURLAND-GEILO - - 1 - - - 0.0002834467 - 2.2932 - 35.28 - 0 - - false - true - 5301 6001 '1 ' - 420AURLAND-HAGAFOSS - - 1 - - - 0.0003968254 - 2.9988 - 42.336 - 0 - - false - true - 5102 5304 '1 ' - 420DAGALI-GEILO - - - - 0.0007369615 - 5.292 - 81.144 - 0 - - false - true - 5102 6001 '1 ' - 420DAGALI-HAGAFOSS - - - - 0.000170068 - 0.7056 - 12.348 - 0 - - false - true - 5102 5103 '1 ' - 420DAGALI-KONGSBER - - - - 0 - 0 - 1.764 - 0 - - false - true - 3000 3020 '1 ' - 420FORSMARK-DANNEBO - - - - 0.0002834467 - 1.4112 - 21.168 - 0 - - false - true - 3000 3245 '1 ' - 420FORSMARK-JARPSTRO 1 - - - - 0.0002834467 - 3.1752 - 35.28 - 0 - - false - true - 3000 3245 '2 ' - 420FORSMARK-JARPSTRO 2 - - - - 0.000170068 - 1.0584 - 14.112 - 0 - - false - true - 3000 3300 '1 ' - 420FORSMARK-OSKARHA 1 - - - - 0.0001417234 - 1.5876 - 17.64 - 0 - - false - true - 3000 3300 '2 ' - 420FORSMARK-OSKARHA 2 - - - - 0.0028344671 - 13.23 - 158.76 - 0 - - false - true - 3000 3115 '1 ' - 420FORSMARK-PORJUS - - - - 0.0002834467 - 1.764 - 26.46 - 0 - - false - true - 5304 5305 '1 ' - 420GEILO-EIDFJORD 1 - - - - 0.0002267574 - 2.2932 - 2.9988 - 0 - - false - true - 5304 5305 '2 ' - 420GEILO-EIDFJORD 2 - - - - 0.0004421769 - 3.528 - 13.23 - 0 - - false - true - 3249 7100 '1 ' - 420GRUNDFOR-OULU - - - - 0.0005102041 - 1.4112 - 17.64 - 0 - - false - true - 5101 5102 '1 ' - 420HALDEN-DAGALI - - - - 0.0002267574 - 1.764 - 24.696 - 0 - - false - true - 5101 5103 '1 ' - 420HALDEN-KONGSBER - - - - 0.0031179138 - 1.764 - 26.46 - 0 - - false - true - 5101 5501 '1 ' - 420HALDEN-SKIEN - - - - 0 - 0 - 1.764 - 0 - - false - true - 7000 7020 '1 ' - 420HELSINKI-ESTLINK - - - - 0.0007369615 - 7.056 - 21.168 - 0 - - false - true - 7000 7100 '1 ' - 420HELSINKI-OULU 1 - - - - 0.0007369615 - 7.056 - 21.168 - 0 - - false - true - 7000 7100 '2 ' - 420HELSINKI-OULU 2 - - - - 0.0007369615 - 7.056 - 24.696 - 0 - - false - true - 7000 7100 '3 ' - 420HELSINKI-OULU 3 - - - - 0 - 0 - 1.764 - 0 - - false - true - 7000 7010 '1 ' - 420HELSINKI-VYBORG - - - - 0.0011337868 - 2.646 - 37.926 - 0 - - false - true - 3100 3249 '1 ' - 420HJALTA-GRUNDFOR - - - - 0.0011337868 - 2.646 - 35.28 - 0 - - false - true - 3100 3115 '1 ' - 420HJALTA-PORJUS - - - - 0.0014172336 - 14.112 - 88.2 - 0 - - false - true - 3100 3359 '1 ' - 420HJALTA-RINGHALS 1 - - - - 0.0013605442 - 7.056 - 40.572 - 0 - - false - true - 3100 3359 '2 ' - 420HJALTA-RINGHALS 2 - - - - 0.0011337868 - 7.056 - 42.336 - 0 - - false - true - 3100 3200 '1 ' - 420HJALTA-TENHULT 1 - - - - 0.0011337868 - 7.056 - 42.336 - 0 - - false - true - 3100 3200 '2 ' - 420HJALTA-TENHULT 2 - - - - 0.0011337868 - 7.056 - 42.336 - 0 - - false - true - 3100 3200 '3 ' - 420HJALTA-TENHULT 3 - - - - 0.0003968254 - 3.528 - 44.1 - 0 - - false - true - 5103 5304 '1 ' - 420KONGSBER-GEILO 1 - - - - 0.0003401361 - 2.2932 - 35.28 - 0 - - false - true - 5103 5304 '2 ' - 420KONGSBER-GEILO 2 - - - - 0 - 0 - 1.764 - 0 - - false - true - 8500 8600 '1 ' - 420MALMO-ARRIE - - - - 0 - 0 - 1.764 - 0 - - false - true - 8500 8700 '1 ' - 420MALMO-KARLSH - - - - 0.0003401361 - 3.528 - 40.572 - 0 - - false - true - 3300 8500 '1 ' - 420OSKARHA-MALMO 1 - - - - 0.0005668934 - 2.1168 - 47.628 - 0 - - false - true - 3300 8500 '2 ' - 420OSKARHA-MALMO 2 - - - - 0.0003401361 - 3.528 - 35.28 - 0 - - false - true - 3200 3300 '1 ' - 420OSKARHA-TENHULT - - - - 0.0004535147 - 2.646 - 35.28 - 0 - - false - true - 3115 3249 '1 ' - 420PORJUS-GRUNDFOR - - - - 0.0007936508 - 7.938 - 88.2 - 0 - - false - true - 3115 3245 '1 ' - 420PORJUS-JARPSTRO - - - - 0.0005668934 - 7.056 - 70.56 - 0 - - false - true - 3115 6701 '1 ' - 420PORJUS-NARVIK - - - - 0.0007369615 - 7.056 - 22.932 - 0 - - false - true - 3115 7100 '1 ' - 420PORJUS-OULU - - - - 0.0005102041 - 2.8224 - 45.864 - 0 - - false - true - 3359 5101 '1 ' - 420RINGHALS-HALDEN 1 - - - - - 0.0003401361 - 3.528 - 38.808 - 0 - - false - true - 3359 5101 '2 ' - 420RINGHALS-HALDEN 2 - - - - - 0.0005668934 - 2.1168 - 47.628 - 0 - - false - true - 3359 8500 '1 ' - 420RINGHALS-MALMO 1 - - - - 0.0005102041 - 4.41 - 56.448 - 0 - - false - true - 3359 8500 '2 ' - 420RINGHALS-MALMO 2 - - - - 0.0001587302 - 1.12896 - 17.64 - 0 - - false - true - 5401 6001 '1 ' - 420SYLLING-HAGAFOSS - - - - 0.0005102041 - 2.8224 - 44.982 - 0 - - false - true - 5401 5602 '1 ' - 420SYLLING-SANDEFJORD - - - - 0.0004535147 - 3.087 - 47.628 - 0 - - false - true - 5401 5501 '1 ' - 420SYLLING-SKIEN - - - - 1.70068E-05 - 0.12348 - 1.764 - 0 - - false - true - 5402 6001 '1 ' - 420SYSLEHAGAFOSS - - - - 0.0003401361 - 1.764 - 29.988 - 0 - - false - true - 3200 8500 '1 ' - 420TENHULT-MALMO - - - - 0.0003968254 - 1.764 - 35.28 - 0 - - false - true - 3200 3359 '1 ' - 420TENHULT-RINGHALS - - - - 2130 - 2130 - - - APL High WV - - - 1775 - 1775 - - - APL PATL at 20degree - - - 1605 - 1605 - - - APL PATL at 30degree - - - 1600 - 1600 - - - APL STABILITY - - - 2130 - 2130 - - - APL TATL at 20degree - - - 1925 - 1925 - - - APL TATL at 30degree - - - NO-BZ-1 HYDA P - NO1 BiddingZone Hydro Actual Active Power - - ThreePhaseActivePower - B11 - - - - - NO-BZ-1 HYDF P - NO1 BiddingZone Hydro Forecast Active Power - - ThreePhaseActivePower - B11 - - - - - NO-BZ-1 HYRAF P - NO1 BiddingZone Hydro Reservoir Availability Forecast Active Power - - ThreePhaseActivePower - B12 - - - - - NO-BZ-1 IGENC P - NO1 BiddingZone Installed Generating Capacity Active Power - - ThreePhaseActivePower - A68 - - - - - NO-BZ-1 IHYPC P - NO1 BiddingZone Installed Hydro Pump Capacity Active Power - - ThreePhaseActivePower - B10 - - - - - NO-BZ-1 IHYRC P - NO1 BiddingZone Installed Hydro Reservoir Capacity Active Power - - ThreePhaseActivePower - B12 - - - - - NO-BZ-1 IRORC P - NO1 BiddingZone Installed Run-of-river Capacity Active Power - - ThreePhaseActivePower - B11 - - - - - NO-BZ-1 IWINC P - NO1 BiddingZone Installed Wind Capacity Active Power - - ThreePhaseActivePower - B19 - - - - - NO-BZ-1 LA P - NO1 BiddingZone Load Actual Active Power - - ThreePhaseActivePower - A04 - - - - - NO-BZ-1 LF P - NO1 BiddingZone Load Forecast Active Power - - ThreePhaseActivePower - A04 - - - - - NO-BZ-1 NEXA P - NO1 BiddingZone Net Exchange Actual Active Power - - ThreePhaseActivePower - xx - - - - - NO-BZ-1 NEXF P - NO1 BiddingZone Net Exchange Forecast Active Power - - ThreePhaseActivePower - xx - - - - - NO-BZ-1 NUCA P - NO1 BiddingZone Nuclear Actual Active Power - - ThreePhaseActivePower - B14 - - - - - NO-BZ-1 NUCF P - NO1 BiddingZone Nuclear Forecast Active Power - - ThreePhaseActivePower - B14 - - - - - NO-BZ-1 OTHA P - NO1 BiddingZone Other Actual Active Power - - ThreePhaseActivePower - B20 - - - - - NO-BZ-1 OTHF P - NO1 BiddingZone Other Forecast Active Power - - ThreePhaseActivePower - B20 - - - - - NO-BZ-1 PROA P - NO1 BiddingZone Production Actual Active Power - - ThreePhaseActivePower - A38 - - - - - NO-BZ-1 PROF P - NO1 BiddingZone Production Forecast Active Power - - ThreePhaseActivePower - A38 - - - - - NO-BZ-1 PUMAF P - NO1 BiddingZone Hydro Pump Availability Forecast Active Power - - ThreePhaseActivePower - B10 - - - - - NO-BZ-1 RCF P - NO1 BiddingZone Remaining Capacity Forecast Active Power - - ThreePhaseActivePower - A38 - - - - - NO-BZ-1 RORA P - NO1 BiddingZone Run-of-river Actual Active Power - - ThreePhaseActivePower - B11 - - - - - NO-BZ-1 RORF P - NO1 BiddingZone Run-of-river Forecast Active Power - - ThreePhaseActivePower - B11 - - - - - NO-BZ-1 THRA P - NO1 BiddingZone Thermal Actual Active Power - - ThreePhaseActivePower - B04 - - - - - NO-BZ-1 THRF P - NO1 BiddingZone Thermal Forecast Active Power - - ThreePhaseActivePower - B04 - - - - - NO-BZ-1 WINA P - NO1 BiddingZone Wind Actual Active Power - - ThreePhaseActivePower - B19 - - - - - NO-BZ-1 WINF P - NO1 BiddingZone Wind Forecast Active Power - - ThreePhaseActivePower - B19 - - - - - NO-BZ-2 LA P - NO2 BiddingZone Load Actual Active Power - - ThreePhaseActivePower - A04 - - - - - NO-BZ-2 LF P - NO2 BiddingZone Load Forecast Active Power - - ThreePhaseActivePower - A04 - - - - - NO-BZ-3 LA P - NO3 BiddingZone Load Actual Active Power - - ThreePhaseActivePower - A04 - - - - - NO-BZ-3 LF P - NO3 BiddingZone Load Forecast Active Power - - ThreePhaseActivePower - A04 - - - - - - NO-BZ-1 HYD 1M P_V - NO1 BiddingZone Hydro Forecast Month-ahead - - 4050 - - - - NO-BZ-1 HYD 1Y P_V - NO1 BiddingZone Hydro Forecast Year-ahead - - 4050 - - - - NO-BZ-1 HYD 2D P_V - NO1 BiddingZone Hydro Forecast Two Day-ahead - - 4050 - - - - NO-BZ-1 HYD 3D P_V - NO1 BiddingZone Hydro Forecast Three Day-ahead - - 4050 - - - - NO-BZ-1 HYD 4D P_V - NO1 BiddingZone Hydro Forecast Four Day-ahead - - 4050 - - - - NO-BZ-1 HYD 5D P_V - NO1 BiddingZone Hydro Forecast Five Day-ahead - - 4050 - - - - NO-BZ-1 HYD 6D P_V - NO1 BiddingZone Hydro Forecast Six Day-ahead - - 4050 - - - - NO-BZ-1 HYD 7D P_V - NO1 BiddingZone Hydro Forecast Seven Day-ahead - - 4050 - - - - NO-BZ-1 HYDO P_V - NO1 BiddingZone Hydro Actual Observed - - 4000 - - - - NO-BZ-1 HYDS P_V - NO1 BiddingZone Hydro Actual Scheduled - - 4050 - - - - NO-BZ-1 HYP 1D P_V - NO1 BiddingZone Hydro Pump Availability Forecast Day-ahead - - 3720 - - - - NO-BZ-1 HYP 1M P_V - NO1 BiddingZone Hydro Pump Availability Forecast Month-ahead - - 3720 - - - - NO-BZ-1 HYP 1Y P_V - NO1 BiddingZone Hydro Pump Availability Forecast Year-ahead - - 3720 - - - - NO-BZ-1 HYP 2D P_V - NO1 BiddingZone Hydro Pump Availability Forecast Two Day-ahead - - 3720 - - - - NO-BZ-1 HYP 3D P_V - NO1 BiddingZone Hydro Pump Availability Forecast Three Day-ahead - - 3720 - - - - NO-BZ-1 HYP 4D P_V - NO1 BiddingZone Hydro Pump Availability Forecast Four Day-ahead - - 3720 - - - - NO-BZ-1 HYP 5D P_V - NO1 BiddingZone Hydro Pump Availability Forecast Five Day-ahead - - 3720 - - - - NO-BZ-1 HYP 6D P_V - NO1 BiddingZone Hydro Pump Availability Forecast Six Day-ahead - - 3720 - - - - NO-BZ-1 HYP 7D P_V - NO1 BiddingZone Hydro Pump Availability Forecast Seven Day-ahead - - 3720 - - - - NO-BZ-1 HYP ID P_V - NO1 BiddingZone Hydro Pump Availability Forecast Intraday - - 3720 - - - - NO-BZ-1 HYRA 1D P_V - NO1 BiddingZone Hydro Reservoir Availability Forecast Day-ahead - - 3720 - - - - NO-BZ-1 HYRA 1M P_V - NO1 BiddingZone Hydro Reservoir Availability Forecast Month-ahead - - 3720 - - - - NO-BZ-1 HYRA 1Y P_V - NO1 BiddingZone Hydro Reservoir Availability Forecast Year-ahead - - 3720 - - - - NO-BZ-1 HYRA 2D P_V - NO1 BiddingZone Hydro Reservoir Availability Forecast Two Day-ahead - - 3720 - - - - NO-BZ-1 HYRA 3D P_V - NO1 BiddingZone Hydro Reservoir Availability Forecast Three Day-ahead - - 3720 - - - - NO-BZ-1 HYRA 4D P_V - NO1 BiddingZone Hydro Reservoir Availability Forecast Four Day-ahead - - 3720 - - - - NO-BZ-1 HYRA 5D P_V - NO1 BiddingZone Hydro Reservoir Availability Forecast Five Day-ahead - - 3720 - - - - NO-BZ-1 HYRA 6D P_V - NO1 BiddingZone Hydro Reservoir Availability Forecast Six Day-ahead - - 3720 - - - - NO-BZ-1 HYRA 7D P_V - NO1 BiddingZone Hydro Reservoir Availability Forecast Seven Day-ahead - - 3720 - - - - NO-BZ-1 HYRA ID P_V - NO1 BiddingZone Hydro Reservoir Availability Forecast Intraday - - 3720 - - - - NO-BZ-1 IGENC P_V - NO1 BiddingZone Installed Generating Capacity - - 2370 - - - - NO-BZ-1 IHYPC P_V - NO1 BiddingZone Installed Hydro Pump Capacity - - 2370 - - - - NO-BZ-1 IHYRC P_V - NO1 BiddingZone Installed Hydro Reservoir Capacity - - 2370 - - - - NO-BZ-1 IRORC P_V - NO1 BiddingZone Installed Run-of-river Capacity - - 2370 - - - - NO-BZ-1 IWINCF P_V - NO1 BiddingZone Installed Wind Capacity - - 2370 - - - - NO-BZ-1 LAO P_V - NO1 BiddingZone Load Actual Observed - - 3700 - - - - NO-BZ-1 LAS P_V - NO1 BiddingZone Load Actual Scheduled - - 3750 - - - - NO-BZ-1 LF 1D P_V - NO1 BiddingZone Load Forecast Day-ahead - - 3720 - - - - NO-BZ-1 LF 1M P_V - NO1 BiddingZone Load Forecast Month-ahead - - 3831 - - - - NO-BZ-1 LF 1Y P_V - NO1 BiddingZone Load Forecast Year-ahead - - 3831 - - - - NO-BZ-1 LF 2D P_V - NO1 BiddingZone Load Forecast Two Day-ahead - - 3888 - - - - NO-BZ-1 LF 3D P_V - NO1 BiddingZone Load Forecast Three Day-ahead - - 3878 - - - - NO-BZ-1 LF 4D P_V - NO1 BiddingZone Load Forecast Four Day-ahead - - 3870 - - - - NO-BZ-1 LF 5D P_V - NO1 BiddingZone Load Forecast Five Day-ahead - - 3850 - - - - NO-BZ-1 LF 6D P_V - NO1 BiddingZone Load Forecast Six Day-ahead - - 3855 - - - - NO-BZ-1 LF 7D P_V - NO1 BiddingZone Load Forecast Seven Day-ahead - - 3831 - - - - NO-BZ-1 LF ID P_V - NO1 BiddingZone Load Forecast Intraday - - 3720 - - - - NO-BZ-1 NEX 1M P_V - NO1 BiddingZone Net Exchange Forecast Month-ahead - - 4050 - - - - NO-BZ-1 NEX 1Y P_V - NO1 BiddingZone Net Exchange Forecast Year-ahead - - 4050 - - - - NO-BZ-1 NEX 2D P_V - NO1 BiddingZone Net Exchange Forecast Two Day-ahead - - 4050 - - - - NO-BZ-1 NEX 3D P_V - NO1 BiddingZone Net Exchange Forecast Three Day-ahead - - 4050 - - - - NO-BZ-1 NEX 4D P_V - NO1 BiddingZone Net Exchange Forecast Four Day-ahead - - 4050 - - - - NO-BZ-1 NEX 5D P_V - NO1 BiddingZone Net Exchange Forecast Five Day-ahead - - 4050 - - - - NO-BZ-1 NEX 6D P_V - NO1 BiddingZone Net Exchange Forecast Six Day-ahead - - 4050 - - - - NO-BZ-1 NEX 7D P_V - NO1 BiddingZone Net Exchange Forecast Seven Day-ahead - - 4050 - - - - NO-BZ-1 NEXO P_V - NO1 BiddingZone Net Exchange Actual Observed - - 4000 - - - - NO-BZ-1 NEXS P_V - NO1 BiddingZone Net Exchange Actual Scheduled - - 4050 - - - - NO-BZ-1 NUC 1M P_V - NO1 BiddingZone Nuclear Forecast Month-ahead - - 4050 - - - - NO-BZ-1 NUC 1Y P_V - NO1 BiddingZone Nuclear Forecast Year-ahead - - 4050 - - - - NO-BZ-1 NUC 2D P_V - NO1 BiddingZone Nuclear Forecast Two Day-ahead - - 4050 - - - - NO-BZ-1 NUC 3D P_V - NO1 BiddingZone Nuclear Forecast Three Day-ahead - - 4050 - - - - NO-BZ-1 NUC 4D P_V - NO1 BiddingZone Nuclear Forecast Four Day-ahead - - 4050 - - - - NO-BZ-1 NUC 5D P_V - NO1 BiddingZone Nuclear Forecast Five Day-ahead - - 4050 - - - - NO-BZ-1 NUC 6D P_V - NO1 BiddingZone Nuclear Forecast Six Day-ahead - - 4050 - - - - NO-BZ-1 NUC 7D P_V - NO1 BiddingZone Nuclear Forecast Seven Day-ahead - - 4050 - - - - NO-BZ-1 NUCO P_V - NO1 BiddingZone Nuclear Actual Observed - - 4000 - - - - NO-BZ-1 NUCS P_V - NO1 BiddingZone Nuclear Actual Scheduled - - 4050 - - - - NO-BZ-1 OTH 1M P_V - NO1 BiddingZone Other Production Forecast Month-ahead - - 4050 - - - - NO-BZ-1 OTH 1Y P_V - NO1 BiddingZone Other Production Forecast Year-ahead - - 4050 - - - - NO-BZ-1 OTH 2D P_V - NO1 BiddingZone Other Production Forecast Two Day-ahead - - 4050 - - - - NO-BZ-1 OTH 3D P_V - NO1 BiddingZone Other Production Forecast Three Day-ahead - - 4050 - - - - NO-BZ-1 OTH 4D P_V - NO1 BiddingZone Other Production Forecast Four Day-ahead - - 4050 - - - - NO-BZ-1 OTH 5D P_V - NO1 BiddingZone Other Production Forecast Five Day-ahead - - 4050 - - - - NO-BZ-1 OTH 6D P_V - NO1 BiddingZone Other Production Forecast Six Day-ahead - - 4050 - - - - NO-BZ-1 OTH 7D P_V - NO1 BiddingZone Other Production Forecast Seven Day-ahead - - 4050 - - - - NO-BZ-1 OTHO P_V - NO1 BiddingZone Other Production Actual Observed - - 4000 - - - - NO-BZ-1 OTHS P_V - NO1 BiddingZone Other Production Actual Scheduled - - 4050 - - - - NO-BZ-1 PRO 1M P_V - NO1 BiddingZone Production Forecast Month-ahead - - 4050 - - - - NO-BZ-1 PRO 1Y P_V - NO1 BiddingZone Production Forecast Year-ahead - - 4050 - - - - NO-BZ-1 PRO 2D P_V - NO1 BiddingZone Production Forecast Two Day-ahead - - 4050 - - - - NO-BZ-1 PRO 3D P_V - NO1 BiddingZone Production Forecast Three Day-ahead - - 4050 - - - - NO-BZ-1 PRO 4D P_V - NO1 BiddingZone Production Forecast Four Day-ahead - - 4050 - - - - NO-BZ-1 PRO 5D P_V - NO1 BiddingZone Production Forecast Five Day-ahead - - 4050 - - - - NO-BZ-1 PRO 6D P_V - NO1 BiddingZone Production Forecast Six Day-ahead - - 4050 - - - - NO-BZ-1 PRO 7D P_V - NO1 BiddingZone Production Forecast Seven Day-ahead - - 4050 - - - - NO-BZ-1 PROO P_V - NO1 BiddingZone Production Actual Observed - - 4000 - - - - NO-BZ-1 PROS P_V - NO1 BiddingZone Production Actual Scheduled - - 4050 - - - - NO-BZ-1 RCA 1D P_V - NO1 BiddingZone Remaining Capacity Forecast Day-ahead - - 3720 - - - - NO-BZ-1 RCA 1M P_V - NO1 BiddingZone Remaining Capacity Forecast Month-ahead - - 3720 - - - - NO-BZ-1 RCA 1Y P_V - NO1 BiddingZone Remaining Capacity Forecast Year-ahead - - 3720 - - - - NO-BZ-1 RCA 2D P_V - NO1 BiddingZone Remaining Capacity Forecast Two Day-ahead - - 3720 - - - - NO-BZ-1 RCA 3D P_V - NO1 BiddingZone Remaining Capacity Forecast Three Day-ahead - - 3720 - - - - NO-BZ-1 RCA 4D P_V - NO1 BiddingZone Remaining Capacity Forecast Four Day-ahead - - 3720 - - - - NO-BZ-1 RCA 5D P_V - NO1 BiddingZone Remaining Capacity Forecast Five Day-ahead - - 3720 - - - - NO-BZ-1 RCA 6D P_V - NO1 BiddingZone Remaining Capacity Forecast Six Day-ahead - - 3720 - - - - NO-BZ-1 RCA 7D P_V - NO1 BiddingZone Remaining Capacity Forecast Seven Day-ahead - - 3720 - - - - NO-BZ-1 RCA ID P_V - NO1 BiddingZone Remaining Capacity Forecast Intraday - - 3720 - - - - NO-BZ-1 ROR 1M P_V - NO1 BiddingZone Run-of-river Forecast Month-ahead - - 1199 - - - - NO-BZ-1 ROR 1Y P_V - NO1 BiddingZone Run-of-river Forecast Year-ahead - - 1202 - - - - NO-BZ-1 ROR 2D P_V - NO1 BiddingZone Run-of-river Forecast Two Day-ahead - - 1200 - - - - NO-BZ-1 ROR 3D P_V - NO1 BiddingZone Run-of-river Forecast Three Day-ahead - - 1234 - - - - NO-BZ-1 ROR 4D P_V - NO1 BiddingZone Run-of-river Forecast Four Day-ahead - - 1188 - - - - NO-BZ-1 ROR 5D P_V - NO1 BiddingZone Run-of-river Forecast Five Day-ahead - - 1300 - - - - NO-BZ-1 ROR 6D P_V - NO1 BiddingZone Run-of-river Forecast Six Day-ahead - - 1221 - - - - NO-BZ-1 ROR 7D P_V - NO1 BiddingZone Run-of-river Forecast Seven Day-ahead - - 1200 - - - - NO-BZ-1 THR 1M P_V - NO1 BiddingZone Thermal Forecast Month-ahead - - 4050 - - - - NO-BZ-1 THR 1Y P_V - NO1 BiddingZone Thermal Forecast Year-ahead - - 4050 - - - - NO-BZ-1 THR 2D P_V - NO1 BiddingZone Thermal Forecast Two Day-ahead - - 4050 - - - - NO-BZ-1 THR 3D P_V - NO1 BiddingZone Thermal Forecast Three Day-ahead - - 4050 - - - - NO-BZ-1 THR 4D P_V - NO1 BiddingZone Thermal Forecast Four Day-ahead - - 4050 - - - - NO-BZ-1 THR 5D P_V - NO1 BiddingZone Thermal Forecast Five Day-ahead - - 4050 - - - - NO-BZ-1 THR 6D P_V - NO1 BiddingZone Thermal Forecast Six Day-ahead - - 4050 - - - - NO-BZ-1 THR 7D P_V - NO1 BiddingZone Thermal Forecast Seven Day-ahead - - 4050 - - - - NO-BZ-1 THRO P_V - NO1 BiddingZone Thermal Actual Observed - - 4000 - - - - NO-BZ-1 THRS P_V - NO1 BiddingZone Thermal Actual Scheduled - - 4050 - - - - NO-BZ-1 WIN 1M P_V - NO1 BiddingZone Wind Forecast Month-ahead - - 4050 - - - - NO-BZ-1 WIN 1Y P_V - NO1 BiddingZone Wind Forecast Year-ahead - - 4050 - - - - NO-BZ-1 WIN 2D P_V - NO1 BiddingZone Wind Forecast Two Day-ahead - - 4050 - - - - NO-BZ-1 WIN 3D P_V - NO1 BiddingZone Wind Forecast Three Day-ahead - - 4050 - - - - NO-BZ-1 WIN 4D P_V - NO1 BiddingZone Wind Forecast Four Day-ahead - - 4050 - - - - NO-BZ-1 WIN 5D P_V - NO1 BiddingZone Wind Forecast Five Day-ahead - - 4050 - - - - NO-BZ-1 WIN 6D P_V - NO1 BiddingZone Wind Forecast Six Day-ahead - - 4050 - - - - NO-BZ-1 WIN 7D P_V - NO1 BiddingZone Wind Forecast Seven Day-ahead - - 4050 - - - - NO-BZ-1 WINO P_V - NO1 BiddingZone Wind Actual Observed - - 4000 - - - - NO-BZ-1 WINS P_V - NO1 BiddingZone Wind Actual Scheduled - - 4050 - - - - NO-BZ-2 LAO P_V - NO2 BiddingZone Load Actual Observed - - 6000 - - - - NO-BZ-2 LAS P_V - NO2 BiddingZone Load Actual Scheduled - - 6010 - - - - NO-BZ-2 LF 1D P_V - NO2 BiddingZone Load Forecast Day-ahead - - 6100 - - - - NO-BZ-2 LF 2D P_V - NO2 BiddingZone Load Forecast Two Day-ahead - - 6060 - - - - NO-BZ-3 LAO P_V - NO3 BiddingZone Load Actual Observed - - 2350 - - - - NO-BZ-3 LAS P_V - NO3 BiddingZone Load Actual Scheduled - - 2360 - - - - NO-BZ-3 LF 1D P_V - NO3 BiddingZone Load Forecast Day-ahead - - 2370 - - - - NO-BZ-3 LF 2D P_V - NO3 BiddingZone Load Forecast Two Day-ahead - - 2349 - - - 1000 - - - 135 - 135kV - - - 300 - 300kV - - - 420 - 420kV - - - ARENDAL 300AS1 - - Arendal 300 Asker 1 Bay - - - ARENDAL 300KR1 - - Arendal 300 Kristiansand 1 Bay - - - ARENDAL 300KR2 - - Arendal 300 Kristiansand HVDC 2 Bay - - - ARENDAL 300SC1 - - Arendal 300 Shunt Compensator 1 Bay - - - ARENDAL 300T1 - - Arendal 300 Transformer 1 Bay - - - ARENDAL 420T1 - - Arendal 420 Transformer 1 Bay - - - KRISTIAN300AR1 - - Kristiansand 300 Arendal 1 Bay - - - KRISTIAN300FE1 - - Kristiansand 300 Feda 1 Bay - - - KRISTIAN300G1 - - Kristiansand 300 Gen 1 Bay - - - KRISTIAN300G2 - - Kristiansand 300 Gen 2 Bay - - - KRISTIAN300G3 - - Kristiansand 300 Gen 3 Bay - - - KRISTIAN300G4 - - Kristiansand 300 Gen 4 Bay - - - KRISTIAN300KV1 - - Kristiansand 300 Kvilldal 1 Bay - - - KRISTIAN300L1 - - Kristiansand 300 Load 1 Bay - - - KRISTIAN300L2 - - Kristiansand 300 Load 2 Bay - - - KRISTIAN300ST1 - - Kristiansand 300 Stavanger 1 Bay - - - ARENDAL 300AS1 AB_S - - false - false - - false - Arendal 300 Asker 1 A Breaker - - - ARENDAL 300AS1 BB_S - - false - true - - false - Arendal 300 Asker 1 B Breaker - - - ARENDAL 300KR1 AB_S - - false - false - - false - Arendal 300 Kristiansand 1 A Breaker - - - ARENDAL 300KR1 BB_S - - false - true - - false - Arendal 300 Kristiansand 1 B Breaker - - - ARENDAL 300KR2 AB_S - - false - false - - false - Arendal 300 Kristiansand 2 A Breaker - - - ARENDAL 300KR2 BB_S - - false - true - - false - Arendal 300 Kristiansand 2 B Breaker - - - ARENDAL 300SC1 AB_S - - false - false - - false - Arendal 300 Shunt Compensator 1 A Breaker - - - ARENDAL 300T1 AB_S - - false - false - - false - Arendal 300 Transformer 1 A Breaker - - - ARENDAL 420T1 AB_S - - false - false - - false - Arendal 420 Transformer 1 A Breaker - - - KRISTIAN300AR1 AB_S - - false - false - - false - Kristiansand 300 Arendal 1 A Breaker - - - KRISTIAN300AR1 BB_S - - false - true - - false - Kristiansand 300 Arendal 1 B Breaker - - - KRISTIAN300FE1 AB_S - - false - false - - false - Kristiansand 300 Feda 1 A Breaker - - - KRISTIAN300FE1 BB_S - - false - true - - false - Kristiansand 300 Feda 1 B Breaker - - - KRISTIAN300G1 AB_S - - false - false - - false - Kristiansand 300 Gen 1 A Breaker - - - KRISTIAN300G1 BB_S - - false - true - - false - Kristiansand 300 Gen 1 B Breaker - - - KRISTIAN300G2 AB_S - - false - false - - false - Kristiansand 300 Gen 2 A Breaker - - - KRISTIAN300G2 BB_S - - false - true - - false - Kristiansand 300 Gen 2 B Breaker - - - KRISTIAN300G3 AB_S - - false - false - - false - Kristiansand 300 Gen 3 A Breaker - - - KRISTIAN300G3 BB_S - - false - true - - false - Kristiansand 300 Gen 3 B Breaker - - - KRISTIAN300G4 AB_S - - false - false - - false - Kristiansand 300 Gen 4 A Breaker - - - KRISTIAN300G4 BB_S - - false - true - - false - Kristiansand 300 Gen 4 B Breaker - - - KRISTIAN300KV1 AB_S - - false - false - - false - Kristiansand 300 Kvilldal 1 A Breaker - - - KRISTIAN300KV1 BB_S - - false - true - - false - Kristiansand 300 Kvilldal 1 B Breaker - - - KRISTIAN300L1 AB_S - - false - false - - false - Kristiansand 300 Load 1 A Breaker - - - KRISTIAN300L1 BB_S - - false - true - - false - Kristiansand 300 Load 1 B Breaker - - - KRISTIAN300L2 AB_S - - false - false - - false - Kristiansand 300 Load 2 A Breaker - - - KRISTIAN300L2 BB_S - - false - true - - false - Kristiansand 300 Load 2 B Breaker - - - KRISTIAN300ST1 AB_S - - false - false - - false - Kristiansand 300 Stavanger 1 A Breaker - - - KRISTIAN300ST1 BB_S - - false - true - - false - Kristiansand 300 Stavanger 1 B Breaker - - - - AJAURE 300 A - - false - - - - ARENDAL 300 A - - false - - - - ARENDAL 300 B - - false - - - - ARRIE 420 A - - false - - - - ASKER 300 A - - false - - - - AURLAND 420 A - - false - - - - BLAFALLI300 A - - false - - - - DAGALI 420 A - - false - - - - DANNEBO 420 A - - false - - - - EIDFJORD420 A - - false - - - - ESTLINK 420 A - - false - - - - FEDA 300 A - - false - - - - FORSMARK420 A - - false - - - - GEILO 420 A - - false - - - - GRUNDFOR420 A - - false - - - - HAGAFOSS420 A - - false - - - - HALDEN 420 A - - false - - - - HELSINKI420 A - - false - - - - HJALTA 420 A - - false - - - - HOGASEN 300 A - - false - - - - JARPSTRO420 A - - false - - - - KARLSH 420 A - - false - - - - KONGSBER420 A - - false - - - - KRISTIA 300 A - - false - - - - KRISTIAN300 A - - false - - - - KRISTIAN300 B - - false - - - - KVILLDAL300 A - - false - - - - MALMO 420 A - - false - - - - MO 300 A - - false - - - - NARVIK 420 A - - false - - - - OSKARSHA420 A - - false - - - - OSLO 300 A - - false - - - - OULU 420 A - - false - - - - PORJUS 420 A - - false - - - - RINGHALS420 A - - false - - - - SANDEFJO420 A - - false - - - - SIMA 300 A - - false - - - - SKIEN 420 A - - false - - - - STAVANGE300 A - - false - - - - STENKU 135 A - - false - - - - SYLLING 420 A - - false - - - - SYSLE 420 A - - false - - - - TENHULT 420 A - - false - - - - TRETTEN 300 A - - false - - - - TRONDHEI300 A - - false - - - - VYBORG 420 A - - false - - - - - 1030.381 - 430.058 - - - false - true - 5500 '1 ' - ASKER 300 L1 - - - - - - 1030.381 - 430.058 - - - false - true - 5500 '2 ' - ASKER 300 L2 - - - - - - 670.271 - 245.824 - - - false - true - 6100 '1 ' - BLAFALLI300 L1 - - - - - - 670.271 - 245.824 - - - false - true - 6100 '2 ' - BLAFALLI300 L2 - - - - - - 272.451 - 92.183 - - - false - true - 3000 '1 ' - FORSMARK420 L1 - - - - - - 272.451 - 92.183 - - - false - true - 3000 '2 ' - FORSMARK420 L2 - - - - - - 272.451 - 92.183 - - - false - true - 3000 '3 ' - FORSMARK420 L3 - - - - - - 1418.454 - 417.731 - - - false - true - 3249 '1 ' - GRUNDFOR420 L1 - - - - - - 1319.43 - 275.111 - - - false - true - 7000 '1 ' - HELSINKI420 L1 - - - - - - 1319.43 - 275.111 - - - false - true - 7000 '2 ' - HELSINKI420 L2 - - - - - - 1319.43 - 275.111 - - - false - true - 7000 '3 ' - HELSINKI420 L3 - - - - - - 1319.43 - 275.111 - - - false - true - 7000 '4 ' - HELSINKI420 L4 - - - - - - 1319.43 - 275.111 - - - false - true - 7000 '5 ' - HELSINKI420 L5 - - - - - - 67.546 - 70.693 - - - false - true - 3100 '1 ' - HJALTA 420 L1 - - - - - - 1312.229 - 267.37 - - - false - true - 5600 '1 ' - KRISTIAN300 L1 - - - - - - 1312.229 - 267.37 - - - false - true - 5600 '2 ' - KRISTIAN300 L2 - - - - - - 764 - 251.115 - - - false - true - 8500 '1 ' - MALMO 420 L1 - - - - - - 764 - 251.115 - - - false - true - 8500 '2 ' - MALMO 420 L2 - - - - - - 764 - 251.115 - - - false - true - 8500 '3 ' - MALMO 420 L3 - - - - - - 2070 - 680.376 - - - false - true - 6700 '1 ' - MO 300 L1 - - - - - - 1192.154 - 332.079 - - - false - true - 3300 '1 ' - OSKARSHA420 L1 - - - - - - 1192.154 - 332.079 - - - false - true - 3300 '2 ' - OSKARSHA420 L2 - - - - - - 1128.514 - 215.027 - - - false - true - 5400 '1 ' - OSLO 300 L1 - - - - - - 1185.425 - 786.043 - - - false - true - 7100 '1 ' - OULU 420 L1 - - - - - - 1185.425 - 786.043 - - - false - true - 7100 '2 ' - OULU 420 L2 - - - - - - 1041 - 342.16 - - - false - true - 3115 '1 ' - PORJUS 420 L1 - - - - - - 1430.584 - 498.117 - - - false - true - 3359 '1 ' - RINGHALS420 L1 - - - - - - 1430.584 - 498.117 - - - false - true - 3359 '2 ' - RINGHALS420 L2 - - - - - - 1430.584 - 498.117 - - - false - true - 3359 '3 ' - RINGHALS420 L3 - - - - - - 1430.584 - 498.117 - - - false - true - 3359 '4 ' - RINGHALS420 L4 - - - - - - 2195 - 721.461 - - - false - true - 5300 '2 ' - SIMA 300 L2 - - - - - - 539.723 - 150.52 - - - false - true - 5100 '1 ' - TRETTEN 300 L1 - - - - - - 779.667 - 256.264 - - - false - true - 6500 '1 ' - TRONDHEI300 L1 - - - - - - 779.667 - 256.264 - - - false - true - 6500 '2 ' - TRONDHEI300 L2 - - - - - - 779.667 - 256.264 - - - false - true - 6500 '3 ' - TRONDHEI300 L3 - - - - - ASKER 300 LG1 - 5500_1 - - - - ASKER 300 LG2 - 5500_2 - - - - BLAFALLI300 LG1 - 6100_1 - - - - BLAFALLI300 LG1 - 6100_2 - - - - FORSMARK420 LG1 - 3000_1 - - - - FORSMARK420 LG2 - 3000_2 - - - - FORSMARK420 LG3 - 3000_3 - - - - GRUNDFOR420 LG1 - 3249_1 - - - - HELSINKI420 LG1 - 7000_1 - - - - HELSINKI420 LG2 - 7000_2 - - - - HELSINKI420 LG3 - 7000_3 - - - - HELSINKI420 LG4 - 7000_4 - - - - HELSINKI420 LG5 - 7000_5 - - - - HJALTA 420 LG1 - 3100_1 - - - - KRISTIANCLG1 - KRISTIAN Conf Load Group 1 - - - - KRISTIANCLG2 - KRISTIAN Conf Load Group 2 - - - - MALMO 420 LG1 - 8500_1 - - - - MALMO 420 LG2 - 8500_2 - - - - MALMO 420 LG3 - 8500_3 - - - - MO 300 LG1 - 6700_1 - - - - OSKARSHA420 LG1 - 3300_1 - - - - OSKARSHA420 LG2 - 3300_2 - - - - OSLO 300 LG1 - 5400_1 - - - - OULU 420 LG1 - 7100_1 - - - - OULU 420 LG1 - 7100_2 - - - - PORJUS 420 LG1 - 3115_1 - - - - RINGHALS420 LG1 - 3359_1 - - - - RINGHALS420 LG2 - 3359_2 - - - - RINGHALS420 LG3 - 3359_3 - - - - RINGHALS420 LG4 - 3359_4 - - - - SIMA 300 LG2 - 5300_2 - - - - TRETTEN 300 LG1 - 5100_1 - - - - TRONDHEI300 LG1 - 6500_1 - - - - TRONDHEI300 LG2 - 6500_2 - - - - TRONDHEI300 LG3 - 6500_3 - - - - AJAURE - - - - ARENDAL CN 001 - - - - ARENDAL CN 002 - - - - ARENDAL CN 003 - - - - ARENDAL CN 004 - - - - ARENDAL CN 005 - - - - ARENDAL CN 006 - - - - ARENDAL CN 007 - - - - ARENDAL CN 008 - - - - ARENDAL CN 009 - - - - ARENDAL CN 010 - - - - ARENDAL CN 011 - - - - ARENDAL CN 012 - - - - ARENDAL CN 013 - - - - ARENDAL CN 014 - - - - ARENDAL CN 015 - - - - ARRIE_HVDC - - - - AURLAND - - - - BLAFALLI - - - - DAGALI - - - - DANNEBO_HVDC - - - - EIDFJORD - - - - ESTLINK_HVDC - - - - FEDA_HVDC - - - - FORSMARK - - - - GEILO - - - - GRUNDFORS - - - - HAGAFOSS - - - - HASLE - - - - HELSINKI - - - - HJALTA - - - - HOGASEN - - - - JARPSTROMMEN - - - - KAGGEFOSS - - - - KARLSH_HVDC - - - - KONGSBERG - - - - KRISTIAN CN 001 - - - - KRISTIAN CN 002 - - - - KRISTIAN CN 003 - - - - KRISTIAN CN 004 - - - - KRISTIAN CN 005 - - - - KRISTIAN CN 006 - - - - KRISTIAN CN 007 - - - - KRISTIAN CN 008 - - - - KRISTIAN CN 009 - - - - KRISTIAN CN 010 - - - - KRISTIAN CN 011 - - - - KRISTIAN CN 012 - - - - KRISTIAN CN 013 - - - - KRISTIAN CN 014 - - - - KRISTIAN CN 015 - - - - KRISTIAN CN 016 - - - - KRISTIAN CN 017 - - - - KRISTIAN CN 018 - - - - KRISTIAN CN 019 - - - - KRISTIAN CN 020 - - - - KRISTIAN CN 021 - - - - KRISTIAN CN 022 - - - - KRISTIAN CN 023 - - - - KRISTIAN CN 024 - - - - KRISTIAN CN 025 - - - - KRISTIAN CN 026 - - - - KRISTIAN CN 027 - - - - KRISTIAN CN 028 - - - - KRISTIAN CN 029 - - - - KRISTIAN CN 030 - - - - KRISTIAN CN 031 - - - - KRISTIAN CN 032 - - - - KRISTIA_HVDC - - - - KVILLDAL - - - - MALMO - - - - OFOTEN - - - - OSKARSHAMN - - - - OSLO1 - - - - OSLO2 - - - - OULU - - - - PORJUS - - - - RINGHALS - - - - ROSSAGA - - - - SANDEFJORD - - - - SIMA - - - - SKIEN - - - - STAVANGER - - - - STENKU_HVDC - - - - SYLLING - - - - TENHULT - - - - TRETTEN - - - - TRONDHEIM - - - - VYBORG_HVDC - - - - - FI CA - - - - - NO CA - - - - - SE CA - - - - - CAGU_FI_HELSINKIG1 - SMPF_7000_1 - - - - - - CAGU_NO1_ASKER G1 - SMPF_5500_1 - - - - - - CAGU_NO2_KRISTING1 - SMPF_5600_1 - - - - - - CAGU_NO3_TRONDHEIG1 - SMPF_6500_1 - - - - - - CAGU_NO4_MO G1 - SMPF_6700_1 - - - - - - CAGU_NO5_SIMA G1 - SMPF_5300_1 - - - - - - CAGU_SE1_PORJUS G1 - SMPF_3115_1 - - - - - - CAGU_SE2_GROUNDFORG1 - SMPF_3249_1 - - - - - - CAGU_SE4_MALMO G1 - SMPF_8500_1 - - - - - - SMPF_SE3_OSKARSHAG1 - SMPF_3300_1 - - - - 1810 - - - CL_PATL_-10degreeCelsius - CurrentLimit PATL Negative 10 degree Celsius - 1810 - - - 4010 - - - CL_PATL_-10degreeCelsius - CurrentLimit PATL Negative 10 degree Celsius - 4010 - - - 1815 - - - CL_PATL_-15degreeCelsius - CurrentLimit PATL Negative 15 degree Celsius - 1815 - - - 4115 - - - CL_PATL_-15degreeCelsius - CurrentLimit PATL Negative 15 degree Celsius - 4115 - - - 4220 - - - CL_PATL_-20degreeCelsius - CurrentLimit PATL Negative 20 degree Celsius - 4220 - - - 1820 - - - CL_PATL_-20degreeCelsius - CurrentLimit PATL Negative 20 degree Celsius - 1820 - - - 4325 - - - CL_PATL_-25degreeCelsius - CurrentLimit PATL Negative 25 degree Celsius - 4325 - - - 1825 - - - CL_PATL_-25degreeCelsius - CurrentLimit PATL Negative 25 degree Celsius - 1825 - - - 4430 - - - CL_PATL_-30degreeCelsius - CurrentLimit PATL Negative 30 degree Celsius - 4430 - - - 1830 - - - CL_PATL_-30degreeCelsius - CurrentLimit PATL Negative 30 degree Celsius - 1830 - - - 6736 - - - CL_PATL_-30degreeCelsius - CurrentLimit PATL Negative 30 degree Celsius - 6736 - - - 1805 - - - CL_PATL_-5degreeCelsius - CurrentLimit PATL Negative 5 degree Celsius - 1805 - - - 3905 - - - CL_PATL_-5degreeCelsius - CurrentLimit PATL Negative 5 degree Celsius - 3905 - - - 1800 - - - CL_PATL_0degreeCelsius - CurrentLimit PATL 0 degree Celsius - 1800 - - - 3800 - - - CL_PATL_0degreeCelsius - CurrentLimit PATL 0 degree Celsius - 3800 - - - 3610 - - - CL_PATL_10degreeCelsius - CurrentLimit PATL 10 degree Celsius - 3610 - - - 1790 - - - CL_PATL_10degreeCelsius - CurrentLimit PATL 10 degree Celsius - 1790 - - - 3515 - - - CL_PATL_15degreeCelsius - CurrentLimit PATL 15 degree Celsius - 3515 - - - 1785 - - - CL_PATL_15degreeCelsius - CurrentLimit PATL 15 degree Celsius - 1785 - - - 1780 - - - CL_PATL_20degreeCelsius - CurrentLimit PATL 20 degree Celsius - 1780 - - - 3420 - - - CL_PATL_20degreeCelsius - CurrentLimit PATL 20 degree Celsius - 3420 - - - 4812 - - - CL_PATL_20degreeCelsius - CurrentLimit PATL 20 degree Celsius - 4812 - - - 3325 - - - CL_PATL_25degreeCelsius - CurrentLimit PATL 25 degree Celsius - 3325 - - - 1775 - - - CL_PATL_25degreeCelsius - CurrentLimit PATL 25 degree Celsius - 1775 - - - 1770 - - - CL_PATL_30degreeCelsius - CurrentLimit PATL 30 degree Celsius - 1770 - - - 3230 - - - CL_PATL_30degreeCelsius - CurrentLimit PATL 30 degree Celsius - 3230 - - - 1795 - - - CL_PATL_5degreeCelsius - CurrentLimit PATL 5 degree Celsius - 1795 - - - 3705 - - - CL_PATL_5degreeCelsius - CurrentLimit PATL 5 degree Celsius - 3705 - - - 7910 - - - CL_TATL_-10degreeCelsius - CurrentLimit TATL Negative 10 degree Celsius - 7910 - - - 2510 - - - CL_TATL_-10degreeCelsius - CurrentLimit TATL Negative 10 degree Celsius - 2510 - - - 2515 - - - CL_TATL_-15degreeCelsius - CurrentLimit TATL Negative 15 degree Celsius - 2515 - - - 8015 - - - CL_TATL_-15degreeCelsius - CurrentLimit TATL Negative 15 degree Celsius - 8015 - - - 8120 - - - CL_TATL_-20degreeCelsius - CurrentLimit TATL Negative 20 degree Celsius - 8120 - - - 2520 - - - CL_TATL_-20degreeCelsius - CurrentLimit TATL Negative 20 degree Celsius - 2520 - - - 8225 - - - CL_TATL_-25degreeCelsius - CurrentLimit TATL Negative 25 degree Celsius - 8225 - - - 2525 - - - CL_TATL_-25degreeCelsius - CurrentLimit TATL Negative 25 degree Celsius - 2525 - - - 8330 - - - CL_TATL_-30degreeCelsius - CurrentLimit TATL Negative 30 degree Celsius - 8330 - - - 2530 - - - CL_TATL_-30degreeCelsius - CurrentLimit TATL Negative 30 degree Celsius - 2530 - - - 7880 - - - CL_TATL_-30degreeCelsius - CurrentLimit TATL Negative 30 degree Celsius - 7880 - - - 7805 - - - CL_TATL_-5degreeCelsius - CurrentLimit TATL Negative 5 degree Celsius - 7805 - - - 2505 - - - CL_TATL_-5degreeCelsius - CurrentLimit TATL Negative 5 degree Celsius - 2505 - - - 2500 - - - CL_TATL_0degreeCelsius - CurrentLimit TATL 0 degree Celsius - 2500 - - - 7700 - - - CL_TATL_0degreeCelsius - CurrentLimit TATL 0 degree Celsius - 7700 - - - 7510 - - - CL_TATL_10degreeCelsius - CurrentLimit TATL 10 degree Celsius - 7510 - - - 2490 - - - CL_TATL_10degreeCelsius - CurrentLimit TATL 10 degree Celsius - 2490 - - - 7415 - - - CL_TATL_15degreeCelsius - CurrentLimit TATL 15 degree Celsius - 7415 - - - 2485 - - - CL_TATL_15degreeCelsius - CurrentLimit TATL 15 degree Celsius - 2485 - - - 2480 - - - CL_TATL_20degreeCelsius - CurrentLimit TATL 20 degree Celsius - 2480 - - - 7320 - - - CL_TATL_20degreeCelsius - CurrentLimit TATL 20 degree Celsius - 7320 - - - 5774 - - - CL_TATL_20degreeCelsius - CurrentLimit TATL 20 degree Celsius - 5774 - - - 2475 - - - CL_TATL_25degreeCelsius - CurrentLimit TATL 25 degree Celsius - 2475 - - - 7225 - - - CL_TATL_25degreeCelsius - CurrentLimit TATL 25 degree Celsius - 7225 - - - 2470 - - - CL_TATL_30degreeCelsius - CurrentLimit TATL 30 degree Celsius - 2470 - - - 7130 - - - CL_TATL_30degreeCelsius - CurrentLimit TATL 30 degree Celsius - 7130 - - - 2495 - - - CL_TATL_5degreeCelsius - CurrentLimit TATL 5 degree Celsius - 2495 - - - 7605 - - - CL_TATL_5degreeCelsius - CurrentLimit TATL 5 degree Celsius - 7605 - - - 2061.965247 - - - RATEA - 2061.965247 - - - 2061.965247 - - - RATEA - 2061.965247 - - - 2749.286996 - - - RATEA - 2749.286996 - - - 2749.286996 - - - RATEA - 2749.286996 - - - 1649.572198 - - - RATEA - 1649.572198 - - - 1649.572198 - - - RATEA - 1649.572198 - - - 1099.714798 - - - RATEA - 1099.714798 - - - 1099.714798 - - - RATEA - 1099.714798 - - - 1512.107848 - - - RATEA - 1512.107848 - - - 1512.107848 - - - RATEA - 1512.107848 - - - 1512.107848 - - - RATEA - 1512.107848 - - - 1512.107848 - - - RATEA - 1512.107848 - - - 2749.286996 - - - RATEA - 2749.286996 - - - 2749.286996 - - - RATEA - 2749.286996 - - - 1649.572198 - - - RATEA - 1649.572198 - - - 1649.572198 - - - RATEA - 1649.572198 - - - 1649.572198 - - - RATEA - 1649.572198 - - - 1649.572198 - - - RATEA - 1649.572198 - - - 2749.286996 - - - RATEA - 2749.286996 - - - 2749.286996 - - - RATEA - 2749.286996 - - - 1237.179148 - - - RATEA - 1237.179148 - - - 1237.179148 - - - RATEA - 1237.179148 - - - 1649.572198 - - - RATEA - 1649.572198 - - - 1649.572198 - - - RATEA - 1649.572198 - - - 1787.036547 - - - RATEA - 1787.036547 - - - 1787.036547 - - - RATEA - 1787.036547 - - - 1512.107848 - - - RATEA - 1512.107848 - - - 1512.107848 - - - RATEA - 1512.107848 - - - 1168.446973 - - - RATEA - 1168.446973 - - - 1168.446973 - - - RATEA - 1168.446973 - - - 1787.036547 - - - RATEA - 1787.036547 - - - 1787.036547 - - - RATEA - 1787.036547 - - - 1099.714798 - - - RATEA - 1099.714798 - - - 1099.714798 - - - RATEA - 1099.714798 - - - 1787.036547 - - - RATEA - 1787.036547 - - - 1787.036547 - - - RATEA - 1787.036547 - - - 2611.822646 - - - RATEA - 2611.822646 - - - 2611.822646 - - - RATEA - 2611.822646 - - - 3464.101615 - - - RATEA - 3464.101615 - - - 3464.101615 - - - RATEA - 3464.101615 - - - 2611.822646 - - - RATEA - 2611.822646 - - - 2611.822646 - - - RATEA - 2611.822646 - - - 2611.822646 - - - RATEA - 2611.822646 - - - 2611.822646 - - - RATEA - 2611.822646 - - - 2611.822646 - - - RATEA - 2611.822646 - - - 2611.822646 - - - RATEA - 2611.822646 - - - 2611.822646 - - - RATEA - 2611.822646 - - - 2611.822646 - - - RATEA - 2611.822646 - - - 2199.429597 - - - RATEA - 2199.429597 - - - 2199.429597 - - - RATEA - 2199.429597 - - - 2061.965247 - - - RATEA - 2061.965247 - - - 2061.965247 - - - RATEA - 2061.965247 - - - 1512.107848 - - - RATEA - 1512.107848 - - - 1512.107848 - - - RATEA - 1512.107848 - - - 577.350269 - - - RATEA - 577.350269 - - - 577.350269 - - - RATEA - 577.350269 - - - 1347.150628 - - - RATEA - 1347.150628 - - - 1347.150628 - - - RATEA - 1347.150628 - - - 1539.600718 - - - RATEA - 1539.600718 - - - 1539.600718 - - - RATEA - 1539.600718 - - - 2336.893947 - - - RATEA - 2336.893947 - - - 2336.893947 - - - RATEA - 2336.893947 - - - 1855.768722 - - - RATEA - 1855.768722 - - - 1855.768722 - - - RATEA - 1855.768722 - - - 2749.286996 - - - RATEA - 2749.286996 - - - 2749.286996 - - - RATEA - 2749.286996 - - - 2749.286996 - - - RATEA - 2749.286996 - - - 2749.286996 - - - RATEA - 2749.286996 - - - 2061.965247 - - - RATEA - 2061.965247 - - - 2061.965247 - - - RATEA - 2061.965247 - - - 1993.233072 - - - RATEA - 1993.233072 - - - 1993.233072 - - - RATEA - 1993.233072 - - - 2061.965247 - - - RATEA - 2061.965247 - - - 2061.965247 - - - RATEA - 2061.965247 - - - 2061.965247 - - - RATEA - 2061.965247 - - - 2061.965247 - - - RATEA - 2061.965247 - - - 3656.551705 - - - RATEA - 3656.551705 - - - 3656.551705 - - - RATEA - 3656.551705 - - - 1718.304373 - - - RATEA - 1718.304373 - - - 1718.304373 - - - RATEA - 1718.304373 - - - 2749.286996 - - - RATEA - 2749.286996 - - - 2749.286996 - - - RATEA - 2749.286996 - - - 2474.358297 - - - RATEA - 2474.358297 - - - 2474.358297 - - - RATEA - 2474.358297 - - - 2680.554821 - - - RATEA - 2680.554821 - - - 2680.554821 - - - RATEA - 2680.554821 - - - 1855.768722 - - - RATEA - 1855.768722 - - - 1855.768722 - - - RATEA - 1855.768722 - - - 3656.551705 - - - RATEA - 3656.551705 - - - 3656.551705 - - - RATEA - 3656.551705 - - - 1539.600718 - - - RATEA - 1539.600718 - - - 1539.600718 - - - RATEA - 1539.600718 - - - 2061.965247 - - - RATEA - 2061.965247 - - - 2061.965247 - - - RATEA - 2061.965247 - - - 3024.215696 - - - RATEA - 3024.215696 - - - 3024.215696 - - - RATEA - 3024.215696 - - - 1718.304373 - - - RATEA - 1718.304373 - - - 1718.304373 - - - RATEA - 1718.304373 - - - 2886.751346 - - - RATEA - 2886.751346 - - - 2886.751346 - - - RATEA - 2886.751346 - - - 1539.600718 - - - RATEA - 1539.600718 - - - 1539.600718 - - - RATEA - 1539.600718 - - - 2309.401077 - - - RATEA - 2309.401077 - - - 2309.401077 - - - RATEA - 2309.401077 - - - 4811.252243 - - - RATEA - 4811.252243 - - - 4811.252243 - - - RATEA - 4811.252243 - - - 1924.500897 - - - RATEA - 1924.500897 - - - 1924.500897 - - - RATEA - 1924.500897 - - - 4041.451884 - - - RATEA - 4041.451884 - - - 4041.451884 - - - RATEA - 4041.451884 - - - 4811.252243 - - - RATEA - 4811.252243 - - - 4811.252243 - - - RATEA - 4811.252243 - - - 3752.77675 - - - RATEA - 3752.77675 - - - 3752.77675 - - - RATEA - 3752.77675 - - - 1539.600718 - - - RATEA - 1539.600718 - - - 1539.600718 - - - RATEA - 1539.600718 - - - 1924.500897 - - - RATEA - 1924.500897 - - - 1924.500897 - - - RATEA - 1924.500897 - - - 3436.608745 - - - RATEA - 3436.608745 - - - 3436.608745 - - - RATEA - 3436.608745 - - - 1374.643498 - - - RATEA - 1374.643498 - - - 1374.643498 - - - RATEA - 1374.643498 - - - 1429.629238 - - - RATEA - 1429.629238 - - - 1429.629238 - - - RATEA - 1429.629238 - - - 1429.629238 - - - RATEA - 1429.629238 - - - 1429.629238 - - - RATEA - 1429.629238 - - - 1649.572198 - - - RATEA - 1649.572198 - - - 1649.572198 - - - RATEA - 1649.572198 - - - 1374.643498 - - - RATEA - 1374.643498 - - - 1374.643498 - - - RATEA - 1374.643498 - - - 1374.643498 - - - RATEA - 1374.643498 - - - 1374.643498 - - - RATEA - 1374.643498 - - - 3849.001795 - - - RATEA - 3849.001795 - - - 2749.286996 - - - RATEA - 2749.286996 - - - 3849.001795 - - - RATEA - 3849.001795 - - - 2749.286996 - - - RATEA - 2749.286996 - - - 2749.286996 - - - RATEA - 2749.286996 - - - 8553.337321 - - - RATEA - 8553.337321 - - - 2749.286996 - - - RATEA - 2749.286996 - - - 3849.001795 - - - RATEA - 3849.001795 - - - 7698.003589 - - - RATEA - 7698.003589 - - - 5498.573992 - - - RATEA - 5498.573992 - - - 3849.001795 - - - RATEA - 3849.001795 - - - 2749.286996 - - - RATEA - 2749.286996 - - - 4233.901974 - - - RATEA - 4233.901974 - - - 3024.215696 - - - RATEA - 3024.215696 - - - 3849.001795 - - - RATEA - 3849.001795 - - - 2749.286996 - - - RATEA - 2749.286996 - - - 3849.001795 - - - RATEA - 3849.001795 - - - 2749.286996 - - - RATEA - 2749.286996 - - - 3849.001795 - - - RATEA - 3849.001795 - - - 2749.286996 - - - RATEA - 2749.286996 - - - 4233.901974 - - - RATEA - 4233.901974 - - - 3024.215696 - - - RATEA - 3024.215696 - - - 3849.001795 - - - RATEA - 3849.001795 - - - 2749.286996 - - - RATEA - 2749.286996 - - - 1649.572198 - - - RATEA - 1649.572198 - - - 1649.572198 - - - RATEA - 1649.572198 - - - 2474.358297 - - - RATEB - 2474.358297 - - - 2474.358297 - - - RATEB - 2474.358297 - - - 3436.608745 - - - RATEB - 3436.608745 - - - 3436.608745 - - - RATEB - 3436.608745 - - - 2199.429597 - - - RATEB - 2199.429597 - - - 2199.429597 - - - RATEB - 2199.429597 - - - 1787.036547 - - - RATEB - 1787.036547 - - - 1787.036547 - - - RATEB - 1787.036547 - - - 1787.036547 - - - RATEB - 1787.036547 - - - 1787.036547 - - - RATEB - 1787.036547 - - - 1787.036547 - - - RATEB - 1787.036547 - - - 1787.036547 - - - RATEB - 1787.036547 - - - 3436.608745 - - - RATEB - 3436.608745 - - - 3436.608745 - - - RATEB - 3436.608745 - - - 2749.286996 - - - RATEB - 2749.286996 - - - 2749.286996 - - - RATEB - 2749.286996 - - - 2749.286996 - - - RATEB - 2749.286996 - - - 2749.286996 - - - RATEB - 2749.286996 - - - 3436.608745 - - - RATEB - 3436.608745 - - - 3436.608745 - - - RATEB - 3436.608745 - - - 1787.036547 - - - RATEB - 1787.036547 - - - 1787.036547 - - - RATEB - 1787.036547 - - - 2749.286996 - - - RATEB - 2749.286996 - - - 2749.286996 - - - RATEB - 2749.286996 - - - 2474.358297 - - - RATEB - 2474.358297 - - - 2474.358297 - - - RATEB - 2474.358297 - - - 1787.036547 - - - RATEB - 1787.036547 - - - 1787.036547 - - - RATEB - 1787.036547 - - - 1374.643498 - - - RATEB - 1374.643498 - - - 1374.643498 - - - RATEB - 1374.643498 - - - 2061.965247 - - - RATEB - 2061.965247 - - - 2061.965247 - - - RATEB - 2061.965247 - - - 1512.107848 - - - RATEB - 1512.107848 - - - 1512.107848 - - - RATEB - 1512.107848 - - - 2474.358297 - - - RATEB - 2474.358297 - - - 2474.358297 - - - RATEB - 2474.358297 - - - 3024.215696 - - - RATEB - 3024.215696 - - - 3024.215696 - - - RATEB - 3024.215696 - - - 4426.352064 - - - RATEB - 4426.352064 - - - 4426.352064 - - - RATEB - 4426.352064 - - - 3024.215696 - - - RATEB - 3024.215696 - - - 3024.215696 - - - RATEB - 3024.215696 - - - 3024.215696 - - - RATEB - 3024.215696 - - - 3024.215696 - - - RATEB - 3024.215696 - - - 3024.215696 - - - RATEB - 3024.215696 - - - 3024.215696 - - - RATEB - 3024.215696 - - - 3024.215696 - - - RATEB - 3024.215696 - - - 3024.215696 - - - RATEB - 3024.215696 - - - 2749.286996 - - - RATEB - 2749.286996 - - - 2749.286996 - - - RATEB - 2749.286996 - - - 2749.286996 - - - RATEB - 2749.286996 - - - 2749.286996 - - - RATEB - 2749.286996 - - - 1787.036547 - - - RATEB - 1787.036547 - - - 1787.036547 - - - RATEB - 1787.036547 - - - 769.800359 - - - RATEB - 769.800359 - - - 769.800359 - - - RATEB - 769.800359 - - - 1539.600718 - - - RATEB - 1539.600718 - - - 1539.600718 - - - RATEB - 1539.600718 - - - 1732.050808 - - - RATEB - 1732.050808 - - - 1732.050808 - - - RATEB - 1732.050808 - - - 2474.358297 - - - RATEB - 2474.358297 - - - 2474.358297 - - - RATEB - 2474.358297 - - - 2199.429597 - - - RATEB - 2199.429597 - - - 2199.429597 - - - RATEB - 2199.429597 - - - 3024.215696 - - - RATEB - 3024.215696 - - - 3024.215696 - - - RATEB - 3024.215696 - - - 3024.215696 - - - RATEB - 3024.215696 - - - 3024.215696 - - - RATEB - 3024.215696 - - - 2474.358297 - - - RATEB - 2474.358297 - - - 2474.358297 - - - RATEB - 2474.358297 - - - 2336.893947 - - - RATEB - 2336.893947 - - - 2336.893947 - - - RATEB - 2336.893947 - - - 2405.626122 - - - RATEB - 2405.626122 - - - 2405.626122 - - - RATEB - 2405.626122 - - - 2474.358297 - - - RATEB - 2474.358297 - - - 2474.358297 - - - RATEB - 2474.358297 - - - 4233.901974 - - - RATEB - 4233.901974 - - - 4233.901974 - - - RATEB - 4233.901974 - - - 2061.965247 - - - RATEB - 2061.965247 - - - 2061.965247 - - - RATEB - 2061.965247 - - - 3024.215696 - - - RATEB - 3024.215696 - - - 3024.215696 - - - RATEB - 3024.215696 - - - 2749.286996 - - - RATEB - 2749.286996 - - - 2749.286996 - - - RATEB - 2749.286996 - - - 3024.215696 - - - RATEB - 3024.215696 - - - 3024.215696 - - - RATEB - 3024.215696 - - - 1924.500897 - - - RATEB - 1924.500897 - - - 1924.500897 - - - RATEB - 1924.500897 - - - 4233.901974 - - - RATEB - 4233.901974 - - - 4233.901974 - - - RATEB - 4233.901974 - - - 1732.050808 - - - RATEB - 1732.050808 - - - 1732.050808 - - - RATEB - 1732.050808 - - - 2474.358297 - - - RATEB - 2474.358297 - - - 2474.358297 - - - RATEB - 2474.358297 - - - 3299.144395 - - - RATEB - 3299.144395 - - - 3299.144395 - - - RATEB - 3299.144395 - - - 2061.965247 - - - RATEB - 2061.965247 - - - 2061.965247 - - - RATEB - 2061.965247 - - - 3436.608745 - - - RATEB - 3436.608745 - - - 3436.608745 - - - RATEB - 3436.608745 - - - 1732.050808 - - - RATEB - 1732.050808 - - - 1732.050808 - - - RATEB - 1732.050808 - - - 2886.751346 - - - RATEB - 2886.751346 - - - 2886.751346 - - - RATEB - 2886.751346 - - - 5773.502692 - - - RATEB - 5773.502692 - - - 5773.502692 - - - RATEB - 5773.502692 - - - 2309.401077 - - - RATEB - 2309.401077 - - - 2309.401077 - - - RATEB - 2309.401077 - - - 4811.252243 - - - RATEB - 4811.252243 - - - 4811.252243 - - - RATEB - 4811.252243 - - - 5773.502692 - - - RATEB - 5773.502692 - - - 5773.502692 - - - RATEB - 5773.502692 - - - 4233.901974 - - - RATEB - 4233.901974 - - - 4233.901974 - - - RATEB - 4233.901974 - - - 1732.050808 - - - RATEB - 1732.050808 - - - 1732.050808 - - - RATEB - 1732.050808 - - - 2309.401077 - - - RATEB - 2309.401077 - - - 2309.401077 - - - RATEB - 2309.401077 - - - 4123.930494 - - - RATEB - 4123.930494 - - - 4123.930494 - - - RATEB - 4123.930494 - - - 1649.572198 - - - RATEB - 1649.572198 - - - 1649.572198 - - - RATEB - 1649.572198 - - - 1649.572198 - - - RATEB - 1649.572198 - - - 1649.572198 - - - RATEB - 1649.572198 - - - 1649.572198 - - - RATEB - 1649.572198 - - - 1649.572198 - - - RATEB - 1649.572198 - - - 2061.965247 - - - RATEB - 2061.965247 - - - 2061.965247 - - - RATEB - 2061.965247 - - - 1649.572198 - - - RATEB - 1649.572198 - - - 1649.572198 - - - RATEB - 1649.572198 - - - 1649.572198 - - - RATEB - 1649.572198 - - - 1649.572198 - - - RATEB - 1649.572198 - - - 5773.502692 - - - RATEB - 5773.502692 - - - 4123.930494 - - - RATEB - 4123.930494 - - - 5773.502692 - - - RATEB - 5773.502692 - - - 4123.930494 - - - RATEB - 4123.930494 - - - 4123.930494 - - - RATEB - 4123.930494 - - - 12830.005982 - - - RATEB - 12830.005982 - - - 4123.930494 - - - RATEB - 4123.930494 - - - 5773.502692 - - - RATEB - 5773.502692 - - - 8660.254038 - - - RATEB - 8660.254038 - - - 6185.895741 - - - RATEB - 6185.895741 - - - 5773.502692 - - - RATEB - 5773.502692 - - - 4123.930494 - - - RATEB - 4123.930494 - - - 5773.502692 - - - RATEB - 5773.502692 - - - 4123.930494 - - - RATEB - 4123.930494 - - - 5773.502692 - - - RATEB - 5773.502692 - - - 4123.930494 - - - RATEB - 4123.930494 - - - 5773.502692 - - - RATEB - 5773.502692 - - - 4123.930494 - - - RATEB - 4123.930494 - - - 5773.502692 - - - RATEB - 5773.502692 - - - 4123.930494 - - - RATEB - 4123.930494 - - - 5773.502692 - - - RATEB - 5773.502692 - - - 4123.930494 - - - RATEB - 4123.930494 - - - 5773.502692 - - - RATEB - 5773.502692 - - - 4123.930494 - - - RATEB - 4123.930494 - - - 2749.286996 - - - RATEB - 2749.286996 - - - 2749.286996 - - - RATEB - 2749.286996 - - - 2886.751346 - - - RATEC - 2886.751346 - - - 2886.751346 - - - RATEC - 2886.751346 - - - 4123.930494 - - - RATEC - 4123.930494 - - - 4123.930494 - - - RATEC - 4123.930494 - - - 2474.358297 - - - RATEC - 2474.358297 - - - 2474.358297 - - - RATEC - 2474.358297 - - - 2199.429597 - - - RATEC - 2199.429597 - - - 2199.429597 - - - RATEC - 2199.429597 - - - 1924.500897 - - - RATEC - 1924.500897 - - - 1924.500897 - - - RATEC - 1924.500897 - - - 1924.500897 - - - RATEC - 1924.500897 - - - 1924.500897 - - - RATEC - 1924.500897 - - - 4123.930494 - - - RATEC - 4123.930494 - - - 4123.930494 - - - RATEC - 4123.930494 - - - 3436.608745 - - - RATEC - 3436.608745 - - - 3436.608745 - - - RATEC - 3436.608745 - - - 3436.608745 - - - RATEC - 3436.608745 - - - 3436.608745 - - - RATEC - 3436.608745 - - - 4123.930494 - - - RATEC - 4123.930494 - - - 4123.930494 - - - RATEC - 4123.930494 - - - 2199.429597 - - - RATEC - 2199.429597 - - - 2199.429597 - - - RATEC - 2199.429597 - - - 3436.608745 - - - RATEC - 3436.608745 - - - 3436.608745 - - - RATEC - 3436.608745 - - - 2749.286996 - - - RATEC - 2749.286996 - - - 2749.286996 - - - RATEC - 2749.286996 - - - 1924.500897 - - - RATEC - 1924.500897 - - - 1924.500897 - - - RATEC - 1924.500897 - - - 1512.107848 - - - RATEC - 1512.107848 - - - 1512.107848 - - - RATEC - 1512.107848 - - - 2336.893947 - - - RATEC - 2336.893947 - - - 2336.893947 - - - RATEC - 2336.893947 - - - 1787.036547 - - - RATEC - 1787.036547 - - - 1787.036547 - - - RATEC - 1787.036547 - - - 2749.286996 - - - RATEC - 2749.286996 - - - 2749.286996 - - - RATEC - 2749.286996 - - - 3574.073095 - - - RATEC - 3574.073095 - - - 3574.073095 - - - RATEC - 3574.073095 - - - 4811.252243 - - - RATEC - 4811.252243 - - - 4811.252243 - - - RATEC - 4811.252243 - - - 3574.073095 - - - RATEC - 3574.073095 - - - 3574.073095 - - - RATEC - 3574.073095 - - - 3574.073095 - - - RATEC - 3574.073095 - - - 3574.073095 - - - RATEC - 3574.073095 - - - 3574.073095 - - - RATEC - 3574.073095 - - - 3574.073095 - - - RATEC - 3574.073095 - - - 3574.073095 - - - RATEC - 3574.073095 - - - 3574.073095 - - - RATEC - 3574.073095 - - - 3436.608745 - - - RATEC - 3436.608745 - - - 3436.608745 - - - RATEC - 3436.608745 - - - 3436.608745 - - - RATEC - 3436.608745 - - - 3436.608745 - - - RATEC - 3436.608745 - - - 1924.500897 - - - RATEC - 1924.500897 - - - 1924.500897 - - - RATEC - 1924.500897 - - - 962.250449 - - - RATEC - 962.250449 - - - 962.250449 - - - RATEC - 962.250449 - - - 1732.050808 - - - RATEC - 1732.050808 - - - 1732.050808 - - - RATEC - 1732.050808 - - - 1828.275852 - - - RATEC - 1828.275852 - - - 1828.275852 - - - RATEC - 1828.275852 - - - 2611.822646 - - - RATEC - 2611.822646 - - - 2611.822646 - - - RATEC - 2611.822646 - - - 2474.358297 - - - RATEC - 2474.358297 - - - 2474.358297 - - - RATEC - 2474.358297 - - - 3436.608745 - - - RATEC - 3436.608745 - - - 3436.608745 - - - RATEC - 3436.608745 - - - 3299.144395 - - - RATEC - 3299.144395 - - - 3299.144395 - - - RATEC - 3299.144395 - - - 2749.286996 - - - RATEC - 2749.286996 - - - 2749.286996 - - - RATEC - 2749.286996 - - - 2749.286996 - - - RATEC - 2749.286996 - - - 2749.286996 - - - RATEC - 2749.286996 - - - 2749.286996 - - - RATEC - 2749.286996 - - - 2749.286996 - - - RATEC - 2749.286996 - - - 2749.286996 - - - RATEC - 2749.286996 - - - 2749.286996 - - - RATEC - 2749.286996 - - - 5003.702333 - - - RATEC - 5003.702333 - - - 5003.702333 - - - RATEC - 5003.702333 - - - 2336.893947 - - - RATEC - 2336.893947 - - - 2336.893947 - - - RATEC - 2336.893947 - - - 3436.608745 - - - RATEC - 3436.608745 - - - 3436.608745 - - - RATEC - 3436.608745 - - - 2955.483521 - - - RATEC - 2955.483521 - - - 2955.483521 - - - RATEC - 2955.483521 - - - 3299.144395 - - - RATEC - 3299.144395 - - - 3299.144395 - - - RATEC - 3299.144395 - - - 2061.965247 - - - RATEC - 2061.965247 - - - 2061.965247 - - - RATEC - 2061.965247 - - - 5003.702333 - - - RATEC - 5003.702333 - - - 5003.702333 - - - RATEC - 5003.702333 - - - 1828.275852 - - - RATEC - 1828.275852 - - - 1828.275852 - - - RATEC - 1828.275852 - - - 2749.286996 - - - RATEC - 2749.286996 - - - 2749.286996 - - - RATEC - 2749.286996 - - - 3574.073095 - - - RATEC - 3574.073095 - - - 3574.073095 - - - RATEC - 3574.073095 - - - 2336.893947 - - - RATEC - 2336.893947 - - - 2336.893947 - - - RATEC - 2336.893947 - - - 4123.930494 - - - RATEC - 4123.930494 - - - 4123.930494 - - - RATEC - 4123.930494 - - - 1828.275852 - - - RATEC - 1828.275852 - - - 1828.275852 - - - RATEC - 1828.275852 - - - 3464.101615 - - - RATEC - 3464.101615 - - - 3464.101615 - - - RATEC - 3464.101615 - - - 6735.753141 - - - RATEC - 6735.753141 - - - 6735.753141 - - - RATEC - 6735.753141 - - - 2886.751346 - - - RATEC - 2886.751346 - - - 2886.751346 - - - RATEC - 2886.751346 - - - 5581.052602 - - - RATEC - 5581.052602 - - - 5581.052602 - - - RATEC - 5581.052602 - - - 6735.753141 - - - RATEC - 6735.753141 - - - 6735.753141 - - - RATEC - 6735.753141 - - - 4618.802154 - - - RATEC - 4618.802154 - - - 4618.802154 - - - RATEC - 4618.802154 - - - 1828.275852 - - - RATEC - 1828.275852 - - - 1828.275852 - - - RATEC - 1828.275852 - - - 2501.851166 - - - RATEC - 2501.851166 - - - 2501.851166 - - - RATEC - 2501.851166 - - - 4811.252243 - - - RATEC - 4811.252243 - - - 4811.252243 - - - RATEC - 4811.252243 - - - 2061.965247 - - - RATEC - 2061.965247 - - - 2061.965247 - - - RATEC - 2061.965247 - - - 2061.965247 - - - RATEC - 2061.965247 - - - 2061.965247 - - - RATEC - 2061.965247 - - - 2061.965247 - - - RATEC - 2061.965247 - - - 2061.965247 - - - RATEC - 2061.965247 - - - 2336.893947 - - - RATEC - 2336.893947 - - - 2336.893947 - - - RATEC - 2336.893947 - - - 2061.965247 - - - RATEC - 2061.965247 - - - 2061.965247 - - - RATEC - 2061.965247 - - - 2061.965247 - - - RATEC - 2061.965247 - - - 2061.965247 - - - RATEC - 2061.965247 - - - 7698.003589 - - - RATEC - 7698.003589 - - - 5498.573992 - - - RATEC - 5498.573992 - - - 7698.003589 - - - RATEC - 7698.003589 - - - 5498.573992 - - - RATEC - 5498.573992 - - - 5498.573992 - - - RATEC - 5498.573992 - - - 17106.674643 - - - RATEC - 17106.674643 - - - 5498.573992 - - - RATEC - 5498.573992 - - - 7698.003589 - - - RATEC - 7698.003589 - - - 9622.504486 - - - RATEC - 9622.504486 - - - 6873.21749 - - - RATEC - 6873.21749 - - - 7698.003589 - - - RATEC - 7698.003589 - - - 5498.573992 - - - RATEC - 5498.573992 - - - 7698.003589 - - - RATEC - 7698.003589 - - - 5498.573992 - - - RATEC - 5498.573992 - - - 7698.003589 - - - RATEC - 7698.003589 - - - 5498.573992 - - - RATEC - 5498.573992 - - - 7698.003589 - - - RATEC - 7698.003589 - - - 5498.573992 - - - RATEC - 5498.573992 - - - 7698.003589 - - - RATEC - 7698.003589 - - - 5498.573992 - - - RATEC - 5498.573992 - - - 7698.003589 - - - RATEC - 7698.003589 - - - 5498.573992 - - - RATEC - 5498.573992 - - - 7698.003589 - - - RATEC - 7698.003589 - - - 5498.573992 - - - RATEC - 5498.573992 - - - 3436.608745 - - - RATEC - 3436.608745 - - - 3436.608745 - - - RATEC - 3436.608745 - - - ARENDAL 300AS1 AD_S - - false - false - - Arendal 300 Asker 1 A Disconnector - - - ARENDAL 300AS1 BD_S - - false - true - - Arendal 300 Asker 1 B Disconnector - - - ARENDAL 300KR1 AD_S - - false - false - - Arendal 300 Kristiansand 1 A Disconnector - - - ARENDAL 300KR1 BD_S - - false - true - - Arendal 300 Kristiansand 1 B Disconnector - - - ARENDAL 300KR2 AD_S - - false - false - - Arendal 300 Kristiansand 2 A Disconnector - - - ARENDAL 300KR2 BD_S - - false - true - - Arendal 300 Kristiansand 2 B Disconnector - - - KRISTIAN300AR1 AD_S - - false - false - - Kristiansand 300 Arendal 1 A Disconnector - false - - - KRISTIAN300AR1 BD_S - - false - true - - Kristiansand 300 Feda 1 B Disconnector - false - - - KRISTIAN300FE1 AD_S - - false - false - - Kristiansand 300 Feda 1 A Disconnector - false - - - KRISTIAN300FE1 BD_S - - false - true - - Kristiansand 300 Feda 1 B Disconnector - false - - - KRISTIAN300G1 AD_S - - false - false - - Kristiansand 300 Gen 1 A Disconnector - false - - - KRISTIAN300G1 BD_S - - false - true - - Kristiansand 300 Gen 1 B Disconnector - false - - - KRISTIAN300G2 AD_S - - false - false - - Kristiansand 300 Gen 2 A Disconnector - false - - - KRISTIAN300G2 BD_S - - false - true - - Kristiansand 300 Gen 2 B Disconnector - false - - - KRISTIAN300G3 AD_S - - false - false - - Kristiansand 300 Gen 3 A Disconnector - false - - - KRISTIAN300G3 BD_S - - false - true - - Kristiansand 300 Gen 3 B Disconnector - false - - - KRISTIAN300G4 AD_S - - false - false - - Kristiansand 300 Gen 4 A Disconnector - false - - - KRISTIAN300G4 BD_S - - false - true - - Kristiansand 300 Gen 4 B Disconnector - false - - - KRISTIAN300KV1 AD_S - - false - false - - Kristiansand 300 Kvilldal 1 A Disconnector - false - - - KRISTIAN300KV1 BD_S - - false - true - - Kristiansand 300 Kvilldal 1 B Disconnector - false - - - KRISTIAN300L1 AD_S - - false - false - - Kristiansand 300 Load 1 A Disconnector - false - - - KRISTIAN300L1 BD_S - - false - true - - Kristiansand 300 Load 1 B Disconnector - false - - - KRISTIAN300L2 AD_S - - false - false - - Kristiansand 300 Load 2 A Disconnector - false - - - KRISTIAN300L2 BD_S - - false - true - - Kristiansand 300 Load 2 B Disconnector - false - - - KRISTIAN300ST1 AD_S - - false - false - - Kristiansand 300 Stavanger 1 A Disconnector - false - - - KRISTIAN300ST1 BD_S - - false - true - - Kristiansand 300 Stavanger 1 B Disconnector - false - - - 1280 - 248.65 - 0 - 1280 - 1280 - 0 - 0 - 248.65 - 1280 - 1280 - 100 - 0 - 0 - 1 - - false - true - 5500 '1 ' - ASKER G1 - false - 1 - - - - 1280 - 248.65 - 0 - 1280 - 1280 - 0 - 0 - 248.65 - 1280 - 1280 - 100 - 0 - 0 - 1 - - false - true - 5500 '2 ' - ASKER G2 - false - 2 - - - - 1120 - 681.833 - 0 - 1120 - 1120 - 0 - 0 - 681.833 - 1120 - 1120 - 20 - 0 - 0 - 1 - - false - true - 6100 '1 ' - BLAFALLIG1 - true - 1 - - - - 1120 - 681.833 - 0 - 1120 - 1120 - 0 - 0 - 681.833 - 1120 - 1120 - 20 - 0 - 0 - 1 - - false - true - 6100 '2 ' - BLAFALLIG2 - true - 2 - - - - 1120 - 681.833 - 0 - 1120 - 1120 - 0 - 0 - 681.833 - 1120 - 1120 - 20 - 0 - 0 - 1 - - false - true - 6100 '3 ' - BLAFALLIG3 - true - 3 - - - - 1120 - 681.833 - 0 - 1120 - 1120 - 0 - 0 - 681.833 - 1120 - 1120 - 20 - 0 - 0 - 1 - - false - true - 6100 '4 ' - BLAFALLIG4 - true - 4 - - - - 1120 - 681.833 - 0 - 1120 - 1120 - 0 - 0 - 681.833 - 1120 - 1120 - 20 - 0 - 0 - 1 - - false - true - 6100 '5 ' - BLAFALLIG5 - true - 5 - - - - 1167 - 392.863 - 0 - 1167 - 1167 - 0 - 0 - 392.863 - 1167 - 1167 - 33.3 - 0 - 0 - 1 - - false - true - 3000 '1 ' - FORSMARK G1 - true - 1 - - - - 1167 - 392.863 - 0 - 1167 - 1167 - 0 - 0 - 392.863 - 1167 - 1167 - 33.3 - 0 - 0 - 1 - - false - true - 3000 '2 ' - FORSMARK G2 - true - 2 - - - - 1167 - 392.863 - 0 - 1167 - 1167 - 0 - 0 - 392.863 - 1167 - 1167 - 33.3 - 0 - 0 - 1 - - false - true - 3000 '3 ' - FORSMARK G3 - false - 3 - - - - 1230 - 440.397 - 0 - 1230 - 1230 - 0 - 0 - 440.397 - 1230 - 1230 - 14.3 - 0 - 0 - 1 - - false - true - 3249 '1 ' - GRUNDFORG1 - false - 1 - - - - 1230 - 440.397 - 0 - 1230 - 1230 - 0 - 0 - 440.397 - 1230 - 1230 - 14.3 - 0 - 0 - 1 - - false - true - 3249 '2 ' - GRUNDFORG2 - true - 2 - - - - 1230 - 440.397 - 0 - 1230 - 1230 - 0 - 0 - 440.397 - 1230 - 1230 - 14.3 - 0 - 0 - 1 - - false - true - 3249 '3 ' - GRUNDFORG3 - true - 3 - - - - 1230 - 440.397 - 0 - 1230 - 1230 - 0 - 0 - 440.397 - 1230 - 1230 - 14.3 - 0 - 0 - 1 - - false - true - 3249 '4 ' - GRUNDFORG4 - true - 4 - - - - 1230 - 440.397 - 0 - 1230 - 1230 - 0 - 0 - 440.397 - 1230 - 1230 - 14.3 - 0 - 0 - 1 - - false - true - 3249 '5 ' - GRUNDFORG5 - true - 5 - - - - 1230 - 440.397 - 0 - 1230 - 1230 - 0 - 0 - 440.397 - 1230 - 1230 - 14.3 - 0 - 0 - 1 - - false - true - 3249 '6 ' - GRUNDFORG6 - true - 6 - - - - 1230 - 440.397 - 0 - 1230 - 1230 - 0 - 0 - 440.397 - 1230 - 1230 - 14.3 - 0 - 0 - 1 - - false - true - 3249 '7 ' - GRUNDFORG7 - true - 7 - - - - 1230 - 440.397 - 0 - 1230 - 1230 - 0 - 0 - 440.397 - 1230 - 1230 - 14.3 - 0 - 0 - 1 - - false - true - 3249 '8 ' - GRUNDFORG8 - true - 8 - - - - 1167 - 685.687 - 0 - 1167 - 1167 - 0 - 0 - 685.687 - 1167 - 1167 - 11.1 - 0 - 0 - 1 - - false - true - 7000 '1 ' - HELSINKIG1 - true - 1 - - - - 1167 - 685.687 - 0 - 1167 - 1167 - 0 - 0 - 685.687 - 1167 - 1167 - 11.1 - 0 - 0 - 1 - - false - true - 7000 '2 ' - HELSINKIG2 - true - 2 - - - - 1167 - 685.687 - 0 - 1167 - 1167 - 0 - 0 - 685.687 - 1167 - 1167 - 11.1 - 0 - 0 - 1 - - false - true - 7000 '3 ' - HELSINKIG3 - true - 3 - - - - 1167 - 685.687 - 0 - 1167 - 1167 - 0 - 0 - 685.687 - 1167 - 1167 - 11.1 - 0 - 0 - 1 - - false - true - 7000 '4 ' - HELSINKIG4 - true - 4 - - - - 1167 - 685.687 - 0 - 1167 - 1167 - 0 - 0 - 685.687 - 1167 - 1167 - 11.1 - 0 - 0 - 1 - - false - true - 7000 '5 ' - HELSINKIG5 - true - 5 - - - - 1167 - 685.687 - 0 - 1167 - 1167 - 0 - 0 - 685.687 - 1167 - 1167 - 11.1 - 0 - 0 - 1 - - false - true - 7000 '6 ' - HELSINKIG6 - true - 6 - - - - 1167 - 685.687 - 0 - 1167 - 1167 - 0 - 0 - 685.687 - 1167 - 1167 - 11.1 - 0 - 0 - 1 - - false - true - 7000 '7 ' - HELSINKIG7 - true - 7 - - - - 1167 - 685.687 - 0 - 1167 - 1167 - 0 - 0 - 685.687 - 1167 - 1167 - 11.1 - 0 - 0 - 1 - - false - true - 7000 '8 ' - HELSINKIG8 - false - 8 - - - - 1167 - 685.687 - 0 - 1167 - 1167 - 0 - 0 - 685.687 - 1167 - 1167 - 11.1 - 0 - 0 - 1 - - false - true - 7000 '9 ' - HELSINKIG9 - false - 9 - - - - 850 - 199.925 - 0 - 850 - 850 - 0 - 0 - 199.925 - 850 - 850 - 100 - 0 - 0 - 1 - - false - true - 3245 '1 ' - JARPSTROG1 - false - 1 - - - - 1050 - 323.652 - 0 - 1050 - 1050 - 0 - 0 - 323.652 - 1050 - 1050 - 50 - 0 - 0 - 0.999 - - false - true - 5600 '1 ' - KRISTIANG1 - true - 1 - - - - 1050 - 323.652 - 0 - 1050 - 1050 - 0 - 0 - 323.652 - 1050 - 1050 - 50 - 0 - 0 - 0.999 - - false - true - 5600 '2 ' - KRISTIANG2 - true - 2 - - - - 1050 - 323.652 - 0 - 1050 - 1050 - 0 - 0 - 323.652 - 1050 - 1050 - 50 - 0 - 0 - 0.999 - - false - true - 5600 '3 ' - KRISTIANG3 - true - 3 - - - - 1050 - 323.652 - 0 - 1050 - 1050 - 0 - 0 - 323.652 - 1050 - 1050 - 50 - 0 - 0 - 0.999 - - false - true - 5600 '4 ' - KRISTIANG4 - false - 4 - - - - 620 - 466.081 - 0 - 620 - 620 - 0 - 0 - 466.081 - 620 - 620 - 100 - 0 - 0 - 1 - - false - true - 6000 '1 ' - KVILLDALG1 - true - 1 - - - - 620 - 466.081 - 0 - 620 - 620 - 0 - 0 - 466.081 - 620 - 620 - 100 - 0 - 0 - 1 - - false - true - 6000 '2 ' - KVILLDALG2 - true - 2 - - - - 620 - 466.081 - 0 - 620 - 620 - 0 - 0 - 466.081 - 620 - 620 - 100 - 0 - 0 - 1 - - false - true - 6000 '3 ' - KVILLDALG3 - true - 3 - - - - 620 - 466.081 - 0 - 620 - 620 - 0 - 0 - 466.081 - 620 - 620 - 100 - 0 - 0 - 1 - - false - true - 6000 '4 ' - KVILLDALG4 - true - 4 - - - - 1183 - 139.462 - 0 - 1183 - 1183 - 0 - 0 - 139.462 - 1183 - 1183 - 16.7 - 0 - 0 - 1 - - false - true - 8500 '1 ' - MALMO G1 - true - 1 - - - - 1183 - 139.462 - 0 - 1183 - 1183 - 0 - 0 - 139.462 - 1183 - 1183 - 16.7 - 0 - 0 - 1 - - false - true - 8500 '2 ' - MALMO G2 - true - 2 - - - - 1183 - 139.462 - 0 - 1183 - 1183 - 0 - 0 - 139.462 - 1183 - 1183 - 16.7 - 0 - 0 - 1 - - false - true - 8500 '3 ' - MALMO G3 - true - 3 - - - - 1183 - 139.462 - 0 - 1183 - 1183 - 0 - 0 - 139.462 - 1183 - 1183 - 16.7 - 0 - 0 - 1 - - false - true - 8500 '4 ' - MALMO G4 - true - 4 - - - - 1183 - 139.462 - 0 - 1183 - 1183 - 0 - 0 - 139.462 - 1183 - 1183 - 16.7 - 0 - 0 - 1 - - false - true - 8500 '5 ' - MALMO G5 - true - 5 - - - - 1183 - 139.462 - 0 - 1183 - 1183 - 0 - 0 - 139.462 - 1183 - 1183 - 16.7 - 0 - 0 - 1 - - false - true - 8500 '6 ' - MALMO G6 - true - 6 - - - - 1050 - 612.145 - 0 - 1050 - 1050 - 0 - 0 - 612.145 - 1050 - 1050 - 50 - 0 - 0 - 1 - - false - true - 6700 '1 ' - MO G1 - true - 1 - - - - 1050 - 612.145 - 0 - 1050 - 1050 - 0 - 0 - 612.145 - 1050 - 1050 - 50 - 0 - 0 - 1 - - false - true - 6700 '2 ' - MO G2 - true - 2 - - - - 1050 - 612.145 - 0 - 1050 - 1050 - 0 - 0 - 612.145 - 1050 - 1050 - 50 - 0 - 0 - 1 - - false - true - 6700 '3 ' - MO G3 - true - 3 - - - - 1050 - 612.145 - 0 - 1050 - 1050 - 0 - 0 - 612.145 - 1050 - 1050 - 50 - 0 - 0 - 1 - - false - true - 6700 '4 ' - MO G4 - true - 4 - - - - 1000 - 750.456 - 0 - 1000 - 1000 - 0 - 0 - 750.456 - 1000 - 1000 - 33.3 - 0 - 0 - 1 - - false - true - 3300 '1 ' - OSKARSHAG1 - true - 1 - - - - 1000 - 750.456 - 0 - 1000 - 1000 - 0 - 0 - 750.456 - 1000 - 1000 - 33.3 - 0 - 0 - 1 - - false - true - 3300 '2 ' - OSKARSHAG2 - true - 2 - - - - 1000 - 750.456 - 0 - 1000 - 1000 - 0 - 0 - 750.456 - 1000 - 1000 - 33.3 - 0 - 0 - 1 - - false - true - 3300 '3 ' - OSKARSHAG3 - true - 3 - - - - 1000 - 750.456 - 0 - 1000 - 1000 - 0 - 0 - 750.456 - 1000 - 1000 - 33.3 - 0 - 0 - 1 - - false - true - 3300 '4 ' - OSKARSHAG4 - true - 4 - - - - 1000 - 750.456 - 0 - 1000 - 1000 - 0 - 0 - 750.456 - 1000 - 1000 - 33.3 - 0 - 0 - 1 - - false - true - 3300 '5 ' - OSKARSHAG5 - true - 5 - - - - 1000 - 750.456 - 0 - 1000 - 1000 - 0 - 0 - 750.456 - 1000 - 1000 - 33.3 - 0 - 0 - 1 - - false - true - 3300 '6 ' - OSKARSHAG6 - true - 6 - - - - 1100 - 260.104 - 0 - 1100 - 1100 - 0 - 0 - 260.104 - 1100 - 1100 - 50 - 0 - 0 - 1 - - false - true - 5400 '1 ' - OSLO G1 - true - 1 - - - - 1100 - 260.104 - 0 - 1100 - 1100 - 0 - 0 - 260.104 - 1100 - 1100 - 50 - 0 - 0 - 1 - - false - true - 5400 '2 ' - OSLO G2 - true - 2 - - - - 900 - 435.385 - 0 - 900 - 900 - 0 - 0 - 435.385 - 900 - 900 - 33.3 - 0 - 0 - 1 - - false - true - 7100 '1 ' - OULU G1 - true - 1 - - - - 900 - 435.385 - 0 - 900 - 900 - 0 - 0 - 435.385 - 900 - 900 - 33.3 - 0 - 0 - 1 - - false - true - 7100 '2 ' - OULU G2 - true - 2 - - - - 900 - 435.385 - 0 - 900 - 900 - 0 - 0 - 435.385 - 900 - 900 - 33.3 - 0 - 0 - 1 - - false - true - 7100 '3 ' - OULU G3 - true - 3 - - - - 1000 - 335.404 - 0 - 1000 - 1000 - 0 - 0 - 335.404 - 1000 - 1000 - 33.3 - 0 - 0 - 1 - - false - true - 3115 '1 ' - PORJUS G1 - true - 1 - - - - 1000 - 335.404 - 0 - 1000 - 1000 - 0 - 0 - 335.404 - 1000 - 1000 - 33.3 - 0 - 0 - 1 - - false - true - 3115 '2 ' - PORJUS G2 - true - 2 - - - - 1000 - 335.404 - 0 - 1000 - 1000 - 0 - 0 - 335.404 - 1000 - 1000 - 33.3 - 0 - 0 - 1 - - false - true - 3115 '3 ' - PORJUS G3 - true - 3 - - - - 1000 - 335.404 - 0 - 1000 - 1000 - 0 - 0 - 335.404 - 1000 - 1000 - 33.3 - 0 - 0 - 1 - - false - true - 3115 '4 ' - PORJUS G4 - true - 4 - - - - 1000 - 335.404 - 0 - 1000 - 1000 - 0 - 0 - 335.404 - 1000 - 1000 - 33.3 - 0 - 0 - 1 - - false - true - 3115 '5 ' - PORJUS G5 - false - 5 - - - - 1217 - 739.674 - 0 - 1217 - 1217 - 0 - 0 - 739.674 - 1217 - 1217 - 16.7 - 0 - 0 - 1 - - false - true - 3359 '1 ' - RINGHALSG1 - true - 1 - - - - 1217 - 739.674 - 0 - 1217 - 1217 - 0 - 0 - 739.674 - 1217 - 1217 - 16.7 - 0 - 0 - 1 - - false - true - 3359 '2 ' - RINGHALSG2 - true - 2 - - - - 1217 - 739.674 - 0 - 1217 - 1217 - 0 - 0 - 739.674 - 1217 - 1217 - 16.7 - 0 - 0 - 1 - - false - true - 3359 '3 ' - RINGHALSG3 - true - 3 - - - - 1217 - 739.674 - 0 - 1217 - 1217 - 0 - 0 - 739.674 - 1217 - 1217 - 16.7 - 0 - 0 - 1 - - false - true - 3359 '4 ' - RINGHALSG4 - true - 4 - - - - 1217 - 739.674 - 0 - 1217 - 1217 - 0 - 0 - 739.674 - 1217 - 1217 - 16.7 - 0 - 0 - 1 - - false - true - 3359 '5 ' - RINGHALSG5 - false - 5 - - - - 1217 - 739.674 - 0 - 1217 - 1217 - 0 - 0 - 739.674 - 1217 - 1217 - 16.7 - 0 - 0 - 1 - - false - true - 3359 '6 ' - RINGHALSG6 - false - 6 - - - - 1075 - 807.547 - 0 - 1075 - 1075 - 0 - 0 - 807.547 - 1075 - 1075 - 50 - 0 - 0 - 1 - - false - true - 5300 '1 ' - SIMA G1 - true - 1 - - - - 1075 - 807.547 - 0 - 1075 - 1075 - 0 - 0 - 807.547 - 1075 - 1075 - 50 - 0 - 0 - 1 - - false - true - 5300 '2 ' - SIMA G2 - true - 2 - - - - 1075 - 807.547 - 0 - 1075 - 1075 - 0 - 0 - 807.547 - 1075 - 1075 - 50 - 0 - 0 - 1 - - false - true - 5300 '3 ' - SIMA G3 - true - 3 - - - - 1075 - 807.547 - 0 - 1075 - 1075 - 0 - 0 - 807.547 - 1075 - 1075 - 50 - 0 - 0 - 1 - - false - true - 5300 '4 ' - SIMA G4 - true - 4 - - - - 1075 - 807.547 - 0 - 1075 - 1075 - 0 - 0 - 807.547 - 1075 - 1075 - 50 - 0 - 0 - 1 - - false - true - 5300 '5 ' - SIMA G5 - true - 5 - - - - 1075 - 807.547 - 0 - 1075 - 1075 - 0 - 0 - 807.547 - 1075 - 1075 - 50 - 0 - 0 - 1 - - false - true - 5300 '6 ' - SIMA G6 - true - 6 - - - - 1100 - 274.554 - 0 - 1100 - 1100 - 0 - 0 - 274.554 - 1100 - 1100 - 100 - 0 - 0 - 1 - - false - true - 5100 '1 ' - TRETTEN G1 - false - 1 - - - - 1100 - 274.554 - 0 - 1100 - 1100 - 0 - 0 - 274.554 - 1100 - 1100 - 100 - 0 - 0 - 1 - - false - true - 5100 '2 ' - TRETTEN G2 - false - 2 - - - - 1000 - 269.964 - 0 - 1000 - 1000 - 0 - 0 - 269.964 - 1000 - 1000 - 25 - 0 - 0 - 1 - - false - true - 6500 '1 ' - TRONDHEIG1 - true - 1 - - - - 1000 - 269.964 - 0 - 1000 - 1000 - 0 - 0 - 269.964 - 1000 - 1000 - 25 - 0 - 0 - 1 - - false - true - 6500 '2 ' - TRONDHEIG2 - true - 2 - - - - 1000 - 269.964 - 0 - 1000 - 1000 - 0 - 0 - 269.964 - 1000 - 1000 - 25 - 0 - 0 - 1 - - false - true - 6500 '3 ' - TRONDHEIG3 - true - 3 - - - - 1000 - 269.964 - 0 - 1000 - 1000 - 0 - 0 - 269.964 - 1000 - 1000 - 25 - 0 - 0 - 1 - - false - true - 6500 '4 ' - TRONDHEIG4 - true - 4 - - - - NO - Nordic - - - LA - Serbia - - - - LC 300AJAURE-MO - - - - LC 300ARENDAL-KRISTA_HVDC - - - - LC 300ASKER-ARENDAL - - - - LC 300HOGASEN-TRONDHEIM - - - - LC 300KRISTIAN-ARENDAL - - - - LC 300KRISTIAN-FEDA - - - - LC 300KRISTIAN-KVILLDAL - - - - LC 300KRISTIAN-STAVANGE - - - - LC 300KVILLDAL-BLAFALLI - - - - LC 300OSLO-ASKER - - - - LC 300OSLO-KVILLDAL - - - - LC 300SIMA-BLAFALLI - - - - LC 300TRETTEN-ASKER - - - - LC 300TRETTEN-TRONDHEIM - - - - LC 300TRONDHEIM-MO 1 - - - - LC 300TRONDHEIM-MO 2 - - - - LC 420ARENDAL-SANDEFJORD - - - - LC 420AURLAND-EIDFJORD - - - - LC 420AURLAND-GEILO - - - - LC 420AURLAND-HAGAFOSS - - - - LC 420DAGALI-GEILO - - - - LC 420DAGALI-HAGAFOSS - - - - LC 420DAGALI-KONGSBER - - - - LC 420FORSMARK-DANNEBO - - - - LC 420FORSMARK-JARPSTRO 1 - - - - LC 420FORSMARK-JARPSTRO 2 - - - - LC 420FORSMARK-OSKARHA 1 - - - - LC 420FORSMARK-OSKARHA 2 - - - - LC 420FORSMARK-PORJUS - - - - LC 420GEILO-EIDFJORD 1 - - - - LC 420GEILO-EIDFJORD 2 - - - - LC 420GRUNDFOR-OULU - - - - LC 420HALDEN-DAGALI - - - - LC 420HALDEN-KONGSBER - - - - LC 420HALDEN-SKIEN - - - - LC 420HELSINKI-ESTLINK - - - - LC 420HELSINKI-OULU 1 - - - - LC 420HELSINKI-OULU 2 - - - - LC 420HELSINKI-OULU 3 - - - - LC 420HELSINKI-VYBORG - - - - LC 420HJALTA-GRUNDFOR - - - - LC 420HJALTA-PORJUS - - - - LC 420HJALTA-RINGHALS 1 - - - - LC 420HJALTA-RINGHALS 2 - - - - LC 420HJALTA-TENHULT 1 - - - - LC 420HJALTA-TENHULT 2 - - - - LC 420HJALTA-TENHULT 3 - - - - LC 420KONGSBER-GEILO 1 - - - - LC 420KONGSBER-GEILO 2 - - - - LC 420MALMO-ARRIE - - - - LC 420MALMO-KARLSH - - - - LC 420OSKARHA-MALMO 1 - - - - LC 420OSKARHA-MALMO 2 - - - - LC 420OSKARHA-TENHULT - - - - LC 420PORJUS-GRUNDFOR - - - - LC 420PORJUS-JARPSTRO - - - - LC 420PORJUS-NARVIK - - - - LC 420PORJUS-OULU - - - - LC 420RINGHALS-HALDEN 1 - - - - LC 420RINGHALS-HALDEN 2 - - - - LC 420RINGHALS-MALMO 1 - - - - LC 420RINGHALS-MALMO 2 - - - - LC 420SYLLING-HAGAFOSS - - - - LC 420SYLLING-SANDEFJORD - - - - LC 420SYLLING-SKIEN - - - - LC 420SYSLE-HAGAFOSS - - - - LC 420TENHULT-MALMO - - - - LC 420TENHULT-RINGHALS - - - -1.44444E-05 - -3.3333E-06 - 0 - 1 - 300 - 1 - - - - false - true - Branch Shunt 5500_5603_1 - ARENDAL 300 LSC1 - 1 - - - 1.44444E-05 - 3.3333E-06 - 0 - 1 - 300 - 1 - - - - false - true - Branch Shunt 5500_5603_1 - ASKER 300 LSC1 - - - 5.669E-07 - 1.1338E-06 - 0 - 1 - 420 - 1 - - - - false - true - Branch Shunt 5102_6001_1 - DAGALI 420 LSC1 - - - -5.669E-07 - 1.1338E-06 - 0 - 1 - 420 - 1 - - - - false - true - Branch Shunt 5102_6001_1 - HAGAFOSS420 LSC1 - - - 2.8345E-06 - 1.1338E-06 - 0 - 1 - 420 - 1 - - - - false - true - Branch Shunt 5401_6001_1 - HAGAFOSS420 LSC1 - - - -0.0055238095 - 0.0001264172 - 0 - 1 - 420 - 1 - - - - false - true - Branch Shunt 5101_5501_1 - HALDEN 420 LSC1 - - - 0.0055238095 - -0.000122449 - 0 - 1 - 420 - 1 - - - - false - true - Branch Shunt 5101_5501_1 - SKIEN 420 LSC1 - - - -2.8345E-06 - -1.1338E-06 - 0 - 1 - 420 - 1 - - - - false - true - Branch Shunt 5401_6001_1 - SYLLING 420 LSC1 - - - FI LA - - - NO LA - - - SE LA - - - false - 0 - 0 - 1 - 0 - 0 - 1 - CONST_P_Q LRC - Constant q and q power load response characteristic - - - Allocated - MMSv334 - Market Management System v3.3.3 - - - Calculated - Cac - Calculation based on other analog values - - - Forecasted - LFTYv101 - Load Forecasting Tool Year v.1.0.1 - - - Forecasted - LFTDv109 - Load Forecasting Tool Day v.1.0.9 - - - Forecasted - LFTMv520 - Load Forecasting Tool Month v.5.2.0 - - - Forecasted - WFv101 - Wind Forecast Tool v.1.0.1 - - - Forecasted - RoRFv222 - Run-of-river Forecast Tool v.2.2.2 - - - Forecasted - LFTWv234 - Load Forecasting Tool Week v.2.3.4 - - - ICCP - ICCP - From Elcom, ICCP from partner - - - Operator - Operator - User entry - - - PowerFlow - SEv267 - State Estimator v.2.6.7 - - - SCADA - SCADA - From RTU or 61850 - - - - - 493 - 162.041 - - - false - true - 8600 '1 ' - ARRIE 420 L1 - - - - - 608 - 199.84 - - - false - true - 3020 '1 ' - DANNEBO 420 L1 - - - - - 519 - 170.587 - - - false - true - 7020 '1 ' - ESTLINK 420 L1 - - - - - 729 - 239.611 - - - false - true - 5620 '1 ' - FEDA 300 L1 - - - - - -608 - -199.84 - - - false - true - 7000 '6 ' - HELSINKI420 L6 - - - - - 3 - 0.986 - - - false - true - 8700 '1 ' - KARLSH 420 L1 - - - - - 1230 - 175.266 - - - false - true - 5610 '1 ' - KRISTIA 300 L1 - - - - - 946 - 310.935 - - - false - true - 8500 '4 ' - MALMO 420 L4 - - - - - 12 - 3.944 - - - false - true - 6701 '1 ' - NARVIK 420 L1 - - - - - -19 - -6.245 - - - false - true - 6701 '3 ' - NARVIK 420 L3 - - - - - -12 - -3.944 - - - false - true - 7100 '3 ' - OULU 420 L3 - - - - - -80 - -26.295 - - - false - true - 3360 '1 ' - STEENKU 135 L1 - - - - - -903 - -296.802 - - - false - true - 7010 '1 ' - VYBORG 420 L1 - - - - ARRIE 420 LG1 - 8600_1 - - - - DANNEBO 420 LG1 - 3020_1 - - - - ESTLINK 420 LG1 - 7020_1 - - - - FEDA 300 LG1 - 5620_1 - - - - HELSINKI420 LG6 - 7000_6 - - - - KARLSH 420 LG1 - 8700_1 - - - - KRISTIA 300 LG1 - 5610_1 - - - - MALMO 420 LG4 - 8500_4 - - - - NARVIK 420 LG1 - 6701_1 - - - - NARVIK 420 LG3 - 6701_3 - - - - OULU 420 LG3 - 7100_3 - - - - STEENKU 135 LG1 - 3360_1 - - - - VYBORG 420 LG1 - 7010_1 - - - Grid Operator1 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - - 100 - - - - PTC 420RINGHALS-HALDEN LimitSet for direction Halden to Ringhals - LS PTC 420RING-HALDEN F HA - - - - - PTC 420RINGHALS-HALDEN LimitSet for PATL 20 direction Ringhals to Halden - LS PTC 420RING-HALDEN PATL10 RI - - - - - - PTC 420RINGHALS-HALDEN LimitSet for PATL 30 direction Ringhals to Halden - LS PTC 420RING-HALDEN PATL30 RI - - - - - - PTC 420RINGHALS-HALDEN LimitSet for TATL 20 direction Ringhals to Halden - LS PTC 420RING-HALDEN TATL20 RI - - - - - - PTC 420RINGHALS-HALDEN LimitSet for TATL 30 direction Ringhals to Halden - LS PTC 420RING-HALDEN TATL30 RI - - - - - - PTC 420RINGHALS-HALDEN LimitSet for warning voltage direction Ringhals to Halden - LS PTC 420RING-HALDEN WV RI - - - - - Branch 3000_3020_1 Limits <Default> - Limits <Default> - - - - Branch 3000_3020_1 Limits <Default> - Limits <Default> - - - - Branch 3000_3115_1 Limits <Default> - Limits <Default> - - - - Branch 3000_3115_1 Limits <Default> - Limits <Default> - - - - Branch 3000_3245_1 Limits <Default> - Limits <Default> - - - - Branch 3000_3245_1 Limits <Default> - Limits <Default> - - - - Branch 3000_3245_2 Limits <Default> - Limits <Default> - - - - Branch 3000_3245_2 Limits <Default> - Limits <Default> - - - - Branch 3000_3300_1 Limits <Default> - Limits <Default> - - - - Branch 3000_3300_1 Limits <Default> - Limits <Default> - - - - Branch 3000_3300_2 Limits <Default> - Limits <Default> - - - - Branch 3000_3300_2 Limits <Default> - Limits <Default> - - - - Branch 3100_3115_1 Limits <Default> - Limits <Default> - - - - Branch 3100_3115_1 Limits <Default> - Limits <Default> - - - - Branch 3100_3200_1 Limits <Default> - Limits <Default> - - - - Branch 3100_3200_1 Limits <Default> - Limits <Default> - - - - Branch 3100_3200_3 Limits <Default> - Limits <Default> - - - - Branch 3100_3200_3 Limits <Default> - Limits <Default> - - - - Branch 3100_3249_1 Limits <Default> - Limits <Default> - - - - Branch 3100_3249_1 Limits <Default> - Limits <Default> - - - - Branch 3100_3359_1 Limits <Default> - Limits <Default> - - - - Branch 3100_3359_1 Limits <Default> - Limits <Default> - - - - Branch 3100_3359_2 Limits <Default> - Limits <Default> - - - - Branch 3100_3359_2 Limits <Default> - Limits <Default> - - - - Branch 3115_3245_1 Limits <Default> - Limits <Default> - - - - Branch 3115_3245_1 Limits <Default> - Limits <Default> - - - - Branch 3115_3249_1 Limits <Default> - Limits <Default> - - - - Branch 3115_3249_1 Limits <Default> - Limits <Default> - - - - Branch 3115_6701_1 Limits <Default> - Limits <Default> - - - - Branch 3115_6701_1 Limits <Default> - Limits <Default> - - - - Branch 3115_7100_1 Limits <Default> - Limits <Default> - - - - Branch 3115_7100_1 Limits <Default> - Limits <Default> - - - - Branch 3200_3300_1 Limits <Default> - Limits <Default> - - - - Branch 3200_3300_1 Limits <Default> - Limits <Default> - - - - Branch 3200_3359_1 Limits <Default> - Limits <Default> - - - - Branch 3200_3359_1 Limits <Default> - Limits <Default> - - - - Branch 3200_8500_1 Limits <Default> - Limits <Default> - - - - Branch 3200_8500_1 Limits <Default> - Limits <Default> - - - - Branch 3244_6500_1 Limits <Default> - Limits <Default> - - - - Branch 3244_6500_1 Limits <Default> - Limits <Default> - - - - Branch 3249_7100_1 Limits <Default> - Limits <Default> - - - - Branch 3249_7100_1 Limits <Default> - Limits <Default> - - - - Branch 3300_8500_1 Limits <Default> - Limits <Default> - - - - Branch 3300_8500_1 Limits <Default> - Limits <Default> - - - - Branch 3300_8500_2 Limits <Default> - Limits <Default> - - - - Branch 3300_8500_2 Limits <Default> - Limits <Default> - - - - Branch 3359_5101_1 Limits <Default> - Limits <Default> - - - - Branch 3359_5101_1 Limits <Default> - Limits <Default> - - - - Branch 3359_5101_2 Limits <Default> - Limits <Default> - - - - Branch 3359_5101_2 Limits <Default> - Limits <Default> - - - - Branch 3359_8500_1 Limits <Default> - Limits <Default> - - - - Branch 3359_8500_1 Limits <Default> - Limits <Default> - - - - Branch 3359_8500_2 Limits <Default> - Limits <Default> - - - - Branch 3359_8500_2 Limits <Default> - Limits <Default> - - - - Branch 3701_6700_1 Limits <Default> - Limits <Default> - - - - Branch 3701_6700_1 Limits <Default> - Limits <Default> - - - - Branch 5100_5500_1 Limits <Default> - Limits <Default> - - - - Branch 5100_5500_1 Limits <Default> - Limits <Default> - - - - Branch 5100_6500_1 Limits <Default> - Limits <Default> - - - - Branch 5100_6500_1 Limits <Default> - Limits <Default> - - - - Branch 5101_5102_1 Limits <Default> - Limits <Default> - - - - Branch 5101_5102_1 Limits <Default> - Limits <Default> - - - - Branch 5101_5103_1 Limits <Default> - Limits <Default> - - - - Branch 5101_5103_1 Limits <Default> - Limits <Default> - - - - Branch 5101_5501_1 Limits <Default> - Limits <Default> - - - - Branch 5101_5501_1 Limits <Default> - Limits <Default> - - - - Branch 5102_5103_1 Limits <Default> - Limits <Default> - - - - Branch 5102_5103_1 Limits <Default> - Limits <Default> - - - - Branch 5102_5304_1 Limits <Default> - Limits <Default> - - - - Branch 5102_5304_1 Limits <Default> - Limits <Default> - - - - Branch 5102_6001_1 Limits <Default> - Limits <Default> - - - - Branch 5102_6001_1 Limits <Default> - Limits <Default> - - - - Branch 5103_5304_1 Limits <Default> - Limits <Default> - - - - Branch 5103_5304_1 Limits <Default> - Limits <Default> - - - - Branch 5103_5304_2 Limits <Default> - Limits <Default> - - - - Branch 5103_5304_2 Limits <Default> - Limits <Default> - - - - Branch 5300_6100_1 Limits <Default> - Limits <Default> - - - - Branch 5300_6100_1 Limits <Default> - Limits <Default> - - - - Branch 5301_5304_1 Limits <Default> - Limits <Default> - - - - Branch 5301_5304_1 Limits <Default> - Limits <Default> - - - - Branch 5301_5305_1 Limits <Default> - Limits <Default> - - - - Branch 5301_5305_1 Limits <Default> - Limits <Default> - - - - Branch 5301_6001_1 Limits <Default> - Limits <Default> - - - - Branch 5301_6001_1 Limits <Default> - Limits <Default> - - - - Branch 5304_5305_1 Limits <Default> - Limits <Default> - - - - Branch 5304_5305_1 Limits <Default> - Limits <Default> - - - - Branch 5304_5305_2 Limits <Default> - Limits <Default> - - - - Branch 5304_5305_2 Limits <Default> - Limits <Default> - - - - Branch 5400_5500_1 Limits <Default> - Limits <Default> - - - - Branch 5400_5500_1 Limits <Default> - Limits <Default> - - - - Branch 5400_6000_1 Limits <Default> - Limits <Default> - - - - Branch 5400_6000_1 Limits <Default> - Limits <Default> - - - - Branch 5401_5501_1 Limits <Default> - Limits <Default> - - - - Branch 5401_5501_1 Limits <Default> - Limits <Default> - - - - Branch 5401_5602_1 Limits <Default> - Limits <Default> - - - - Branch 5401_5602_1 Limits <Default> - Limits <Default> - - - - Branch 5401_6001_1 Limits <Default> - Limits <Default> - - - - Branch 5401_6001_1 Limits <Default> - Limits <Default> - - - - Branch 5402_6001_1 Limits <Default> - Limits <Default> - - - - Branch 5402_6001_1 Limits <Default> - Limits <Default> - - - - Branch 5500_5603_1 Limits <Default> - Limits <Default> - - - - Branch 5500_5603_1 Limits <Default> - Limits <Default> - - - - Branch 5600_5601_1 Limits <Default> - Limits <Default> - - - - Branch 5600_5601_1 Limits <Default> - Limits <Default> - - - - Branch 5600_5603_1 Limits <Default> - Limits <Default> - - - - Branch 5600_5603_1 Limits <Default> - Limits <Default> - - - - Branch 5600_5620_1 Limits <Default> - Limits <Default> - - - - Branch 5600_5620_1 Limits <Default> - Limits <Default> - - - - Branch 5600_6000_1 Limits <Default> - Limits <Default> - - - - Branch 5600_6000_1 Limits <Default> - Limits <Default> - - - - Branch 5603_5610_1 Limits <Default> - Limits <Default> - - - - Branch 5603_5610_1 Limits <Default> - Limits <Default> - - - - Branch 6000_6100_1 Limits <Default> - Limits <Default> - - - - Branch 6000_6100_1 Limits <Default> - Limits <Default> - - - - Branch 6500_6700_1 Limits <Default> - Limits <Default> - - - - Branch 6500_6700_1 Limits <Default> - Limits <Default> - - - - Branch 6500_6700_2 Limits <Default> - Limits <Default> - - - - Branch 6500_6700_2 Limits <Default> - Limits <Default> - - - - Branch 7000_7010_1 Limits <Default> - Limits <Default> - - - - Branch 7000_7010_1 Limits <Default> - Limits <Default> - - - - Branch 7000_7020_1 Limits <Default> - Limits <Default> - - - - Branch 7000_7020_1 Limits <Default> - Limits <Default> - - - - Branch 7000_7100_1 Limits <Default> - Limits <Default> - - - - Branch 7000_7100_1 Limits <Default> - Limits <Default> - - - - Branch 7000_7100_2 Limits <Default> - Limits <Default> - - - - Branch 7000_7100_2 Limits <Default> - Limits <Default> - - - - Branch 7000_7100_3 Limits <Default> - Limits <Default> - - - - Branch 7000_7100_3 Limits <Default> - Limits <Default> - - - - Branch 8500_8600_1 Limits <Default> - Limits <Default> - - - - Branch 8500_8600_1 Limits <Default> - Limits <Default> - - - - Branch 8500_8700_1 Limits <Default> - Limits <Default> - - - - Branch 8500_8700_1 Limits <Default> - Limits <Default> - - - - Branch 3244_3245_0_1 Limits <Default> - Limits <Default> - - - - Branch 3244_3245_0_1 Limits <Default> - Limits <Default> - - - - Branch 3701_3249_0_1 Limits <Default> - Limits <Default> - - - - Branch 3701_3249_0_1 Limits <Default> - Limits <Default> - - - - Branch 3359_3360_0_1 Limits <Default> - Limits <Default> - - - - Branch 3359_3360_0_1 Limits <Default> - Limits <Default> - - - - Branch 5101_5100_0_1 Limits <Default> - Limits <Default> - - - - Branch 5101_5100_0_1 Limits <Default> - Limits <Default> - - - - Branch 5300_5301_0_1 Limits <Default> - Limits <Default> - - - - Branch 5300_5301_0_1 Limits <Default> - Limits <Default> - - - - Branch 5400_5401_0_1 Limits <Default> - Limits <Default> - - - - Branch 5400_5401_0_1 Limits <Default> - Limits <Default> - - - - Branch 5400_5402_0_1 Limits <Default> - Limits <Default> - - - - Branch 5400_5402_0_1 Limits <Default> - Limits <Default> - - - - Branch 5500_5501_0_1 Limits <Default> - Limits <Default> - - - - Branch 5500_5501_0_1 Limits <Default> - Limits <Default> - - - - Branch 5601_6001_0_1 Limits <Default> - Limits <Default> - - - - Branch 5601_6001_0_1 Limits <Default> - Limits <Default> - - - - Branch 5603_5602_0_1 Limits <Default> - Limits <Default> - - - - Branch 5603_5602_0_1 Limits <Default> - Limits <Default> - - - - Branch 6000_6001_0_1 Limits <Default> - Limits <Default> - - - - Branch 6000_6001_0_1 Limits <Default> - Limits <Default> - - - - Branch 6700_6701_0_1 Limits <Default> - Limits <Default> - - - - Branch 6700_6701_0_1 Limits <Default> - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - Branch 3100_3200_2 Limits <Default> - Limits <Default> - - - - Branch 3100_3200_2 Limits <Default> - Limits <Default> - - - - Limits <Default> - - - - Limits <Default> - - - - OperationalLimitSet Negative 10 degree Celsius - OLS_-10dg6700_6701_0_1 - - - - OperationalLimitSet Negative 10 degree Celsius - OLS_-10dg_5101_5103_1 - - - - - - OperationalLimitSet Negative 15 degree Celsius - OLS_-15dg6700_6701_0_1 - - - - OperationalLimitSet Negative 15 degree Celsius - OLS_-15dg_5101_5103_1 - - - - - - OperationalLimitSet Negative 20 degree Celsius - OLS_-20dg6700_6701_0_1 - - - - OperationalLimitSet Negative 20 degree Celsius - OLS_-20dg_5101_5103_1 - - - - - - OperationalLimitSet Negative 25 degree Celsius - OLS_-25dg6700_6701_0_1 - - - - OperationalLimitSet Negative 25 degree Celsius - OLS_-25dg_5101_5103_1 - - - - - - OperationalLimitSet Negative 30 degree Celsius - OLS_-30dg6700_6701_0_1 - - - - - OperationalLimitSet Negative 30 degree Celsius - OLS_-30dg_300KRISTIAN-ARENDAL - - - - OperationalLimitSet Negative 30 degree Celsius - OLS_-30dg_5101_5103_1 - - - - - - OperationalLimitSet Negative 5 degree Celsius - OLS_-5dg6700_6701_0_1 - - - - OperationalLimitSet Negative 5 degree Celsius - OLS_-5dg_5101_5103_1 - - - - - - OperationalLimitSet 0 degree Celsius - OLS_0dg6700_6701_0_1 - - - - OperationalLimitSet 0 degree Celsius - OLS_0dg_5101_5103_1 - - - - - - OperationalLimitSet 10 degree Celsius - OLS_10dg6700_6701_0_1 - - - - OperationalLimitSet 10 degree Celsius - OLS_10dg_5101_5103_1 - - - - - - OperationalLimitSet 15 degree Celsius - OLS_15dg6700_6701_0_1 - - - - OperationalLimitSet 15 degree Celsius - OLS_15dg_5101_5103_1 - - - - - - OperationalLimitSet 20 degree Celsius - OLS_20dg6700_6701_0_1 - - - - - OperationalLimitSet 20 degree Celsius - OLS_20dg_300KRISTIAN-ARENDAL - - - - OperationalLimitSet 20 degree Celsius - OLS_20dg_5101_5103_1 - - - - - - OperationalLimitSet 25 degree Celsius - OLS_25dg6700_6701_0_1 - - - - OperationalLimitSet 25 degree Celsius - OLS_25dg_5101_5103_1 - - - - - - OperationalLimitSet 30 degree Celsius - OLS_30dg6700_6701_0_1 - - - - OperationalLimitSet 30 degree Celsius - OLS_30dg_5101_5103_1 - - - - - - OperationalLimitSet 5 degree Celsius - OLS_5dg6700_6701_0_1 - - - - OperationalLimitSet 5 degree Celsius - OLS_5dg_5101_5103_1 - - - - - 900 - - - HighVoltage - - - 0 - - - HighVoltage - - - 900 - - - LowVoltage - - - 0 - - - LowVoltage - - - 0 - - - OLT High PATL at 20degree - - - - 0 - - - OLT High PATL at 30degree - - - - 0 - - - OLT High warning Voltage - - - - 0 - - - OLT Low Stability - - - - 0 - - - OLT TATL at 20degree - - - - 0 - - - OLT TATL at 30degree - - - - 0 - - - PATL - - - 7200 - - - TATL - - - 900 - - - TATL - - - 1 - 1 - 1 - 0 - 0 - 1.4 - 0.6 - 2 - - true - 3701 3249 0 '1 ' - AJAURE T1 - - - 1 - 1 - 1 - 0 - 0 - 1.4 - 0.6 - 2 - - true - 5603 5602 0 '1 ' - ARENDAL T1 - 1 - - - 1 - 1 - 1 - 0 - 0 - 1.4 - 0.6 - 2 - - true - 5500 5501 0 '1 ' - ASKER T1 - - - 1 - 1 - 1 - 0 - 0 - 1.4 - 0.6 - 2 - - true - 5101 5100 0 '1 ' - HALDEN T1 - - - 1 - 1 - 1 - 0 - 0 - 1.4 - 0.6 - 2 - - true - 3244 3245 0 '1 ' - HOGASEN T1 - - - 1 - 1 - 1 - 0 - 0 - 1.4 - 0.6 - 2 - - true - 6000 6001 0 '1 ' - KVILLDALT1 - - - 1 - 1 - 1 - 0 - 0 - 1.4 - 0.6 - 2 - - true - 6700 6701 0 '1 ' - MO T1 - - - 1 - 1 - 1 - 0 - 0 - 1.4 - 0.6 - 2 - - true - 5400 5401 0 '1 ' - OSLO T1 - - - 1 - 1 - 1 - 0 - 0 - 1.4 - 0.6 - 2 - - true - 5400 5402 0 '1 ' - OSLO T2 - - - 1 - 1 - 1 - 0 - 0 - 1.4 - 0.6 - 2 - - true - 3359 3360 0 '1 ' - RINGHALST1 - - - 1 - 1 - 1 - 0 - 0 - 1.4 - 0.6 - 2 - - true - 5300 5301 0 '1 ' - SIMA T1 - 1 - - - 1 - 1 - 1 - 0 - 0 - 1.4 - 0.6 - 2 - - true - 5601 6001 0 '1 ' - STAVANGET1 - - - - 0 - - 0 - 0 - 0 - 1000 - 300 - 0 - 300 - - - 2 - false - 3701_3249_0_1 Primary Winding - AJAURE T1 P - 0 - 1000 - - - - 0 - - 0 - 0 - 3.528 - 1000 - 420 - 88.2 - 420 - - - 1 - false - 3701_3249_0_1 Secondary Winding - AJAURE T1 S - - - - 0 - - 0 - 0 - 0 - 1000 - 300 - 0 - 300 - - - 2 - false - 5603_5602_0_1 Primary Winding - ARENDALST1 P - 0 - 1000 - - - - 0 - - 0 - 0 - 0.14112 - 1000 - 420 - 5.3802 - 420 - - - 1 - false - 5603_5602_0_1 Secondary Winding - ARENDALST1 S - - - - 0 - - 0 - 0 - 0 - 1000 - 300 - 0 - 300 - - - 2 - false - 5500_5501_0_1 Primary Winding - ASKER T1 P - 0 - 1000 - - - - 0 - - 0 - 0 - 0 - 1000 - 300 - 0 - 300 - - - 2 - false - 5101_5100_0_1 Secondary Winding - HALDEN T1 S - - - - 0 - - 0 - 0 - 0 - 1000 - 300 - 0 - 300 - - - 2 - false - 3244_3245_0_1 Primary Winding - HOGASEN T1 P - 0 - 1000 - - - - 0 - - 0 - 0 - 0.882 - 1000 - 420 - 3.528 - 420 - - - 1 - false - 3244_3245_0_1 Secondary Winding - HOGASEN T1 S - - - - 0 - - 0 - 0 - 0 - 1000 - 300 - 0 - 300 - - - 2 - false - 6000_6001_0_1 Primary Winding - KVILLDALT1 P - 0 - 1000 - - - - 0 - - 0 - 0 - 0.07056 - 1000 - 420 - 2.646 - 420 - - - 1 - false - 6000_6001_0_1 Secondary Winding - KVILLDALT1 S - - - - 0 - - 0 - 0 - 0 - 1000 - 300 - 0 - 300 - - - 2 - false - 6700_6701_0_1 Primary Winding - MO T1 P - 0 - 1000 - - - - 0 - - 0 - 0 - 0.882 - 1000 - 420 - 3.528 - 420 - - - 1 - false - 6700_6701_0_1 Secondary Winding - MO T1 S - - - - 0 - - 0 - 0 - 0 - 1000 - 300 - 0 - 300 - - - 2 - false - 5400_5401_0_1 Primary Winding - OSLO T1 P - 0 - 1000 - - - - 0 - - 0 - 0 - 0.56448 - 1000 - 420 - 21.168 - 420 - - - 1 - false - 5400_5401_0_1 Secondary Winding - OSLO T1 S - - - - 0 - - 0 - 0 - 0 - 1000 - 300 - 0 - 300 - - - 2 - false - 5400_5402_0_1 Primary Winding - OSLO T2 P - 0 - 1000 - - - - 0 - - 0 - 0 - 0.07056 - 1000 - 420 - 2.646 - 420 - - - 1 - false - 5400_5402_0_1 Secondary Winding - OSLO T2 S - - - - 0 - - 0 - 0 - 0.07056 - 1000 - 420 - 2.646 - 420 - - - 1 - false - 5500_5501_0_1 Secondary Winding - OSLO T2 S - - - - 0 - - 0 - 0 - 0.882 - 1000 - 420 - 3.528 - 420 - - - 1 - false - 3359_3360_0_1 Primary Winding - RINGHALST1 P - 0 - 1000 - - - - 0 - - 0 - 0 - 0.14112 - 1000 - 420 - 5.3802 - 420 - - - 1 - false - 5101_5100_0_1 Primary Winding - RINGHALST1 P - 0 - 1000 - - - - 0 - - 0 - 0 - 0 - 1000 - 135 - 0 - 135 - - - 2 - false - 3359_3360_0_1 Secondary Winding - RINGHALST1 S - - - - 0 - - 0 - 0 - 0 - 1000 - 300 - 0 - 300 - - - 2 - false - 5300_5301_0_1 Primary Winding - SIMA T1 P - 0 - 1000 - - - - 0 - - 0 - 0 - 0.28224 - 1000 - 420 - 10.76 - 420 - - - 1 - false - 5300_5301_0_1 Secondary Winding - SIMA T1 S - - - - 0 - - 0 - 0 - 0 - 1000 - 300 - 0 - 300 - - - 2 - false - 5601_6001_0_1 Primary Winding - STAVANGET1 P - 0 - 1000 - - - - 0 - - 0 - 0 - 0.03528 - 1000 - 420 - 1.3406 - 420 - - - 1 - false - 5601_6001_0_1 Secondary Winding - STAVANGET1 S - - - - 2.5 - - - 33 - 1 - true - 17 - 300 - 17 - Primary Tap - AJAURE T1 P RTC1 - - - - 2.5 - - - 33 - 1 - true - 17 - 300 - 17 - Primary Tap - ARENDALST1 P RTC1 - - - - 2.5 - - - 33 - 1 - true - 17 - 300 - 18 - Primary Tap - ASKER T1 P RTC1 - - - - 2.5 - - - 33 - 1 - true - 17 - 300 - 17 - Primary Tap - HOGASEN T1 P RTC1 - - - - 2.5 - - - 33 - 1 - true - 17 - 300 - 17 - Primary Tap - KVILLDALT1 P RTC1 - - - - 2.5 - - - 33 - 1 - true - 17 - 300 - 18 - Primary Tap - MO T1 P RTC1 - - - - 2.5 - - - 33 - 1 - true - 17 - 300 - 17 - Primary Tap - OSLO T1 P RTC1 - - - - 2.5 - - - 33 - 1 - true - 17 - 300 - 17 - Primary Tap - OSLO T2 P RTC1 - - - - 2.5 - - - 33 - 1 - true - 17 - 420 - 18 - Primary Tap - RINGHALST1 P RTC1 - - - - 2.5 - - - 33 - 1 - true - 17 - 420 - 17 - Primary Tap - RINGHALST1 P RTC1 - - - - 2.5 - - - 33 - 1 - true - 17 - 300 - 16 - Primary Tap - SIMA T1 P RTC1 - - - - 2.5 - - - 33 - 1 - true - 17 - 300 - 17 - Primary Tap - STAVANGET1 P RTC1 - - - - - ASKER 300 RC1 - 5500 - - - - - BLAFALLI300 RC1 - 6100 - - - - - FORSMARK420 RC1 - 3000 - - - - - GRUNDFOR420 RC1 - 3249 - - - - - HELSINKI420 RC1 - 7000 - - - - - JARPSTRO420 RC1 - 3245 - - - - - KRISTIAN300 RC1 - - - - - KVILLDAL300 RC1 - 6000 - - - - - MALMO 420 RC1 - 8500 - - - - - MO 300 RC1 - 6700 - - - - - OSKARSHA420 RC1 - 3300 - - - - - OSLO 300 RC1 - 5400 - - - - - OULU 420 RC1 - 7100 - - - - - PORJUS 420 RC1 - 3115 - - - - - RINGHALS420 RC1 - 3359 - - - - - SIMA 300 RC1 - 5300 - - - - - TRETTEN 300 RC1 - 5100 - - - - - TRONDHEI300 RC1 - 6500 - - - - FI1 SGR - - - - - NO1 SGR - - - - NO2 SGR - - - - NO3 SGR - - - - NO4 SGR - - - - NO5 SGR - - - - SE1 SGR - - - - SE2 SGR - - - - SE3 SGR - - - - SE4 SGR - - - - FI1 SLA - - - - NO1 SLA - - - - NO2 SLA - - - - NO3 SLA - - - - NO4 SLA - - - - NO5 SLA - - - - SE1 SLA - - - - SE2 SLA - - - - SE3 SLA - - - - SE4 SLA - - - - AJAURE - - - - - ARENDAL - - - - - ARRIE_HVDC - - - - - ASKER - - - - - AURLAND - - - - - BLAFALLI - - - - - DAGALI - - - - - DANNEBO_HVDC - - - - - EIDFJORD - - - - - ESTLINK_HVDC - - - - - FEDA_HVDC - - - - - FORSMARK - - - - - GEILO - - - - - GRUNDFORS - - - - - HAGAFOSS - - - - - HALDEN - - - - - HELSINKI - - - - - HJALTA - - - - - HOGASEN - - - - - JARPSTROMMEN - - - - - KARLSH_HVDC - - - - - KONGSBERG - - - - - KRISTIANSAND - - - - - KRISTIA_HVDC - - - - - KVILLDAL - - - - - MALMO - - - - - MO - - - - - NARVIK - - - - - OSKARSHAMN - - - - - OSLO - - - - - OULU - - - - - PORJUS - - - - - RINGHALS - - - - - SANDEFJORD - - - - - SIMA - - - - - SKIEN - - - - - STAVANGER - - - - - STENKU_HVDC - - - - - SYLLING - - - - - SYSLE - - - - - TENHULT - - - - - TRETTEN - - - - - TRONDHEIM - - - - - VYBORG_HVDC - - - - 1200 - 301.2 - -1200 - 301.2 - 100 - 0 - - 14.1672413793 - - 1450 - - - 5500 '1 ' - ASKER 300 M1 - - - 1200 - 301.2 - -1200 - 301.2 - 100 - 0 - - 14.1672413793 - - 1450 - - - 5500 '2 ' - ASKER 300 M2 - - - 900 - 300 - -900 - 300 - 20 - 0 - - 13.064516129 - - 1240 - - - 6100 '1 ' - BLAFALLI300 M1 - - - 900 - 300 - -900 - 300 - 20 - 0 - - 13.064516129 - - 1240 - - - 6100 '2 ' - BLAFALLI300 M2 - - - 900 - 300 - -900 - 300 - 20 - 0 - - 13.064516129 - - 1240 - - - 6100 '3 ' - BLAFALLI300 M3 - - - 900 - 300 - -900 - 300 - 20 - 0 - - 13.064516129 - - 1240 - - - 6100 '4 ' - BLAFALLI300 M4 - - - 900 - 300 - -900 - 300 - 20 - 0 - - 13.064516129 - - 1240 - - - 6100 '5 ' - BLAFALLI300 M5 - - - 967 - 420 - -967 - 420 - 33.3 - 0 - - 30.5307692308 - - 1300 - - - 3000 '1 ' - FORSMARK420 M1 - - - 967 - 420 - -967 - 420 - 33.3 - 0 - - 30.5307692308 - - 1300 - - - 3000 '2 ' - FORSMARK420 M2 - - - 967 - 420 - -967 - 420 - 33.3 - 0 - - 30.5307692308 - - 1300 - - - 3000 '3 ' - FORSMARK420 M3 - - - 986 - 420 - -986 - 420 - 14.3 - 0 - - 27.2984524687 - - 1357 - - - 3249 '1 ' - GRUNDFOR420 M1 - - - 986 - 420 - -986 - 420 - 14.3 - 0 - - 27.2984524687 - - 1357 - - - 3249 '2 ' - GRUNDFOR420 M2 - - - 986 - 420 - -986 - 420 - 14.3 - 0 - - 27.2984524687 - - 1357 - - - 3249 '3 ' - GRUNDFOR420 M3 - - - 986 - 420 - -986 - 420 - 14.3 - 0 - - 27.2984524687 - - 1357 - - - 3249 '4 ' - GRUNDFOR420 M4 - - - 986 - 420 - -986 - 420 - 14.3 - 0 - - 27.2984524687 - - 1357 - - - 3249 '5 ' - GRUNDFOR420 M5 - - - 986 - 420 - -986 - 420 - 14.3 - 0 - - 27.2984524687 - - 1357 - - - 3249 '6 ' - GRUNDFOR420 M6 - - - 986 - 420 - -986 - 420 - 14.3 - 0 - - 27.2984524687 - - 1357 - - - 3249 '7 ' - GRUNDFOR420 M7 - - - 986 - 420 - -986 - 420 - 14.3 - 0 - - 27.2984524687 - - 1357 - - - 3249 '8 ' - GRUNDFOR420 M8 - - - 911 - 420 - -911 - 420 - 11.1 - 0 - - 31.0563380282 - - 1278 - - - 7000 '1 ' - HELSINKI420 M1 - - - 911 - 420 - -911 - 420 - 11.1 - 0 - - 31.0563380282 - - 1278 - - - 7000 '2 ' - HELSINKI420 M2 - - - 911 - 420 - -911 - 420 - 11.1 - 0 - - 31.0563380282 - - 1278 - - - 7000 '3 ' - HELSINKI420 M3 - - - 911 - 420 - -911 - 420 - 11.1 - 0 - - 31.0563380282 - - 1278 - - - 7000 '4 ' - HELSINKI420 M4 - - - 911 - 420 - -911 - 420 - 11.1 - 0 - - 31.0563380282 - - 1278 - - - 7000 '5 ' - HELSINKI420 M5 - - - 911 - 420 - -911 - 420 - 11.1 - 0 - - 31.0563380282 - - 1278 - - - 7000 '6 ' - HELSINKI420 M6 - - - 911 - 420 - -911 - 420 - 11.1 - 0 - - 31.0563380282 - - 1278 - - - 7000 '7 ' - HELSINKI420 M7 - - - 911 - 420 - -911 - 420 - 11.1 - 0 - - 31.0563380282 - - 1278 - - - 7000 '8 ' - HELSINKI420 M8 - - - 911 - 420 - -911 - 420 - 11.1 - 0 - - 31.0563380282 - - 1278 - - - 7000 '9 ' - HELSINKI420 M9 - - - 670 - 420 - -670 - 420 - 100 - 0 - - 28.5675157895 - - 950 - - - 3245 '1 ' - JARPSTRO420 M1 - - - 850 - 303 - -850 - 303 - 50 - 0 - - 15.2727272727 - - 1650 - - - 5600 '1 ' - KRISTIAN300 M1 - - false - - - 850 - 303 - -850 - 303 - 50 - 0 - - 15.2727272727 - - 1650 - - - 5600 '2 ' - KRISTIAN300 M2 - - false - - - 850 - 303 - -850 - 303 - 50 - 0 - - 15.2727272727 - - 1650 - - - 5600 '3 ' - KRISTIAN300 M3 - - false - - - 850 - 303 - -850 - 303 - 50 - 0 - - 15.2727272727 - - 1650 - - - 5600 '4 ' - KRISTIAN300 M4 - - false - - - 500 - 301.5 - -500 - 301.5 - 100 - 0 - - 37.0588235294 - - 680 - - - 6000 '1 ' - KVILLDAL300 M1 - - - 500 - 301.5 - -500 - 301.5 - 100 - 0 - - 37.0588235294 - - 680 - - - 6000 '2 ' - KVILLDAL300 M2 - - - 500 - 301.5 - -500 - 301.5 - 100 - 0 - - 37.0588235294 - - 680 - - - 6000 '3 ' - KVILLDAL300 M3 - - - 500 - 301.5 - -500 - 301.5 - 100 - 0 - - 37.0588235294 - - 680 - - - 6000 '4 ' - KVILLDAL300 M4 - - - 917 - 428.4 - -917 - 428.4 - 16.7 - 0 - - 23.1518215385 - - 1300 - - - 8500 '1 ' - MALMO 420 M1 - - - 917 - 428.4 - -917 - 428.4 - 16.7 - 0 - - 23.1518215385 - - 1300 - - - 8500 '2 ' - MALMO 420 M2 - - - 917 - 428.4 - -917 - 428.4 - 16.7 - 0 - - 23.1518215385 - - 1300 - - - 8500 '3 ' - MALMO 420 M3 - - - 917 - 428.4 - -917 - 428.4 - 16.7 - 0 - - 23.1518215385 - - 1300 - - - 8500 '4 ' - MALMO 420 M4 - - - 917 - 428.4 - -917 - 428.4 - 16.7 - 0 - - 23.1518215385 - - 1300 - - - 8500 '5 ' - MALMO 420 M5 - - - 917 - 428.4 - -917 - 428.4 - 16.7 - 0 - - 23.1518215385 - - 1300 - - - 8500 '6 ' - MALMO 420 M6 - - - 900 - 306 - -900 - 306 - 50 - 0 - - 12.7965 - - 1200 - - - 6700 '1 ' - MO 300 M1 - - - 900 - 306 - -900 - 306 - 50 - 0 - - 12.7965 - - 1200 - - - 6700 '2 ' - MO 300 M2 - - - 900 - 306 - -900 - 306 - 50 - 0 - - 12.7965 - - 1200 - - - 6700 '3 ' - MO 300 M3 - - - 900 - 306 - -900 - 306 - 50 - 0 - - 12.7965 - - 1200 - - - 6700 '4 ' - MO 300 M4 - - - 767 - 420 - -767 - 420 - 33.3 - 0 - - 25.6581818182 - - 1100 - - - 3300 '1 ' - OSKARSHA420 M1 - - - 767 - 420 - -767 - 420 - 33.3 - 0 - - 25.6581818182 - - 1100 - - - 3300 '2 ' - OSKARSHA420 M2 - - - 767 - 420 - -767 - 420 - 33.3 - 0 - - 25.6581818182 - - 1100 - - - 3300 '3 ' - OSKARSHA420 M3 - - - 767 - 420 - -767 - 420 - 33.3 - 0 - - 25.6581818182 - - 1100 - - - 3300 '4 ' - OSKARSHA420 M4 - - - 767 - 420 - -767 - 420 - 33.3 - 0 - - 25.6581818182 - - 1100 - - - 3300 '5 ' - OSKARSHA420 M5 - - - 767 - 420 - -767 - 420 - 33.3 - 0 - - 25.6581818182 - - 1100 - - - 3300 '6 ' - OSKARSHA420 M6 - - - 900 - 302.1 - -900 - 302.1 - 50 - 0 - - 11.7551020408 - - 1225 - - - 5400 '1 ' - OSLO 300 M1 - - - 900 - 302.1 - -900 - 302.1 - 50 - 0 - - 11.7551020408 - - 1225 - - - 5400 '2 ' - OSLO 300 M2 - - - 700 - 420 - -700 - 420 - 33.3 - 0 - - 27.13914 - - 1000 - - - 7100 '1 ' - OULU 420 M1 - - - 700 - 420 - -700 - 420 - 33.3 - 0 - - 27.13914 - - 1000 - - - 7100 '2 ' - OULU 420 M2 - - - 700 - 420 - -700 - 420 - 33.3 - 0 - - 27.13914 - - 1000 - - - 7100 '3 ' - OULU 420 M3 - - - 933 - 420 - -933 - 420 - 33.3 - 0 - - 36.8836363636 - - 1100 - - - 3115 '1 ' - PORJUS 420 M1 - - - 933 - 420 - -933 - 420 - 33.3 - 0 - - 36.8836363636 - - 1100 - - - 3115 '2 ' - PORJUS 420 M2 - - - 933 - 420 - -933 - 420 - 33.3 - 0 - - 36.8836363636 - - 1100 - - - 3115 '3 ' - PORJUS 420 M3 - - - 933 - 420 - -933 - 420 - 33.3 - 0 - - 36.8836363636 - - 1100 - - - 3115 '4 ' - PORJUS 420 M4 - - - 933 - 420 - -933 - 420 - 33.3 - 0 - - 36.8836363636 - - 1100 - - - 3115 '5 ' - PORJUS 420 M5 - - - 983 - 420 - -983 - 420 - 16.7 - 0 - - 25.3166666667 - - 1350 - - - 3359 '1 ' - RINGHALS420 M1 - - - 983 - 420 - -983 - 420 - 16.7 - 0 - - 25.3166666667 - - 1350 - - - 3359 '2 ' - RINGHALS420 M2 - - - 983 - 420 - -983 - 420 - 16.7 - 0 - - 25.3166666667 - - 1350 - - - 3359 '3 ' - RINGHALS420 M3 - - - 983 - 420 - -983 - 420 - 16.7 - 0 - - 25.3166666667 - - 1350 - - - 3359 '4 ' - RINGHALS420 M4 - - - 983 - 420 - -983 - 420 - 16.7 - 0 - - 25.3166666667 - - 1350 - - - 3359 '5 ' - RINGHALS420 M5 - - - 983 - 420 - -983 - 420 - 16.7 - 0 - - 25.3166666667 - - 1350 - - - 3359 '6 ' - RINGHALS420 M6 - - - 850 - 300 - -850 - 300 - 50 - 0 - - 19.5 - - 1200 - - - 5300 '1 ' - SIMA 300 M1 - - - 850 - 300 - -850 - 300 - 50 - 0 - - 19.5 - - 1200 - - - 5300 '2 ' - SIMA 300 M2 - - - 850 - 300 - -850 - 300 - 50 - 0 - - 19.5 - - 1200 - - - 5300 '3 ' - SIMA 300 M3 - - - 850 - 300 - -850 - 300 - 50 - 0 - - 19.5 - - 1200 - - - 5300 '4 ' - SIMA 300 M4 - - - 850 - 300 - -850 - 300 - 50 - 0 - - 19.5 - - 1200 - - - 5300 '5 ' - SIMA 300 M5 - - - 850 - 300 - -850 - 300 - 50 - 0 - - 19.5 - - 1200 - - - 5300 '6 ' - SIMA 300 M6 - - - 850 - 300 - -850 - 300 - 100 - 0 - - 11.35125 - - 1200 - - - 5100 '1 ' - TRETTEN 300 M1 - - - 850 - 300 - -850 - 300 - 100 - 0 - - 11.35125 - - 1200 - - - 5100 '2 ' - TRETTEN 300 M2 - - - 800 - 300 - -800 - 300 - 25 - 0 - - 12.9289090909 - - 1100 - - - 6500 '1 ' - TRONDHEI300 M1 - - - 800 - 300 - -800 - 300 - 25 - 0 - - 12.9289090909 - - 1100 - - - 6500 '2 ' - TRONDHEI300 M2 - - - 800 - 300 - -800 - 300 - 25 - 0 - - 12.9289090909 - - 1100 - - - 6500 '3 ' - TRONDHEI300 M3 - - - 800 - 300 - -800 - 300 - 25 - 0 - - 12.9289090909 - - 1100 - - - 6500 '4 ' - TRONDHEI300 M4 - - - - - AJAURE T1 P C1 - 3701 - - - - - ARENDALST1 P C1 - 5602 - - - - - ASKER T1 P C1 - 5501 - - - - - HOGASEN T1 P C1 - 3245 - - - - - KVILLDALT1 P C1 - 6001 - - - - - MO T1 P C1 - 6701 - - - - - OSLO T1 P C1 - 5401 - - - - - OSLO T2 P C1 - 5402 - - - - - RINGHALST1 P C1 - 3360 - - - - - RINGHALST1 P C1 - 5101 - - - - - SIMA T1 P C1 - 5301 - - - - - STAVANGET1 P C1 - 5601 - - - - - 300ASKER-ARENDAL T1 - 5500 5603 '1 ' - - 2 - - - - - 300KRISTIAN-ARENDAL T1 - 5603 5610 '1 ' - 1 - - - - - 300KRISTIAN-ARENDAL T2 - 5600 5603 '1 ' - 2 - - - - - 300KRISTIAN-ARENDAL_T1 - 5600 5603 '1 ' - 1 - - - - - 300KRISTIAN-FEDA_T1 - 5600 5620 '1 ' - 1 - - - - - 300KRISTIAN-KVILLDAL_T1 - 5600 6000 '1 ' - 1 - - - - - 300KRISTIAN-STAVANGE_T1 - 5600 5601 '1 ' - 1 - - - - - 420ARENDAL-SANDEFJORD T1 - 1 - - - - - ARENDAL 300 A T1 - 1 - - - - - ARENDAL 300 B T1 - 1 - - - - - ARENDAL 300 LSC1 T1 - Branch Shunt 5500_5603_1 - 1 - - - - - ARENDAL 300AS1 AB_S T1 - 1 - - - - - ARENDAL 300AS1 AB_S T2 - 2 - - - - - ARENDAL 300AS1 AD_S T1 - 1 - - - - - ARENDAL 300AS1 AD_S T2 - 2 - - - - - ARENDAL 300AS1 BB_S T1 - 1 - - - - - ARENDAL 300AS1 BB_S T2 - 2 - - - - - ARENDAL 300AS1 BD_S T1 - 1 - - - - - ARENDAL 300AS1 BD_S T2 - 2 - - - - - ARENDAL 300KR1 AB_S T1 - 1 - - - - - ARENDAL 300KR1 AB_S T2 - 2 - - - - - ARENDAL 300KR1 AD_S T1 - 1 - - - - - ARENDAL 300KR1 AD_S T2 - 2 - - - - - ARENDAL 300KR1 BB_S T1 - 1 - - - - - ARENDAL 300KR1 BB_S T2 - 2 - - - - - ARENDAL 300KR1 BD_S T1 - 1 - - - - - ARENDAL 300KR1 BD_S T2 - 2 - - - - - ARENDAL 300KR2 AB_S T1 - 1 - - - - - ARENDAL 300KR2 AB_S T2 - 2 - - - - - ARENDAL 300KR2 AD_S T1 - 1 - - - - - ARENDAL 300KR2 AD_S T2 - 2 - - - - - ARENDAL 300KR2 BB_S T1 - 1 - - - - - ARENDAL 300KR2 BB_S T2 - 2 - - - - - ARENDAL 300KR2 BD_S T1 - 1 - - - - - ARENDAL 300KR2 BD_S T2 - 2 - - - - - ARENDAL 300SC1 AB_S T1 - 1 - - - - - ARENDAL 300SC1 AB_S T2 - 2 - - - - - ARENDAL 300T1 AB_S T1 - 1 - - - - - ARENDAL 300T1 AB_S T2 - 2 - - - - - ARENDAL 420T1 AB_S T1 - 1 - - - - - ARENDAL 420T1 AB_S T2 - 2 - - - - - ARENDAL T1 T1 - 5603_5602_0_1 Primary Winding - 1 - - - - - ARENDAL T1 T2 - 5603_5602_0_1 Secondary Winding - 2 - - - - - KRISTIAN300 B BS T1 - 1 - - - - - KRISTIAN300 BS1 T1 - 1 - - - - - KRISTIAN300 L1 T1 - 5600 '1 ' - 1 - - - - - KRISTIAN300 L2 T1 - 5600 '2 ' - 1 - - - - - KRISTIAN300 M1 T1 - 5600 '1 ' - 1 - - - - - KRISTIAN300 M2 T1 - 5600 '2 ' - 1 - - - - - KRISTIAN300 M3 T1 - 5600 '3 ' - 1 - - - - - KRISTIAN300 M4 T1 - 5600 '4 ' - 1 - - - - - KRISTIAN300AR1 AB_S T1 - 1 - - - - - KRISTIAN300AR1 AB_S T2 - 2 - - - - - KRISTIAN300AR1 AD_S T1 - 1 - - - - - KRISTIAN300AR1 AD_S T2 - 2 - - - - - KRISTIAN300AR1 BB_S T1 - 1 - - - - - KRISTIAN300AR1 BB_S T2 - 2 - - - - - KRISTIAN300AR1 BD_S T1 - 1 - - - - - KRISTIAN300AR1 BD_S T2 - 2 - - - - - KRISTIAN300FE1 AB_S T1 - 1 - - - - - KRISTIAN300FE1 AB_S T2 - 2 - - - - - KRISTIAN300FE1 AD_S T1 - 1 - - - - - KRISTIAN300FE1 AD_S T2 - 2 - - - - - KRISTIAN300FE1 BB_S T1 - 1 - - - - - KRISTIAN300FE1 BB_S T2 - 2 - - - - - KRISTIAN300FE1 BD_S T1 - 1 - - - - - KRISTIAN300FE1 BD_S T2 - 2 - - - - - KRISTIAN300G1 AB_S T1 - 1 - - - - - KRISTIAN300G1 AB_S T2 - 2 - - - - - KRISTIAN300G1 AD_S T1 - 1 - - - - - KRISTIAN300G1 AD_S T2 - 2 - - - - - KRISTIAN300G1 BB_S T1 - 1 - - - - - KRISTIAN300G1 BB_S T2 - 2 - - - - - KRISTIAN300G1 BD_S T1 - 1 - - - - - KRISTIAN300G1 BD_S T2 - 2 - - - - - KRISTIAN300G2 AB_S T1 - 1 - - - - - KRISTIAN300G2 AB_S T2 - 2 - - - - - KRISTIAN300G2 AD_S T1 - 1 - - - - - KRISTIAN300G2 AD_S T2 - 2 - - - - - KRISTIAN300G2 BB_S T1 - 1 - - - - - KRISTIAN300G2 BB_S T2 - 2 - - - - - KRISTIAN300G2 BD_S T1 - 1 - - - - - KRISTIAN300G2 BD_S T2 - 2 - - - - - KRISTIAN300G3 AB_S T1 - 1 - - - - - KRISTIAN300G3 AB_S T2 - 2 - - - - - KRISTIAN300G3 AD_S T1 - 1 - - - - - KRISTIAN300G3 AD_S T2 - 2 - - - - - KRISTIAN300G3 BB_S T1 - 1 - - - - - KRISTIAN300G3 BB_S T2 - 2 - - - - - KRISTIAN300G3 BD_S T1 - 1 - - - - - KRISTIAN300G3 BD_S T2 - 2 - - - - - KRISTIAN300G4 AB_S T1 - 1 - - - - - KRISTIAN300G4 AB_S T2 - 2 - - - - - KRISTIAN300G4 AD_S T1 - 1 - - - - - KRISTIAN300G4 AD_S T2 - 2 - - - - - KRISTIAN300G4 BB_S T1 - 1 - - - - - KRISTIAN300G4 BB_S T2 - 2 - - - - - KRISTIAN300G4 BD_S T1 - 1 - - - - - KRISTIAN300G4 BD_S T2 - 2 - - - - - KRISTIAN300KV1 AB_S T1 - 1 - - - - - KRISTIAN300KV1 AB_S T2 - 2 - - - - - KRISTIAN300KV1 AD_S T1 - 1 - - - - - KRISTIAN300KV1 AD_S T2 - 2 - - - - - KRISTIAN300KV1 BB_S T1 - 1 - - - - - KRISTIAN300KV1 BB_S T1 - 1 - - - - - KRISTIAN300KV1 BB_S T2 - 2 - - - - - KRISTIAN300KV1 BB_S T2 - 2 - - - - - KRISTIAN300KV1 BD_S T1 - 1 - - - - - KRISTIAN300KV1 BD_S T2 - 2 - - - - - KRISTIAN300L1 AB_S T1 - 1 - - - - - KRISTIAN300L1 AB_S T2 - 2 - - - - - KRISTIAN300L1 AD_S T1 - 1 - - - - - KRISTIAN300L1 AD_S T2 - 2 - - - - - KRISTIAN300L1 BB_S T1 - 1 - - - - - KRISTIAN300L1 BB_S T2 - 2 - - - - - KRISTIAN300L1 BD_S T1 - 1 - - - - - KRISTIAN300L1 BD_S T2 - 2 - - - - - KRISTIAN300L2 AB_S T1 - 1 - - - - - KRISTIAN300L2 AB_S T2 - 2 - - - - - KRISTIAN300L2 AD_S T1 - 1 - - - - - KRISTIAN300L2 AD_S T2 - 2 - - - - - KRISTIAN300L2 BB_S T1 - 1 - - - - - KRISTIAN300L2 BB_S T2 - 2 - - - - - KRISTIAN300L2 BD_S T1 - 1 - - - - - KRISTIAN300L2 BD_S T2 - 2 - - - - - KRISTIAN300ST1 AB_S T1 - 1 - - - - - KRISTIAN300ST1 AB_S T2 - 2 - - - - - KRISTIAN300ST1 AD_S T1 - 1 - - - - - KRISTIAN300ST1 AD_S T2 - 2 - - - - - KRISTIAN300ST1 BD_S T1 - 1 - - - - - KRISTIAN300ST1 BD_S T2 - 2 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - Some Alias Name Which is not used since name exist - T1 - - - - - Alias Name - - - - - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - - - - - T1 - 3000 '1 ' - - - - - T1 - 3000 '2 ' - - - - - T1 - 3000 '3 ' - - - - - T1 - 3020 '1 ' - - - - - T1 - 3100 '1 ' - - - - - T1 - 3115 '1 ' - - - - - T1 - 3249 '1 ' - - - - - T1 - 3300 '1 ' - - - - - T1 - 3300 '2 ' - - - - - T1 - 3359 '1 ' - - - - - T1 - 3359 '2 ' - - - - - T1 - 3359 '3 ' - - - - - T1 - 3359 '4 ' - - - - - T1 - 3360 '1 ' - - - - - T1 - 5100 '1 ' - - - - - T1 - 5300 '2 ' - - - - - T1 - 5400 '1 ' - - - - - T1 - 5500 '1 ' - - - - - T1 - 5500 '2 ' - - - - - T1 - - - - - T1 - 5610 '1 ' - - - - - T1 - 5620 '1 ' - - - - - T1 - 6100 '1 ' - - - - - T1 - 6100 '2 ' - - - - - T1 - 6500 '1 ' - - - - - T1 - 6500 '2 ' - - - - - T1 - 6500 '3 ' - - - - - T1 - 6700 '1 ' - - - - - T1 - 6701 '1 ' - - - - - T1 - 6701 '3 ' - - - - - T1 - 7000 '1 ' - - - - - T1 - 7000 '2 ' - - - - - T1 - 7000 '3 ' - - - - - T1 - 7000 '4 ' - - - - - T1 - 7000 '5 ' - - - - - T1 - 7000 '6 ' - - - - - T1 - 7010 '1 ' - - - - - T1 - 7020 '1 ' - - - - - T1 - 7100 '1 ' - - - - - T1 - 7100 '2 ' - - - - - T1 - 7100 '3 ' - - - - - T1 - 8500 '1 ' - - - - - T1 - 8500 '2 ' - - - - - T1 - 8500 '3 ' - - - - - T1 - 8500 '4 ' - - - - - T1 - 8600 '1 ' - - - - - T1 - 8700 '1 ' - - - - - T1 - 3000 '1 ' - - - - - T1 - 3000 '2 ' - - - - - T1 - 3000 '3 ' - - - - - T1 - 3115 '1 ' - - - - - T1 - 3115 '2 ' - - - - - T1 - 3115 '3 ' - - - - - T1 - 3115 '4 ' - - - - - T1 - 3115 '5 ' - - - - - T1 - 3245 '1 ' - - - - - T1 - 3249 '1 ' - - - - - T1 - 3249 '2 ' - - - - - T1 - 3249 '3 ' - - - - - T1 - 3249 '4 ' - - - - - T1 - 3249 '5 ' - - - - - T1 - 3249 '6 ' - - - - - T1 - 3249 '7 ' - - - - - T1 - 3249 '8 ' - - - - - T1 - 3300 '1 ' - - - - - T1 - 3300 '2 ' - - - - - T1 - 3300 '3 ' - - - - - T1 - 3300 '4 ' - - - - - T1 - 3300 '5 ' - - - - - T1 - 3300 '6 ' - - - - - T1 - 3359 '1 ' - - - - - T1 - 3359 '2 ' - - - - - T1 - 3359 '3 ' - - - - - T1 - 3359 '4 ' - - - - - T1 - 3359 '5 ' - - - - - T1 - 3359 '6 ' - - - - - T1 - 5100 '1 ' - - - - - T1 - 5100 '2 ' - - - - - T1 - 5300 '1 ' - - - - - T1 - 5300 '2 ' - - - - - T1 - 5300 '3 ' - - - - - T1 - 5300 '4 ' - - - - - T1 - 5300 '5 ' - - - - - T1 - 5300 '6 ' - - - - - T1 - 5400 '1 ' - - - - - T1 - 5400 '2 ' - - - - - T1 - 5500 '1 ' - - - - - T1 - 5500 '2 ' - - - - - T1 - 6000 '1 ' - - - - - T1 - 6000 '2 ' - - - - - T1 - 6000 '3 ' - - - - - T1 - 6000 '4 ' - - - - - T1 - 6100 '1 ' - - - - - T1 - 6100 '2 ' - - - - - T1 - 6100 '3 ' - - - - - T1 - 6100 '4 ' - - - - - T1 - 6100 '5 ' - - - - - T1 - 6500 '1 ' - - - - - T1 - 6500 '2 ' - - - - - T1 - 6500 '3 ' - - - - - T1 - 6500 '4 ' - - - - - T1 - 6700 '1 ' - - - - - T1 - 6700 '2 ' - - - - - T1 - 6700 '3 ' - - - - - T1 - 6700 '4 ' - - - - - T1 - 7000 '1 ' - - - - - T1 - 7000 '2 ' - - - - - T1 - 7000 '3 ' - - - - - T1 - 7000 '4 ' - - - - - T1 - 7000 '5 ' - - - - - T1 - 7000 '6 ' - - - - - T1 - 7000 '7 ' - - - - - T1 - 7000 '8 ' - - - - - T1 - 7000 '9 ' - - - - - T1 - 7100 '1 ' - - - - - T1 - 7100 '2 ' - - - - - T1 - 7100 '3 ' - - - - - T1 - 8500 '1 ' - - - - - T1 - 8500 '2 ' - - - - - T1 - 8500 '3 ' - - - - - T1 - 8500 '4 ' - - - - - T1 - 8500 '5 ' - - - - - T1 - 8500 '6 ' - - - - - T1 - 3000 3020 '1 ' - 1 - - - - - T1 - 3000 3115 '1 ' - 1 - - - - - T1 - 3000 3245 '1 ' - 1 - - - - - T1 - 3000 3245 '2 ' - 1 - - - - - T1 - 3000 3300 '1 ' - 1 - - - - - T1 - 3000 3300 '2 ' - 1 - - - - - T1 - 3100 3115 '1 ' - 1 - - - - - T1 - 3100 3200 '1 ' - 1 - - - - - T1 - 3100 3200 '2 ' - 1 - - - - - T1 - 3100 3200 '3 ' - 1 - - - - - T1 - 3100 3249 '1 ' - 1 - - - - - T1 - 3100 3359 '1 ' - 1 - - - - - T1 - 3100 3359 '2 ' - 1 - - - - - T1 - 3115 3245 '1 ' - 1 - - - - - T1 - 3115 3249 '1 ' - 1 - - - - - T1 - 3115 6701 '1 ' - 1 - - - - - T1 - 3115 7100 '1 ' - 1 - - - - - T1 - 3200 3300 '1 ' - 1 - - - - - T1 - 3200 3359 '1 ' - 1 - - - - - T1 - 3200 8500 '1 ' - 1 - - - - - T1 - 3244 6500 '1 ' - 1 - - - - - T1 - 3249 7100 '1 ' - 1 - - - - - T1 - 3300 8500 '1 ' - 1 - - - - - T1 - 3300 8500 '2 ' - 1 - - - - - T1 - 3359 5101 '1 ' - 1 - - - - - T1 - 3359 5101 '2 ' - 1 - - - - - T1 - 3359 8500 '1 ' - 1 - - - - - T1 - 3359 8500 '2 ' - 1 - - - - - T1 - 3701 6700 '1 ' - 1 - - - - - T1 - 5100 5500 '1 ' - 1 - - - - - T1 - 5100 6500 '1 ' - 1 - - - - - T1 - 5101 5102 '1 ' - 1 - - - - - T1 - 5101 5103 '1 ' - 1 - - - - - T1 - 5101 5501 '1 ' - 1 - - - - - T1 - Branch Shunt 5101_5501_1 - - - - - T1 - Branch Shunt 5101_5501_1 - - - - - T1 - 5102 5103 '1 ' - 1 - - - - - T1 - 5102 5304 '1 ' - 1 - - - - - T1 - 5102 6001 '1 ' - 1 - - - - - T1 - Branch Shunt 5102_6001_1 - - - - - T1 - Branch Shunt 5102_6001_1 - - - - - T1 - 5103 5304 '1 ' - 1 - - - - - T1 - 5103 5304 '2 ' - 1 - - - - - T1 - 5300 6100 '1 ' - 1 - - - - - T1 - 5301 5304 '1 ' - 1 - - - - - T1 - 5301 5305 '1 ' - 1 - - - - - T1 - 5301 6001 '1 ' - 1 - - - - - T1 - 5304 5305 '1 ' - 1 - - - - - T1 - 5304 5305 '2 ' - 1 - - - - - T1 - 5400 5500 '1 ' - 1 - - - - - T1 - 5400 6000 '1 ' - 1 - - - - - T1 - 5401 5501 '1 ' - 1 - - - - - T1 - 5401 5602 '1 ' - 1 - - - - - T1 - 5401 6001 '1 ' - 1 - - - - - T1 - Branch Shunt 5401_6001_1 - - - - - T1 - Branch Shunt 5401_6001_1 - - - - - T1 - 5402 6001 '1 ' - 1 - - - - - T1 - 5500 5603 '1 ' - 1 - - - - - T1 - Branch Shunt 5500_5603_1 - - - - - T1 - 6000 6100 '1 ' - 1 - - - - - T1 - 6500 6700 '1 ' - 1 - - - - - T1 - 6500 6700 '2 ' - 1 - - - - - T1 - 7000 7010 '1 ' - 1 - - - - - T1 - 7000 7020 '1 ' - 1 - - - - - T1 - 7000 7100 '1 ' - 1 - - - - - T1 - 7000 7100 '2 ' - 1 - - - - - T1 - 7000 7100 '3 ' - 1 - - - - - T1 - 8500 8600 '1 ' - 1 - - - - - T1 - 8500 8700 '1 ' - 1 - - - - - T1 - 3244_3245_0_1 Primary Winding - 1 - - - - - T1 - 3701_3249_0_1 Primary Winding - 1 - - - - - T1 - 3359_3360_0_1 Primary Winding - 1 - - - - - T1 - 5101_5100_0_1 Primary Winding - 1 - - - - - T1 - 5300_5301_0_1 Primary Winding - 1 - - - - - T1 - 5400_5401_0_1 Primary Winding - 1 - - - - - T1 - 5400_5402_0_1 Primary Winding - 1 - - - - - T1 - 5500_5501_0_1 Primary Winding - 1 - - - - - T1 - 5601_6001_0_1 Primary Winding - 1 - - - - - T1 - 6000_6001_0_1 Primary Winding - 1 - - - - - T1 - 6700_6701_0_1 Primary Winding - 1 - - - - - T2 - 3000 3020 '1 ' - 2 - - - - - T2 - 3000 3115 '1 ' - 2 - - - - - T2 - 3000 3245 '1 ' - 2 - - - - - T2 - 3000 3245 '2 ' - 2 - - - - - T2 - 3000 3300 '1 ' - 2 - - - - - T2 - 3000 3300 '2 ' - 2 - - - - - T2 - 3100 3115 '1 ' - 2 - - - - - T2 - 3100 3200 '1 ' - 2 - - - - - T2 - 3100 3200 '2 ' - 2 - - - - - T2 - 3100 3200 '3 ' - 2 - - - - - T2 - 3100 3249 '1 ' - 2 - - - - - T2 - 3100 3359 '1 ' - 2 - - - - - T2 - 3100 3359 '2 ' - 2 - - - - - T2 - 3115 3245 '1 ' - 2 - - - - - T2 - 3115 3249 '1 ' - 2 - - - - - T2 - 3115 6701 '1 ' - 2 - - - - - T2 - 3115 7100 '1 ' - 2 - - - - - T2 - 3200 3300 '1 ' - 2 - - - - - T2 - 3200 3359 '1 ' - 2 - - - - - T2 - 3200 8500 '1 ' - 2 - - - - - T2 - 3244 6500 '1 ' - 2 - - - - - T2 - 3249 7100 '1 ' - 2 - - - - - T2 - 3300 8500 '1 ' - 2 - - - - - T2 - 3300 8500 '2 ' - 2 - - - - - T2 - 3359 5101 '1 ' - 2 - - - - - T2 - 3359 5101 '2 ' - 2 - - - - - T2 - 3359 8500 '1 ' - 2 - - - - - T2 - 3359 8500 '2 ' - 2 - - - - - T2 - 3701 6700 '1 ' - 2 - - - - - T2 - 5100 5500 '1 ' - 2 - - - - - T2 - 5100 6500 '1 ' - 2 - - - - - T2 - 5101 5102 '1 ' - 2 - - - - - T2 - 5101 5103 '1 ' - 2 - - - - - T2 - 5101 5501 '1 ' - 2 - - - - - T2 - 5102 5103 '1 ' - 2 - - - - - T2 - 5102 5304 '1 ' - 2 - - - - - T2 - 5102 6001 '1 ' - 2 - - - - - T2 - 5103 5304 '1 ' - 2 - - - - - T2 - 5103 5304 '2 ' - 2 - - - - - T2 - 5300 6100 '1 ' - 2 - - - - - T2 - 5301 5304 '1 ' - 2 - - - - - T2 - 5301 5305 '1 ' - 2 - - - - - T2 - 5301 6001 '1 ' - 2 - - - - - T2 - 5304 5305 '1 ' - 2 - - - - - T2 - 5304 5305 '2 ' - 2 - - - - - T2 - 5400 5500 '1 ' - 2 - - - - - T2 - 5400 6000 '1 ' - 2 - - - - - T2 - 5401 5501 '1 ' - 2 - - - - - T2 - 5401 5602 '1 ' - 2 - - - - - T2 - 5401 6001 '1 ' - 2 - - - - - T2 - 5402 6001 '1 ' - 2 - - - - - T2 - 5600 5601 '1 ' - 2 - - - - - T2 - 5600 5620 '1 ' - 2 - - - - - T2 - 5600 6000 '1 ' - 2 - - - - - T2 - 5603 5610 '1 ' - 2 - - - - - T2 - 6000 6100 '1 ' - 2 - - - - - T2 - 6500 6700 '1 ' - 2 - - - - - T2 - 6500 6700 '2 ' - 2 - - - - - T2 - 7000 7010 '1 ' - 2 - - - - - T2 - 7000 7020 '1 ' - 2 - - - - - T2 - 7000 7100 '1 ' - 2 - - - - - T2 - 7000 7100 '2 ' - 2 - - - - - T2 - 7000 7100 '3 ' - 2 - - - - - T2 - 8500 8600 '1 ' - 2 - - - - - T2 - 8500 8700 '1 ' - 2 - - - - - T2 - 3244_3245_0_1 Secondary Winding - 2 - - - - - T2 - 3701_3249_0_1 Secondary Winding - 2 - - - - - T2 - 3359_3360_0_1 Secondary Winding - 2 - - - - - T2 - 5101_5100_0_1 Secondary Winding - 2 - - - - - T2 - 5300_5301_0_1 Secondary Winding - 2 - - - - - T2 - 5400_5401_0_1 Secondary Winding - 2 - - - - - T2 - 5400_5402_0_1 Secondary Winding - 2 - - - - - T2 - 5500_5501_0_1 Secondary Winding - 2 - - - - - T2 - 5601_6001_0_1 Secondary Winding - 2 - - - - - T2 - 6000_6001_0_1 Secondary Winding - 2 - - - - - T2 - 6700_6701_0_1 Secondary Winding - 2 - - - - - T2 - 2 - - - - - AJAURE 300 - - - - - ARENDAL 300 - - - - - ARENDAL 420 - - - - - ARRIE 420 - - - - - ASKER 300 - - - - - AURLAND 420 - - - - - BLAFALLI300 - - - - - DAGALI 420 - - - - - DANNEBO 420 - - - - - EIDFJORD420 - - - - - ESTLINK 420 - - - - - FEDA 300 - - - - - FORSMARK420 - - - - - GEILO 420 - - - - - GRUNDFOR420 - - - - - HAGAFOSS420 - - - - - HALDEN 420 - - - - - HELSINKI420 - - - - - HJALTA 420 - - - - - HOGASEN 300 - - - - - JARPSTRO420 - - - - - KARLSH 420 - - - - - KONGSBER420 - - - - - KRISTIA 300 - - - - - KRISTIAN300 - - - - - KVILLDAL300 - - - - - MALMO 420 - - - - - MO 300 - - - - - NARVIK 420 - - - - - OSKARSHA420 - - - - - OSLO 300 - - - - - OULU 420 - - - - - PORJUS 420 - - - - - RINGHALS420 - - - - - SANDEFJO420 - - - - - SIMA 300 - - - - - SKIEN 420 - - - - - STAVANGE300 - - - - - STEENKU 135 - - - - - SYLLING 420 - - - - - SYSLE 420 - - - - - TENHULT 420 - - - - - TRETTEN 300 - - - - - TRONDHEI300 - - - - - VYBORG 420 - - - 462 - - - EVHI - 462 - - - 462 - - - EVHI - 462 - - - 462 - - - EVHI - 462 - - - 462 - - - EVHI - 462 - - - 462 - - - EVHI - 462 - - - 330 - - - EVHI - 330 - - - 462 - - - EVHI - 462 - - - 462 - - - EVHI - 462 - - - 462 - - - EVHI - 462 - - - 462 - - - EVHI - 462 - - - 148.5 - - - EVHI - 148.5 - - - 330 - - - EVHI - 330 - - - 330 - - - EVHI - 330 - - - 462 - - - EVHI - 462 - - - 462 - - - EVHI - 462 - - - 462 - - - EVHI - 462 - - - 330 - - - EVHI - 330 - - - 462 - - - EVHI - 462 - - - 462 - - - EVHI - 462 - - - 462 - - - EVHI - 462 - - - 330 - - - EVHI - 330 - - - 462 - - - EVHI - 462 - - - 462 - - - EVHI - 462 - - - 330 - - - EVHI - 330 - - - 462 - - - EVHI - 462 - - - 330 - - - EVHI - 330 - - - 330 - - - EVHI - 330 - - - 462 - - - EVHI - 462 - - - 330 - - - EVHI - 330 - - - 330 - - - EVHI - 330 - - - 330 - - - EVHI - 330 - - - 330 - - - EVHI - 330 - - - 462 - - - EVHI - 462 - - - 330 - - - EVHI - 330 - - - 330 - - - EVHI - 330 - - - 330 - - - EVHI - 330 - - - 462 - - - EVHI - 462 - - - 462 - - - EVHI - 462 - - - 462 - - - EVHI - 462 - - - 462 - - - EVHI - 462 - - - 462 - - - EVHI - 462 - - - 462 - - - EVHI - 462 - - - 462 - - - EVHI - 462 - - - 462 - - - EVHI - 462 - - - 330 - - - EVHI - 330 - - - 330 - - - EVHI - 330 - - - 378 - - - EVLO - 378 - - - 378 - - - EVLO - 378 - - - 378 - - - EVLO - 378 - - - 378 - - - EVLO - 378 - - - 378 - - - EVLO - 378 - - - 270 - - - EVLO - 270 - - - 378 - - - EVLO - 378 - - - 378 - - - EVLO - 378 - - - 378 - - - EVLO - 378 - - - 378 - - - EVLO - 378 - - - 121.5 - - - EVLO - 121.5 - - - 270 - - - EVLO - 270 - - - 270 - - - EVLO - 270 - - - 378 - - - EVLO - 378 - - - 378 - - - EVLO - 378 - - - 378 - - - EVLO - 378 - - - 270 - - - EVLO - 270 - - - 378 - - - EVLO - 378 - - - 378 - - - EVLO - 378 - - - 378 - - - EVLO - 378 - - - 270 - - - EVLO - 270 - - - 378 - - - EVLO - 378 - - - 378 - - - EVLO - 378 - - - 270 - - - EVLO - 270 - - - 378 - - - EVLO - 378 - - - 270 - - - EVLO - 270 - - - 270 - - - EVLO - 270 - - - 378 - - - EVLO - 378 - - - 270 - - - EVLO - 270 - - - 270 - - - EVLO - 270 - - - 270 - - - EVLO - 270 - - - 270 - - - EVLO - 270 - - - 378 - - - EVLO - 378 - - - 270 - - - EVLO - 270 - - - 270 - - - EVLO - 270 - - - 270 - - - EVLO - 270 - - - 378 - - - EVLO - 378 - - - 378 - - - EVLO - 378 - - - 378 - - - EVLO - 378 - - - 378 - - - EVLO - 378 - - - 378 - - - EVLO - 378 - - - 378 - - - EVLO - 378 - - - 378 - - - EVLO - 378 - - - 378 - - - EVLO - 378 - - - 270 - - - EVLO - 270 - - - 270 - - - EVLO - 270 - - - 462 - - - NVHI - 462 - - - 462 - - - NVHI - 462 - - - 462 - - - NVHI - 462 - - - 462 - - - NVHI - 462 - - - 462 - - - NVHI - 462 - - - 330 - - - NVHI - 330 - - - 462 - - - NVHI - 462 - - - 462 - - - NVHI - 462 - - - 462 - - - NVHI - 462 - - - 462 - - - NVHI - 462 - - - 148.5 - - - NVHI - 148.5 - - - 330 - - - NVHI - 330 - - - 330 - - - NVHI - 330 - - - 462 - - - NVHI - 462 - - - 462 - - - NVHI - 462 - - - 462 - - - NVHI - 462 - - - 330 - - - NVHI - 330 - - - 462 - - - NVHI - 462 - - - 462 - - - NVHI - 462 - - - 462 - - - NVHI - 462 - - - 330 - - - NVHI - 330 - - - 462 - - - NVHI - 462 - - - 462 - - - NVHI - 462 - - - 330 - - - NVHI - 330 - - - 462 - - - NVHI - 462 - - - 330 - - - NVHI - 330 - - - 330 - - - NVHI - 330 - - - 462 - - - NVHI - 462 - - - 330 - - - NVHI - 330 - - - 330 - - - NVHI - 330 - - - 330 - - - NVHI - 330 - - - 330 - - - NVHI - 330 - - - 462 - - - NVHI - 462 - - - 330 - - - NVHI - 330 - - - 330 - - - NVHI - 330 - - - 330 - - - NVHI - 330 - - - 462 - - - NVHI - 462 - - - 462 - - - NVHI - 462 - - - 462 - - - NVHI - 462 - - - 462 - - - NVHI - 462 - - - 462 - - - NVHI - 462 - - - 462 - - - NVHI - 462 - - - 462 - - - NVHI - 462 - - - 462 - - - NVHI - 462 - - - 330 - - - NVHI - 330 - - - 330 - - - NVHI - 330 - - - 378 - - - NVLO - 378 - - - 378 - - - NVLO - 378 - - - 378 - - - NVLO - 378 - - - 378 - - - NVLO - 378 - - - 378 - - - NVLO - 378 - - - 270 - - - NVLO - 270 - - - 378 - - - NVLO - 378 - - - 378 - - - NVLO - 378 - - - 378 - - - NVLO - 378 - - - 378 - - - NVLO - 378 - - - 121.5 - - - NVLO - 121.5 - - - 270 - - - NVLO - 270 - - - 270 - - - NVLO - 270 - - - 378 - - - NVLO - 378 - - - 378 - - - NVLO - 378 - - - 378 - - - NVLO - 378 - - - 270 - - - NVLO - 270 - - - 378 - - - NVLO - 378 - - - 378 - - - NVLO - 378 - - - 378 - - - NVLO - 378 - - - 270 - - - NVLO - 270 - - - 378 - - - NVLO - 378 - - - 378 - - - NVLO - 378 - - - 270 - - - NVLO - 270 - - - 378 - - - NVLO - 378 - - - 270 - - - NVLO - 270 - - - 270 - - - NVLO - 270 - - - 378 - - - NVLO - 378 - - - 270 - - - NVLO - 270 - - - 270 - - - NVLO - 270 - - - 270 - - - NVLO - 270 - - - 270 - - - NVLO - 270 - - - 378 - - - NVLO - 378 - - - 270 - - - NVLO - 270 - - - 270 - - - NVLO - 270 - - - 270 - - - NVLO - 270 - - - 378 - - - NVLO - 378 - - - 378 - - - NVLO - 378 - - - 378 - - - NVLO - 378 - - - 378 - - - NVLO - 378 - - - 378 - - - NVLO - 378 - - - 378 - - - NVLO - 378 - - - 378 - - - NVLO - 378 - - - 378 - - - NVLO - 378 - - - 270 - - - NVLO - 270 - - - 270 - - - NVLO - 270 - - - CS 420RINGHALS-HALDEN 1 - Circuit contribution share of CC 420RINGHALS-HALDEN 1 on PTC 420RINGHALS-HALDEN - 1 - - - - CS 420RINGHALS-HALDEN 2 - Circuit contribution share of CC 420RINGHALS-HALDEN 2 on PTC 420RINGHALS-HALDEN - 1 - - - - FI-ELSP-1 - - Finland - FI-ELSP-1 - - - NO-ELSP-1 - Elspot NO1 - NO1 - 2011-05-16: Eastern Norway. South of Vågåmo (300kV) and Litjfo. East of Rød 420kV), Rjukan (420kV), Vemorktopp00kV), Hof (300kV) and Fardal (300kV). - NO1 - - - NO-ELSP-2 - Elspot NO2 - NO2 - 2011-05-16: Southern Norway. West of Hasle (420kV), Sylling (4kV). East of Sauda (300kV) - NO2 - - - NO-ELSP-3 - Elspot NO3 - NO3 - 2011-05-16: Mid Part of Norway, n.o.Ø.Vinstra (300kV), n.o.Litjf(132 kV bus bars), s.o.Tunnsjødal-Verdal (300kV), s (300kV) - NO3 - - - NO-ELSP-4 - Elspot NO4 - NO4 - 2011-05-16: Mid Part of Norway, n.o.Tunnsjødal-Verdal (300kV),os (300kV) Northern Norway - NO4 - - - NO-ELSP-5 - Elspot NO5 - NO5 - 2011-05-16: Western Norway. West of Hylen (300kv), Nesflaten (300kv) - NO5 - - - SE-ELSP-1 - Elspot SE1 - Sverige 1 - SE-ELSP-1 - - - SE-ELSP-2 - Elspot SE2 - Sverige 2 - SE-ELSP-2 - - - SE-ELSP-3 - Sverige 3 - SE-ELSP-3 - - - SE-ELSP-4 - Elspot SE4 - Sverige 4 - SE-ELSP-4 - - - FI-SCAR-1 - SA FI - Finland - - - - - NO-SCAR-1 - SA NO1 - Viken North - - - - - NO-SCAR-2 - SA NO2 - NO2 - 2011-05-16: Southern Norway. West of Hasle (420kV), Sylling (4kV). East of Sauda (300kV) - - - - - NO-SCAR-3 - SA NO3 - NO3 - 2011-05-16: Mid Part of Norway, n.o.Ø.Vinstra (300kV), n.o.Litjf(132 kV bus bars), s.o.Tunnsjødal-Verdal (300kV), s (300kV) - - - - - NO-SCAR-4 - SA NO4 - NO4 - 2011-05-16: Mid Part of Norway, n.o.Tunnsjødal-Verdal (300kV),os (300kV) Northern Norway - - - - - NO-SCAR-5 - SA NO5 - NO5 - 2011-05-16: Western Norway. West of Hylen (300kv), Nesflaten (300kv) - - - - - SE-SCAR-1 - SA SE1 - Sverige 1 - - - - - SE-SCAR-2 - SA SE2 - Sverige 2 - - - - - SE-SCAR-3 - SA SE3 - Sverige 3 - - - - - SE-SCAR-4 - SA SE4 - Sverige 4 - - - - - NO-SCAR-6 - SA Ostfold - Energy Scheduling Area Ostfold - - - - - CC 420RINGHALS-HALDEN 1 - Line Circuit container for 420RINGHALS-HALDEN 1. - - - - - CC 420RINGHALS-HALDEN 2 - Line Circuit container for 420RINGHALS-HALDEN 2. - - - - - PTC 420RINGHALS-HALDEN - PowerTransferCorridor 420RINGHALS-HALDEN - - - -10 - Negative 10 degree Celsius - -10degreeCelsius - - - -15 - Negative 15 degree Celsius - -15degreeCelsius - - - -20 - Negative 20 degree Celsius - -20degreeCelsius - - - -25 - Negative 25 degree Celsius - -25degreeCelsius - - - -30 - Negative 30 degree Celsius - -30degreeCelsius - - - -5 - Negative 5 degree Celsius - -5degreeCelsius - - - 0 - 0 degree Celsius - 0degreeCelsius - - - 10 - 10 degree Celsius - 10degreeCelsius - - - 15 - 15 degree Celsius - 15degreeCelsius - - - 20 - 20 degree Celsius - 20degreeCelsius - - - 25 - 25 degree Celsius - 25degreeCelsius - - - 30 - 30 degree Celsius - 30degreeCelsius - - - 5 - 5 degree Celsius - 5degreeCelsius - - - AND G 300KRISTIAN-ARENDAL - - - - AND G 300KRISTIAN-ARENDAL F DIR - - Check the flow and direction on 300KRISTIAN-ARENDAL. - - - AND G 300KRISTIAN-KVILLDAL F DIR - - Check the flow and direction on 300KRISTIAN-KVILLDAL. - - - OR G KRISTIAN300 VL - - - - OR G NorNed - - - - GP 300KRISTIAN-ARENDAL FLOW - One out of two check for monitoring for flow on the line. This check for the amount. - - - - 1.15 - - - - GP 300KRISTIAN-ARENDAL IC - One out of two check for monitoring for tripping. This check that the line not connected - - - - true - - - GP 300KRISTIAN-ARENDAL IS - One out of two check for monitoring for tripping. This check that the line is in service - - - - - - GP 300KRISTIAN-KVILLDAL FLOW - One out of two check for monitoring for flow on the line. This check for the amount. - - - - 1.15 - - - - GP 300KRISTIAN-ARENDAL FLOW DIR - Output from the flow and direction gate for 300KRISTIAN-ARENDAL. - - - - - GP 300KRISTIAN-KVILLDAL FLOW DIR - Output from the flow and direction gate for 300KRISTIAN-KVILLDAL. - - - - - GP 300KRISTIAN-ARENDAL DIR - One out of two check for monitoring for flow on the line. This check for the direction. - - - 0 - - - - - GP 300KRISTIAN-KVILLDAL DIR - One out of two check for monitoring for flow on the line. This check for the direction. - - - 0 - - - - - GP KRISTIAN300 A VL - Check the voltage level on the terminal of the busbarsection. - - - 270 - - - - - GP KRISTIAN300 B VL - Check the voltage level on the terminal of the busbarsection. - - - 270 - - - - - PAA FEDA 300 L1 - true - - 150 - - true - false - - - - PAA KVI KRISTIAN G1 - true - - 25 - - true - false - - - - PAA KVI KRISTIAN G2 - true - - 30 - - true - false - - - - PAC FEDA PAA - - - PAC KRISTIAN-ARENDAL PAA - - - PAC KRISTIAN300 L2 PAA - Isolating the load KRISTIAN300 L2. - - - PAA KRISTIAN300 L2 - true - - false - - - - PAA KRISTIAN300L1 AD_S - true - - false - - - - PAA KRISTIAN300L2 AB_S - true - - false - - - - PAA KRISTIAN300L2 BB_S - true - - false - - - - PAA KRISTIAN300L2 BD_S - true - - false - - - - SIPS 300KRISTIAN-ARENDAL - true - - - - SIPS KRISTIAN300 VL - true - - - - SIPS NorNed - - true - - - S1 KRI-ARE - - 1 - - - S1 KRISTIAN300 VL - - 1 - - - S1 NorNed - - 1 - - - ST1 S1 KRI-ARE - - - true - - - - ST1 S1 KRISTIAN300 VL - - - true - - - - ST1 S1 NorNed - - - - true - - - Blafalli_SR - Blafalli_SR - NOKG11 - - - Forsmark_SR - Forsmark_SR - NOKG01 - - - Helsinki_SR - Helsinki_SR - NOKG14 - - - Jarpstrommen_Grundfors_SR - Jarpstrommen_Grundfors_SR - NOKG03 - - - Kristiansand_SR - Kristiansand_SR - NOKG09 - - - Kvilldal_SR - Kvilldal_SR - NOKG10 - - - Malmo_SR - Malmo_SR - NOKG16 - - - Oskarshamn_SR - Oskarshamn_SR - NOKG04 - - - Oslo_SR - Oslo_SR - NOKG08 - - - Oulu_SR - Oulu_SR - NOKG15 - - - Porjus_SR - Porjus_SR - NOKG02 - - - Ringhals_SR - Ringhals_SR - NOKG05 - - - Rossaga_SR - Rossaga_SR - NOKG13 - - - Sima_SR - Sima_SR - NOKG07 - - - Tretten_SR - Tretten_SR - NOKG06 - - - Trondheim_SR - Trondheim_SR - NOKG12 - - diff --git a/cognite/neat/legacy/graph/examples/__init__.py b/cognite/neat/legacy/graph/examples/__init__.py deleted file mode 100644 index a7607d707..000000000 --- a/cognite/neat/legacy/graph/examples/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -from pathlib import Path - -# we should make this a proper package that loads examples -# similar how they handle it in xarray: -# https://github.com/pydata/xarray/blob/main/xarray/tutorial.py -# Currently there are simple paths to the examples which are then easily loaded in the notebooks -nordic44_knowledge_graph = Path(__file__).parent / "Knowledge-Graph-Nordic44.xml" -nordic44_knowledge_graph_dirty = ( - Path(__file__).parent / "Knowledge-Graph-Nordic44-dirty.xml" -) diff --git a/cognite/neat/legacy/graph/examples/skos-capturing-sheet-wind-topics.xlsx b/cognite/neat/legacy/graph/examples/skos-capturing-sheet-wind-topics.xlsx deleted file mode 100644 index 70fb9bf1377b70edd180a256867aa292ae0bd4cd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 45798 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

SzO{Y)kcNTL0DE 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 Ka>`QRyZ;B1*esR+ diff --git a/cognite/neat/legacy/graph/exceptions.py b/cognite/neat/legacy/graph/exceptions.py deleted file mode 100644 index 616a97792..000000000 --- a/cognite/neat/legacy/graph/exceptions.py +++ /dev/null @@ -1,90 +0,0 @@ -"""This module contains the definition of validation errors and warnings raised during graph methods""" - -from cognite.neat.constants import DEFAULT_DOCS_URL -from cognite.neat.exceptions import NeatException - -DOCS_BASE_URL = f"{DEFAULT_DOCS_URL}api/exceptions.html#{__name__}" - - -class UnsupportedPropertyType(NeatException): - """Unsupported property type when processing the graph capturing sheet - - Args: - property_type: property type that is not supported - verbose: flag that indicates whether to provide enhanced exception message, by default False - """ - - type_: str = "UnsupportedPropertyType" - code: int = 1000 - description: str = "Unsupported property type when processing the graph capturing sheet." - example: str = "" - fix: str = "" - - def __init__(self, property_type: str, verbose: bool = False): - self.property_type = property_type - - self.message = ( - f"Property type {self.property_type} is not supported. " - " Only the following property types are supported: DatatypeProperty and ObjectProperty" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - super().__init__(self.message) - - -class NamespaceRequired(NeatException): - """The functionality requires namespace in the TransformationRules. - - Args: - functionality: functionality that requires namespace - verbose: flag that indicates whether to provide enhanced exception message, by default False - """ - - type_ = "NamespaceRequired" - description: str = "The functionality requires namespace in the TransformationRules." - example: str = "" - fix: str = "" - - def __init__(self, functionality: str, verbose: bool = False): - self.message = ( - f"Namespace is required to be set in the Transformation rules" - f"to use {functionality}." - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - super().__init__(self.message) - - -class DatasetIdRequired(NeatException): - """The functionality requires data_set_id in the TransformationRules. - - Args: - functionality: functionality that requires namespace - verbose: flag that indicates whether to provide enhanced exception message, by default False - """ - - type_ = "DatasetIdRequired" - description: str = "The functionality requires data_set_id in the TransformationRules." - example: str = "" - fix: str = "" - - def __init__(self, functionality: str, verbose: bool = False): - self.message = ( - f"DataSetId is required to be set in the Transformation rules" - f"to use {functionality}." - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - super().__init__(self.message) diff --git a/cognite/neat/legacy/graph/extractors/__init__.py b/cognite/neat/legacy/graph/extractors/__init__.py deleted file mode 100644 index 78ffbc7e2..000000000 --- a/cognite/neat/legacy/graph/extractors/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -from ._base import BaseExtractor -from ._dexpi import DexpiXML -from ._graph_capturing_sheet import GraphCapturingSheet -from ._mock_graph_generator import MockGraphGenerator - -__all__ = ["BaseExtractor", "MockGraphGenerator", "GraphCapturingSheet", "DexpiXML"] diff --git a/cognite/neat/legacy/graph/extractors/_base.py b/cognite/neat/legacy/graph/extractors/_base.py deleted file mode 100644 index 9f3e71b9c..000000000 --- a/cognite/neat/legacy/graph/extractors/_base.py +++ /dev/null @@ -1,14 +0,0 @@ -from abc import abstractmethod -from collections.abc import Iterable - -from cognite.neat.legacy.graph.models import Triple - - -class BaseExtractor: - """This is the base class for all extractors. It defines the interface that - extractors must implement. - """ - - @abstractmethod - def extract(self) -> Iterable[Triple]: - raise NotImplementedError() diff --git a/cognite/neat/legacy/graph/extractors/_dexpi.py b/cognite/neat/legacy/graph/extractors/_dexpi.py deleted file mode 100644 index ecd4f62c6..000000000 --- a/cognite/neat/legacy/graph/extractors/_dexpi.py +++ /dev/null @@ -1,44 +0,0 @@ -import xml.etree.ElementTree as ET -from pathlib import Path - -from rdflib import Namespace - -from cognite.neat.constants import DEFAULT_NAMESPACE -from cognite.neat.graph.extractors._dexpi import DexpiExtractor -from cognite.neat.legacy.graph.models import Triple - -from ._base import BaseExtractor - - -class DexpiXML(BaseExtractor): - """ - DEXPI-XML extractor of RDF triples - - Args: - filepath: File path to DEXPI XML file. - namespace: Optional custom namespace to use for extracted triples that define data - model instances. Defaults to http://purl.org/cognite/neat/. - """ - - def __init__( - self, - filepath: Path | str, - base_namespace: str | None = None, - ): - self.filepath = Path(filepath) - self.namespace = Namespace(base_namespace) if isinstance(base_namespace, str | Namespace) else DEFAULT_NAMESPACE - - def extract(self) -> set[Triple]: - """ - Extracts RDF triples from the graph capturing sheet. - - Returns: - List of RDF triples, represented as tuples `(subject, predicate, object)`, that define data model instances - """ - if self.filepath is None: - raise ValueError("File path to the graph capturing sheet is not provided!") - - root = ET.parse(self.filepath).getroot() - - # removing legacy code by reusing the maintained version of DexpiExtractor - return set(DexpiExtractor(root, self.namespace).extract()) diff --git a/cognite/neat/legacy/graph/extractors/_graph_capturing_sheet.py b/cognite/neat/legacy/graph/extractors/_graph_capturing_sheet.py deleted file mode 100644 index cdcdfac02..000000000 --- a/cognite/neat/legacy/graph/extractors/_graph_capturing_sheet.py +++ /dev/null @@ -1,403 +0,0 @@ -import logging -import uuid -import warnings -from pathlib import Path -from typing import cast - -import numpy as np -import pandas as pd -from openpyxl import Workbook, load_workbook -from openpyxl.cell import Cell -from openpyxl.styles import Alignment, Border, Font, NamedStyle, PatternFill, Side -from openpyxl.utils import get_column_letter -from openpyxl.worksheet.datavalidation import DataValidation -from openpyxl.worksheet.worksheet import Worksheet -from rdflib import RDF, XSD, Literal, Namespace, URIRef - -from cognite.neat.legacy.graph import exceptions -from cognite.neat.legacy.graph.exceptions import NamespaceRequired -from cognite.neat.legacy.graph.models import Triple -from cognite.neat.legacy.rules.analysis import get_defined_classes, to_class_property_pairs -from cognite.neat.legacy.rules.exporters._rules2rules import to_dms_name -from cognite.neat.legacy.rules.models.rules import Rules - -from ._base import BaseExtractor - - -class GraphCapturingSheet(BaseExtractor): - """ - Graph capturing sheet class that provides methods for creating a graph capturing sheet and extracting RDF triples. - - Args: - rules: Transformation rules which holds data model that is used to validate - the graph capturing sheet and extract data model instances from it (i.e. RDF triples) - filepath: File path to save the sheet to. Defaults to None. - separator: Multi value separator at cell level. Defaults to ",". - namespace: Optional custom namespace to use for extracted triples that define data - model instances. Defaults to None, meaning namespace of rules will be used. - store_graph_capturing_sheet: Whether to store the graph capturing sheet in the object. Will be stored in the - `sheet` attribute. Defaults to False. - use_source_ids : Whether to use source ids for properties and classes stored in Source column if they exist. - Defaults to False, meaning that the source ids will be ignored. - - """ - - def __init__( - self, - rules: Rules, - filepath: Path | str | None = None, - separator: str = ",", - namespace: str | None = None, - store_graph_capturing_sheet: bool = False, - use_source_ids: bool = False, - ): - self.rules = rules - self.filepath = Path(filepath) if isinstance(filepath, str | Path) else None - self.separator = separator - self.namespace = namespace - self.store_graph_capturing_sheet = store_graph_capturing_sheet - self.use_source_ids = use_source_ids - self.sheet: dict[str, pd.DataFrame] = {} - - def create_template(self, filepath: Path | None = None, overwrite: bool = False) -> None: - """ - Creates a graph capturing sheet template based on the transformation rules. - - Args: - filepath: File path to save the sheet to. Defaults to None. - overwrite: Overwrite existing file. Defaults to False. - """ - if filepath is None: - filepath = self.filepath - if filepath is None: - raise ValueError("File path to the graph capturing sheet is not provided!") - if filepath.exists() and not overwrite: - raise FileExistsError(f"File {filepath} already exists! Set overwrite to True to overwrite it!") - rules2graph_capturing_sheet(self.rules, filepath) - - def extract(self) -> list[Triple]: - """ - Extracts RDF triples from the graph capturing sheet. - - Returns: - List of RDF triples, represented as tuples `(subject, predicate, object)`, that define data model instances - """ - if self.filepath is None: - raise ValueError("File path to the graph capturing sheet is not provided!") - graph_capturing_sheet = read_graph_excel_file_to_table_by_name(self.filepath) - if self.store_graph_capturing_sheet: - self.sheet = graph_capturing_sheet - - print(self.namespace) - - return sheet2triples(graph_capturing_sheet, self.rules, self.separator, self.namespace, self.use_source_ids) - - -def extract_graph_from_sheet( - filepath: Path, transformation_rule: Rules, separator: str = ",", namespace: str | None = None -) -> list[Triple]: - """Converts a graph capturing sheet to RDF triples that define data model instances - - Args: - filepath : Path to the graph capturing sheet - transformation_rule : Transformation rules which holds data model that is used to validate - the graph capturing sheet and extract data model instances from it (i.e. RDF triples) - separator : Multi value separator at cell level. Defaults to ",". - namespace : Optional custom namespace to use for extracted triples that define data - model instances. Defaults to None, meaning namespace of rules will be used. - - Returns: - List of RDF triples, represented as tuples `(subject, predicate, object)`, that define data model instances - """ - - graph_capturing_sheet = read_graph_excel_file_to_table_by_name(filepath) - - return sheet2triples(graph_capturing_sheet, transformation_rule, separator, namespace) - - -def sheet2triples( - graph_capturing_sheet: dict[str, pd.DataFrame], - rules: Rules, - separator: str = ",", - namespace: str | None = None, - use_source_ids: bool = False, -) -> list[Triple]: - """Converts a graph capturing sheet represented as dictionary of dataframes to rdf triples - - Args: - graph_capturing_sheet : Graph capturing sheet provided as dictionary of dataframes - transformation_rule : Transformation rules which holds data model that is used to validate - the graph capturing sheet and extract data model instances from it (i.e. RDF triples) - separator : Multi value separator at cell level. Defaults to ",". - namespace : Optional custom namespace to use for extracted triples that define - data model instances. Defaults to None, meaning namespace of rules will be used. - use_source_ids : Whether to use source ids for properties and classes stored in Source column if they exist. - Defaults to False, meaning that the source ids will be ignored. - - Returns: - List of RDF triples, represented as tuples `(subject, predicate, object)`, that define data model instances - """ - - # Validation that everything is in order before proceeding - validate_if_graph_capturing_sheet_empty(graph_capturing_sheet) - validate_rules_graph_pair(graph_capturing_sheet, rules) - - # get class property pairs - class_property_pairs = to_class_property_pairs(rules) - - # namespace selection - if namespace is None and rules.metadata.namespace is not None: - instance_namespace = rules.metadata.namespace - elif namespace: - instance_namespace = Namespace(namespace) - else: - raise NamespaceRequired("Extract instances from sheet") - - if rules.metadata.namespace is not None: - model_namespace = Namespace(rules.metadata.namespace) - else: - raise NamespaceRequired("Extract instances from sheet") - - # Now create empty graph - triples: list[Triple] = [] - - # Add triples from the capturing sheet to the graph by iterating over the capturing sheet - # iterate over sheets - for sheet_name, df in graph_capturing_sheet.items(): - # iterate over sheet rows - - class_uri = ( - URIRef(str(rules.classes[sheet_name].source)) - if use_source_ids and rules.classes[sheet_name].source - else model_namespace[sheet_name] - ) - - for _, row in df.iterrows(): - if row.identifier is None: - msg = f"Missing identifier in sheet {sheet_name} at row {row.name}! Skipping..." - logging.warning(msg) - warnings.warn(msg, stacklevel=2) - continue - - # iterate over sheet rows properties - for property_name, value in row.to_dict().items(): - # Setting RDF type of the instance - if property_name == "identifier": - triples.append((instance_namespace[row.identifier], RDF.type, class_uri)) - continue - elif not value: - continue - - property_uri = ( - URIRef(str(class_property_pairs[sheet_name][property_name].source)) - if use_source_ids and class_property_pairs[sheet_name][property_name].source - else model_namespace[property_name] - ) - - property_ = class_property_pairs[sheet_name][property_name] - - is_one_to_many = separator and ( - (property_.max_count and property_.max_count > 1) or not property_.max_count - ) - - values = value.split(separator) if is_one_to_many else [value] - - # Adding object properties - if property_.property_type == "ObjectProperty": - triples.extend( - ( - instance_namespace[row.identifier], - property_uri, - instance_namespace[v.strip()], - ) - for v in values - ) - # Adding data properties - elif property_.property_type == "DatatypeProperty": - for v in values: - try: - literal_value = v.strip() - except AttributeError: - literal_value = v - - triples.append( - ( - instance_namespace[row.identifier], - property_uri, - Literal(literal_value, datatype=XSD[property_.expected_value_type.suffix]), - ) - ) - - else: - raise exceptions.UnsupportedPropertyType(property_.property_type) - return triples - - -def validate_if_graph_capturing_sheet_empty(graph_capturing_sheet: dict[str, pd.DataFrame]): - """Validate if the graph capturing sheet is empty - - Args: - graph_capturing_sheet : Graph capturing sheet - """ - if all(df.empty for df in graph_capturing_sheet.values()): - msg = "Graph capturing sheet is empty! Aborting!" - logging.error(msg) - raise ValueError(msg) - - -def validate_rules_graph_pair(graph_capturing_sheet: dict[str, pd.DataFrame], transformation_rule: Rules): - """Validate if the graph capturing sheet is based on the transformation rules - - Args: - graph_capturing_sheet : Graph capturing sheet - transformation_rule : Transformation rules - """ - intersection = set(graph_capturing_sheet.keys()).intersection(set(get_defined_classes(transformation_rule))) - - if not intersection: - msg = "Graph capturing sheet is not based on transformation rules! Aborting!" - logging.error(msg) - raise ValueError(msg) - - elif len(intersection) == len(graph_capturing_sheet.keys()): - logging.info("All classes in the graph capturing sheet are defined in the transformation rules!") - - elif len(intersection) < len(graph_capturing_sheet.keys()): - msg = "Graph capturing sheet contains classes that are not defined in the transformation rules! Proceeding..." - logging.warning(msg) - warnings.warn(msg, stacklevel=2) - - elif len(intersection) < len(get_defined_classes(transformation_rule)): - msg = "Transformation rules contain classes that are not present in the graph capturing sheet! Proceeding..." - logging.warning(msg) - warnings.warn(msg, stacklevel=2) - - -def read_graph_excel_file_to_table_by_name(filepath: Path) -> dict[str, pd.DataFrame]: - workbook: Workbook = load_workbook(filepath) - - parsed_sheets = { - sheetname: pd.read_excel(filepath, sheet_name=sheetname, header=0) for sheetname in workbook.sheetnames - } - - for sheetname, df in parsed_sheets.items(): - if "identifier" in df.columns: - parsed_sheets[sheetname] = df.drop(df[df.identifier == 0].index) - parsed_sheets[sheetname] = df.replace({np.nan: None}) - else: - logging.error(f"Sheet {sheetname} does not have an identifier column") - raise ValueError(f"Sheet {sheetname} does not have an identifier column") - - return parsed_sheets - - -def rules2graph_capturing_sheet( - rules: Rules, - file_path: Path, - no_rows: int = 1000, - auto_identifier_type: str = "index-based", - add_drop_down_list: bool = True, -): - """ - Converts a TransformationRules object to a graph capturing sheet - - Args: - rules: The TransformationRules object to convert to the graph capturing sheet - file_path: File path to save the sheet to - no_rows: Number of rows for processing, by default 1000 - auto_identifier_type: Type of automatic identifier, by default "index" based, alternative is "uuid" based - add_drop_down_list: Add drop down selection for columns that contain linking properties, by default True - - !!! note "no_rows parameter" - no_rows should be set to the maximum expected number of instances of any of the classes. - By default, it is set to 1000, increase it accordingly if you have more instances. - - """ - - workbook = Workbook() - # Remove default sheet named "Sheet" - workbook.remove(workbook["Sheet"]) - - for class_, properties in to_class_property_pairs(rules).items(): - workbook.create_sheet(title=class_) - - # Add header rows - cast(Worksheet, workbook[class_]).append(["identifier", *list(properties.keys())]) - - if auto_identifier_type and auto_identifier_type == "index-based": # default, easy to read - logging.debug(f"Configuring index-based automatic identifiers for sheet {class_}") - _add_index_identifiers(workbook, class_, no_rows) - elif auto_identifier_type and auto_identifier_type == "uuid-based": - logging.debug(f"Configuring UUID-based automatic identifiers for sheet {class_}") - _add_uuid_identifiers(workbook, class_, no_rows) - else: - logging.debug(f"No automatic identifier set for sheet {class_}!") - - for i, property_ in enumerate(properties.values()): - if property_.property_type == "ObjectProperty" and add_drop_down_list: - _add_drop_down_list( - workbook, class_, get_column_letter(i + 2), no_rows, property_.expected_value_type.suffix, "A" - ) - - _adjust_column_width(workbook) - _set_header_style(workbook) - - logging.info(f"Graph capturing sheet generated and stored at {file_path}!") - workbook.save(file_path) - workbook.close() - - -def _add_index_identifiers(workbook: Workbook, sheet: str, no_rows: int): - """Adds index-based auto identifier to a sheet identifier column""" - for i in range(no_rows): - prefix = to_dms_name(sheet, "class", True) - workbook[sheet][f"A{i+2}"] = f'=IF(ISBLANK(B{i+2}), "","{prefix}-{i+1}")' # type: ignore[index] - - -def _add_uuid_identifiers(workbook: Workbook, sheet: str, no_rows: int): - """Adds UUID-based auto identifier to a sheet identifier column""" - for i in range(no_rows): - prefix = to_dms_name(sheet, "class", True) - workbook[sheet][f"A{i+2}"] = f'=IF(ISBLANK(B{i+2}), "","{prefix}-{uuid.uuid4()}")' # type: ignore[index] - - -def _add_drop_down_list(workbook: Workbook, sheet: str, column: str, no_rows: int, value_sheet: str, value_column: str): - """Adds a drop down list to a column""" - drop_down_list = DataValidation(type="list", formula1=f"={value_sheet}!{value_column}$2:{value_column}${no_rows}") - - cast(Worksheet, workbook[sheet]).add_data_validation(drop_down_list) - - for i in range(no_rows): - drop_down_list.add(workbook[sheet][f"{column}{i+2}"]) # type: ignore[index, misc] - - -def _adjust_column_width(workbook: Workbook): - """Adjusts the column width based on the content""" - for sheet in workbook.sheetnames: - for cell_tuple in cast(Worksheet, workbook[sheet]).columns: - # Wrong type annotation in openpyxl - cell = cast(Cell, cell_tuple[0]) # type: ignore[index] - if cell.value: - adjusted_width = (len(str(cell.value)) + 5) * 1.2 - cast(Worksheet, workbook[sheet]).column_dimensions[cell.column_letter].width = adjusted_width - - -def _set_header_style(workbook: Workbook): - """Sets the header style for all sheets in the workbook""" - style = NamedStyle(name="header style") - style.font = Font(bold=True, size=16) - side = Side(style="thin", color="000000") - style.border = Border(left=side, right=side, top=side, bottom=side) - workbook.add_named_style(style) - - for sheet in workbook.sheetnames: - for cell_tuple in cast(Worksheet, workbook[sheet]).columns: - # Wrong type annotation in openpyxl - cell = cast(Cell, cell_tuple[0]) # type: ignore[index] - worksheet = cast(Worksheet, workbook[sheet]) - worksheet[f"{cell.column_letter}1"].style = style - if f"{cell.column_letter}1" == "A1": - worksheet[f"{cell.column_letter}1"].fill = PatternFill("solid", start_color="2FB5F2") - else: - worksheet[f"{cell.column_letter}1"].fill = PatternFill("solid", start_color="FFB202") - worksheet[f"{cell.column_letter}1"].alignment = Alignment(horizontal="center", vertical="center") diff --git a/cognite/neat/legacy/graph/extractors/_mock_graph_generator.py b/cognite/neat/legacy/graph/extractors/_mock_graph_generator.py deleted file mode 100644 index cd344eb4d..000000000 --- a/cognite/neat/legacy/graph/extractors/_mock_graph_generator.py +++ /dev/null @@ -1,361 +0,0 @@ -"""This module is used to generate mock graph data for purposes of testing of NEAT.""" - -import logging -import random -import warnings -from collections import OrderedDict - -import numpy -import pandas as pd -from prometheus_client import Gauge -from rdflib import RDF, Literal, Namespace, URIRef - -from cognite.neat.legacy.graph.models import Triple -from cognite.neat.legacy.rules.analysis import ( - get_class_linkage, - get_classes_with_properties, - get_defined_classes, - get_symmetric_pairs, -) -from cognite.neat.legacy.rules.exporters._rules2rules import subset_rules -from cognite.neat.legacy.rules.models import Rules -from cognite.neat.legacy.rules.models.value_types import XSD_VALUE_TYPE_MAPPINGS -from cognite.neat.utils.rdf_ import remove_namespace_from_uri - -from ._base import BaseExtractor - -neat_total_processed_mock_triples = Gauge( - "neat_total_processed_mock_triples", "Number of processed mock triples", ["state"] -) -neat_mock_triples_processing_timing = Gauge( - "neat_mock_triples_processing_timing", "Generation of mock knowledge graph timing metrics", ["aggregation"] -) - - -class MockGraphGenerator(BaseExtractor): - """ - Class used to generate mock graph data for purposes of testing of NEAT. - - Args: - rules: Transformation rules defining the classes with their properties. - class_count: Target class count for each class in the ontology - stop_on_exception: To stop if exception is encountered or not, default is False - allow_isolated_classes: To allow generation of instances for classes that are not - connected to any other class, default is True - """ - - def __init__( - self, rules: Rules, class_count: dict, stop_on_exception: bool = False, allow_isolated_classes: bool = True - ): - self.rules = rules - self.class_count = class_count - self.stop_on_exception = stop_on_exception - self.allow_isolated_classes = allow_isolated_classes - - def extract(self) -> list[Triple]: - """Generate mock triples based on data model defined transformation rules and desired number - of class instances - - Returns: - List of RDF triples, represented as tuples `(subject, predicate, object)`, that define data model instances - """ - return generate_triples( - self.rules, - self.class_count, - stop_on_exception=self.stop_on_exception, - allow_isolated_classes=self.allow_isolated_classes, - ) - - -def generate_triples( - transformation_rules: Rules, class_count: dict, stop_on_exception: bool = False, allow_isolated_classes: bool = True -) -> list[Triple]: - """Generate mock triples based on data model defined transformation rules and desired number - of class instances - - Args: - transformation_rules : Transformation rules defining the data model - class_count: Target class count for each class in the ontology - stop_on_exception: To stop if exception is encountered or not, default is False - allow_isolated_classes: To allow generation of instances for classes that are not - connected to any other class, default is True - - Returns: - List of RDF triples, represented as tuples `(subject, predicate, object)`, that define data model instances - """ - - # Figure out which classes are defined in the data model and which are not - if transformation_rules.metadata.namespace is None: - raise ValueError("Namespace must be defined in transformation rules!") - namespace = transformation_rules.metadata.namespace - - defined_classes = get_defined_classes(transformation_rules) - - if non_existing_classes := set(class_count.keys()) - defined_classes: - msg = f"Class count contains classes {non_existing_classes} for which properties are not defined in Data Model!" - if stop_on_exception: - logging.error(msg) - raise ValueError(msg) - else: - msg += " These classes will be ignored." - logging.warning(msg) - warnings.warn(msg, stacklevel=2) - for class_ in non_existing_classes: - class_count.pop(class_) - - # Subset data model to only classes that are defined in class count - transformation_rules = ( - subset_rules(transformation_rules, set(class_count.keys()), skip_validation=True) - if defined_classes != set(class_count.keys()) - else transformation_rules - ) - - class_linkage = get_class_linkage(transformation_rules) - - # Remove one of symmetric pairs from class linkage to maintain proper linking - # among instances of symmetrically linked classes - if sym_pairs := get_symmetric_pairs(transformation_rules): - class_linkage = _remove_higher_occurring_sym_pair(class_linkage, sym_pairs) - - # Remove any of symmetric pairs containing classes that are not present class count - class_linkage = _remove_non_requested_sym_pairs(class_linkage, class_count) - - # Generate generation order for classes instances - generation_order = _prettify_generation_order(_get_generation_order(class_linkage)) - - # Generated simple view of data model - class_definitions = _rules_to_dict(transformation_rules) - - # pregenerate instance ids for each remaining class - instance_ids = {key: [URIRef(namespace[f"{key}-{i}"]) for i in range(value)] for key, value in class_count.items()} - - # create triple for each class instance defining its type - triples: list[Triple] = [] - for class_ in class_count: - triples += [ - (class_instance_id, RDF.type, URIRef(namespace[class_])) for class_instance_id in instance_ids[class_] - ] - - # generate triples for connected classes - for class_ in generation_order: - triples += _generate_triples_per_class( - class_, class_definitions, sym_pairs, instance_ids, namespace, stop_on_exception - ) - - # generate triples for isolated classes - if allow_isolated_classes: - for class_ in set(class_count.keys()) - set(generation_order): - triples += _generate_triples_per_class( - class_, class_definitions, sym_pairs, instance_ids, namespace, stop_on_exception - ) - - return triples - - -def _get_generation_order( - class_linkage: pd.DataFrame, parent_col: str = "source_class", child_col: str = "target_class" -) -> dict: - parent_child_list: list[list[str]] = class_linkage[[parent_col, child_col]].values.tolist() - # Build a directed graph and a list of all names that have no parent - graph: dict[str, set] = {name: set() for tup in parent_child_list for name in tup} - has_parent: dict[str, bool] = {name: False for tup in parent_child_list for name in tup} - for parent, child in parent_child_list: - graph[parent].add(child) - has_parent[child] = True - - # All names that have absolutely no parent: - roots = [name for name, parents in has_parent.items() if not parents] - - return _traverse({}, graph, roots) - - -def _traverse(hierarchy: dict, graph: dict, names: list[str]) -> dict: - """traverse the graph and return the hierarchy""" - for name in names: - hierarchy[name] = _traverse({}, graph, graph[name]) - return hierarchy - - -def _prettify_generation_order(generation_order: dict, depth: dict | None = None, start=-1) -> dict: - """Prettifies generation order dictionary for easier consumption.""" - depth = depth or {} - for key, value in generation_order.items(): - depth[key] = start + 1 - if isinstance(value, dict): - _prettify_generation_order(value, depth, start=start + 1) - return OrderedDict(sorted(depth.items(), key=lambda item: item[1])) - - -def _remove_non_hierarchy_linking(class_linkage: pd.DataFrame) -> pd.DataFrame: - """Remove linkage which is not creating asset hierarchy.""" - return class_linkage[class_linkage.linking_type == "hierarchy"] - - -def _remove_higher_occurring_sym_pair(class_linkage: pd.DataFrame, sym_pairs: set[tuple[str, str]]) -> pd.DataFrame: - """Remove symmetric pair which is higher in occurrence.""" - rows_to_remove = set() - for source, target in sym_pairs: - first_sym_property_occurrence = class_linkage[ - (class_linkage.source_class == source) & (class_linkage.target_class == target) - ].max_occurrence.values[0] - second_sym_property_occurrence = class_linkage[ - (class_linkage.source_class == target) & (class_linkage.target_class == source) - ].max_occurrence.values[0] - - if first_sym_property_occurrence is None: - # this means that source occurrence is unbounded - index = class_linkage[ - (class_linkage.source_class == source) & (class_linkage.target_class == target) - ].index.values[0] - elif ( - second_sym_property_occurrence is None - or first_sym_property_occurrence <= second_sym_property_occurrence - and second_sym_property_occurrence > first_sym_property_occurrence - ): - # this means that target occurrence is unbounded - index = class_linkage[ - (class_linkage.source_class == target) & (class_linkage.target_class == source) - ].index.values[0] - else: - index = class_linkage[ - (class_linkage.source_class == source) & (class_linkage.target_class == target) - ].index.values[0] - rows_to_remove.add(index) - - return class_linkage.drop(list(rows_to_remove)) - - -def _remove_non_requested_sym_pairs(class_linkage: pd.DataFrame, class_count: dict) -> pd.DataFrame: - """Remove symmetric pairs which classes are not found in class count.""" - rows_to_remove = set(class_linkage[~(class_linkage["source_class"].isin(set(class_count.keys())))].index.values) - rows_to_remove |= set(class_linkage[~(class_linkage["target_class"].isin(set(class_count.keys())))].index.values) - - return class_linkage.drop(list(rows_to_remove)) - - -def _generate_mock_data_property_triples( - instance_ids: list[URIRef], property_: str, namespace: Namespace, value_type: str = "string" -) -> list[tuple[URIRef, URIRef, Literal]]: - """Generates triples for data properties.""" - - python_type = XSD_VALUE_TYPE_MAPPINGS[value_type].python - triples = [] - for id_ in instance_ids: - if python_type is int: - triples.append((id_, URIRef(namespace[property_]), Literal(random.randint(1, 1983)))) - elif python_type is float: - triples.append((id_, URIRef(namespace[property_]), Literal(numpy.float32(random.uniform(1, 1983))))) - # generate string - else: - triples.append( - (id_, URIRef(namespace[property_]), Literal(remove_namespace_from_uri(id_).replace("-", " "))) - ) - return triples - - -def _generate_mock_object_property_triples( - class_: str, - property_definition: pd.Series, - class_definitions: dict[str, pd.DataFrame], - sym_pairs: set[tuple[str, str]], - instance_ids: dict[str, list[URIRef]], - namespace: Namespace, - stop_on_exception: bool, -) -> list[tuple[URIRef, URIRef, URIRef]]: - """Generates triples for object properties.""" - if property_definition.value_type not in instance_ids: - msg = f"Class {property_definition.value_type} not found in class count! " - if stop_on_exception: - logging.error(msg) - raise ValueError(msg) - else: - msg += ( - f"Skipping creating triples for property {property_definition.name} " - f"of class {class_} which expects values of this type!" - ) - logging.warning(msg) - warnings.warn(msg, stacklevel=2) - return [] - - # Handling symmetric property - symmetric_property = ( - class_definitions[property_definition.value_type][ - class_definitions[property_definition.value_type].value_type == class_ - ].index.values[0] - if tuple((class_, property_definition.value_type)) in sym_pairs - else None - ) - - triples = [] - - for i, source in enumerate(instance_ids[class_]): - target = instance_ids[property_definition.value_type][i % len(instance_ids[property_definition.value_type])] - triples += [(URIRef(source), URIRef(namespace[str(property_definition.name)]), URIRef(target))] - - if symmetric_property: - triples += [(URIRef(target), URIRef(namespace[symmetric_property]), URIRef(source))] - - # remove symmetric property from class definition of downstream class - # to avoid asymmetric linking in mock graph - if symmetric_property: - class_definitions[property_definition.value_type].drop(symmetric_property, inplace=True) - - return triples - - -def _generate_triples_per_class( - class_: str, - class_definitions: dict[str, pd.DataFrame], - sym_pairs: set[tuple[str, str]], - instance_ids: dict[str, list[URIRef]], - namespace: Namespace, - stop_on_exception: bool, -) -> list[Triple]: - """Generate triples for a given class.""" - triples: list[Triple] = [] - for _, property_definition in class_definitions[class_].iterrows(): - if property_definition.property_type == "DatatypeProperty": - triples += _generate_mock_data_property_triples( - instance_ids[class_], str(property_definition.name), namespace, property_definition.value_type - ) - - elif property_definition.property_type == "ObjectProperty": - triples += _generate_mock_object_property_triples( - class_, property_definition, class_definitions, sym_pairs, instance_ids, namespace, stop_on_exception - ) - - else: - logging.error(f"Property type {property_definition.property_type} not supported!") - raise ValueError(f"Property type {property_definition.property_type} not supported!") - - return triples - - -def _rules_to_dict(transformation_rules: Rules) -> dict[str, pd.DataFrame]: - """Represent data model as a dictionary of data frames, where each data frame - represents properties defined for a given class. - - Args: - transformation_rules: Transformation rules defining the data model - - Returns: - Simplified representation of the data model - """ - - data_model: dict[str, pd.DataFrame] = {} - - defined_classes = get_classes_with_properties(transformation_rules) - - for class_ in defined_classes: - properties = {} - for property_ in defined_classes[class_]: - if property_.property_id not in properties: - properties[property_.property_id] = { - "property_type": property_.property_type, - "value_type": property_.expected_value_type.suffix, - "min_count": property_.min_count, - "max_count": property_.max_count, - } - - data_model[class_] = pd.DataFrame(properties).T - return data_model diff --git a/cognite/neat/legacy/graph/loaders/__init__.py b/cognite/neat/legacy/graph/loaders/__init__.py deleted file mode 100644 index bf95b365a..000000000 --- a/cognite/neat/legacy/graph/loaders/__init__.py +++ /dev/null @@ -1,23 +0,0 @@ -from cognite.neat.legacy.graph.loaders.core.labels import upload_labels - -from ._asset_loader import AssetLoader -from ._base import BaseLoader, CogniteLoader -from .core.rdf_to_assets import categorize_assets, rdf2assets, upload_assets -from .core.rdf_to_relationships import categorize_relationships, rdf2relationships, upload_relationships -from .rdf_to_dms import DMSLoader, upload_edges, upload_nodes - -__all__ = [ - "BaseLoader", - "CogniteLoader", - "AssetLoader", - "rdf2relationships", - "categorize_assets", - "upload_assets", - "rdf2assets", - "categorize_relationships", - "upload_relationships", - "upload_labels", - "upload_nodes", - "upload_edges", - "DMSLoader", -] diff --git a/cognite/neat/legacy/graph/loaders/_asset_loader.py b/cognite/neat/legacy/graph/loaders/_asset_loader.py deleted file mode 100644 index adf97fa5f..000000000 --- a/cognite/neat/legacy/graph/loaders/_asset_loader.py +++ /dev/null @@ -1,511 +0,0 @@ -import logging -import sys -from collections import defaultdict -from collections.abc import Iterable -from dataclasses import dataclass, fields -from datetime import datetime -from itertools import groupby -from typing import Any, Literal, overload - -from cognite.client import CogniteClient -from cognite.client.data_classes import AssetWrite, LabelDefinitionWrite, RelationshipWrite, RelationshipWriteList -from pydantic_core import ErrorDetails -from rdflib.query import ResultRow - -from cognite.neat.legacy.graph.stores import NeatGraphStoreBase -from cognite.neat.legacy.rules.models import Rules -from cognite.neat.legacy.rules.models.rules import Property -from cognite.neat.utils.rdf_ import remove_namespace_from_uri -from cognite.neat.utils.time_ import epoch_now_ms - -from ._base import CogniteLoader - -if sys.version_info >= (3, 11): - from datetime import UTC -else: - from datetime import timezone - - UTC = timezone.utc - -METADATA_VALUE_MAX_LENGTH = 5120 - - -@dataclass(frozen=True) -class AssetLoaderMetadataKeys: - """Class holding mapping between NEAT metadata key names and their desired names - in CDF Asset metadata - - Args: - start_time: Start time key name - end_time: End time key name - update_time: Update time key name - resurrection_time: Resurrection time key name - identifier: Identifier key name - active: Active key name - type: Type key name - """ - - start_time: str = "start_time" - end_time: str = "end_time" - update_time: str = "update_time" - resurrection_time: str = "resurrection_time" - identifier: str = "identifier" - active: str = "active" - type: str = "type" - - def as_aliases(self) -> dict[str, str]: - return {str(field.default): getattr(self, field.name) for field in fields(self)} - - -AssetResource = AssetWrite | RelationshipWrite | LabelDefinitionWrite - - -class AssetLoader(CogniteLoader[AssetResource]): - """The Asset Loader is used to load a triple store into the CDF core Asset Hierarchy model. - - Args: - rules: The transformation rules to use - graph_store: The graph store to load from - data_set_id: The data set id to load into - label_data_set_id: The data set id to load labels into. If not set, the same as data_set_id will be used. - use_orphanage: Whether to use an orphanage for assets without a parent - use_labels: Whether to use labels attached to the assets and relationships. - asset_external_id_prefix: Prefix to add to all external ids - default_metadata_value: Default metadata value to use for assets without a value. If set to None, - the metadata key will be omitted. Setting this to an empty string will set the metadata key to an empty - string, thus ensuring that all assets have the metadata keys. - metadata_keys: Metadata key names to use - always_store_in_metadata: Whether to store all properties in metadata. This will be the same as setting - resource_type_property to metadata for all properties. For example, if you have the property `Terminal.name` - set with resource_type_property = ["name"], then the property will be stored in metadata as - `Terminal.name=`. This also includes properties that are relationships. - """ - - # This is guaranteed ot be in the data - _identifier: str = "identifier" - # This label is added to all assets if use_labels is True - _non_historic_label: str = "non-historic" - - def __init__( - self, - rules: Rules, - graph_store: NeatGraphStoreBase, - data_set_id: int, - label_data_set_id: int | None = None, - use_orphanage: bool = True, - use_labels: bool = True, - asset_external_id_prefix: str | None = None, - default_metadata_value: str | None = "", - metadata_keys: AssetLoaderMetadataKeys | None = None, - always_store_in_metadata: bool = False, - ): - super().__init__(rules, graph_store) - self._data_set_id = data_set_id - self._label_data_set_id = label_data_set_id or data_set_id - self._use_labels = use_labels - self._use_orphanage = use_orphanage - self._orphanage_external_id = ( - f"{asset_external_id_prefix or ''}orphanage-{data_set_id}" if use_orphanage else None - ) - self._asset_external_id_prefix = asset_external_id_prefix - self._default_metadata_value = default_metadata_value - self._metadata_keys = metadata_keys or AssetLoaderMetadataKeys() - # This is used in a hot loop, so we cache it - self._metadata_key_aliases = self._metadata_keys.as_aliases() - self._always_store_in_metadata = always_store_in_metadata - - # State: - self._loaded_assets: set[str] = set() - self._loaded_labels: set[str] = set() - - @overload - def load(self, stop_on_exception: Literal[True]) -> Iterable[AssetResource]: ... - - @overload - def load(self, stop_on_exception: Literal[False] = False) -> Iterable[AssetResource | ErrorDetails]: ... - - def load(self, stop_on_exception: bool = False) -> Iterable[AssetResource | ErrorDetails]: - if self.rules.metadata.namespace is None: - raise ValueError("Namespace must be provided in transformation rules!") - namespace = self.rules.metadata.namespace - - properties_by_class_name: dict[str, list[Property]] = defaultdict(list) - for prop in self.rules.properties.values(): - properties_by_class_name[prop.class_id].append(prop) - - if self._use_labels: - self._loaded_labels.add(self._non_historic_label) - yield LabelDefinitionWrite( - external_id=self._non_historic_label, name=self._non_historic_label, data_set_id=self._label_data_set_id - ) - - # Todo Extend rules to sort topological sorting to ensure that parents are loaded before children - counter = 0 - for class_name in self.rules.classes.keys(): - if self._use_labels: - self._loaded_labels.add(class_name) - yield LabelDefinitionWrite(external_id=class_name, name=class_name, data_set_id=self._label_data_set_id) - - class_uri = namespace[class_name] - logging.debug(f"Processing class <{class_uri}>.") - - try: - result = self.graph_store.queries.list_instances_of_type(class_uri) - except Exception as e: - logging.error(f"Error while querying for instances of class <{class_uri}> into cache. Reason: {e}") - if stop_on_exception: - raise e - yield ErrorDetails( - input={"class_uri": class_uri}, - loc=("AssetLoader", "load"), - msg=f"Error while querying instances of class <{class_uri}> into cache. Reason: {e}", - type=f"Exception of type {type(e).__name__} occurred when processing instance of {class_name}", - ) - continue - - logging.debug(f"Class <{class_name}> has {len(result)} instances") - - for instance_uri, properties_values in groupby(result, lambda x: x[0]): - instance_id = remove_namespace_from_uri(instance_uri) - values_by_property = self._prepare_instance_data(instance_id, properties_values) - try: - asset = self._load_asset( - properties_by_class_name[class_name], - values_by_property, - instance_id, - class_name, - ) - except Exception as e: - logging.error(f"Error while loading asset from instance <{instance_id}>. Reason: {e}") - if stop_on_exception: - raise e - yield ErrorDetails( - input={"instance_id": instance_id}, - loc=("AssetLoader", "load"), - msg=f"Error while loading asset from <{instance_id}>. Reason: {e}", - type=f"Exception of type {type(e).__name__} occurred when processing instance of {class_name}", - ) - continue - else: - # We know that external_id is always set - self._loaded_assets.add(asset.external_id) # type: ignore[arg-type] - yield asset - - try: - relationships = self._load_relationships( - properties_by_class_name[class_name], - values_by_property, - remove_namespace_from_uri(instance_id), - class_name, - ) - except Exception as e: - logging.error(f"Error while loading relationships from instance <{instance_id}>. Reason: {e}") - if stop_on_exception: - raise e - yield ErrorDetails( - input={"instance_id": instance_id}, - loc=("AssetLoader", "load"), - msg=f"Error while loading relationships from <{instance_id}>. Reason: {e}", - type=f"Exception of type {type(e).__name__} occurred when processing instance of {class_name}", - ) - continue - else: - for relationship in relationships: - for label in relationship.labels or []: - if label.external_id and label.external_id not in self._loaded_labels: - self._loaded_labels.add(label.external_id) - yield LabelDefinitionWrite( - name=label.external_id, - external_id=label.external_id, - data_set_id=self._label_data_set_id, - ) - - yield from relationships - - counter += 1 - # log every 10000 assets - if counter % 10_000 == 0: - logging.info(" Next 10,000 Assets processed") - - logging.debug(f"Class <{class_name}> processed") - - # Todo Move orphanage to the beginning to ensure that it is loaded first. - if self._use_orphanage and self._orphanage_external_id not in self._loaded_assets: - logging.warning(f"Orphanage with external id {self._orphanage_external_id} not found in asset hierarchy!") - logging.warning(f"Adding default orphanage with external id {self._orphanage_external_id}") - now = str(datetime.now(UTC)) - if self._use_labels: - yield LabelDefinitionWrite( - external_id="Orphanage", name="Orphanage", data_set_id=self._label_data_set_id - ) - - yield AssetWrite( - external_id=self._orphanage_external_id, - name="Orphanage", - data_set_id=self._data_set_id, - parent_external_id=None, - description="Used to store all assets which parent does not exist", - labels=["Orphanage", self._non_historic_label] if self._use_labels else None, - metadata={ - self._metadata_keys.type: "Orphanage", - "cdfResourceType": "Asset", - self._metadata_keys.start_time: now, - self._metadata_keys.update_time: now, - self._metadata_keys.identifier: "orphanage", - self._metadata_keys.active: "true", - }, - ) - - @overload - def load_assets(self, stop_on_exception: Literal[True]) -> Iterable[AssetWrite]: ... - - @overload - def load_assets(self, stop_on_exception: Literal[False] = False) -> Iterable[AssetWrite | ErrorDetails]: ... - - def load_assets(self, stop_on_exception: Literal[True, False] = False) -> Iterable[AssetWrite | ErrorDetails]: - for asset_resource in self.load(stop_on_exception): - if isinstance(asset_resource, AssetWrite): - yield asset_resource - - @overload - def load_relationships(self, stop_on_exception: Literal[True]) -> Iterable[RelationshipWrite]: ... - - @overload - def load_relationships( - self, stop_on_exception: Literal[False] = False - ) -> Iterable[RelationshipWrite | ErrorDetails]: ... - - def load_relationships( - self, stop_on_exception: Literal[True, False] = False - ) -> Iterable[RelationshipWrite | ErrorDetails]: - for asset_resource in self.load(stop_on_exception): - if isinstance(asset_resource, RelationshipWrite): - yield asset_resource - - def load_to_cdf( - self, client: CogniteClient, batch_size: int | None = 1000, max_retries: int = 1, retry_delay: int = 3 - ) -> None: - raise NotImplementedError - - def _load_asset( - self, properties: list[Property], values_by_property: dict[str, str | list[str]], instance_id, class_name: str - ) -> AssetWrite: - """Converts a set of properties and values to an AssetWrite object.""" - asset_raw = self._load_asset_data(properties, values_by_property, instance_id, class_name) - - # Loading Asset assumes camel case. - # Todo Change rules to use camel case? - for snake_case, came_case in [ - ("external_id", "externalId"), - ("parent_external_id", "parentExternalId"), - ("geo_location", "geoLocation"), - ]: - if snake_case in asset_raw: - asset_raw[came_case] = asset_raw.pop(snake_case) - - return AssetWrite.load(asset_raw) - - def _load_asset_data( - self, - properties: list[Property], - values_by_property: dict[str, str | list[str]], - instance_id: str, - class_name: str, - ) -> dict[str, Any]: - """Creates a raw asset dict from a set of properties and values.""" - asset_raw, missing_metadata_properties = self._load_instance_data(properties, values_by_property) - - self._append_missing_properties(asset_raw, missing_metadata_properties, instance_id, class_name) - - asset_raw["dataSetId"] = self._data_set_id - - # Neat specific metadata keys - if self._use_labels: - asset_raw["labels"] = [class_name, self._non_historic_label] - now = str(datetime.now(UTC)) - asset_raw["metadata"][self._metadata_keys.start_time] = now - asset_raw["metadata"][self._metadata_keys.update_time] = now - asset_raw["metadata"][self._metadata_keys.identifier] = instance_id - asset_raw["metadata"][self._metadata_keys.active] = "true" - asset_raw["metadata"][self._metadata_keys.type] = class_name - - # Rename metadata keys based on configuration - asset_raw["metadata"] = {self._metadata_key_aliases.get(k, k): v for k, v in asset_raw["metadata"].items()} - return asset_raw - - def _prepare_instance_data( - self, instance_id: str, properties_values: Iterable[ResultRow] - ) -> dict[str, str | list[str]]: - """Groups the properties and values by property type. - - Returns: - A dictionary with property type as key and a list of values as value. - """ - properties_value_tuples: list[tuple[str, str]] = [ - remove_namespace_from_uri(prop, value) # type: ignore[misc] - for _, prop, value in properties_values - ] - # We add an identifier which will be used as fallback for external_id - properties_value_tuples.append((self._identifier, remove_namespace_from_uri(instance_id))) - values_by_property: dict[str, str | list[str]] = {} - for prop, values in groupby(sorted(properties_value_tuples), lambda x: x[0]): - values_list: list[str] = [value for _, value in values] # type: ignore[misc, has-type] - if len(values_list) == 1: - values_by_property[prop] = values_list[0] - else: - values_by_property[prop] = values_list - return values_by_property - - def _load_instance_data( - self, properties: list[Property], values_by_property: dict[str, str | list[str]] - ) -> tuple[dict[str, Any], set[str]]: - """This function loads the instance data into a raw asset dict. It also returns a set of metadata keys that - were not found in the instance data. - - Returns: - A tuple with the raw asset dict and a set of metadata keys that were not found in the instance data. - """ - asset_raw: dict[str, Any] = {"metadata": {}} - missing_metadata_properties: set[str] = set() - for prop in properties: - if prop.property_name is None: - continue - is_relationship = "Asset" not in prop.cdf_resource_type - if not self._always_store_in_metadata and is_relationship: - continue - - if prop.property_name not in values_by_property: - for property_type in prop.resource_type_property or []: - if property_type.casefold() == "metadata": - missing_metadata_properties.add(prop.property_name) - elif self._always_store_in_metadata: - missing_metadata_properties.add(prop.property_name) - continue - values = values_by_property[prop.property_name] - for property_type in prop.resource_type_property or []: - if property_type.casefold() == "metadata": - asset_raw["metadata"][prop.property_name] = self._to_metadata_value(values) - else: - if property_type not in asset_raw: - asset_raw[property_type] = values - if self._always_store_in_metadata and prop.property_name not in asset_raw["metadata"]: - asset_raw["metadata"][prop.property_name] = self._to_metadata_value(values) - if is_relationship: - asset_raw["metadata"][prop.property_name] = self._to_metadata_value(values) - - return asset_raw, missing_metadata_properties - - def _append_missing_properties( - self, asset_raw: dict[str, Any], missing_metadata_properties: set[str], identifier: str, class_name: str - ) -> None: - """This function ensures that the raw asset dict has all the required properties such as external_id, name, - and parent_external_id. It also ensures that the metadata dict has all the required keys.""" - - if "external_id" not in asset_raw: - msg = f"Missing external_id for {class_name} instance {identifier}. Using value <{identifier}>." - logging.debug(msg) - asset_raw["external_id"] = identifier - elif "external_id" in asset_raw and isinstance(asset_raw["external_id"], list): - external_ids = asset_raw["external_id"] - msg = ( - f"Multiple values for {class_name} instance {identifier} external_id. " - f"Using the first one <{external_ids[0]}>." - ) - logging.debug(msg) - asset_raw["external_id"] = external_ids[0] - - if self._asset_external_id_prefix: - asset_raw["external_id"] = f"{self._asset_external_id_prefix}{asset_raw['external_id']}" - - external_id = asset_raw["external_id"] - - if "name" not in asset_raw: - msg = f"Missing name for {class_name} instance {external_id}. Using value <{identifier}>." - logging.debug(msg) - asset_raw["name"] = identifier - elif "name" in asset_raw and isinstance(asset_raw["name"], list): - msg = f"Multiple values for {class_name} instance {external_id} name. Joining them into one." - logging.debug(msg) - asset_raw["name"] = self._to_metadata_value(asset_raw["name"]) - - if "description" in asset_raw and isinstance(asset_raw["description"], list): - msg = f"Multiple values for {class_name} instance {external_id} description. Joining them into one." - logging.debug(msg) - asset_raw["description"] = self._to_metadata_value(asset_raw["description"]) - - if "parent_external_id" not in asset_raw and self._orphanage_external_id is not None: - asset_raw["parent_external_id"] = self._orphanage_external_id - elif "parent_external_id" in asset_raw and isinstance(asset_raw["parent_external_id"], list): - msg = ( - f"Multiple values for {class_name} instance {external_id} parent_external_id. " - f"Using the first one <{asset_raw['parent_external_id'][0]}>." - ) - logging.debug(msg) - asset_raw["parent_external_id"] = asset_raw["parent_external_id"][0] - - if "parent_external_id" in asset_raw: - asset_raw["parent_external_id"] = f"{self._asset_external_id_prefix or ''}{asset_raw['parent_external_id']}" - - for metadata_key in missing_metadata_properties: - msg = f"{external_id} of type {class_name} is missing metadata key {metadata_key}." - logging.debug(msg) - if self._default_metadata_value is not None and metadata_key not in asset_raw["metadata"]: - asset_raw["metadata"][metadata_key] = self._default_metadata_value - logging.debug(f"\tKey {metadata_key} added to <{external_id}> metadata!") - - @staticmethod - def _to_metadata_value(values: str | list[str]) -> str: - """A helper function to convert a list of values to a metadata value string respecting metadata value length.""" - # Sorting for deterministic results - return values if isinstance(values, str) else ", ".join(sorted(values))[: METADATA_VALUE_MAX_LENGTH - 1] - - def _load_relationships( - self, - properties: list[Property], - values_by_property: dict[str, str | list[str]], - instance_id: str, - class_name: str, - ) -> RelationshipWriteList: - """Converts a set of properties and values to a RelationshipWriteList object.""" - relationships = RelationshipWriteList([]) - relationship_properties = [prop for prop in properties if "Relationship" in prop.cdf_resource_type] - epoch_now = epoch_now_ms() - for prop in relationship_properties: - if not prop.property_name: - continue - values = values_by_property.get(prop.property_name, []) - if not values: - continue - value_list = [values] if isinstance(values, str) else values - for value in value_list: - relationship = self._load_relationship(prop, value, instance_id, class_name, epoch_now) - relationships.append(relationship) - - return relationships - - def _load_relationship( - self, - prop: Property, - target_id: str, - instance_id: str, - class_name: str, - epoch_now: int, - ) -> RelationshipWrite: - """Converts a set of properties and values to a RelationshipWrite object.""" - prefix = self._asset_external_id_prefix or "" - labels = ( - [class_name, self._non_historic_label, prop.expected_value_type.suffix, prop.property_id] - if self._use_labels - else None - ) - relationship_raw = { - "externalId": f"{prefix}{instance_id}:{prefix}{target_id}", - "sourceExternalId": f"{prefix}{instance_id}", - "targetExternalId": f"{prefix}{target_id}", - "sourceType": prop.source_type, - "targetType": prop.target_type, - "dataSetId": self._data_set_id, - "labels": labels, - "startTime": epoch_now, - } - return RelationshipWrite.load(relationship_raw) diff --git a/cognite/neat/legacy/graph/loaders/_base.py b/cognite/neat/legacy/graph/loaders/_base.py deleted file mode 100644 index 47c87a748..000000000 --- a/cognite/neat/legacy/graph/loaders/_base.py +++ /dev/null @@ -1,67 +0,0 @@ -import logging -from abc import ABC, abstractmethod -from collections.abc import Iterable -from typing import Generic, Literal, TypeVar, overload - -from cognite.client import CogniteClient -from pydantic_core import ErrorDetails - -from cognite.neat.legacy.graph.models import Triple -from cognite.neat.legacy.graph.stores import NeatGraphStoreBase -from cognite.neat.legacy.graph.transformations.query_generator.sparql import build_construct_query -from cognite.neat.legacy.rules.models import Rules - -T_Output = TypeVar("T_Output") - - -class BaseLoader(ABC, Generic[T_Output]): - """Base class for all loaders. - - A loader is a class that loads data from a source graph into - target outside Neat. - """ - - def __init__(self, rules: Rules, graph_store: NeatGraphStoreBase): - self.rules = rules - self.graph_store = graph_store - - @overload - def load(self, stop_on_exception: Literal[True]) -> Iterable[T_Output]: ... - - @overload - def load(self, stop_on_exception: Literal[False] = False) -> Iterable[T_Output | ErrorDetails]: ... - - @abstractmethod - def load(self, stop_on_exception: bool = False) -> Iterable[T_Output | ErrorDetails]: - """Load the graph with data.""" - pass - - def _iterate_class_triples(self, exclude_classes: set[str] | None = None) -> Iterable[tuple[str, Iterable[Triple]]]: - """Iterate over all classes and their triples.""" - for class_name in self.rules.classes: - if exclude_classes is not None and class_name in exclude_classes: - continue - try: - sparql_construct_query = build_construct_query( - self.graph_store.graph, class_name, self.rules, properties_optional=True - ) - except Exception as e: - logging.error(f"Failed to build construct query for class {class_name}: {e}") - continue - - yield class_name, self.graph_store.query_delayed(sparql_construct_query) - - -class CogniteLoader(BaseLoader[T_Output], ABC): - """Base class for all loaders. - - A loader is a class that loads data from a source graph into - target outside Neat. - """ - - @abstractmethod - def load_to_cdf( - self, client: CogniteClient, batch_size: int | None = 1000, max_retries: int = 1, retry_delay: int = 3 - ) -> None: - """Load the graph with data.""" - pass diff --git a/cognite/neat/legacy/graph/loaders/_exceptions.py b/cognite/neat/legacy/graph/loaders/_exceptions.py deleted file mode 100644 index 705c182b2..000000000 --- a/cognite/neat/legacy/graph/loaders/_exceptions.py +++ /dev/null @@ -1,85 +0,0 @@ -"""This module contains the definition of validation errors and warnings raised by various extractors""" - -from pydantic_core import ErrorDetails, PydanticCustomError - - -class NeatError(Exception): - type_: str - code: int - description: str - example: str - fix: str - message: str - - def to_pydantic_custom_error(self): - return PydanticCustomError( - self.type_, - self.message, - dict(type_=self.type_, code=self.code, description=self.description, example=self.example, fix=self.fix), - ) - - def to_error_dict(self) -> ErrorDetails: - return { - "type": self.type_, - "loc": (), - "msg": self.message, - "input": None, - "ctx": dict( - type_=self.type_, - code=self.code, - description=self.description, - example=self.example, - fix=self.fix, - ), - } - - -class NeatWarning(UserWarning): - type_: str - code: int - description: str - example: str - fix: str - message: str - - -class Warning1(NeatWarning): - type_: str = "OWLGeneratedTransformationRulesHasErrors" - code: int = 1 - description: str = ( - "This warning occurs when generating transformation rules from OWL ontology are invalid/incomplete." - ) - example: str = "" - fix: str = "Go through the generated report file and fix the warnings in generated Transformation Rules." - - def __init__(self, verbose=False): - self.message = ( - "Transformation rules generated from OWL ontology are invalid!" - " Consult report.txt for details on the errors and fix them before using the rules file." - ) - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - # hint on a specific web docs page - - -class Warning2(NeatWarning): - type_: str = "OWLGeneratedTransformationRulesHasWarnings" - code: int = 2 - description: str = ( - "This warning occurs when generating transformation rules from OWL ontology are invalid/incomplete." - ) - example: str = "" - fix: str = "Go through the generated report file and fix the warnings in generated Transformation Rules." - - def __init__(self, verbose=False): - self.message = ( - "Transformation rules generated from OWL ontology raised warnings!" - " Consult report.txt for details on warnings, and fix them prior using the rules file." - ) - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - # hint on a specific web docs page diff --git a/cognite/neat/legacy/graph/loaders/core/__init__.py b/cognite/neat/legacy/graph/loaders/core/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/cognite/neat/legacy/graph/loaders/core/labels.py b/cognite/neat/legacy/graph/loaders/core/labels.py deleted file mode 100644 index 566e1293a..000000000 --- a/cognite/neat/legacy/graph/loaders/core/labels.py +++ /dev/null @@ -1,58 +0,0 @@ -import logging -import traceback -from typing import cast - -from cognite.client import CogniteClient -from cognite.client.data_classes import LabelDefinition, LabelDefinitionList - -from cognite.neat.legacy.rules.exporters._core import get_labels -from cognite.neat.legacy.rules.models.rules import Rules - - -def upload_labels( - client: CogniteClient, - rules: Rules, - data_set_id: int, - extra_labels: list | None = None, - stop_on_exception: bool = False, -): - """Generates labels from transformation rules and upload them to CDF - - Args: - client : Instance of CogniteClient - transformation_rules : Instance of TransformationRules which contains the labels to upload - data_set_id : Id of the dataset to upload the labels to - extra_labels : Any additional labels not defined in TransformationRules , - by default ["historic", "non-historic"] - stop_on_exception : If this function fails to stop the process, by default False - """ - if extra_labels is None: - extra_labels = [] - - try: - logging.debug("Fetching existing labels from CDF") - # This is to list all labels, not just the ones in the dataset since labels - # must have unique external_ids, fixing issue that Daniel encountered - if retrieved_labels := client.labels.list(limit=-1): - existing_labels = set(retrieved_labels.to_pandas().external_id) - else: - existing_labels = set() - logging.debug(f"Found {len(existing_labels)} existing labels in CDF") - except Exception as e: - logging.debug("Error fetching existing labels from CDF, no ") - traceback.print_exc() - if stop_on_exception: - raise e - existing_labels = set() - - non_existing_labels = set(list(get_labels(rules)) + extra_labels).difference(existing_labels) - - if non_existing_labels: - logging.debug(f"Creating total of {len(existing_labels)} new labels in CDF") - labels = [ - LabelDefinition(external_id=label, name=label, data_set_id=data_set_id) for label in non_existing_labels - ] - res = cast(LabelDefinitionList, client.labels.create(labels)) - logging.debug(f"Created total of {len(res)} new labels in CDF") - else: - logging.debug("No new labels created in CDF") diff --git a/cognite/neat/legacy/graph/loaders/core/models.py b/cognite/neat/legacy/graph/loaders/core/models.py deleted file mode 100644 index e89a5f58b..000000000 --- a/cognite/neat/legacy/graph/loaders/core/models.py +++ /dev/null @@ -1,136 +0,0 @@ -from __future__ import annotations - -import logging -from typing import ClassVar - -from pydantic import BaseModel, ConfigDict, root_validator, validator -from rdflib import Namespace - -from cognite.neat.legacy.rules.models.rules import METADATA_VALUE_MAX_LENGTH - - -class AssetClassMapping(BaseModel): - external_id: str - name: str - parent_external_id: str | None = None - description: str | None = None - metadata: dict | None = {} - - @root_validator(pre=True) - def create_metadata(cls, values: dict): - fields = values.keys() - - # adding metadata key in case if it is missing - values["metadata"] = {} if "metadata" not in values else values["metadata"] - - for field in fields: - if field not in ["external_id", "name", "parent_external_id", "data_set_id", "metadata", "description"]: - values["metadata"][field] = "" - return values - - -class AssetTemplate(BaseModel): - """This class is used to validate, repair and wrangle rdf asset dictionary according to the - expected format of cognite sdk Asset dataclass.""" - - external_id_prefix: str | None = None # convenience field to add prefix to external_ids - external_id: str - name: str | None = None - parent_external_id: str | None = None - metadata: dict | None = {} - description: str | None = None - data_set_id: int | None = None - - @root_validator(pre=True) - def preprocess_fields(cls, values: dict): - fields = values.keys() - - # Adding metadata key in case if it is missing - values["metadata"] = {} if "metadata" not in values else values["metadata"] - - for field in fields: - # Enrich: adding any field that is not in the list of expected fields to metadata - if field not in [ - "external_id", - "name", - "parent_external_id", - "data_set_id", - "metadata", - "description", - "external_id_prefix", - ]: - values["metadata"][field] = values[field] - - # Repair: in case if name/description is list instead of single value list elements are joined - elif field in ["name", "description"] and isinstance(values[field], list): - msg = f"{values['type']} instance {values['identifier']} property {field} " - msg += f"has multiple values {values[field]}, " - msg += f"these values will be joined in a single string: {', '.join(values[field])}" - logging.info(msg) - values[field] = ", ".join(sorted(values[field]))[: METADATA_VALUE_MAX_LENGTH - 1] - - # Repair: in case if external_id or parent_external_id are lists, we take the first value - elif field in ["external_id", "parent_external_id"] and isinstance(values[field], list): - msg = f"{values['type']} instance {values['identifier']} property {field} " - msg += f"has multiple values {values[field]}, " - msg += f"only the first one will be used: {values[field][0]}" - logging.info(msg) - values[field] = values[field][0] - - # Setting asset to be by default active - values["metadata"]["active"] = "true" - - # Handling case when the external_id is not provided by defaulting to the original identifier - # The original identifier probably has its namespace removed - if "external_id" not in fields and "identifier" in fields: - values["external_id"] = values["identifier"] - - return values - - @validator("metadata") - def to_list_if_comma(cls, value): - for key, v in value.items(): - if isinstance(v, list): - value[key] = ", ".join(sorted(v))[: METADATA_VALUE_MAX_LENGTH - 1] - return value - - @validator("metadata") - def to_str(cls, value): - for key, v in value.items(): - value[key] = str(v) - return value - - @validator("external_id", always=True) - def add_prefix_to_external_id(cls, value, values): - if values["external_id_prefix"]: - return values["external_id_prefix"] + value - else: - return value - - @validator("parent_external_id") - def add_prefix_to_parent_external_id(cls, value, values): - if values["external_id_prefix"]: - return values["external_id_prefix"] + value - else: - return value - - -class RelationshipDefinition(BaseModel): - source_class: str - target_class: str - property_: str - labels: list[str] | None = None - target_type: str = "Asset" - source_type: str = "Asset" - relationship_external_id_rule: str | None = None - - -class RelationshipDefinitions(BaseModel): - model_config: ClassVar[ConfigDict] = ConfigDict( - populate_by_name=True, str_strip_whitespace=True, arbitrary_types_allowed=True, strict=False - ) - - data_set_id: int - prefix: str - namespace: Namespace - relationships: dict[str, RelationshipDefinition] diff --git a/cognite/neat/legacy/graph/loaders/core/rdf_to_assets.py b/cognite/neat/legacy/graph/loaders/core/rdf_to_assets.py deleted file mode 100644 index ab0e5bbe2..000000000 --- a/cognite/neat/legacy/graph/loaders/core/rdf_to_assets.py +++ /dev/null @@ -1,1046 +0,0 @@ -import logging -import sys -from collections.abc import Iterable, Mapping, Sequence -from dataclasses import dataclass, fields -from datetime import datetime -from typing import Any, Literal, TypeAlias, cast, overload -from warnings import warn - -import numpy as np -import pandas as pd -from cognite.client import CogniteClient -from cognite.client.data_classes import Asset, AssetHierarchy, AssetList, AssetUpdate -from cognite.client.exceptions import CogniteDuplicatedError -from deepdiff import DeepDiff # type: ignore[import] -from rdflib import Graph -from rdflib.term import URIRef - -from cognite.neat.legacy.graph.loaders.core.models import AssetTemplate -from cognite.neat.legacy.graph.stores import NeatGraphStoreBase -from cognite.neat.legacy.rules.models.rules import Property, Rules -from cognite.neat.utils.auxiliary import retry_decorator -from cognite.neat.utils.collection_ import chunker -from cognite.neat.utils.rdf_ import remove_namespace_from_uri -from cognite.neat.utils.time_ import datetime_utc_now - -if sys.version_info >= (3, 11): - from datetime import UTC - from typing import Self -else: - from datetime import timezone - - from typing_extensions import Self - - UTC = timezone.utc - -EXCLUDE_PATHS = [ - "root['labels']", - "root['metadata']['create_time']", - "root['metadata']['start_time']", - "root['metadata']['update_time']", - "root['metadata']['end_time']", - "root['metadata']['resurrection_time']", # need to account for assets that are brought back to life -] - - -@dataclass -class NeatMetadataKeys: - """Class holding mapping between NEAT metadata key names and their desired names in - in CDF Asset metadata - - Args: - start_time: Start time key name - end_time: End time key name - update_time: Update time key name - resurrection_time: Resurrection time key name - identifier: Identifier key name - active: Active key name - type: Type key name - """ - - start_time: str = "start_time" - end_time: str = "end_time" - update_time: str = "update_time" - resurrection_time: str = "resurrection_time" - identifier: str = "identifier" - active: str = "active" - type: str = "type" - - @classmethod - def load(cls, data: dict) -> Self: - cls_field_names = {f.name for f in fields(cls)} - valid_keys = {} - for key, value in data.items(): - if key in cls_field_names: - valid_keys[key] = value - else: - logging.warning(f"Invalid key set {key}") - - return cls(**valid_keys) - - def as_aliases(self) -> dict[str, str]: - return {str(field.default): getattr(self, field.name) for field in fields(self)} - - -def _get_class_instance_ids(graph: Graph, class_uri: URIRef, limit: int = -1) -> list[URIRef]: - """Get instances ids for a given class - - Args: - graph: Graph containing class instances - class_uri: Class for which instances are to be found - limit: Max number of instances to return, by default -1 meaning all instances - - Returns: - List of class instance URIs - """ - - query_statement = "SELECT DISTINCT ?subject WHERE { ?subject a .} LIMIT X".replace( - "class", class_uri - ).replace("LIMIT X", "" if limit == -1 else f"LIMIT {limit}") - logging.debug(f"Query statement: {query_statement}") - return [cast(tuple, res)[0] for res in list(graph.query(query_statement))] - - -def _get_class_instance(graph: Graph, instance: URIRef) -> list[tuple]: - """Get instance by means of tuples containing property-value pairs - Args: - graph: Graph containing class instances - instance: Instance URI - - Returns: - list of property-value pairs for given instance - """ - - query_statement = "SELECT DISTINCT ?predicate ?object WHERE { ?predicate ?object .}".replace( - "subject", instance - ) - result = list(cast(tuple, graph.query(query_statement))) - - # Adds instance id for the sake of keep the chain of custody - result += [(URIRef("http://purl.org/dc/terms/identifier"), instance)] - - return result - - -def _get_class_property_pairs(transformation_rules: Rules) -> dict[str, list[Property]]: - """Define classes in terms of their properties - - Args: - transformation_rules : Instance of TransformationRules containing class and property definitions - - Returns: - Dict containing keys as class ids and list of their properties - """ - - classes: dict[str, list[Property]] = {} - - for property_ in transformation_rules.properties.keys(): - class_ = transformation_rules.properties[property_].class_id - if class_ in classes: - classes[class_] += [transformation_rules.properties[property_]] - else: - classes[class_] = [transformation_rules.properties[property_]] - - return classes - - -def _define_asset_class_mapping(transformation_rules: Rules) -> dict[str, dict[str, list]]: - """Define mapping from class to asset properties - - Args: - transformation_rules : Instance of TransformationRules containing class and property definitions - - Returns: - Dict containing mapping from class to asset properties - """ - solution2cdf_mapping_rules = _get_class_property_pairs(transformation_rules) - - asset_class_mapping: dict[str, dict[str, list]] = {} - - for class_, properties in solution2cdf_mapping_rules.items(): - asset_class_mapping[class_] = { - "external_id": [], - "name": [], - "description": [], - "parent_external_id": [], - "metadata": [], - } - - for property_ in properties: - if "Asset" in property_.cdf_resource_type and property_.property_name != "*": - for resource_type_property in property_.resource_type_property or []: - if ( - resource_type_property in asset_class_mapping[class_] - and property_.property_name not in asset_class_mapping[class_][resource_type_property] - ): - asset_class_mapping[class_][resource_type_property] += [property_.property_name] - - if property_.property_name not in asset_class_mapping[class_]["metadata"]: - # Todo; Why Nikola? This adds for example name property to metadata? Isn't that - # controlled by the resource_type_property? If you would like this behavior you - # would set resource_type_property to ["metadata", "name"]? - asset_class_mapping[class_]["metadata"] += [property_.property_name] - - return asset_class_mapping - - -def _remap_class_properties(class_instance: dict, asset_class_mapping: dict) -> tuple[dict, set, set]: - """Remaps original class instance properties to asset properties (e.g., external_id, name, description, metadata) - - Args: - class_instance: Dictionary containing class instance properties and values - originating from RDF stripped from namespaces - asset_class_mapping: Property mapping from class to asset - - Returns: - Remapped class instance, set of missing asset properties and set of missing asset metadata - """ - # Make distinction between missing properties that map into Asset fields - # and missing RDF properties that are defined by sheet - instance_properties = list(class_instance.keys()) - missing_properties = set() - - for property_group, ordered_properties in asset_class_mapping.items(): - if property_group != "metadata" and ordered_properties: - if matching_property := next((a for a in ordered_properties if a in instance_properties), None): - class_instance[property_group] = class_instance[matching_property] - else: - missing_properties.add(property_group) - - missing_metadata = set(asset_class_mapping["metadata"]).difference(set(instance_properties)) - - return class_instance, missing_properties, missing_metadata - - -def _class2asset_instance( - class_: str, - class_instance: dict, - asset_class_mapping: dict, - data_set_id: int, - meta_keys: NeatMetadataKeys, - orphanage_asset_external_id: str | None = None, - external_id_prefix: str | None = None, - fallback_property: str = NeatMetadataKeys.identifier, - empty_name_default: str = "Missing Name", - add_missing_metadata: bool = True, -) -> dict[str, Any]: - """Converts class instance to asset instance dictionary - - Args: - class_: Class name which instance is being converted to asset instance - class_instance: Dictionary containing class instance properties and values originating from RDF - stripped from namespaces - asset_class_mapping: Property mapping from class to asset - data_set_id: data set id to which asset belongs - orphanage_asset_id: Orphanage asset external id, by default None - external_id_prefix: External id prefix to be added to any external id, by default None - fallback_property: Property from class instance to be used as fallback in case of - missing properties, by default "identifier" - - - Returns: - Asset instance dictionary - """ - - remapped_class_instance, missing_properties, missing_metadata = _remap_class_properties( - class_instance, asset_class_mapping - ) - - # setting class instance type to class name - remapped_class_instance[meta_keys.type] = class_ - # This will be a default case since we want to use original identifier as external_id - # We are though dropping namespace from the original identifier (avoiding long-tail URIs) - - if "external_id" in missing_properties or asset_class_mapping["external_id"] == []: - try: - __extracted_from___class2asset_instance_49( - remapped_class_instance, fallback_property, "external_id", class_ - ) - except Exception: - __extracted_from___class2asset_instance_56(fallback_property, class_, remapped_class_instance) - # This should not be the use case however to still have name of the object we are using - # fallback property here as well (typically identifier) - if "name" in missing_properties: - try: - __extracted_from___class2asset_instance_49(remapped_class_instance, fallback_property, "name", class_) - except Exception: - __extracted_from___class2asset_instance_56(fallback_property, class_, remapped_class_instance) - - # If object is expected to have parent, but parent is not provided, it is added to orphanage - # This is typically sign of objects not following proposed ontology/data model/schema - if "parent_external_id" in missing_properties and orphanage_asset_external_id: - remapped_class_instance["parent_external_id"] = orphanage_asset_external_id - - if "name" in remapped_class_instance and remapped_class_instance["name"] == "": - remapped_class_instance["name"] = empty_name_default - # To maintain shape across of all assets of specific type we are adding missing metadata - # keys as empty strings, this was request by a customer - # Generally this is bad practice, but more of a workaround of their bad data - if missing_metadata and add_missing_metadata: - msg = f"Adding missing metadata keys with values set to empty string for {class_}" - msg += f" instance <{remapped_class_instance['identifier']}>. " - logging.debug(msg) - for key in missing_metadata: - if key not in remapped_class_instance.keys(): - remapped_class_instance[key] = "" - logging.debug(f"\tKey {key} added to <{remapped_class_instance['identifier']}> metadata!") - - asset_instance = AssetTemplate( - **remapped_class_instance, external_id_prefix=external_id_prefix, data_set_id=data_set_id - ) - # Removing field external_id_prefix from asset instance dictionary as it is only - # convenience field for external_id and parent_external_id update in AssetTemplate - return asset_instance.model_dump(exclude={"external_id_prefix"}) - - -# TODO Rename this here and in `__class2asset_instance` -def __extracted_from___class2asset_instance_49(remapped_class_instance, fallback_property, arg2, class_): - remapped_class_instance[arg2] = remapped_class_instance[fallback_property] - msg = f"Missing external_id for {class_} instance <{remapped_class_instance['identifier']}>. " - msg += f"Using value <{remapped_class_instance[fallback_property]}> provided " - msg += f"by property <{fallback_property}>!" - - logging.debug(msg) - - -# TODO Rename this here and in `__class2asset_instance` -def __extracted_from___class2asset_instance_56(fallback_property, class_, remapped_class_instance): - msg = f"Fallback property <{fallback_property}> not found for {class_} " - msg += f"instance <{remapped_class_instance['identifier']}>." - logging.error(msg) - raise ValueError(msg) - - -def _list2dict(class_instance: list) -> dict[str, Any]: - """Converting list of class instance properties and values to dictionary - - Args: - class_instance: Class instance properties and values originating from RDF as list of tuples - - Returns: - Class instance properties and values as dictionary - """ - - class_instance_dict: dict[str, Any] = {} - for property_value_pair in class_instance: - property_ = remove_namespace_from_uri(property_value_pair[0]) - - # Remove namespace from URIRef values, otherwise convert Literal to string - # ideally this should react upon property type provided in sheet - # however Assets only support string values - value = ( - remove_namespace_from_uri(property_value_pair[1]) - if isinstance(property_value_pair[1], URIRef) - else str(property_value_pair[1]) - ) - - if property_ in class_instance_dict and value not in class_instance_dict[property_]: - class_instance_dict[property_] = ( - class_instance_dict[property_] + [value] - if isinstance(class_instance_dict[property_], list) - else [class_instance_dict[property_], value] - ) - else: - class_instance_dict[property_] = value - - return class_instance_dict - - -def rdf2assets( - graph_store: NeatGraphStoreBase, - rules: Rules, - data_set_id: int, - stop_on_exception: bool = False, - use_orphanage: bool = True, - meta_keys: NeatMetadataKeys | None = None, - asset_external_id_prefix: str | None = None, -) -> dict[str, dict[str, Any]]: - """Creates assets from RDF graph - - Args: - graph_store : Graph containing RDF data - rules : Instance of TransformationRules class containing transformation rules - data_set_id: data set id to which assets belong - stop_on_exception : Whether to stop upon exception. - use_orphanage : Whether to use an orphanage for assets without parent_external_id - meta_keys : The names of neat metadat keys to use. - - Returns: - Dictionary representations of assets by external id. - """ - meta_keys = NeatMetadataKeys() if meta_keys is None else meta_keys - if rules.metadata.namespace is None: - raise ValueError("Namespace must be provided in transformation rules!") - namespace = rules.metadata.namespace - - orphanage_asset_external_id = f"{asset_external_id_prefix or ''}orphanage-{data_set_id}" - - graph = graph_store.get_graph() - # Step 1: Create rdf to asset property mapping - logging.info("Generating rdf to asset property mapping") - asset_class_mapping = _define_asset_class_mapping(rules) - - # Step 4: Get ids of classes - logging.info("Get ids of instances of classes") - assets: dict[str, dict[str, Any]] = {} - class_ids = {class_: _get_class_instance_ids(graph, namespace[class_]) for class_ in asset_class_mapping} - # Step 5: Create Assets based on class instances - logging.info("Create Assets based on class instances") - meta_keys_aliases = meta_keys.as_aliases() - for class_ in asset_class_mapping: - # TODO: Rename class_id to instance_id - class_ns = namespace[class_] - logging.debug(f"Processing class <{class_ns}> . Number of instances: {len(class_ids[class_])}") - progress_counter = 0 - # loading all instances into cache - try: - query = ( - f"SELECT ?instance ?prop ?value " - f"WHERE {{ ?instance rdf:type <{class_ns}> . ?instance ?prop ?value . }} order by ?instance " - ) - logging.info(query) - response_df = graph_store.query_to_dataframe(query) - except Exception as e: - logging.error(f"Error while loading instances of class <{class_ns}> into cache. Reason: {e}") - if stop_on_exception: - raise e - continue - - grouped_df = response_df.groupby("instance") - - for instance_id, group_df in grouped_df: - try: - instance_property_values = group_df.filter(items=["property", "value"]).values.tolist() - instance_property_values += [(URIRef("http://purl.org/dc/terms/identifier"), URIRef(str(instance_id)))] - - # this will strip namespace from property names and values - class_instance = _list2dict(instance_property_values) - - # class instance is repaired and converted to asset dictionary - asset = _class2asset_instance( - class_, - class_instance, - asset_class_mapping[class_], - data_set_id, - meta_keys, - orphanage_asset_external_id if use_orphanage else None, # we need only base external id - asset_external_id_prefix or None, - fallback_property=meta_keys.identifier, - ) - - # adding labels and timestamps - asset["labels"] = [asset["metadata"][meta_keys.type], "non-historic"] - now = str(datetime.now(UTC)) - asset["metadata"][meta_keys.start_time] = now - asset["metadata"][meta_keys.update_time] = now - asset["metadata"] = {meta_keys_aliases.get(k, k): v for k, v in asset["metadata"].items()} - - # log every 10000 assets - if progress_counter % 10000 == 0: - logging.info(" Next 10000 Assets processed") - - assets[asset["external_id"]] = asset - progress_counter += 1 - except Exception as ValidationError: - logging.error( - f"Skipping class <{class_}> instance <{remove_namespace_from_uri(str(instance_id))}>, " - f"reason:\n{ValidationError}\n" - ) - if stop_on_exception: - raise ValidationError - - logging.debug(f"Class <{class_}> processed") - - if orphanage_asset_external_id not in assets: - logging.warning(f"Orphanage with external id {orphanage_asset_external_id} not found in asset hierarchy!") - logging.warning(f"Adding default orphanage with external id {orphanage_asset_external_id}") - assets[orphanage_asset_external_id] = _create_orphanage(orphanage_asset_external_id, data_set_id, meta_keys) - - logging.info("Assets dictionary created") - - return assets - - -def rdf2asset_dictionary( - graph_store: NeatGraphStoreBase, - transformation_rules: Rules, - stop_on_exception: bool = False, - use_orphanage: bool = True, -) -> dict[str, dict[str, Any]]: - warn("'rdf2asset_dictionary' is deprecated, please use 'rdf2assets' instead!", stacklevel=2) - logging.warning("'rdf2asset_dictionary' is deprecated, please use 'rdf2assets' instead!") - return rdf2assets(graph_store, transformation_rules, stop_on_exception, use_orphanage) - - -def _create_orphanage(orphanage_external_id: str, dataset_id: int, meta_keys: NeatMetadataKeys) -> dict: - now = str(datetime_utc_now()) - return { - "external_id": orphanage_external_id, - "name": "Orphanage", - "parent_external_id": None, - "description": "Used to store all assets which parent does not exist", - "metadata": { - meta_keys.type: "Orphanage", - "cdfResourceType": "Asset", - meta_keys.identifier: "orphanage", - meta_keys.active: "true", - meta_keys.start_time: now, - meta_keys.update_time: now, - }, - "data_set_id": dataset_id, - "labels": ["Orphanage", "non-historic"], - } - - -def _asset2dict(asset: Asset) -> dict: - """Return asset as dict representation - - Args: - asset : Instance of Asset class - - Returns: - Asset in dict representation - """ - - return { - "external_id": asset.external_id, - "name": asset.name, - "description": asset.description, - "parent_external_id": asset.parent_external_id, - "data_set_id": asset.data_set_id, - "metadata": asset.metadata, - } - - -def _flatten_labels(labels: list[dict[str, str]]) -> set[str]: - """Flatten labels""" - result = set() - if labels is None: - return result - for label in labels: - if "externalId" in label: - result.add(label["externalId"]) - elif "external_id" in label: - result.add(label["external_id"]) - else: - logging.warning(f"Label {label} does not have externalId") - return result - - -def _is_historic(labels) -> bool: - """Check if asset is historic""" - return "historic" in labels - - -def _categorize_cdf_assets( - client: CogniteClient, data_set_id: int, partitions: int -) -> tuple[pd.DataFrame | None, dict[str, set]]: - """Categorize CDF assets - - Args: - client : Instance of CogniteClient - data_set_id : Id of data set - partitions : Number of partitions - - Returns: - CDF assets as pandas dataframe and dictionary with categorized assets - """ - cdf_assets = client.assets.list(data_set_ids=data_set_id, limit=-1, partitions=partitions) - - cdf_assets = remove_non_existing_labels(client, cdf_assets) - - cdf_asset_df = AssetList(resources=cdf_assets).to_pandas() - - logging.info(f"Number of assets in CDF {len(cdf_asset_df)} that have been fetched") - - if cdf_asset_df.empty: - return None, {"non-historic": set(), "historic": set()} - if "labels" not in cdf_asset_df: - # Add empty list for labels column. - cdf_asset_df["labels"] = np.empty((len(cdf_asset_df), 0)).tolist() - - cdf_columns = set(cdf_asset_df.columns) - expected_columns = {"external_id", "labels", "parent_external_id", "data_set_id", "name", "description", "metadata"} - - cdf_asset_df = cdf_asset_df[list(expected_columns.intersection(cdf_columns))] - cdf_asset_df = cdf_asset_df.where(pd.notnull(cdf_asset_df), None) - cdf_asset_df["labels"] = cdf_asset_df["labels"].apply(_flatten_labels).values # type: ignore - cdf_asset_df["is_historic"] = cdf_asset_df.labels.apply(_is_historic).values - - categorized_asset_ids = { - "historic": set(cdf_asset_df[cdf_asset_df.is_historic].external_id.values), - "non-historic": set(cdf_asset_df[~cdf_asset_df.is_historic].external_id.values), - } - - cdf_asset_df.drop(["is_historic"], axis=1, inplace=True) - msg = f"CDF assets categorized into {len(categorized_asset_ids['historic'])} historic" - msg += f" and {len(categorized_asset_ids['non-historic'])} non-historic assets" - logging.info(msg) - - return cdf_asset_df, categorized_asset_ids - - -def order_assets(assets: dict[str, dict]) -> list[Asset]: - """Order assets in a way that parent assets are created before child assets - - Args: - assets : List of assets to be created - - Returns: - Ordered list of assets - """ - hierarchy = AssetHierarchy([Asset(**asset) for asset in assets.values()], ignore_orphans=True) - insert_dct = hierarchy.groupby_parent_xid() - subtree_count = hierarchy.count_subtree(insert_dct) - - hierarchy = None - - asset_creation_order = pd.DataFrame.from_dict(subtree_count, orient="index", columns=["order"]).sort_values( - by="order", ascending=False - ) - asset_creation_order["external_id"] = asset_creation_order.index - - hierarchy = AssetList([Asset(**asset) for asset in assets.values()]).to_pandas() - hierarchy = hierarchy.where(pd.notnull(hierarchy), None) - hierarchy = hierarchy.merge(asset_creation_order, left_on="external_id", right_on="external_id") - hierarchy = hierarchy.sort_values(by="order", ascending=False) - hierarchy.reset_index(drop=True, inplace=True) - hierarchy.labels = hierarchy.labels.apply(_flatten_labels) - hierarchy.drop(["order"], axis=1, inplace=True) - - return [Asset(**row.to_dict()) for _, row in hierarchy.iterrows()] - - -def _assets_to_create(rdf_assets: dict, asset_ids: set) -> list[Asset]: - """Return list of assets to be created - - Args: - rdf_assets : Dictionary containing assets derived from knowledge graph (RDF) - asset_ids : Set of asset ids to be created - - Returns: - Ordered list of assets to be created - """ - start_time = datetime_utc_now() - if asset_ids: - logging.info("Wrangling assets to be created into their final form") - ordered_assets = order_assets({external_id: rdf_assets[external_id] for external_id in asset_ids}) - - logging.info(f"Wrangling completed in {(datetime_utc_now() - start_time).seconds} seconds") - return ordered_assets - return [] - - -def _assets_to_update( - rdf_assets: dict, - cdf_assets: pd.DataFrame | None, - asset_ids: set, - meta_keys: NeatMetadataKeys, - exclude_paths: list = EXCLUDE_PATHS, -) -> tuple[list[Asset], dict[str, dict]]: - """Return list of assets to be updated - - Args: - rdf_assets : Dictionary containing assets derived from knowledge graph (RDF) - cdf_assets : Dataframe containing assets from CDF - asset_ids : Candidate assets to be updated - meta_keys : The neat meta data keys. - exclude_paths : Paths not to be checked when diffing rdf and cdf assets, by default EXCLUDE_PATHS - - Returns: - List of assets to be updated and detailed report of changes per asset - """ - - start_time = datetime_utc_now() - assets = [] - report = {} - if not asset_ids: - return [], {} - logging.info("Wrangling assets to be updated into their final form") - if cdf_assets is None: - cdf_asset_subset = {} - else: - cdf_asset_subset = { - row["external_id"]: row - for row in cdf_assets[cdf_assets["external_id"].isin(asset_ids)].to_dict(orient="records") - } - for external_id in asset_ids: - cdf_asset = cdf_asset_subset[external_id] - diffing_result = DeepDiff(cdf_asset, rdf_assets[external_id], exclude_paths=exclude_paths) - - if diffing_result and f"root['metadata']['{meta_keys.active}']" not in diffing_result.affected_paths: - asset = Asset(**rdf_assets[external_id]) - if asset.metadata is None: - asset.metadata = {} - try: - asset.metadata[meta_keys.start_time] = cdf_asset[external_id]["metadata"][meta_keys.start_time] - except KeyError: - asset.metadata[meta_keys.start_time] = str(datetime.now(UTC)) - asset.metadata[meta_keys.update_time] = str(datetime.now(UTC)) - assets.append(asset) - - report[external_id] = dict(diffing_result) - - logging.info(f"Wrangling of {len(assets)} completed in {(datetime_utc_now() - start_time).seconds} seconds") - return assets, report - - -def _assets_to_resurrect( - rdf_assets: dict, cdf_assets: pd.DataFrame | None, asset_ids: set, meta_keys: NeatMetadataKeys -) -> list[Asset]: - """Returns list of assets to be resurrected - - Args: - rdf_assets : Dictionary containing assets derived from knowledge graph (RDF) - cdf_assets : Dataframe containing assets from CDF - asset_ids : Set of asset ids to be resurrected - - Returns: - List of assets to be resurrected - """ - start_time = datetime_utc_now() - assets = [] - if not asset_ids: - return [] - logging.info("Wrangling assets to be resurrected into their final form") - if cdf_assets is None: - cdf_asset_subset = {} - else: - cdf_asset_subset = { - row["external_id"]: row - for row in cdf_assets[cdf_assets["external_id"].isin(asset_ids)].to_dict(orient="records") - } - for external_id in asset_ids: - cdf_asset = cdf_asset_subset[external_id] - - asset = Asset(**rdf_assets[external_id]) - if asset.metadata is None: - asset.metadata = {} - now = str(datetime.now(UTC)) - try: - asset.metadata[meta_keys.start_time] = cdf_asset[external_id]["metadata"][meta_keys.start_time] - except KeyError: - asset.metadata[meta_keys.start_time] = now - asset.metadata[meta_keys.update_time] = now - asset.metadata[meta_keys.resurrection_time] = now - assets.append(asset) - - logging.info(f"Wrangling of {len(assets)} completed in {(datetime_utc_now() - start_time).seconds} seconds") - return assets - - -def _assets_to_decommission( - cdf_assets: pd.DataFrame | None, asset_ids: set[str], meta_keys: NeatMetadataKeys -) -> list[Asset]: - start_time = datetime_utc_now() - - assets = [] - if not asset_ids: - return [] - logging.info("Wrangling assets to be decommissioned into their final form") - if cdf_assets is None: - cdf_asset_subset: dict[str, dict] = {} - else: - cdf_asset_subset = { - row["external_id"]: row - for row in cdf_assets[cdf_assets["external_id"].isin(asset_ids)].to_dict(orient="records") - } - - for external_id in asset_ids: - cdf_asset = cdf_asset_subset[external_id] - - now = str(datetime.now(UTC)) - cdf_asset["metadata"][meta_keys.update_time] = now - cdf_asset["metadata"].pop(meta_keys.resurrection_time, None) - cdf_asset["metadata"][meta_keys.end_time] = now - cdf_asset["metadata"][meta_keys.active] = "false" - try: - cdf_asset["labels"].remove("non-historic") - except KeyError: - logging.info(f"Asset {external_id} missed label 'non-historic'") - cdf_asset["labels"].add("historic") - - assets.append(Asset(**cdf_asset)) - - logging.info(f"Wrangling completed in {(datetime_utc_now() - start_time).seconds} seconds") - return assets - - -@overload -def categorize_assets( - client: CogniteClient, - rdf_assets: dict, - data_set_id: int, - return_report: Literal[False] = False, - partitions: int = 2, - stop_on_exception: bool = False, - meta_keys: NeatMetadataKeys | None = None, -) -> dict: ... - - -@overload -def categorize_assets( - client: CogniteClient, - rdf_assets: dict, - data_set_id: int, - return_report: Literal[True], - partitions: int = 2, - stop_on_exception: bool = False, - meta_keys: NeatMetadataKeys | None = None, -) -> tuple[dict, dict]: ... - - -def categorize_assets( - client: CogniteClient, - rdf_assets: dict, - data_set_id: int, - return_report: bool = False, - partitions: int = 2, - stop_on_exception: bool = False, - meta_keys: NeatMetadataKeys | None = None, -) -> tuple[dict, dict] | dict: - """Categorize assets on those that are to be created, updated and decommissioned - - Args: - client : Instance of CogniteClient - rdf_assets : Dictionary containing asset external_id - asset pairs - data_set_id : Dataset id to which assets are to be/are stored - partitions : Number of partitions to use when fetching assets from CDF, by default 2 - stop_on_exception : Whether to stop on exception or not, by default False - return_report : Whether to report on the diffing results or not, by default False - meta_keys : The metadata keys used by neat. - - Returns: - dictionary containing asset category - list of asset pairs - """ - meta_keys = NeatMetadataKeys() if meta_keys is None else meta_keys - - # TODO: Cache categorized assets somewhere instead of creating them - cdf_assets, categorized_asset_ids = _categorize_cdf_assets(client, data_set_id, partitions) - - rdf_asset_ids = set(rdf_assets.keys()) - - # ids to create - create_ids = rdf_asset_ids.difference( - categorized_asset_ids["historic"].union(categorized_asset_ids["non-historic"]) - ) - - # ids potentially to update - update_ids = rdf_asset_ids.intersection(categorized_asset_ids["non-historic"]) - - # ids to decommission - decommission_ids = categorized_asset_ids["non-historic"].difference(rdf_asset_ids) - - # ids to resurrect - resurrect_ids = categorized_asset_ids["historic"].intersection(rdf_asset_ids) - - logging.info(f"Number of assets to create: { len(create_ids)}") - logging.info(f"Number of assets to potentially update: { len(update_ids)}") - logging.info(f"Number of assets to decommission: { len(decommission_ids)}") - logging.info(f"Number of assets to resurrect: { len(resurrect_ids)}") - - categorized_assets_update, report_update = _assets_to_update( - rdf_assets, cdf_assets, update_ids, meta_keys=meta_keys - ) - report = { - "create": create_ids, - "resurrect": resurrect_ids, - "decommission": decommission_ids, - "update": report_update, - } - categorized_assets = { - "create": _assets_to_create(rdf_assets, create_ids), - "update": categorized_assets_update, - "resurrect": _assets_to_resurrect(rdf_assets, cdf_assets, resurrect_ids, meta_keys), - "decommission": _assets_to_decommission(cdf_assets, decommission_ids, meta_keys), - } - - return (categorized_assets, report) if return_report else categorized_assets - - -def _micro_batch_push( - client: CogniteClient, - assets: Sequence[Asset | AssetUpdate], - batch_size: int = 1000, - push_type: str = "update", - message: str = "Updated", - max_retries: int = 1, - retry_delay: int = 5, -): - """Updates assets in batches of 1000 - - Args: - client : CogniteClient - Instance of CogniteClient - assets : list - List of assets to be created or updated - batch_size : int, optional - Size of batch, by default 1000 - push_type : str, optional - Type of push, either "update" or "create", by default "update" - message : str, optional - Message to logged, by default "Updated" - """ - total = len(assets) - counter = 0 - if push_type not in ["update", "create"]: - logging.info(f"push_type {push_type} not supported") - raise ValueError(f"push_type {push_type} not supported") - for batch in chunker(assets, batch_size): - counter += len(batch) - start_time = datetime_utc_now() - - @retry_decorator(max_retries=max_retries, retry_delay=retry_delay, component_name="microbatch-assets") - def upsert_assets(batch): - if push_type == "update": - client.assets.update(batch) - elif push_type == "create": - client.assets.create_hierarchy(batch) - - try: - upsert_assets(batch) - except CogniteDuplicatedError: - # this is handling of very rare case when some assets might be lost . Normally this should not happen. - # Last attempt to recover - client.assets.create_hierarchy(batch, upsert=True) # type: ignore[arg-type] - - delta_time = (datetime_utc_now() - start_time).seconds - - msg = f"{message} {counter} of {total} assets, batch processing time: {delta_time:.2f} " - msg += f"seconds ETC: {delta_time * (total - counter) / (60*batch_size) :.2f} minutes" - logging.info(msg) - - -def upload_assets( - client: CogniteClient, - categorized_assets: Mapping[str, Sequence[Asset | AssetUpdate]], - batch_size: int = 5000, - max_retries: int = 1, - retry_delay: int = 3, -): - """Uploads categorized assets to CDF - - Args: - client : CogniteClient - Instance of CogniteClient - categorized_assets : Dict[str, list] - dictionary containing asset category - list of asset pairs - batch_size : int, optional - Size of batch, by default 5000 - - !!! note "batch_size" - If batch size is set to 1 or None, all assets will be pushed to CDF in one go. - """ - if batch_size: - logging.info(f"Uploading assets in batches of {batch_size}") - if categorized_assets["create"]: - _micro_batch_push( - client, - categorized_assets["create"], - batch_size, - push_type="create", - message="Created", - max_retries=max_retries, - retry_delay=retry_delay, - ) - - if categorized_assets["update"]: - _micro_batch_push( - client, - categorized_assets["update"], - batch_size, - message="Updated", - max_retries=max_retries, - retry_delay=retry_delay, - ) - - if categorized_assets["resurrect"]: - _micro_batch_push( - client, - categorized_assets["resurrect"], - batch_size, - message="Resurrected", - max_retries=max_retries, - retry_delay=retry_delay, - ) - - if categorized_assets["decommission"]: - _micro_batch_push( - client, - categorized_assets["decommission"], - batch_size, - message="Decommissioned", - max_retries=max_retries, - retry_delay=retry_delay, - ) - - else: - logging.info("Batch size not set, pushing all assets to CDF in one go!") - - @retry_decorator(max_retries=max_retries, retry_delay=retry_delay, component_name="create-assets") - def create_assets(): - if categorized_assets["create"]: - try: - client.assets.create_hierarchy(categorized_assets["create"]) - except CogniteDuplicatedError: - client.assets.create_hierarchy(categorized_assets["create"], upsert=True) - - if categorized_assets["update"]: - client.assets.create_hierarchy(categorized_assets["update"], upsert=True, upsert_mode="replace") - - if categorized_assets["resurrect"]: - client.assets.create_hierarchy(categorized_assets["resurrect"], upsert=True, upsert_mode="replace") - - if categorized_assets["decommission"]: - client.assets.create_hierarchy(categorized_assets["decommission"], upsert=True, upsert_mode="replace") - - create_assets() - - -AssetLike: TypeAlias = Asset | dict[str, Any] - - -@overload -def remove_non_existing_labels(client: CogniteClient, assets: Sequence[AssetLike]) -> Sequence[AssetLike]: ... - - -@overload -def remove_non_existing_labels(client: CogniteClient, assets: Mapping[str, AssetLike]) -> Mapping[str, AssetLike]: ... - - -def remove_non_existing_labels( - client: CogniteClient, assets: Sequence[AssetLike] | Mapping[str, AssetLike] -) -> Sequence[AssetLike] | Mapping[str, AssetLike]: - cdf_labels = client.labels.list(limit=-1) - if not cdf_labels: - # No labels, nothing to check. - return assets - - available_labels = {label.external_id for label in cdf_labels} - - def clean_asset_labels(asset: Asset | dict[str, Any]) -> Asset | dict[str, Any]: - if isinstance(asset, Asset): - asset.labels = [label for label in (asset.labels or []) if label.external_id in available_labels] or None - elif isinstance(asset, dict) and "labels" in asset: - asset["labels"] = [label for label in asset["labels"] if label in available_labels] - return asset - - if isinstance(assets, Sequence): - return [clean_asset_labels(a) for a in assets] - - elif isinstance(assets, dict): - return {external_id: clean_asset_labels(a) for external_id, a in assets.items()} - - raise ValueError(f"Invalid format for Assets={type(assets)}") - - -def unique_asset_labels(assets: Iterable[Asset | dict[str, Any]]) -> set[str]: - labels: set[str] = set() - for asset in assets: - if isinstance(asset, Asset): - labels |= {label.external_id for label in (asset.labels or []) if label.external_id} - elif isinstance(asset, dict) and (asset_labels := asset.get("labels")): - labels |= set(asset_labels) - else: - raise ValueError(f"Unsupported {type(asset)}") - return labels diff --git a/cognite/neat/legacy/graph/loaders/core/rdf_to_relationships.py b/cognite/neat/legacy/graph/loaders/core/rdf_to_relationships.py deleted file mode 100644 index d5f82310c..000000000 --- a/cognite/neat/legacy/graph/loaders/core/rdf_to_relationships.py +++ /dev/null @@ -1,559 +0,0 @@ -import logging -import warnings -from collections.abc import Collection -from typing import Any, Literal, cast, overload -from warnings import warn - -import pandas as pd -from cognite.client import CogniteClient -from cognite.client.data_classes import LabelFilter, Relationship, RelationshipUpdate -from cognite.client.exceptions import CogniteDuplicatedError - -from cognite.neat.legacy.graph.exceptions import NamespaceRequired -from cognite.neat.legacy.graph.loaders.core.models import RelationshipDefinition, RelationshipDefinitions -from cognite.neat.legacy.graph.loaders.core.rdf_to_assets import _categorize_cdf_assets -from cognite.neat.legacy.graph.stores import NeatGraphStoreBase -from cognite.neat.legacy.rules.models.rules import Rules -from cognite.neat.utils.auxiliary import retry_decorator -from cognite.neat.utils.collection_ import chunker -from cognite.neat.utils.rdf_ import remove_namespace_from_uri -from cognite.neat.utils.time_ import datetime_utc_now, epoch_now_ms - - -def define_relationships(rules: Rules, data_set_id: int, stop_on_exception: bool = False) -> RelationshipDefinitions: - """Define relationships from transformation rules - - Args: - rules: Transformation rules which holds data model - data_set_id: CDF data set id to which relationships belong to - stop_on_exception: Whether to stop on exception or to continue. Defaults to False. - - Returns: - RelationshipDefinitions instance holding relationship definitions extracted from transformation rules - which are used to generate CDF relationships - """ - relationships = {} - if rules.metadata.namespace is None: - raise NamespaceRequired("Load Relationships") - namespace = rules.metadata.namespace - prefix = rules.metadata.prefix - - # Unique ids used to check for redefinitions of relationships - ids = set() - - for row, rule in rules.properties.items(): - if "Relationship" in rule.cdf_resource_type: - label_set = {rule.class_id, rule.expected_value_type.suffix, "non-historic", rule.property_id} - if rule.label: - label_set.add(rule.label) - relationship = RelationshipDefinition( - source_class=rule.class_id, - target_class=rule.expected_value_type.suffix, - property_=rule.property_id, - labels=list(label_set), - target_type=rule.target_type, - source_type=rule.source_type, - relationship_external_id_rule=rule.relationship_external_id_rule, - ) - - id_ = f"{rule.class_id}({rule.property_id})" - if id_ in ids: - msg = f"Relationship {rule.property_id} redefined at {row} in transformation rules!" - if stop_on_exception: - logging.error(msg) - raise ValueError(msg) - else: - msg += " Skipping redefinition!" - warnings.warn(msg, stacklevel=2) - logging.warning(msg) - else: - relationships[row] = relationship - ids.add(id_) - - if relationships: - return RelationshipDefinitions( - data_set_id=data_set_id, prefix=prefix, namespace=namespace, relationships=relationships - ) - - msg = "No relationship defined in transformation rule sheet!" - if stop_on_exception: - logging.error(msg) - raise ValueError(msg) - else: - warnings.warn(msg, stacklevel=2) - logging.warning(msg) - return RelationshipDefinitions(data_set_id=data_set_id, prefix=prefix, namespace=namespace, relationships={}) - - -def rdf2relationships( - graph_store: NeatGraphStoreBase, - rules: Rules, - data_set_id: int, - relationship_external_id_prefix: str | None = None, - stop_on_exception: bool = False, -) -> pd.DataFrame: - """Converts RDF triples to relationships - - Args: - graph : Graph instance holding RDF triples - rules : Transformation rules which holds data model and relationship definitions - - Returns: - Dataframe holding relationships - """ - - # Step 1: Generate relationship definitions - relationship_definitions = define_relationships(rules, stop_on_exception) - - # Step 2: Generation relationships - - query_statement_template_by_reference = """ - SELECT ?source ?target - WHERE { - ?source a prefix:source_class . - ?target a prefix:target_class . - ?source prefix:property_ ?target - }""" - - query_statement_template_by_value = """ - SELECT ?source_id ?target_id - WHERE { - ?source a prefix:source_class . - ?source prefix:property_ ?target . - ?source prefix:source_ext_id_prop_name ?source_id . - ?target a prefix:target_class . - ?target prefix:target_ext_id_prop_name ?target_id . - } - """ - - relationship_dfs = [] - for id_, definition in relationship_definitions.relationships.items(): - try: - logging.debug("Processing relationship: " + id_) - external_id_prop_name = definition.relationship_external_id_rule - if external_id_prop_name: - query = ( - query_statement_template_by_value.replace("prefix", relationship_definitions.prefix) - .replace("source_ext_id_prop_name", external_id_prop_name) - .replace("target_ext_id_prop_name", external_id_prop_name) - .replace("source_class", definition.source_class) - .replace("target_class", definition.target_class) - .replace("property_", definition.property_) - ) - else: - query = ( - query_statement_template_by_reference.replace("prefix", relationship_definitions.prefix) - .replace("source_class", definition.source_class) - .replace("target_class", definition.target_class) - .replace("property_", definition.property_) - ) - - logging.debug("Rel query: " + query) - relationship_data_frame = pd.DataFrame(list(graph_store.query(query))) - relationship_data_frame.rename(columns={0: "source_external_id", 1: "target_external_id"}, inplace=True) - - # removes namespace - relationship_data_frame = relationship_data_frame.map(remove_namespace_from_uri) # type: ignore[operator] - - # adding prefix - if relationship_external_id_prefix: - relationship_data_frame["source_external_id"] = ( - relationship_external_id_prefix + relationship_data_frame["source_external_id"] - ) - relationship_data_frame["target_external_id"] = ( - relationship_external_id_prefix + relationship_data_frame["target_external_id"] - ) - - relationship_data_frame["target_type"] = definition.target_type - relationship_data_frame["source_type"] = definition.source_type - - # to make sure that by default we set Relationship to active, i.e. non-historic) - relationship_data_frame["labels"] = [definition.labels] * len(relationship_data_frame) - - # set default external id - relationship_data_frame["external_id"] = ( - relationship_data_frame["source_external_id"] + ":" + relationship_data_frame["target_external_id"] - ) - relationship_data_frame["data_set_id"] = data_set_id - relationship_dfs += [relationship_data_frame] - except Exception as e: - logging.error("Error processing relationship: " + id_) - if stop_on_exception: - raise e - continue - - if relationship_dfs: - relationship_df = pd.concat(relationship_dfs) - relationship_df.reset_index(inplace=True, drop=True) - - # Remove duplicate rows, if any. This should not happen, but it is better to be safe than sorry - relationship_df.drop_duplicates(subset=["external_id"], inplace=True) - - # Remove duplicate rows, if any. This should not happen, but it is better to be safe than sorry - relationship_df.drop_duplicates(subset=["external_id"], inplace=True) - relationship_df["start_time"] = len(relationship_df) * [epoch_now_ms()] - return relationship_df - else: - return pd.DataFrame( - columns=[ - "source_external_id", - "target_external_id", - "target_type", - "source_type", - "labels", - "external_id", - "data_set_id", - "start_time", - ] - ) - - -def rdf2relationship_data_frame( - graph_store: NeatGraphStoreBase, transformation_rules: Rules, stop_on_exception: bool = False -) -> pd.DataFrame: - warn("'rdf2relationship_data_frame' is deprecated, please use 'rdf2relationships' instead!", stacklevel=2) - logging.warning("'rdf2relationship_data_frame' is deprecated, please use 'rdf2relationships' instead!") - return rdf2relationships(graph_store, transformation_rules, stop_on_exception) - - -def _filter_relationship_xids(relationship_data_frame: pd.DataFrame, asset_xids: list | set) -> set: - return set( - relationship_data_frame[ - (relationship_data_frame["source_external_id"].isin(asset_xids)) - | (relationship_data_frame["target_external_id"].isin(asset_xids)) - ]["external_id"] - ) - - -def _categorize_rdf_relationship_xids( - rdf_relationships: pd.DataFrame, categorized_asset_ids: dict -) -> dict[str, set[str]]: - """Categorizes the external ids of the RDF relationship.""" - - missing_asset_ids = ( - set(rdf_relationships.target_external_id) - .union(rdf_relationships.source_external_id) - .difference(categorized_asset_ids["historic"].union(categorized_asset_ids["non-historic"])) - ) - - if missing_asset_ids: - msg = f"Relationships are referring to these assets {missing_asset_ids}, which are missing in CDF." - msg += "Relationships will not be created for assets that are missing in CDF." - msg += "Please make sure that all assets are present in CDF before creating relationships." - logging.warning(msg) - - # First mask all relationships which contain assets that do not exist in CDF - mask_impossible = _filter_relationship_xids(rdf_relationships, missing_asset_ids) - - # Then mask all relationships which contain assets that are historic while masking - # all impossible relationships - mask_historic = _filter_relationship_xids(rdf_relationships, categorized_asset_ids["historic"]).difference( - mask_impossible - ) - - mask_non_historic = ( - _filter_relationship_xids(rdf_relationships, categorized_asset_ids["non-historic"]) - .difference(mask_historic) - .difference(mask_impossible) - ) - - return {"impossible": mask_impossible, "historic": mask_historic, "non-historic": mask_non_historic} - - -def _get_label_based_cdf_relationship_xids(client, data_set_id, labels, partitions) -> set: - """Get external ids of relationships in CDF for a given data set filtered on labels""" - - labels = LabelFilter(contains_any=labels) if labels is not None else None - relationship_data_frame = client.relationships.list( - data_set_ids=data_set_id, limit=-1, labels=labels, partitions=partitions - ).to_pandas() - return set() if relationship_data_frame.empty else set(relationship_data_frame.external_id) - - -def _categorize_cdf_relationship_xids(client, data_set_id, partitions) -> dict[str, set]: - return { - "historic": _get_label_based_cdf_relationship_xids(client, data_set_id, ["historic"], partitions), - "non-historic": _get_label_based_cdf_relationship_xids(client, data_set_id, ["non-historic"], partitions), - } - - -def _relationship_to_create(relationships: pd.DataFrame) -> list[Relationship]: - start_time = datetime_utc_now() - if relationships.empty: - return [] - logging.info("Wrangling assets to be created into their final form") - relationship_list = [Relationship(**cast(dict[str, Any], row)) for row in relationships.to_dict(orient="records")] - logging.info(f"Wrangling completed in {(datetime_utc_now() - start_time).seconds} seconds") - return relationship_list - - -def _relationships_to_decommission(external_ids: Collection[str]) -> list[RelationshipUpdate]: - start_time = datetime_utc_now() - relationships = [] - if not external_ids: - return [] - - logging.info("Wrangling relationships to be decommissioned into their final form") - - for external_id in external_ids: - # Create relationship update object instance - relationship = RelationshipUpdate(external_id=external_id) - - # Remove "non-historic" label and add "historic" label - relationship.labels.remove("non-historic") - relationship.labels.add(["historic"]) - - # Set end time of relationships - relationship.end_time.set(epoch_now_ms()) - - # Add relationship to list of relationship updates - relationships += [relationship] - - logging.info(f"Wrangling of {len(relationships)} completed in {(datetime_utc_now() - start_time).seconds} seconds") - return relationships - - -def _relationships_to_resurrect(external_ids: Collection[str]) -> list[RelationshipUpdate]: - start_time = datetime_utc_now() - relationships = [] - if not external_ids: - return [] - - logging.info("Wrangling relationships to be resurrected into their final form") - - for external_id in external_ids: - # Create relationship update object instance - relationship = RelationshipUpdate(external_id=external_id) - - # Remove "non-historic" label and add "historic" label - relationship.labels.remove("historic") - relationship.labels.add(["non-historic"]) - - # Set end time of relationships - relationship.end_time.set(None) - - # Add relationship to list of relationship updates - relationships += [relationship] - - logging.info(f"Wrangling of {len(relationships)} completed in {(datetime_utc_now() - start_time).seconds} seconds") - return relationships - - -@overload -def categorize_relationships( - client: CogniteClient, - rdf_relationships: pd.DataFrame, - data_set_id: int, - return_report: Literal[False] = False, - partitions: int = 40, -) -> dict[str, list[Relationship] | list[RelationshipUpdate]]: ... - - -@overload -def categorize_relationships( - client: CogniteClient, - rdf_relationships: pd.DataFrame, - data_set_id: int, - return_report: Literal[True], - partitions: int = 40, -) -> tuple[dict[str, list[Relationship] | list[RelationshipUpdate]], dict[str, set]]: ... - - -def categorize_relationships( - client: CogniteClient, - rdf_relationships: pd.DataFrame, - data_set_id: int, - return_report: bool = False, - partitions: int = 40, -) -> ( - tuple[dict[str, list[Relationship] | list[RelationshipUpdate]], dict[str, set]] - | dict[str, list[Relationship] | list[RelationshipUpdate]] -): - """Categorize relationships on those that are to be created, decommissioned or resurrected - - Args: - client : CogniteClient - rdf_relationships : Dataframe holding relationships - data_set_id : CDF data set id to which relationships are to be uploaded - partitions : Number of partitions to use when querying CDF for relationships - return_report : Whether to return report or not - - Returns: - Categorized relationships to be created, decommissioned or resurrected - """ - # TODO also figure out which relationships to be deleted - - _, categorized_asset_ids = _categorize_cdf_assets(client, data_set_id=data_set_id, partitions=partitions) - categorized_rdf_relationships = _categorize_rdf_relationship_xids(rdf_relationships, categorized_asset_ids) - categorized_cdf_relationships = _categorize_cdf_relationship_xids(client, data_set_id, partitions=partitions) - - cdf_relationships_all = categorized_cdf_relationships["historic"].union( - categorized_cdf_relationships["non-historic"] - ) - rdf_relationships_all = categorized_rdf_relationships["historic"].union( - categorized_rdf_relationships["non-historic"] - ) - - # relationships to create - # NonHistoric_rdf - (Historic_cdf U Non-historic_cdf) - create_xids = categorized_rdf_relationships["non-historic"].difference(cdf_relationships_all) - - # relationships to decommission - # rdf: Historic_rdf ∩ NonHistoric_cdf U (All_cdf - All_rdf) - decommission_xids = ( - categorized_rdf_relationships["historic"] - .intersection(categorized_cdf_relationships["non-historic"]) - .union(categorized_cdf_relationships["non-historic"].difference(rdf_relationships_all)) - ) - - # relationships to resurrect - # NonHistoric_rdf ∩ Historic_cdf - resurrect_xids = categorized_rdf_relationships["non-historic"].intersection( - categorized_cdf_relationships["historic"] - ) - - logging.info(f"Number of relationships to create: { len(create_xids)}") - logging.info(f"Number of relationships to decommission: { len(decommission_xids)}") - logging.info(f"Number of relationships to resurrect: { len(resurrect_xids)}") - - report = {"create": create_xids, "resurrect": resurrect_xids, "decommission": decommission_xids} - categorized_relationships: dict[str, list[Relationship] | list[RelationshipUpdate]] = { - "create": _relationship_to_create(rdf_relationships[rdf_relationships.external_id.isin(create_xids)]), - "resurrect": _relationships_to_resurrect(resurrect_xids), - "decommission": _relationships_to_decommission(decommission_xids), - } - - return (categorized_relationships, report) if return_report else categorized_relationships - - -def _micro_batch_push( - client: CogniteClient, - relationships: list, - batch_size: int = 1000, - push_type: str = "update", - message: str = "Updated", - max_retries: int = 1, - retry_delay: int = 5, -): - """Updates assets in batches of 1000 - - Args: - client : CogniteClient - Instance of CogniteClient - relationships : list - List of relationships to be created or updated - batch_size : int, optional - Size of batch, by default 1000 - push_type : str, optional - Type of push, either "update" or "create", by default "update" - message : str, optional - Message to logged, by default "Updated" - """ - total = len(relationships) - counter = 0 - if push_type not in ["update", "create"]: - logging.info(f"push_type {push_type} not supported") - raise ValueError(f"push_type {push_type} not supported") - - for batch in chunker(relationships, batch_size): - counter += len(batch) - start_time = datetime_utc_now() - - @retry_decorator(max_retries=max_retries, retry_delay=retry_delay, component_name="microbatch-relationships") - def update_relationships(batch): - if push_type == "update": - client.relationships.update(batch) - elif push_type == "create": - client.relationships.create(batch) - - try: - update_relationships(batch) - except CogniteDuplicatedError as e: - # This situation should not happen but if it does, we need to handle it - exists = {d["externalId"] for d in e.duplicated} - missing_relationships = [t for t in batch if t.external_id not in exists] - client.relationships.create(missing_relationships) - - delta_time = (datetime_utc_now() - start_time).seconds - - msg = f"{message} {counter} of {total} relationships, batch processing time: {delta_time:.2f} " - msg += f"seconds ETC: {delta_time * (total - counter) / (60*batch_size) :.2f} minutes" - logging.info(msg) - - -def upload_relationships( - client: CogniteClient, - categorized_relationships: dict[str, list[Relationship] | list[RelationshipUpdate]], - batch_size: int = 5000, - max_retries: int = 1, - retry_delay: int = 3, -): - """Uploads categorized relationships to CDF - - Args: - client: Instance of CogniteClient - categorized_relationships: Categories of relationships to be uploaded - batch_size: Size of batch, by default 5000 - max_retries: Maximum times to retry the upload, by default 1 - retry_delay: Time delay before retrying the upload, by default 3 - - !!! note "batch_size" - If batch size is set to 1 or None, all relationships will be pushed to CDF in one go. - """ - if batch_size: - logging.info(f"Uploading relationships in batches of {batch_size}") - if categorized_relationships["create"]: - _micro_batch_push( - client, - categorized_relationships["create"], - batch_size, - push_type="create", - message="Created", - max_retries=max_retries, - retry_delay=retry_delay, - ) - - if categorized_relationships["resurrect"]: - _micro_batch_push( - client, - categorized_relationships["resurrect"], - batch_size, - message="Resurrected", - max_retries=max_retries, - retry_delay=retry_delay, - ) - - if categorized_relationships["decommission"]: - _micro_batch_push( - client, - categorized_relationships["decommission"], - batch_size, - message="Decommissioned", - max_retries=max_retries, - retry_delay=retry_delay, - ) - - else: - logging.info("Batch size not set, pushing all relationships to CDF in one go!") - - @retry_decorator(max_retries=max_retries, retry_delay=retry_delay, component_name="create-relationships") - def create_relationships(): - if categorized_relationships["create"]: - client.relationships.create(categorized_relationships["create"]) - - if categorized_relationships["resurrect"]: - client.relationships.update(categorized_relationships["resurrect"]) - - if categorized_relationships["decommission"]: - client.relationships.update(categorized_relationships["decommission"]) - - try: - create_relationships() - except CogniteDuplicatedError as e: - # This situation should not happen, but if it does, the code attempts to handle it - exists = {d["externalId"] for d in e.duplicated} - missing_relationships = [ - t for t in cast(list[Relationship], categorized_relationships["create"]) if t.external_id not in exists - ] - - client.relationships.create(missing_relationships) diff --git a/cognite/neat/legacy/graph/loaders/rdf_to_dms.py b/cognite/neat/legacy/graph/loaders/rdf_to_dms.py deleted file mode 100644 index 9551af017..000000000 --- a/cognite/neat/legacy/graph/loaders/rdf_to_dms.py +++ /dev/null @@ -1,309 +0,0 @@ -import logging -from collections.abc import Iterable -from itertools import islice -from typing import Literal, cast, overload - -from cognite.client import CogniteClient -from cognite.client.data_classes.data_modeling import EdgeApply, InstanceApply, NodeApply -from pydantic_core import ErrorDetails - -from cognite.neat.exceptions import NeatException -from cognite.neat.legacy.graph.stores import NeatGraphStoreBase -from cognite.neat.legacy.graph.transformations.query_generator.sparql import triples2dictionary -from cognite.neat.legacy.rules.exporters._rules2dms import DMSSchemaComponents -from cognite.neat.legacy.rules.exporters._rules2pydantic_models import add_class_prefix_to_xid, rules_to_pydantic_models -from cognite.neat.legacy.rules.models.rules import Rules -from cognite.neat.utils.auxiliary import retry_decorator -from cognite.neat.utils.collection_ import chunker -from cognite.neat.utils.time_ import datetime_utc_now - -from ._base import CogniteLoader - - -class DMSLoader(CogniteLoader[InstanceApply]): - """Loads a Neat Graph into CDF as nodes and edges. - - Args: - rules: Rules object - graph_store: Graph store - add_class_prefix: Add class prefix to external_id. Defaults to False. - - """ - - def __init__(self, rules: Rules, graph_store: NeatGraphStoreBase, add_class_prefix: bool = False): - super().__init__(rules, graph_store) - self.add_class_prefix = add_class_prefix - - @overload - def load(self, stop_on_exception: Literal[True]) -> Iterable[InstanceApply]: ... - - @overload - def load(self, stop_on_exception: Literal[False] = False) -> Iterable[InstanceApply | ErrorDetails]: ... - - def load(self, stop_on_exception: bool = False) -> Iterable[InstanceApply | ErrorDetails]: - """Load the graph with data.""" - if self.rules.metadata.namespace is None: - raise ValueError("Namespace is not defined in transformation rules metadata") - - data_model = DMSSchemaComponents.from_rules(self.rules) - pydantic_models = rules_to_pydantic_models(self.rules) - - exclude = { - class_name - for class_name in self.rules.classes - if f"{self.rules.space}:{class_name}" not in data_model.containers - } - - for class_name, triples in self._iterate_class_triples(exclude_classes=exclude): - logging.info(f" Processing class : {class_name}") - counter = 0 - start_time = datetime_utc_now() - for instance_dict in triples2dictionary(triples).values(): - counter += 1 - try: - instance = pydantic_models[class_name].from_dict(instance_dict) # type: ignore[attr-defined] - if self.add_class_prefix: - instance.external_id = add_class_prefix_to_xid( - class_name=type(instance).__name__, external_id=instance.external_id - ) - new_node = instance.to_node(data_model, self.add_class_prefix) # type: ignore[attr-defined] - is_valid, reason = is_node_valid(new_node) - if is_valid: - yield new_node - else: - yield ErrorDetails( - input=instance_dict["external_id"], - loc=tuple(["Nodes"]), - msg=f"Not valid node {new_node.external_id}. Reason: {reason}", - type="Node validation error", - ) - continue - - new_edges = instance.to_edge(data_model, self.add_class_prefix) - for new_edge in new_edges: - is_valid, reason = is_edge_valid(new_edge) - if is_valid: - yield new_edge - else: - yield ErrorDetails( - input=instance_dict["external_id"], - loc=tuple(["Edges"]), - msg=f"Not valid edge {new_edge.external_id}. Reason: {reason}", - type="Edge validation error", - ) - continue - - delta_time = datetime_utc_now() - start_time - delta_time = (delta_time.seconds * 1000000 + delta_time.microseconds) / 1000 - - except Exception as e: - logging.error( - f"Instance {instance_dict['external_id']} of {class_name}" - f" cannot be resolved to nodes and edges. Reason: {e}" - ) - if stop_on_exception: - raise e - - if isinstance(e, NeatException): - yield e.to_error_dict() - else: - yield ErrorDetails( - input=instance_dict["external_id"], - loc=tuple(["rdf2nodes_and_edges"]), - msg=str(e), - type=f"Exception of type {type(e).__name__} occurred \ - when processing instance of {class_name}", - ) - - def load_to_cdf( - self, client: CogniteClient, batch_size: int | None = 1000, max_retries: int = 1, retry_delay: int = 3 - ) -> None: - """Uploads nodes to CDF - - Args: - client: Instance of CogniteClient - batch_size: Size of batch. Default to 1000. - max_retries: Maximum times to retry the upload. Default to 1. - retry_delay: Time delay before retrying the upload. Default to 3. - - !!! note "batch_size" - If batch size is set to 1 or None, all nodes will be pushed to CDF in one go. - """ - if batch_size is None: - logging.info("Batch size not set, pushing all nodes and edges to CDF in one go!") - nodes, edges, errors = self.as_nodes_and_edges(stop_on_exception=False) - - @retry_decorator(max_retries=max_retries, retry_delay=retry_delay, component_name="create-instances") - def create_instances(): - client.data_modeling.instances.apply( - nodes=nodes, edges=edges, auto_create_start_nodes=True, auto_create_end_nodes=True - ) - - create_instances() - return - logging.info(f"Uploading nodes in batches of {batch_size}") - for instances in _batched(self.load(stop_on_exception=False), batch_size): - nodes = [instance for instance in instances if isinstance(instance, NodeApply)] - edges = [instance for instance in instances if isinstance(instance, EdgeApply)] - # Todo make _micro_batch_push handle both nodes and edges simultaneously - _micro_batch_push( - client, nodes, batch_size, message="Upload", max_retries=max_retries, retry_delay=retry_delay - ) - _micro_batch_push( - client, edges, batch_size, message="Upload", max_retries=max_retries, retry_delay=retry_delay - ) - - def as_nodes_and_edges( - self, stop_on_exception: bool = False - ) -> tuple[list[NodeApply], list[EdgeApply], list[ErrorDetails]]: - nodes = [] - edges = [] - exceptions: list[ErrorDetails] = [] - for instance in self.load(stop_on_exception): # type: ignore[call-overload] - if isinstance(instance, NodeApply): - nodes.append(instance) - elif isinstance(instance, EdgeApply): - edges.append(instance) - elif isinstance(instance, dict): - exceptions.append(cast(ErrorDetails, instance)) - else: - raise ValueError(f"Unknown instance type: {type(instance)}") - return nodes, edges, exceptions - - -def _batched(iterable: Iterable, size: int): - "Batch data into lists of length n. The last batch may be shorter." - # batched('ABCDEFG', 3) --> ABC DEF G - it = iter(iterable) - while True: - batch = list(islice(it, size)) - if not batch: - return - yield batch - - -def is_node_valid(node: NodeApply) -> tuple[bool, str]: - return is_valid_external_id(node.external_id) - - -def is_edge_valid(edge: EdgeApply) -> tuple[bool, str]: - for external_id in [edge.external_id, edge.start_node.external_id, edge.end_node.external_id]: - is_valid, reason = is_valid_external_id(external_id) - if not is_valid: - return False, reason - return True, "" - - -def is_valid_external_id(external_id: str) -> tuple[bool, str]: - if external_id is None or external_id == "" or len(external_id) >= 255: - return False, f"external_id {external_id} is empty of too long" - return True, "" - - -def upload_nodes( - client: CogniteClient, nodes: list[NodeApply], batch_size: int = 1000, max_retries: int = 1, retry_delay: int = 3 -): - """Uploads nodes to CDF - - Args: - client: Instance of CogniteClient - nodes: List of nodes to upload to CDF - batch_size: Size of batch. Defaults to 1000. - max_retries: Maximum times to retry the upload. Defaults to 1. - retry_delay: Time delay before retrying the upload. Defaults to 3. - - !!! note "batch_size" - If batch size is set to 1 or None, all nodes will be pushed to CDF in one go. - """ - if batch_size: - logging.info(f"Uploading nodes in batches of {batch_size}") - _micro_batch_push(client, nodes, batch_size, message="Upload", max_retries=max_retries, retry_delay=retry_delay) - - else: - logging.info("Batch size not set, pushing all nodes to CDF in one go!") - - @retry_decorator(max_retries=max_retries, retry_delay=retry_delay, component_name="create-nodes") - def create_nodes(): - client.data_modeling.instances.apply(nodes=nodes) - - create_nodes() - - -def upload_edges( - client: CogniteClient, edges: list[EdgeApply], batch_size: int = 5000, max_retries: int = 1, retry_delay: int = 3 -): - """Uploads edges to CDF - - Args: - client: Instance of CogniteClient - edges: List of edges to upload to CDF - batch_size: Size of batch. Defaults to 5000. - max_retries: Maximum times to retry the upload. Defaults to 1. - retry_delay: Time delay before retrying the upload. Defaults to 3. - - !!! note "batch_size" - If batch size is set to 1 or None, all edges will be pushed to CDF in one go. - - """ - if batch_size: - logging.info(f"Uploading edges in batches of {batch_size}") - _micro_batch_push(client, edges, batch_size, message="Upload", max_retries=max_retries, retry_delay=retry_delay) - - else: - logging.info("Batch size not set, pushing all edges to CDF in one go!") - - @retry_decorator(max_retries=max_retries, retry_delay=retry_delay, component_name="create-edges") - def create_nodes(): - client.data_modeling.instances.apply(edges=edges, auto_create_start_nodes=True, auto_create_end_nodes=True) - - create_nodes() - - -def _micro_batch_push( - client: CogniteClient, - nodes_or_edges: list[NodeApply] | list[EdgeApply], - batch_size: int = 1000, - message: str = "Upload", - max_retries: int = 1, - retry_delay: int = 3, -): - """Uploads nodes or edges in batches - - Args: - client: Instance of CogniteClient - nodes_or_edges: List of nodes or edges - batch_size: Size of batch. Defaults to 1000. - message: Message to logged. Defaults to "Upload". - max_retries: Maximum times to retry the upload. Defaults to 1. - retry_delay: Time delay before retrying the upload. Defaults to 3. - """ - total = len(nodes_or_edges) - counter = 0 - - if nodes_or_edges and isinstance(nodes_or_edges[0], NodeApply): - push_type = "nodes" - elif nodes_or_edges and isinstance(nodes_or_edges[0], EdgeApply): - push_type = "edges" - else: - raise ValueError("nodes_or_edges must be a list of NodeApply or EdgeApply objects") - - for batch in chunker(nodes_or_edges, batch_size): - counter += len(batch) - start_time = datetime_utc_now() - - @retry_decorator(max_retries=max_retries, retry_delay=retry_delay, component_name=f"microbatch-{push_type}") - def upsert_nodes_or_edges(upload_batch): - if push_type == "nodes": - client.data_modeling.instances.apply(nodes=upload_batch) - elif push_type == "edges": - client.data_modeling.instances.apply( - edges=upload_batch, auto_create_start_nodes=True, auto_create_end_nodes=True - ) - - upsert_nodes_or_edges(batch) - - delta_time = (datetime_utc_now() - start_time).seconds - - msg = f"{message} {counter} of {total} {push_type}, batch processing time: {delta_time:.2f} " - msg += f"seconds ETC: {delta_time * (total - counter) / (60*batch_size) :.2f} minutes" - logging.info(msg) diff --git a/cognite/neat/legacy/graph/loaders/validator.py b/cognite/neat/legacy/graph/loaders/validator.py deleted file mode 100644 index bff2e58bc..000000000 --- a/cognite/neat/legacy/graph/loaders/validator.py +++ /dev/null @@ -1,87 +0,0 @@ -"""Should contain methods to validate Graph Transformation Rules sheet, -as well App Data Model (RDF) -""" - -import logging -from typing import Any - - -def _find_circular_reference_path( - asset: dict[str, Any], assets: dict[str, dict[str, Any]], max_hierarchy_depth: int = 10000 -) -> list: - original_external_id = asset.get("external_id", "") - circle: list[str] = [original_external_id] - parent_external_id = asset.get("parent_external_id") - if isinstance(parent_external_id, str): - ref = assets.get(parent_external_id) - else: - ref = None - - hop = 0 - while ref is not None and hop < max_hierarchy_depth: - hop += 1 - if external_id := ref.get("external_id"): - circle.append(external_id) - if len(circle) != len(set(circle)): - msg = ( - f"Found circular reference in asset hierarchy which starts with " - f"{original_external_id} and enters loop at {circle[-1]}. " - ) - logging.error(msg) - return circle - if parent_external_id := ref.get("parent_external_id"): - ref = assets.get(parent_external_id) - else: - ref = None - - if hop >= max_hierarchy_depth: - msg = ( - f"Your asset hierarchy is too deep. Max depth is {max_hierarchy_depth}. " - "You probably have a circular reference." - ) - logging.error(msg) - return circle - else: - return [] - - -def validate_asset_hierarchy( - assets: dict[str, dict[str, Any]], -) -> tuple[list[str], list[list[str]], dict[str, list[str]]]: - """Validates asset hierarchy and reports on orphan assets and circular dependency - - Args: - assets : A dictionary of assets with external_id as key - - Returns: - List of orphan assets external ids and list of circular path of external ids. - If both lists are empty, the hierarchy is healthy. - """ - orphan_assets: list[str] = [] - circular_reference_paths: list[list[str]] = [] - parent_children_map: dict[str, list[str]] = {} - - for asset in assets.values(): - parent_external_id = asset.get("parent_external_id") - asset_extarnal_id = asset.get("external_id") - if asset_extarnal_id and parent_external_id: - if parent_external_id in parent_children_map: - parent_children_map[parent_external_id].append(asset_extarnal_id) - else: - parent_children_map[parent_external_id] = [asset_extarnal_id] - if parent_external_id is not None and parent_external_id not in assets: - msg = ( - f"Found orphan asset {asset.get('external_id')} with parent {parent_external_id} which does not exist." - ) - logging.error(msg) - if external_id := asset.get("external_id"): - orphan_assets.append(external_id) - circular_reference_path = _find_circular_reference_path(asset, assets) - if not len(circular_reference_path): - continue - - # Save the circle only once, not once for every asset - if set(circular_reference_path) in [set(path) for path in circular_reference_paths]: - continue - circular_reference_paths.append(circular_reference_path) - return orphan_assets, circular_reference_paths, parent_children_map diff --git a/cognite/neat/legacy/graph/models.py b/cognite/neat/legacy/graph/models.py deleted file mode 100644 index b3b8248e2..000000000 --- a/cognite/neat/legacy/graph/models.py +++ /dev/null @@ -1,6 +0,0 @@ -from typing import TypeAlias - -from rdflib import Literal -from rdflib.term import URIRef - -Triple: TypeAlias = tuple[URIRef, URIRef, Literal | URIRef] diff --git a/cognite/neat/legacy/graph/stores/__init__.py b/cognite/neat/legacy/graph/stores/__init__.py deleted file mode 100644 index 8aae92b82..000000000 --- a/cognite/neat/legacy/graph/stores/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -from ._base import NeatGraphStoreBase -from ._graphdb_store import GraphDBStore -from ._memory_store import MemoryStore -from ._oxigraph_store import OxiGraphStore - -STORE_BY_TYPE: dict[str, type[NeatGraphStoreBase]] = {} -for store in NeatGraphStoreBase.__subclasses__(): - STORE_BY_TYPE[store.rdf_store_type] = store # type: ignore[type-abstract] - -del store # Cleanup namespace -AVAILABLE_STORES = set(STORE_BY_TYPE.keys()) - -__all__ = ["NeatGraphStoreBase", "MemoryStore", "OxiGraphStore", "GraphDBStore", "STORE_BY_TYPE", "AVAILABLE_STORES"] diff --git a/cognite/neat/legacy/graph/stores/_base.py b/cognite/neat/legacy/graph/stores/_base.py deleted file mode 100644 index a5cba0deb..000000000 --- a/cognite/neat/legacy/graph/stores/_base.py +++ /dev/null @@ -1,400 +0,0 @@ -import logging -import sys -import time -from abc import ABC, abstractmethod -from collections.abc import Iterable, Iterator -from pathlib import Path -from typing import Literal, TypeAlias, cast - -import pandas as pd -from prometheus_client import Gauge, Summary -from rdflib import Graph, Namespace, URIRef -from rdflib.query import Result, ResultRow - -from cognite.neat.constants import DEFAULT_NAMESPACE, get_default_prefixes -from cognite.neat.legacy.graph.models import Triple -from cognite.neat.legacy.graph.stores._rdf_to_graph import rdf_file_to_graph -from cognite.neat.legacy.rules.models.rules import Rules - -if sys.version_info >= (3, 11): - from typing import Self -else: - from typing_extensions import Self - -prom_qsm = Summary("store_query_time_summary_legacy", "Time spent processing queries", ["query"]) -prom_sq = Gauge("store_single_query_time_legacy", "Time spent processing a single query", ["query"]) - -MIMETypes: TypeAlias = Literal[ - "application/rdf+xml", - "text/turtle", - "application/n-triple", - "application/n-quads", - "application/trig", -] - - -class NeatGraphStoreBase(ABC): - """NeatGraphStore is a class that stores the graph and provides methods to read/write data it contains - - - Args: - graph : Instance of rdflib.Graph class for graph storage - base_prefix : Used as a base prefix for graph namespace, - allowing querying graph data using a short form of a URI - namespace : Namespace (aka URI) used to resolve any relative URI in the graph - prefixes : Dictionary of additional prefixes used and bounded to the graph - """ - - rdf_store_type: str - - def __init__( - self, - graph: Graph | None = None, - base_prefix: str = "", # usually empty - namespace: Namespace = DEFAULT_NAMESPACE, - prefixes: dict[str, Namespace] | None = None, - ): - self.graph = graph or Graph() - self.base_prefix: str = base_prefix - self.namespace: Namespace = namespace - self.prefixes: dict[str, Namespace] = prefixes or get_default_prefixes() - - self.rdf_store_query_url: str | None = None - self.rdf_store_update_url: str | None = None - self.returnFormat: str | None = None - self.df_cache: pd.DataFrame | None = None - self.internal_storage_dir: Path | None = None - self.graph_name: str | None = None - self.internal_storage_dir_orig: Path | None = None - self.storage_dirs_to_delete: list[Path] = [] - self.queries = _Queries(self) - - @classmethod - def from_rules(cls, rules: Rules) -> Self: - """ - Creates a new instance of NeatGraphStore from TransformationRules and runs the .init_graph() method on it. - - Args: - rules: TransformationRules object containing information about the graph store. - - Returns: - An instantiated instance of NeatGraphStore - - """ - if rules.metadata.namespace is None: - namespace = DEFAULT_NAMESPACE - else: - namespace = rules.metadata.namespace - store = cls(prefixes=rules.prefixes, namespace=namespace) - store.init_graph(base_prefix=rules.metadata.prefix) - return store - - @abstractmethod - def _set_graph(self) -> None: - raise NotImplementedError() - - def init_graph( - self, - rdf_store_query_url: str | None = None, - rdf_store_update_url: str | None = None, - graph_name: str | None = None, - base_prefix: str | None = None, - returnFormat: str = "csv", - internal_storage_dir: Path | None = None, - ): - """Initializes the graph. - - Args: - rdf_store_query_url : URL towards which SPARQL query is executed, by default None - rdf_store_update_url : URL towards which SPARQL update is executed, by default None - graph_name : Name of graph, by default None - base_prefix : Base prefix for graph namespace to change if needed, by default None - returnFormat : Transport format of graph data between, by default "csv" - internal_storage_dir : Path to directory where internal storage is located, - by default None (in-memory storage). - - !!! note "internal_storage_dir" - Used only for Oxigraph - """ - logging.info("Initializing NeatGraphStore") - self.rdf_store_query_url = rdf_store_query_url - self.rdf_store_update_url = rdf_store_update_url - self.graph_name = graph_name - self.returnFormat = returnFormat - self.internal_storage_dir = Path(internal_storage_dir) if internal_storage_dir else None - self.internal_storage_dir_orig = ( - self.internal_storage_dir if self.internal_storage_dir_orig is None else self.internal_storage_dir_orig - ) - - self._set_graph() - - if self.prefixes: - for prefix, namespace in self.prefixes.items(): - logging.info("Adding prefix %s with namespace %s", prefix, namespace) - self.graph.bind(prefix, namespace) - - if base_prefix: - self.base_prefix = base_prefix - - self.graph.bind(self.base_prefix, self.namespace) - logging.info("Adding prefix %s with namespace %s", self.base_prefix, self.namespace) - logging.info("Graph initialized") - - def reinitialize_graph(self): - """Reinitialize the graph.""" - self.init_graph( - self.rdf_store_query_url, - self.rdf_store_update_url, - self.graph_name, - self.base_prefix, - self.returnFormat, - self.internal_storage_dir, - ) - - def upsert_prefixes(self, prefixes: dict[str, Namespace]) -> None: - """Adds prefixes to the graph store.""" - self.prefixes.update(prefixes) - for prefix, namespace in prefixes.items(): - logging.info("Adding prefix %s with namespace %s", prefix, namespace) - self.graph.bind(prefix, namespace) - - def close(self) -> None: - """Closes the graph.""" - # Can be overridden in subclasses - return None - - def restart(self) -> None: - """Restarts the graph""" - # Can be overridden in subclasses - return None - - def import_from_file( - self, - graph_file: Path, - mime_type: MIMETypes = "application/rdf+xml", - add_base_iri: bool = True, - ) -> None: - """Imports graph data from file. - - Args: - graph_file : File path to file containing graph data, by default None - mime_type : MIME type of graph data, by default "application/rdf+xml" - add_base_iri : Add base IRI to graph, by default True - """ - if add_base_iri: - self.graph = rdf_file_to_graph( - self.graph, - graph_file, - base_namespace=self.namespace, - prefixes=self.prefixes, - ) - else: - self.graph = rdf_file_to_graph(self.graph, graph_file, prefixes=self.prefixes) - return None - - def get_graph(self) -> Graph: - """Returns the graph.""" - return self.graph - - def set_graph(self, graph: Graph): - """Sets the graph.""" - self.graph = graph - - def query(self, query: str) -> Result: - """Returns the result of the query.""" - start_time = time.perf_counter() - result = self.graph.query(query) - stop_time = time.perf_counter() - elapsed_time = stop_time - start_time - prom_qsm.labels("query").observe(elapsed_time) - prom_sq.labels("query").set(elapsed_time) - return result - - def serialize(self, *args, **kwargs): - """Serializes the graph.""" - return self.graph.serialize(*args, **kwargs) - - def query_delayed(self, query) -> Iterable[Triple]: - """Returns the result of the query, but does not execute it immediately. - - The query is not executed until the result is iterated over. - - Args: - query: SPARQL query to execute - - Returns: - An iterable of triples - - """ - return _DelayedQuery(self.graph, query) - - @abstractmethod - def drop(self) -> None: - """Drops the graph.""" - raise NotImplementedError() - - def garbage_collector(self) -> None: - """Garbage collection of the graph store.""" - # Can be overridden in subclasses - return None - - def query_to_dataframe( - self, - query: str, - column_mapping: dict | None = None, - save_to_cache: bool = False, - index_column: str = "instance", - ) -> pd.DataFrame: - """Returns the result of the query as a dataframe. - - Args: - query: SPARQL query to execute - column_mapping: Columns name mapping, by default None - save_to_cache: Save result of query to cache, by default False - index_column: Indexing column , by default "instance" - - Returns: - Dataframe with result of query - """ - - if column_mapping is None: - column_mapping = {0: "instance", 1: "property", 2: "value"} - - result = self.graph.query(query, DEBUG=False) - df_cache = pd.DataFrame(list(result)) - df_cache.rename(columns=column_mapping, inplace=True) - df_cache[index_column] = df_cache[index_column].apply(lambda x: str(x)) - if save_to_cache: - self.df_cache = df_cache - return df_cache - - def commit(self): - """Commits the graph.""" - self.graph.commit() - - def get_df(self) -> pd.DataFrame: - """Returns the cached dataframe.""" - if self.df_cache is None: - raise ValueError("Cache is empty. Run query_to_dataframe() first with save_to_cache.") - return self.df_cache - - def get_instance_properties_from_cache(self, instance_id: str) -> pd.DataFrame: - """Returns the properties of an instance.""" - if self.df_cache is None: - raise ValueError("Cache is empty. Run query_to_dataframe() first with save_to_cache.") - return self.df_cache.loc[self.df_cache["instance"] == instance_id] - - def print_triples(self): - """Prints the triples of the graph.""" - for subj, pred, obj in self.graph: - logging.info(f"Triple: {subj} {pred} {obj}") - - def diagnostic_report(self): - """Returns the dictionary representation graph diagnostic data .""" - return { - "rdf_store_type": self.rdf_store_type, - "base_prefix": self.base_prefix, - "namespace": self.namespace, - "prefixes": self.prefixes, - "internal_storage_dir": self.internal_storage_dir, - "rdf_store_query_url": self.rdf_store_query_url, - "rdf_store_update_url": self.rdf_store_update_url, - } - - def add_triples( - self, - triples: list[Triple] | set[Triple], - batch_size: int = 10_000, - verbose: bool = False, - ): - """Adds triples to the graph store in batches. - - Args: - triples: list of triples to be added to the graph store - batch_size: Batch size of triples per commit, by default 10_000 - verbose: Verbose mode, by default False - """ - - commit_counter = 0 - if verbose: - logging.info(f"Committing total of {len(triples)} triples to knowledge graph!") - total_number_of_triples = len(triples) - number_of_uploaded_triples = 0 - - def check_commit(force_commit: bool = False): - """Commit nodes to the graph if batch counter is reached or if force_commit is True""" - nonlocal commit_counter - nonlocal number_of_uploaded_triples - if force_commit: - number_of_uploaded_triples += commit_counter - self.graph.commit() - if verbose: - logging.info(f"Committed {number_of_uploaded_triples} of {total_number_of_triples} triples") - return - commit_counter += 1 - if commit_counter >= batch_size: - number_of_uploaded_triples += commit_counter - self.graph.commit() - if verbose: - logging.info(f"Committed {number_of_uploaded_triples} of {total_number_of_triples} triples") - commit_counter = 0 - - for triple in triples: - self.graph.add(triple) - check_commit() - - check_commit(force_commit=True) - - -class _DelayedQuery(Iterable): - def __init__(self, graph_ref: Graph, query: str): - self.graph_ref = graph_ref - self.query = query - - def __iter__(self) -> Iterator[Triple]: - start_time = time.perf_counter() - result = self.graph_ref.query(self.query) - stop_time = time.perf_counter() - elapsed_time = stop_time - start_time - prom_qsm.labels("query").observe(elapsed_time) - prom_sq.labels("query").set(elapsed_time) - return cast(Iterator[Triple], iter(result)) - - -class _Queries: - """Helper class for storing standard queries for the graph store.""" - - def __init__(self, store: NeatGraphStoreBase): - self.store = store - - def list_instances_ids_of_class(self, class_uri: URIRef, limit: int = -1) -> list[URIRef]: - """Get instances ids for a given class - - Args: - class_uri: Class for which instances are to be found - limit: Max number of instances to return, by default -1 meaning all instances - - Returns: - List of class instance URIs - """ - query_statement = "SELECT DISTINCT ?subject WHERE { ?subject a .} LIMIT X".replace( - "class", class_uri - ).replace("LIMIT X", "" if limit == -1 else f"LIMIT {limit}") - return [cast(tuple, res)[0] for res in list(self.store.query(query_statement))] - - def list_instances_of_type(self, class_uri: URIRef) -> list[ResultRow]: - """Get all triples for instances of a given class - - Args: - class_uri: Class for which instances are to be found - - Returns: - List of triples for instances of the given class - """ - query = ( - f"SELECT ?instance ?prop ?value " - f"WHERE {{ ?instance rdf:type <{class_uri}> . ?instance ?prop ?value . }} order by ?instance " - ) - logging.info(query) - # Select queries gives an iterable of result rows - return cast(list[ResultRow], list(self.store.query(query))) diff --git a/cognite/neat/legacy/graph/stores/_graphdb_store.py b/cognite/neat/legacy/graph/stores/_graphdb_store.py deleted file mode 100644 index eef9f692f..000000000 --- a/cognite/neat/legacy/graph/stores/_graphdb_store.py +++ /dev/null @@ -1,52 +0,0 @@ -import logging - -import requests -from rdflib import Graph, Namespace -from rdflib.plugins.stores.sparqlstore import SPARQLUpdateStore - -from cognite.neat.constants import DEFAULT_NAMESPACE, get_default_prefixes - -from ._base import NeatGraphStoreBase - - -class GraphDBStore(NeatGraphStoreBase): - """GraphDB is a class that stores the graph in a GraphDB instances and provides methods to - read/write data it contains - - - Args: - graph : Instance of rdflib.Graph class for graph storage - base_prefix : Used as a base prefix for graph namespace, allowing querying graph data using a shortform of a URI - namespace : Namespace (aka URI) used to resolve any relative URI in the graph - prefixes : Dictionary of additional prefixes used and bounded to the graph - """ - - rdf_store_type = "graphdb" - - def __init__( - self, - graph: Graph | None = None, - base_prefix: str = "", # usually empty - namespace: Namespace = DEFAULT_NAMESPACE, - prefixes: dict[str, Namespace] | None = None, - ): - prefixes = prefixes if prefixes else get_default_prefixes() - super().__init__(graph, base_prefix, namespace, prefixes) - self.graph_db_rest_url: str = "http://localhost:7200" - - def _set_graph(self) -> None: - logging.info("Initializing graph store with GraphDB") - store = SPARQLUpdateStore( - query_endpoint=self.rdf_store_query_url, - update_endpoint=self.rdf_store_update_url, - returnFormat=self.returnFormat, - context_aware=False, - postAsEncoded=False, - autocommit=False, - ) - self.graph = Graph(store=store) - - def drop(self): - """Drops the graph.""" - r = requests.delete(f"{self.rdf_store_query_url}/rdf-graphs/service?default") - logging.info(f"Dropped graph with state: {r.text}") diff --git a/cognite/neat/legacy/graph/stores/_memory_store.py b/cognite/neat/legacy/graph/stores/_memory_store.py deleted file mode 100644 index 2a6cd7f31..000000000 --- a/cognite/neat/legacy/graph/stores/_memory_store.py +++ /dev/null @@ -1,43 +0,0 @@ -import logging - -from rdflib import Graph, Namespace - -from cognite.neat.constants import DEFAULT_NAMESPACE, get_default_prefixes - -from ._base import NeatGraphStoreBase - - -class MemoryStore(NeatGraphStoreBase): - """MemoryStore is a class that stores the graph in memory using rdflib and provides - methods to read/write data it contains. - - - Args: - graph : Instance of rdflib.Graph class for graph storage - base_prefix : Used as a base prefix for graph namespace, allowing querying graph data using a shortform of a URI - namespace : Namespace (aka URI) used to resolve any relative URI in the graph - prefixes : Dictionary of additional prefixes used and bounded to the graph - """ - - rdf_store_type: str = "memory" - - def __init__( - self, - graph: Graph | None = None, - base_prefix: str = "", # usually empty - namespace: Namespace = DEFAULT_NAMESPACE, - prefixes: dict[str, Namespace] | None = None, - ): - prefixes = prefixes if prefixes else get_default_prefixes() # Init repeated to get nice docstring - super().__init__(graph, base_prefix, namespace, prefixes) - - def _set_graph(self): - logging.info("Initializing graph in memory") - self.graph = Graph() - - def drop(self): - """Drops the graph.""" - # In the case of in-memory graph, we just reinitialize the graph - # otherwise we would lose the prefixes and bindings, which fails - # workflow - self.reinitialize_graph() diff --git a/cognite/neat/legacy/graph/stores/_oxigraph_store.py b/cognite/neat/legacy/graph/stores/_oxigraph_store.py deleted file mode 100644 index 9fdbb4523..000000000 --- a/cognite/neat/legacy/graph/stores/_oxigraph_store.py +++ /dev/null @@ -1,151 +0,0 @@ -import logging -import os -import shutil -from pathlib import Path - -from rdflib import Graph, Namespace - -from cognite.neat.constants import DEFAULT_NAMESPACE, get_default_prefixes -from cognite.neat.utils.auxiliary import local_import - -from ._base import MIMETypes, NeatGraphStoreBase - - -class OxiGraphStore(NeatGraphStoreBase): - """OxiGraph is a class that stores the graph using OxiGraph and provides methods to read/write data it contains - - - Args: - graph : Instance of rdflib.Graph class for graph storage - base_prefix : Used as a base prefix for graph namespace, allowing querying graph data using a shortform of a URI - namespace : Namespace (aka URI) used to resolve any relative URI in the graph - prefixes : Dictionary of additional prefixes used and bounded to the graph - """ - - rdf_store_type = "oxigraph" - - def __init__( - self, - graph: Graph | None = None, - base_prefix: str = "", # usually empty - namespace: Namespace = DEFAULT_NAMESPACE, - prefixes: dict[str, Namespace] | None = None, - ): - prefixes = prefixes if prefixes else get_default_prefixes() - super().__init__(graph, base_prefix, namespace, prefixes) - - def _set_graph(self) -> None: - logging.info("Initializing Oxigraph store") - local_import("pyoxigraph", "oxi") - import pyoxigraph - - from cognite.neat.graph.stores import _oxrdflib - - # Adding support for both in-memory and file-based storage - for i in range(4): - try: - oxstore = pyoxigraph.Store( - path=(str(self.internal_storage_dir) if self.internal_storage_dir else None) - ) # Store (Rust object) accepts only str as path and not Path. - break - except OSError as e: - if "lock" in str(e) and i < 3: - # lock originated from another instance of the store - logging.error("Error initializing Oxigraph store: %s", e) - else: - raise e - else: - raise Exception("Error initializing Oxigraph store") - - self.graph = Graph(store=_oxrdflib.OxigraphStore(store=oxstore)) - self.graph.default_union = True - self.garbage_collector() - - def close(self): - """Closes the graph.""" - if self.graph is not None: - try: - self.graph.store._inner.flush() # type: ignore[attr-defined] - self.graph.close(True) - except Exception as e: - logging.debug("Error closing graph: %s", e) - - def restart(self): - """Restarts the graph""" - self.close() - self.reinitialize_graph() - logging.info("GraphStore restarted") - - def import_from_file( - self, - graph_file: Path, - mime_type: MIMETypes = "application/rdf+xml", - add_base_iri: bool = True, - ) -> None: - """Imports graph data from file. - - Args: - graph_file : File path to file containing graph data, by default None - mime_type : MIME type of the file, by default "application/rdf+xml" - add_base_iri : Add base IRI to the graph, by default True - """ - if add_base_iri: - self.graph.store._inner.bulk_load( # type: ignore[attr-defined] - str(graph_file), mime_type, base_iri=self.namespace - ) - else: - self.graph.store._inner.bulk_load(str(graph_file), mime_type) # type: ignore[attr-defined] - self.graph.store._inner.optimize() # type: ignore[attr-defined] - return None - - def drop(self): - try: - self.close() - # Due to the specifics of Oxigraph, storage directory cannot be deleted immediately - # after closing the graph and creating a new one - if self.internal_storage_dir.exists(): - self.storage_dirs_to_delete.append(self.internal_storage_dir) - self.garbage_collector() - - except Exception as e: - logging.error(f"Error dropping graph : {e}") - - def garbage_collector(self): - """Garbage collection of the graph store.""" - # delete all directories in self.storage_dirs_to_delete - for d in self.storage_dirs_to_delete: - shutil.rmtree(d) - self.storage_dirs_to_delete = [] - - def __del__(self): - if self.graph is not None: - if self.graph.store is not None: - try: - self.graph.store._inner.flush() - except Exception: - logging.debug("Error flushing graph") - self.graph.close() - # It requires more investigation os.remove(self.internal_storage_dir / "LOCK") - - def commit(self): - """Commits the graph.""" - if self.graph: - if self.graph.store: - logging.info("Committing graph - flushing and optimizing") - self.graph.store._inner.flush() - self.graph.store._inner.optimize() - self.graph.commit() - - @staticmethod - def drop_graph_store_storage(storage_path: Path | None) -> None: - """Drop graph store storage on disk. - - Args: - storage_path : Path to storage directory - """ - if storage_path and storage_path.exists(): - for f in os.listdir(storage_path): - (storage_path / f).unlink() - logging.info("Graph store dropped.") - else: - logging.info(f"Storage path {storage_path} does not exist. Skipping drop.") diff --git a/cognite/neat/legacy/graph/stores/_oxrdflib.py b/cognite/neat/legacy/graph/stores/_oxrdflib.py deleted file mode 100644 index 59ab9c164..000000000 --- a/cognite/neat/legacy/graph/stores/_oxrdflib.py +++ /dev/null @@ -1,247 +0,0 @@ -import shutil -from collections.abc import Generator, Iterable, Iterator, Mapping -from typing import Any, cast - -import pyoxigraph as ox -from rdflib import Graph -from rdflib.graph import DATASET_DEFAULT_GRAPH_ID -from rdflib.plugins.sparql.sparql import Query, Update -from rdflib.query import Result -from rdflib.store import VALID_STORE, Store -from rdflib.term import BNode, Identifier, Literal, Node, URIRef, Variable - -__all__ = ["OxigraphStore"] - -from typing import TypeAlias - -_Triple: TypeAlias = tuple[Node, Node, Node] -_Quad: TypeAlias = tuple[Node, Node, Node, Graph] -_TriplePattern: TypeAlias = tuple[Node | None, Node | None, Node | None] - - -class OxigraphStore(Store): - context_aware: bool = True - formula_aware: bool = False - transaction_aware: bool = False - graph_aware: bool = True - - def __init__( - self, configuration: str | None = None, identifier: Identifier | None = None, *, store: ox.Store | None = None - ): - self._store = store - self._prefix_for_namespace: dict[URIRef, str] = {} - self._namespace_for_prefix: dict[str, URIRef] = {} - super().__init__(configuration, identifier) - - def open(self, configuration: str, create: bool = False) -> int | None: - if self._store is not None: - raise ValueError("The open function should be called before any RDF operation") - self._store = ox.Store(configuration) - return VALID_STORE - - def close(self, commit_pending_transaction: bool = False) -> None: - del self._store - - def destroy(self, configuration: str) -> None: - shutil.rmtree(configuration) - - def gc(self) -> None: - pass - - @property - def _inner(self) -> ox.Store: - if self._store is None: - self._store = ox.Store() - return self._store - - def add(self, triple: _Triple, context: Graph, quoted: bool = False) -> None: - if quoted: - raise ValueError("Oxigraph stores are not formula aware") - self._inner.add(_to_ox(triple, context)) - super().add(triple, context, quoted) - - def addN(self, quads: Iterable[_Quad]) -> None: - self._inner.extend([_to_ox(q) for q in quads]) - for quad in quads: - (s, p, o, g) = quad - super().add((s, p, o), g) - - def remove(self, triple: _TriplePattern, context: Graph | None = None) -> None: - for q in self._inner.quads_for_pattern(*_to_ox_quad_pattern(triple, context)): - self._inner.remove(q) - super().remove(triple, context) - - def triples( - self, triple_pattern: _TriplePattern, context: Graph | None = None - ) -> Iterator[tuple[_Triple, Iterator[Graph | None]]]: - return (_from_ox(q) for q in self._inner.quads_for_pattern(*_to_ox_quad_pattern(triple_pattern, context))) - - def __len__(self, context: Graph | None = None) -> int: - if context is None: - # TODO: very bad - return len({q.triple for q in self._inner}) - return sum(1 for _ in self._inner.quads_for_pattern(None, None, None, _to_ox(context))) - - def contexts(self, triple: _Triple | None = None) -> Generator[Graph, None, None]: - if triple is None: - return (_from_ox(g) for g in self._inner.named_graphs()) - return (_from_ox(q[3]) for q in self._inner.quads_for_pattern(*_to_ox_quad_pattern(triple))) - - def query( - self, - query: Query | str, - initNs: Mapping[str, Any], - initBindings: Mapping[str, Identifier], - queryGraph: str, - **kwargs: Any, - ) -> "Result": - if isinstance(queryGraph, Query) or kwargs: - raise NotImplementedError - init_ns = dict(self._namespace_for_prefix, **initNs) - if isinstance(query, Query): - query = str(query) - query = "".join(f"PREFIX {prefix}: <{namespace}>\n" for prefix, namespace in init_ns.items()) + query - if initBindings: - # Todo Anders: This is likely a bug as .n3 is not valid the Identifier. - # There are no tests reaching this code. - query += "\nVALUES ( {} ) {{ ({}) }}".format( - " ".join(f"?{k}" for k in initBindings), - " ".join(v.n3() for v in initBindings.values()), # type: ignore[attr-defined] - ) - result = self._inner.query( - query, - use_default_graph_as_union=queryGraph == "__UNION__", - default_graph=_to_ox(queryGraph) if isinstance(queryGraph, Node) else None, - ) - if isinstance(result, bool): - out = Result("ASK") - out.askAnswer = result - elif isinstance(result, ox.QuerySolutions): - out = Result("SELECT") - out.vars = [Variable(v.value) for v in result.variables] - out.bindings = [ - {v: _from_ox(val) for v, val in zip(out.vars, solution, strict=False)} for solution in result - ] - elif isinstance(result, ox.QueryTriples): - out = Result("CONSTRUCT") - out.graph = Graph() - out.graph += (_from_ox(t) for t in result) - else: - raise ValueError(f"Unexpected query result: {result}") - return out - - def update( - self, - update: Update | str, - initNs: Mapping[str, Any], - initBindings: Mapping[str, Identifier], - queryGraph: str, - **kwargs: Any, - ) -> None: - raise NotImplementedError - - def commit(self) -> None: - # TODO: implement - pass - - def rollback(self) -> None: - # TODO: implement - pass - - def add_graph(self, graph: Graph) -> None: - self._inner.add_graph(_to_ox(graph)) - - def remove_graph(self, graph: Graph) -> None: - self._inner.remove_graph(_to_ox(graph)) - - def bind(self, prefix: str, namespace: URIRef, override: bool = True) -> None: - if not override and (prefix in self._namespace_for_prefix or namespace in self._prefix_for_namespace): - return # nothing to do - self._delete_from_prefix(prefix) - self._delete_from_namespace(namespace) - self._namespace_for_prefix[prefix] = namespace - self._prefix_for_namespace[namespace] = prefix - - def _delete_from_prefix(self, prefix): - if prefix not in self._namespace_for_prefix: - return - namespace = self._namespace_for_prefix[prefix] - del self._namespace_for_prefix[prefix] - self._delete_from_namespace(namespace) - - def _delete_from_namespace(self, namespace): - if namespace not in self._prefix_for_namespace: - return - prefix = self._prefix_for_namespace[namespace] - del self._prefix_for_namespace[namespace] - self._delete_from_prefix(prefix) - - def prefix(self, namespace: URIRef) -> str | None: - return self._prefix_for_namespace.get(namespace) - - def namespace(self, prefix: str) -> URIRef | None: - return self._namespace_for_prefix.get(prefix) - - def namespaces(self) -> Iterator[tuple[str, URIRef]]: - yield from self._namespace_for_prefix.items() - - -def _to_ox(term: Node | _Triple | _Quad | Graph, context: Graph | None = None): - if term is None: - return None - elif term == DATASET_DEFAULT_GRAPH_ID: - return ox.DefaultGraph() - elif isinstance(term, URIRef): - return ox.NamedNode(term) - elif isinstance(term, BNode): - return ox.BlankNode(term) - elif isinstance(term, Literal): - return ox.Literal(term, language=term.language, datatype=ox.NamedNode(term.datatype) if term.datatype else None) - elif isinstance(term, Graph): - return _to_ox(term.identifier) - elif isinstance(term, tuple) and len(term) == 3 and isinstance(context, Graph): - triple = cast(_Triple, term) - return ox.Quad(_to_ox(triple[0]), _to_ox(triple[1]), _to_ox(triple[2]), _to_ox(context)) - elif isinstance(term, tuple) and len(term) == 4: - quad = cast(_Quad, term) - return ox.Quad(_to_ox(quad[0]), _to_ox(quad[1]), _to_ox(quad[2]), _to_ox(quad[3])) - raise ValueError(f"Unexpected rdflib term: {term!r}") - - -def _to_ox_quad_pattern(triple: _TriplePattern, context: Graph | None = None): - (s, p, o) = triple - return _to_ox_term_pattern(s), _to_ox_term_pattern(p), _to_ox_term_pattern(o), _to_ox_term_pattern(context) - - -def _to_ox_term_pattern(term): - if term is None: - return None - if isinstance(term, URIRef): - return ox.NamedNode(term) - elif isinstance(term, BNode): - return ox.BlankNode(term) - elif isinstance(term, Literal): - return ox.Literal(term, language=term.language, datatype=ox.NamedNode(term.datatype) if term.datatype else None) - elif isinstance(term, Graph): - return _to_ox(term.identifier) - raise ValueError(f"Unexpected rdflib term: {term!r}") - - -def _from_ox(term): - if term is None: - return None - if isinstance(term, ox.NamedNode): - return URIRef(term.value) - if isinstance(term, ox.BlankNode): - return BNode(term.value) - if isinstance(term, ox.Literal): - if term.language: - return Literal(term.value, lang=term.language) - return Literal(term.value, datatype=URIRef(term.datatype.value)) - if isinstance(term, ox.DefaultGraph): - return None - if isinstance(term, ox.Triple): - return _from_ox(term.subject), _from_ox(term.predicate), _from_ox(term.object) - if isinstance(term, ox.Quad): - return (_from_ox(term.subject), _from_ox(term.predicate), _from_ox(term.object)), _from_ox(term.graph_name) - raise ValueError(f"Unexpected Oxigraph term: {term!r}") diff --git a/cognite/neat/legacy/graph/stores/_rdf_to_graph.py b/cognite/neat/legacy/graph/stores/_rdf_to_graph.py deleted file mode 100644 index 67127549f..000000000 --- a/cognite/neat/legacy/graph/stores/_rdf_to_graph.py +++ /dev/null @@ -1,42 +0,0 @@ -from pathlib import Path - -from rdflib import Graph, Namespace - -from cognite.neat.constants import get_default_prefixes - - -def rdf_file_to_graph( - graph: Graph, - filepath: Path, - base_prefix: str | None = None, - base_namespace: Namespace | None = None, - prefixes: dict[str, Namespace] | None = None, -) -> Graph: - """Created rdflib Graph instance loaded with RDF triples from file - - Args: - filepath: Path to the RDF file - base_prefix: base prefix for URIs. Defaults to None. - base_namespace: base namespace for URIs . Defaults to None. - prefixes: Dictionary of prefixes to bind to graph. Defaults to internal set of prefixes. - graph: Graph instance to load RDF triples into. Defaults to None. - - Returns: - Graph instance loaded with RDF triples from file - """ - - prefixes = prefixes if prefixes else get_default_prefixes() - - if filepath.is_file(): - graph.parse(filepath, publicID=base_namespace) - else: - for filename in filepath.iterdir(): - if filename.is_file(): - graph.parse(filename, publicID=base_namespace) - if base_prefix and base_namespace: - graph.bind(base_prefix, base_namespace) - if prefixes: - for prefix, namespace in prefixes.items(): - graph.bind(prefix, namespace) - - return graph diff --git a/cognite/neat/legacy/graph/transformations/__init__.py b/cognite/neat/legacy/graph/transformations/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/cognite/neat/legacy/graph/transformations/entity_matcher.py b/cognite/neat/legacy/graph/transformations/entity_matcher.py deleted file mode 100644 index 52ff0459f..000000000 --- a/cognite/neat/legacy/graph/transformations/entity_matcher.py +++ /dev/null @@ -1,101 +0,0 @@ -import logging -from typing import cast - -from rdflib import Literal, URIRef - -from cognite.neat.legacy.graph.stores import NeatGraphStoreBase - - -def simple_entity_matcher( - graph_store: NeatGraphStoreBase, - source_class: str, - source_property: str, - source_value_type: str = "single_value_str", - target_class: str | None = None, - target_property: str | None = None, - relationship_name: str = "link", - link_direction: str = "target_to_source", # source_to_target, bidirectional - matching_method: str = "regexp", # exact_match, similarity - link_namespace: str = "http://purl.org/cognite/neat#", -) -> int: - """simple_entity_matcher performs a simple entity matching between two classes in the graph using - either exact match or regular expression matching. - The matching is performed on the values of the source_property and target_property. - The matching is performed in the direction specified by link_direction. - The matching_method can be either exact_match or regexp. - If the source_value_type is multi_value_str, the values are split on comma and added as separate triples. - Args: - graph_store (NeatGraphStoreBase): The graph store to perform the matching on and add the links to - source_class (str): The class of the source entities - source_property (str): The property of the source entities to match on - source_value_type (str, optional): The value type of the source property. Defaults to "single_value_str". - target_class (str | None, optional): The class of the target entities. Defaults to None. - target_property (str | None, optional): The property of the target entities to match on. Defaults to None. - relationship_name (str, optional): The name of the relationship to add between the matched entities. - link_direction (str, optional): The direction of the relationship. Defaults to "target_to_source". - matching_method (str, optional): The matching method to use. Defaults to "regexp". - Returns: - int: The number of new links added to the graph - """ - if source_value_type == "multi_value_str": - # Split the values and add them as separate triples - query = f""" - SELECT DISTINCT ?source ?source_value - WHERE {{ - ?source rdf:type neat:{source_class} . - ?source neat:{source_property} ?source_value . - }} - """ - triples_to_remove = [] - r1 = cast(tuple, graph_store.query(query)) - result = list(r1) - for row in result: - val_split = row[1].split(",") - if len(val_split) > 1: - triples_to_remove.append((row[0], URIRef(link_namespace + source_property), row[1])) - for val in val_split: - graph_store.graph.add((row[0], URIRef(link_namespace + source_property), Literal(val))) - - for triple in triples_to_remove: - graph_store.graph.remove(triple) - - graph_store.graph.commit() - query = "" - if matching_method == "exact_match": - query = f""" - SELECT DISTINCT ?source ?target - WHERE {{ - ?source rdf:type neat:{source_class} . - ?source neat:{source_property} ?source_value . - ?target rdf:type neat:{target_class} . - ?target neat:{target_property} ?target_value . - FILTER (?source_value = ?target_value) - }} - """ - elif matching_method == "regexp": - query = f""" - SELECT DISTINCT ?source ?target - WHERE {{ - ?source rdf:type neat:{source_class} . - ?source neat:{source_property} ?source_value . - ?target rdf:type neat:{target_class} . - ?target neat:{target_property} ?target_value . - FILTER regex(?target_value,?source_value, "i") - }} - """ - else: - logging.error(f"Unknown matching method {matching_method}") - return 0 - logging.debug(f"Running matcher query {query}") - r1 = cast(tuple, graph_store.query(query)) - result = list(r1) - logging.debug(f"Identified {len(result)} matches from the graph") - new_links_counter = 0 - for row in result: - new_links_counter += 1 - if link_direction == "target_to_source": - graph_store.graph.add((row[1], URIRef(link_namespace + relationship_name), row[0])) - else: - graph_store.graph.add((row[0], URIRef(link_namespace + relationship_name), row[1])) - - return new_links_counter diff --git a/cognite/neat/legacy/graph/transformations/query_generator/__init__.py b/cognite/neat/legacy/graph/transformations/query_generator/__init__.py deleted file mode 100644 index 35f63a804..000000000 --- a/cognite/neat/legacy/graph/transformations/query_generator/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .sparql import build_sparql_query - -__all__ = ["build_sparql_query"] diff --git a/cognite/neat/legacy/graph/transformations/query_generator/sparql.py b/cognite/neat/legacy/graph/transformations/query_generator/sparql.py deleted file mode 100644 index ea4f8e671..000000000 --- a/cognite/neat/legacy/graph/transformations/query_generator/sparql.py +++ /dev/null @@ -1,575 +0,0 @@ -import re -from collections import Counter -from collections.abc import Iterable -from typing import cast - -from rdflib import Graph, Namespace -from rdflib.term import URIRef - -from cognite.neat.constants import get_default_prefixes -from cognite.neat.legacy.rules.analysis import get_classes_with_properties -from cognite.neat.legacy.rules.models._base import Triple -from cognite.neat.legacy.rules.models.rdfpath import ( - AllProperties, - AllReferences, - Hop, - SingleProperty, - Step, - TransformationRuleType, - Traversal, - parse_rule, - parse_traversal, -) -from cognite.neat.legacy.rules.models.rules import Rules -from cognite.neat.utils.rdf_ import remove_namespace_from_uri - - -def _generate_prefix_header(prefixes: dict[str, Namespace] | None = None) -> str: - """Generate prefix header which is added to SPARQL query and allows for shorten query statements - - Parameters - ---------- - prefixes : dict - Dict containing prefix - namespace pairs, default PREFIXES - - Returns - ------- - str - Prefix header - """ - prefixes = prefixes if prefixes else get_default_prefixes() - return "".join(f"PREFIX {key}:<{value}>\n" for key, value in prefixes.items()) - - -def _get_predicate_id( - graph: Graph, - subject_type_id: str, - object_type_id: str, - prefixes: dict[str, Namespace] | None = None, -) -> URIRef: - """Returns predicate (aka property) URI (i.e., ID) that connects subject and object - - Parameters - ---------- - graph : Graph - Data model graph or data model instance (aka knowledge graph) - subject_type_id : str - ID of subject type (aka subject class) - object_type_id : str - ID of object type (aka object class) - prefixes : dict, optional - Dict containing prefix - namespace pairs, default PREFIXES - - Returns - ------- - URIRef - ID of predicate (aka property) connecting subject and object - """ - prefixes = prefixes if prefixes else get_default_prefixes() - query = """ - - SELECT ?predicateTypeID - WHERE { - ?subjectInstanceID a subjectTypeID . - ?objectInstanceID a objectTypeID . - ?subjectInstanceID ?predicateTypeID ?objectInstanceID . - } LIMIT 1""" - - query = query.replace("insertPrefixes", _generate_prefix_header(prefixes)) - final_query = query.replace("subjectTypeID", subject_type_id).replace("objectTypeID", object_type_id) - res = list(cast(tuple, graph.query(final_query))) - - if len(res) != 1: - raise ValueError("Subject and Object must have exactly 1 relation!") - - return res[0][0] - - -def _get_direct_mapping_triples(subject, predicate) -> list[Triple]: - return [Triple(subject=subject, predicate=predicate, object="?object")] - - -def _get_all_references_mapping_triples(object) -> list[Triple]: - return [Triple(subject="?subject", predicate="a", object=object)] - - -def _get_entire_object_mapping(subject) -> list[Triple]: - return [Triple(subject=subject, predicate="*")] - - -def _get_hop_triples(graph, path: Hop, prefixes) -> list[Triple]: - triples = [Triple(subject="?subject", predicate="a", object=path.class_.id)] - previous_step = Step(class_=path.class_, direction="origin") - - # add triples for all steps until destination - for current_step in path.traversal: - sub_entity, obj_entity = ( - (current_step, previous_step) if current_step.direction == "source" else (previous_step, current_step) - ) - - predicate = _get_predicate_id(graph, sub_entity.class_.id, obj_entity.class_.id, prefixes) - - # if this is first step after origin - if previous_step.class_.id == path.class_.id: - if current_step.direction == "source": - sub, obj = f"?{sub_entity.class_.suffix}ID", "?subject" - else: - sub, obj = "?subject", f"?{obj_entity.class_.suffix}ID" - - # Any other step after hoping from origin to first step - else: - sub = f"?{sub_entity.class_.suffix}ID" - obj = f"?{obj_entity.class_.suffix}ID" - - triples.append(Triple(subject=sub, predicate=predicate, object=obj)) - previous_step = current_step - - if previous_step.property: - triples.extend( - [ - Triple( - subject=f"?{previous_step.class_.suffix}ID", - predicate="a", - object=previous_step.class_.id, - ), - Triple( - subject=f"?{previous_step.class_.suffix}ID", - predicate=previous_step.property.id, - object="?object", - ), - Triple( - subject="?predicate", - predicate="a", - object=previous_step.property.id, - ), - ] - ) - else: - if previous_step.direction == "source": - triples[-1].subject = "?object" - else: - triples[-1].object = "?object" - triples.append(Triple(subject="?object", predicate="a", object=previous_step.class_.id)) - - return triples - - -def _get_path_triples(graph: Graph, traversal: Traversal, prefixes: dict[str, Namespace] | None = None) -> list[Triple]: - """Creates triples subject-predicate-object from declarative graph traversal path - - Parameters - ---------- - graph : Graph - Data model graph or data model instance (aka knowledge graph) - traversal : Traversal - Parsed traversal path in declarative form - prefixes : dict, optional - Dict containing prefix - namespace pairs, default PREFIXES - - Returns - ------- - list - List of triples to be consumed by SPARQL query builder - """ - match traversal: - case SingleProperty(): - return _get_direct_mapping_triples(traversal.class_.id, traversal.property.id) - case AllProperties(): - return _get_entire_object_mapping(traversal.class_.id) - case AllReferences(): - return _get_all_references_mapping_triples(traversal.class_.id) - case Hop(): - return _get_hop_triples(graph, traversal, prefixes) - case _: - raise ValueError("Incorrectly set traversal path!") - - -BASIC_SPARQL_QUERY_TEMPLATE = """insertPrefixes - -SELECT DISTINCT ?subject ?predicate ?object - WHERE { -query_insertions - }""" - - -REFERENCES_ONLY_SPARQL_QUERY_TEMPLATE = """insertPrefixes - -SELECT DISTINCT ?subject ?predicate ?object { - {SELECT DISTINCT ?object ?predicate { - query_insertions - BIND(dct:identifier AS ?predicate)}} - - BIND(?object AS ?subject) - - }""" - - -# This template does not work with in-memory graph, so it has been replaced with the one above -REFERENCES_ONLY_SPARQL_QUERY_TEMPLATE_OLD = """insertPrefixes - -SELECT DISTINCT ?subject ?predicate ?object - WHERE { -query_insertions - { - BIND(?object AS ?subject) - BIND(dct:identifier AS ?predicate) - } - }""" - -SINGLE_PROPERTY_SPARQL_QUERY_TEMPLATE = """insertPrefixes - -SELECT DISTINCT ?subject ?predicate ?object - WHERE { -query_insertions - { - BIND(property_insertion AS ?predicate) - } - }""" - - -def _generate_all_properties_query_statement(subject: str, query_template: str = BASIC_SPARQL_QUERY_TEMPLATE) -> str: - query_insertions = "\n".join([f"\t\t?subject a {subject} .", "\t\t?subject ?predicate ?object ."]) - - return query_template.replace("query_insertions", query_insertions) - - -def _generate_all_references_query_statement( - object: str, query_template: str = REFERENCES_ONLY_SPARQL_QUERY_TEMPLATE -) -> str: - query_insertions = "\n".join([f"\t\t?object a {object} ."]) - - return query_template.replace("query_insertions", query_insertions) - - -def _generate_single_property_query_statement( - subject: str, - predicate: str, - query_template: str = SINGLE_PROPERTY_SPARQL_QUERY_TEMPLATE, -) -> str: - query_insertions = "\n".join([f"\t\t?subject a {subject} .", f"\t\t?subject {predicate} ?object ."]) - - return query_template.replace("query_insertions", query_insertions).replace("property_insertion", predicate) - - -def _generate_hop_query_statement(triples: list[Triple], query_template: str = BASIC_SPARQL_QUERY_TEMPLATE) -> str: - terminal_triplet = triples[-1] - - query_insertions = "".join( - f" {triple.subject} {triple.predicate} {triple.object} .\n" for triple in triples[:-1] - ) - - # Creating terminal query statement based on whether we are query for specific - # property of an object (first option) or object ID - if terminal_triplet.subject == "?predicate": - query_insertions += f" BIND({terminal_triplet.object} AS ?predicate)\n" - else: - query_insertions += ( - f" {terminal_triplet.subject} {terminal_triplet.predicate} {terminal_triplet.object} .\n" - ) - query_insertions += " BIND(dct:relation AS ?predicate)\n" - - return query_template.replace("query_insertions", query_insertions) - - -def build_sparql_query( - graph: Graph, - traversal_path: str | Traversal, - prefixes: dict[str, Namespace] | None = None, - insert_prefixes: bool = False, -) -> str: - """Builds SPARQL query based on declarative traversal path - - Parameters - ---------- - graph : Graph - Data model graph or data model instance (aka knowledge graph) - traversal_path : str - String representing graph traversal path in declarative form - prefixes : dict, optional - Dict containing prefix - namespace pairs, default PREFIXES - - Returns - ------- - str - SPARQL query - """ - prefixes = prefixes if prefixes else get_default_prefixes() - traversal = parse_traversal(traversal_path) if isinstance(traversal_path, str) else traversal_path - triples = _get_path_triples(graph, traversal, prefixes) - - if isinstance(traversal, AllProperties): - query = _generate_all_properties_query_statement(cast(str, triples[0].subject)) - elif isinstance(traversal, AllReferences) and isinstance(triples[0].object, str): - query = _generate_all_references_query_statement(triples[0].object) - elif isinstance(traversal, SingleProperty): - query = _generate_single_property_query_statement( - cast(str, triples[0].subject), cast(str, triples[0].predicate) - ) - elif isinstance(traversal, Hop): - query = _generate_hop_query_statement(triples) - else: - raise ValueError("Not Supported!") - - # Replacing long URIs with short form using their prefixes - for prefix, URI in prefixes.items(): - query = query.replace(URI, f"{prefix}:") - - return query.replace( - "insertPrefixes\n\n", - _generate_prefix_header(prefixes) if insert_prefixes else "", - ) - - -def compress_uri(uri: URIRef, prefixes: dict) -> str: - """Compresses URI to prefix:entity_id - - Parameters - ---------- - uri : URIRef - URI of entity - prefixes : dict - Dictionary of prefixes - - Returns - ------- - str - Compressed URI or original URI if no prefix is found - """ - return next( - ( - f"{prefix}:{uri.replace(namespace, '')}" - for prefix, namespace in prefixes.items() - if uri.startswith(namespace) - ), - uri, - ) - - -def _hop2property_path(graph: Graph, hop: Hop, prefixes: dict[str, Namespace]) -> str: - """Converts hop to property path string - - Parameters - ---------- - graph : Graph - Graph containing instances of classes - hop : Hop - Hop to convert - prefixes : dict[str, Namespace] - Dictionary of prefixes to use for compression and predicate quering - - Returns - ------- - str - Property path string for hop traversal (e.g. ^rdf:type/rdfs:subClassOf) - """ - - # setting previous step to origin, as we are starting from there - previous_step = Step(class_=hop.class_, direction="origin") - - # add triples for all steps until destination - property_path = "" - for current_step in hop.traversal: - sub_entity, obj_entity = ( - (current_step, previous_step) if current_step.direction == "source" else (previous_step, current_step) - ) - - predicate_raw = _get_predicate_id(graph, sub_entity.class_.id, obj_entity.class_.id, prefixes) - - predicate = compress_uri(predicate_raw, prefixes) - - predicate = f"^{predicate}" if current_step.direction == "source" else predicate - property_path += f"{predicate}/" - - previous_step = current_step - - if previous_step.property: - return property_path + previous_step.property.id - else: - # removing "/" at the end of property path if there is no property at the end - return property_path[:-1] - - -def build_construct_query( - graph: Graph, - class_: str, - transformation_rules: Rules, - properties_optional: bool = True, - class_instances: list[URIRef] | None = None, -) -> str: - """Builds CONSTRUCT query for given class and rules and optionally filters by class instances - - Parameters - ---------- - graph : Graph - Graph containing instances of classes - class_ : str - ID of class for which class_instance we want to query - transformation_rules : TransformationRules - Transformation rules to use for query generation - properties_optional : bool, optional - Whether to make all properties optional, default True - class_instances : list[URIRef], optional - List of class instances to filter by, default None (no filter return all instances) - - Returns - ------- - str - CONSTRUCT query - - Notes - ----- - Construct query is far less unforgiving than SELECT query, in sense that it will not return - anything if one of the properties that define "shape" of the class instance is missing. - This is the reason why there is option to make all properties optional, so that query will - return all instances that have at least one property defined. - - """ - - query_template = "CONSTRUCT {graph_template\n}\n\nWHERE {graph_pattern\ninsert_filter}" - query_template = _add_filter(class_instances, query_template) - - templates, patterns = _to_construct_triples(graph, class_, transformation_rules, properties_optional) - - graph_template = "\n ".join(_triples2sparql_statement(templates)) - graph_pattern = "\n ".join(_triples2sparql_statement(patterns)) - - return query_template.replace("graph_template", graph_template).replace("graph_pattern", graph_pattern) - - -def _add_filter(class_instances, query_template): - if class_instances: - class_instances_formatted = [f"<{instance}>" for instance in class_instances] - query_template = query_template.replace( - "insert_filter", - f"\n\nFILTER (?subject IN ({', '.join(class_instances_formatted)}))", - ) - else: - query_template = query_template.replace("insert_filter", "") - return query_template - - -def _triples2sparql_statement(triples: list[Triple]): - return [ - ( - f"OPTIONAL {{ {triple.subject} {triple.predicate} {triple.object} . }}" - if triple.optional - else f"{triple.subject} {triple.predicate} {triple.object} ." - ) - for triple in triples - ] - - -def _to_construct_triples( - graph: Graph, - class_: str, - transformation_rules: Rules, - properties_optional: bool = True, -) -> tuple[list[Triple], list[Triple]]: - """Converts class definition to CONSTRUCT triples which are used to generate CONSTRUCT query - - Parameters - ---------- - graph : Graph - Graph containing instances of classes - class_ : str - ID of class for which class_instance we want to query - transformation_rules : TransformationRules - Transformation rules to use for query generation - - Returns - ------- - tuple[list[Triple],list[Triple]] - Tuple of triples that define graph template and graph pattern parts of CONSTRUCT query - """ - # TODO: Add handling of UNIONs in rules - - templates = [] - patterns = [] - - class_ids = [] - for property_ in get_classes_with_properties(transformation_rules)[class_]: - if property_.rule_type != TransformationRuleType.rdfpath or property_.skip_rule: - continue - if not isinstance(property_.rule, str): - raise ValueError("Rule must be string!") - traversal = parse_rule(property_.rule, property_.rule_type).traversal - - if isinstance(traversal, Traversal): - class_ids.append(traversal.class_.id) - - graph_template_triple = Triple( - subject="?subject", - predicate=f"{transformation_rules.metadata.prefix}:{property_.property_id}", - object=f'?{re.sub(r"[^_a-zA-Z0-9/_]", "_", str(property_.property_id).lower())}', - optional=False, - ) - templates.append(graph_template_triple) - - # AllReferences should not be "optional" since we are creating their values - # by binding them to certain property - if isinstance(traversal, AllReferences): - graph_pattern_triple = Triple( - subject="BIND(?subject", - predicate="AS", - object=f"{graph_template_triple.object})", - optional=False, - ) - - elif isinstance(traversal, SingleProperty): - graph_pattern_triple = Triple( - subject=graph_template_triple.subject, - predicate=traversal.property.id, - object=graph_template_triple.object, - optional=True if properties_optional else not property_.is_mandatory, - ) - - elif isinstance(traversal, Hop): - graph_pattern_triple = Triple( - subject="?subject", - predicate=_hop2property_path(graph, traversal, transformation_rules.prefixes), - object=graph_template_triple.object, - optional=True if properties_optional else not property_.is_mandatory, - ) - else: - continue - - patterns.append(graph_pattern_triple) - - # add first triple for graph pattern stating type of object - patterns.insert( - 0, - Triple( - subject="?subject", - predicate="a", - object=_most_occurring_element(class_ids), - optional=False, - ), - ) - - return templates, patterns - - -def _most_occurring_element(list_of_elements: list): - counts = Counter(list_of_elements) - return counts.most_common(1)[0][0] - - -def triples2dictionary(triples: Iterable[tuple[URIRef, URIRef, str | URIRef]]) -> dict[URIRef, dict[str, list[str]]]: - """Converts list of triples to dictionary""" - dictionary: dict[URIRef, dict[str, list[str]]] = {} - for triple in triples: - id_: str - property_: str - value: str - uri: URIRef - id_, property_, value = remove_namespace_from_uri(*triple) # type: ignore[misc] - uri = triple[0] - - if uri not in dictionary: - dictionary[uri] = {"external_id": [id_]} - - if property_ not in dictionary[uri]: - dictionary[uri][property_] = [value] - else: - dictionary[uri][property_].append(value) - return dictionary diff --git a/cognite/neat/legacy/graph/transformations/transformer.py b/cognite/neat/legacy/graph/transformations/transformer.py deleted file mode 100644 index b64e8dcf5..000000000 --- a/cognite/neat/legacy/graph/transformations/transformer.py +++ /dev/null @@ -1,322 +0,0 @@ -"""Methods to transform Domain Knowledge Graph to App Knowledge Graph""" - -import logging -import time -import traceback -from typing import Any - -import pandas as pd -from cognite.client import CogniteClient -from prometheus_client import Gauge -from pydantic import BaseModel -from rdflib import RDF, Graph -from rdflib.term import Literal, Node - -from cognite.neat.legacy.graph.exceptions import NamespaceRequired -from cognite.neat.legacy.graph.transformations.query_generator.sparql import build_sparql_query -from cognite.neat.legacy.rules.models._base import EntityTypes -from cognite.neat.legacy.rules.models.rdfpath import ( - AllProperties, - AllReferences, - Query, - RawLookup, - Traversal, - parse_rule, -) -from cognite.neat.legacy.rules.models.rules import Rules -from cognite.neat.utils.rdf_ import remove_namespace_from_uri - -prom_total_proc_rules_g = Gauge("neat_total_processed_rules", "Number of processed rules", ["state"]) -rules_processing_timing_metric = Gauge( - "neat_rules_processing_timing", "Transformation rules processing timing metrics", ["aggregation"] -) - - -COMMIT_BATCH_SIZE = 10000 - - -class RuleProcessingReportRec(BaseModel): - """Report record for rule processing""" - - row_id: str | None = None - rule_name: str | None = None - rule_type: str | None = None - rule_expression: Any | None = None - status: str | None = None - error_message: str | None = None - elapsed_time: float = 0 - rows_in_response: int = 0 - - -class RuleProcessingReport(BaseModel): - """Report for rule processing""" - - total_rules: int = 0 - total_success: int = 0 - total_success_no_results: int = 0 - total_failed: int = 0 - records: list[RuleProcessingReportRec] = [] - elapsed_time: float = 0 - - -def source2solution_graph( - source_knowledge_graph: Graph, - transformation_rules: Rules, - solution_knowledge_graph: Graph | None = None, - client: CogniteClient | None = None, - cdf_lookup_database: str | None = None, - extra_triples: list[tuple[Node, Node, Node]] | None = None, - stop_on_exception: bool = False, - missing_raw_lookup_value: str = "NaN", - processing_report: RuleProcessingReport | None = None, -) -> Graph: - """Transforms solution knowledge graph based on Domain Knowledge Graph - - Args: - source_knowledge_graph: Domain Knowledge Graph which represents the source graph being - transformed to app/solution specific graph - transformation_rules: Transformation rules holding data model definition and rules to - transform source/domain graph to app/solution specific graph - solution_knowledge_graph: Graph to store app/solution specific graph. - Defaults to None (i.e., empty graph). - client: CogniteClient. Defaults to None. - cdf_lookup_database: CDF RAW database name to use for `rawlookup` rules. Defaults to None. - extra_triples: Additional triples to add to app/solution knowledge graph. Defaults to None. - stop_on_exception: To stop on exception. Defaults to False. - missing_raw_lookup_value: If no value is find for `rawlookup` default value to use. Defaults to "NaN". - processing_report: Processing report to store results to. Defaults to None. - - Returns: - Transformed knowledge graph based on transformation rules - """ - - # TODO: This is to be improved and slowly sunset domain2app_knowledge_graph - - return domain2app_knowledge_graph( - domain_knowledge_graph=source_knowledge_graph, - transformation_rules=transformation_rules, - app_instance_graph=solution_knowledge_graph, - client=client, - cdf_lookup_database=cdf_lookup_database, - extra_triples=extra_triples, - stop_on_exception=stop_on_exception, - missing_raw_lookup_value=missing_raw_lookup_value, - processing_report=processing_report, - ) - - -def domain2app_knowledge_graph( - domain_knowledge_graph: Graph, - transformation_rules: Rules, - app_instance_graph: Graph | None = None, - client: CogniteClient | None = None, - cdf_lookup_database: str | None = None, - extra_triples: list[tuple[Node, Node, Node]] | None = None, - stop_on_exception: bool = False, - missing_raw_lookup_value: str = "NaN", - processing_report: RuleProcessingReport | None = None, -) -> Graph: - """Generates application/solution specific knowledge graph based on Domain Knowledge Graph - - Args: - domain_knowledge_graph: Domain Knowledge Graph which represent the source graph being - transformed to app/solution specific graph - transformation_rules: Transformation rules holding data model definition and rules - to transform source/domain graph to app/solution specific graph - app_instance_graph: Graph to store app/solution specific graph. Defaults to None (i.e., empty graph). - client: CogniteClient. Defaults to None. - cdf_lookup_database: CDF RAW database name to use for `rawlookup` rules. Defaults to None. - extra_triples: Additional triples to add to app/solution knowledge graph. Defaults to None. - stop_on_exception: To stop on exception. Defaults to False. - missing_raw_lookup_value: If no value is find for `rawlookup` default value to use. Defaults to "NaN". - processing_report: Processing report to store results to. Defaults to None. - - Returns: - Transformed knowledge graph based on transformation rules - """ - if transformation_rules.metadata.namespace is None: - raise NamespaceRequired("Transform domain to app knowledge graph") - rule_namespace = transformation_rules.metadata.namespace - - if app_instance_graph is None: - app_instance_graph = Graph() - # Bind App namespace and prefix - app_instance_graph.bind(transformation_rules.metadata.prefix, rule_namespace) - # Bind other prefixes and namespaces - for prefix, namespace in transformation_rules.prefixes.items(): - app_instance_graph.bind(prefix, namespace) - - tables_by_name = {} - if cdf_lookup_database and client: - for table_name in transformation_rules.raw_tables: - logging.debug(f"Loading {table_name} table from database {cdf_lookup_database}") - table = client.raw.rows.retrieve_dataframe(cdf_lookup_database, table_name, limit=-1) - tables_by_name[table_name] = table - - # Add references with their type first - types = [] - success = 0 - success_no_results = 0 - failed = 0 - commit_counter = 0 - timing_traces = [] - prom_total_proc_rules_g.labels(state="success_no_results").set(0) - prom_total_proc_rules_g.labels(state="success").set(0) - prom_total_proc_rules_g.labels(state="failed").set(0) - - def check_commit(force_commit: bool = False): - """'Commit nodes to the graph if batch counter is reached or if force_commit is True""" - - if force_commit: - logging.debug("Committing nodes") - app_instance_graph.commit() - logging.debug("Nodes committed") - return - nonlocal commit_counter - commit_counter += 1 - if commit_counter >= COMMIT_BATCH_SIZE: - logging.info(f"Committing {COMMIT_BATCH_SIZE} nodes") - app_instance_graph.commit() - logging.info(f" {COMMIT_BATCH_SIZE} nodes committed") - commit_counter = 0 - - proc_start_time = time.perf_counter() - for sheet_row, rule_definition in transformation_rules.properties.items(): - if not rule_definition.rule or rule_definition.skip_rule: - continue - msg = f"Processing {sheet_row}: class <{rule_definition.class_id}> " - msg += f"property <{rule_definition.property_name}> rule <{rule_definition.rule}>" - - processing_report_rec = RuleProcessingReportRec( - row_id=sheet_row, - rule_name=f"{rule_definition.class_id}_{rule_definition.property_name}", - rule_type=rule_definition.rule_type, - rule_expression=rule_definition.rule, - ) - logging.info(msg) - try: - start_time = time.perf_counter() - # Parse rule: - rule = parse_rule(rule_definition.rule, rule_definition.rule_type) # type: ignore[arg-type] - - # Build SPARQL if needed: - if isinstance(rule.traversal, Query) and rule_definition.rule_type == "sparql": - query = rule.traversal.query - elif isinstance(rule.traversal, Traversal | str): - query = build_sparql_query(domain_knowledge_graph, rule.traversal, transformation_rules.prefixes) - else: - raise ValueError(f"Unknown traversal type {type(rule.traversal)}") - logging.debug(f"Query: {query}") - - if query_results := list(domain_knowledge_graph.query(query)): - # Generate URI for class and property in target namespace - class_URI = rule_namespace[rule_definition.class_id] - property_URI = rule_namespace[rule_definition.property_name] # type: ignore[index] - - # Turn query results into dataframe - instance_df = pd.DataFrame( - query_results, columns=[EntityTypes.subject, EntityTypes.predicate, EntityTypes.object] - ) - - # If we are not grabbing all properties for class instances - # then we are able to replace source property URI with target property URI - # otherwise we should keep source property URI - if not isinstance(rule.traversal, AllProperties): - instance_df[EntityTypes.predicate] = property_URI - - # If we are storing object from the source graph as literal value(propety type being Datatype Property) - # in the target graph then we should remove namespace from the object URI and store it as literal - if isinstance(rule.traversal, AllReferences) and rule_definition.property_type == "DatatypeProperty": - instance_df[EntityTypes.object] = instance_df[EntityTypes.object].apply( - lambda x: Literal(remove_namespace_from_uri(x)) - ) - - if isinstance(rule, RawLookup): - lookup_map = tables_by_name[rule.table.name].set_index(rule.table.key)[rule.table.value].to_dict() - - def lookup( - literal: Literal, lookup_table=lookup_map, missing_raw_lookup_value=missing_raw_lookup_value - ): - if new_value := lookup_table.get(literal.value): - return Literal(new_value, literal.language, literal.datatype, bool(literal.normalize)) - elif missing_raw_lookup_value: - return Literal( - missing_raw_lookup_value, literal.language, literal.datatype, bool(literal.normalize) - ) - else: - return literal - - instance_df[EntityTypes.object] = instance_df[EntityTypes.object].apply(lookup) - - # Add instances - for _, triple in instance_df.iterrows(): - app_instance_graph.add(triple.values) # type: ignore[arg-type] - check_commit() - # Setting instances type and merging them with df containing instance - type relations - instance_df[EntityTypes.predicate] = RDF.type - instance_df[EntityTypes.object] = class_URI - types.append(instance_df) - success += 1 - prom_total_proc_rules_g.labels(state="success").inc() - elapsed_time = time.perf_counter() - start_time - timing_traces.append(elapsed_time) - processing_report_rec.elapsed_time = elapsed_time - processing_report_rec.status = "success" - processing_report_rec.rows_in_response = len(instance_df) - else: - success_no_results += 1 - prom_total_proc_rules_g.labels(state="success_no_results").inc() - elapsed_time = time.perf_counter() - start_time - timing_traces.append(elapsed_time) - processing_report_rec.elapsed_time = elapsed_time - processing_report_rec.status = "success_no_results" - - except Exception as e: - failed += 1 - elapsed_time = time.perf_counter() - start_time - processing_report_rec.elapsed_time = elapsed_time - processing_report_rec.status = "failed" - processing_report_rec.error_message = str(e) - prom_total_proc_rules_g.labels(state="failed").inc() - logging.error( - f" Error while processing rule {rule_definition.rule} for class {rule_definition.class_id} \ - and property {rule_definition.property_name}" - ) - logging.error(traceback.format_exc()) - if stop_on_exception: - raise e - - if processing_report: - processing_report.records.append(processing_report_rec) - - if processing_report: - processing_report.total_rules = len(transformation_rules.properties) - processing_report.total_success = success - processing_report.total_success_no_results = success_no_results - processing_report.total_failed = failed - processing_report.elapsed_time = time.perf_counter() - proc_start_time - - if timing_traces: - df = pd.Series(timing_traces) - rules_processing_timing_metric.labels(aggregation="sum").set(df.sum()) - rules_processing_timing_metric.labels(aggregation="std_div").set(df.std()) - rules_processing_timing_metric.labels(aggregation="min").set(df.min()) - rules_processing_timing_metric.labels(aggregation="max").set(df.max()) - rules_processing_timing_metric.labels(aggregation="mean").set(df.mean()) - - type_df = pd.concat(types).drop_duplicates(EntityTypes.subject).reset_index(drop=True) - - # Add instance - RDF Type relations - for _, triple in type_df.iterrows(): # type: ignore[assignment] - app_instance_graph.add(triple.values) # type: ignore[arg-type] - check_commit() - - for i, triple in enumerate(extra_triples or []): # type: ignore[assignment] - try: - app_instance_graph.add(triple) # type: ignore[arg-type] - check_commit() - except ValueError as e: - raise ValueError(f"Triple {i} in extra_triples is not correct and cannot be added!") from e - - check_commit(force_commit=True) - return app_instance_graph diff --git a/cognite/neat/legacy/rules/__init__.py b/cognite/neat/legacy/rules/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/cognite/neat/legacy/rules/analysis.py b/cognite/neat/legacy/rules/analysis.py deleted file mode 100644 index 4e3fc9aeb..000000000 --- a/cognite/neat/legacy/rules/analysis.py +++ /dev/null @@ -1,231 +0,0 @@ -import warnings -from collections import defaultdict - -import pandas as pd - -from cognite.neat.legacy.rules.models.rdfpath import TransformationRuleType -from cognite.neat.legacy.rules.models.rules import Property, Rules - - -def get_defined_classes(transformation_rules: Rules) -> set[str]: - """Returns classes that have properties defined for them in the data model. - - Args: - transformation_rules: Instance of TransformationRules holding the data model - - Returns: - Set of classes that have been defined in the data model - """ - return {property.class_id for property in transformation_rules.properties.values()} - - -def get_classes_with_properties(transformation_rules: Rules) -> dict[str, list[Property]]: - """Returns classes that have been defined in the data model. - - Args: - transformation_rules: Instance of TransformationRules holding the data model - - Returns: - Dictionary of classes with a list of properties defined for them - """ - - class_property_pairs: dict[str, list[Property]] = {} - - for property_ in transformation_rules.properties.values(): - class_ = property_.class_id - if class_ in class_property_pairs: - class_property_pairs[class_] += [property_] - else: - class_property_pairs[class_] = [property_] - - return class_property_pairs - - -def to_class_property_pairs(transformation_rules: Rules, only_rdfpath: bool = False) -> dict[str, dict[str, Property]]: - """Returns a dictionary of classes with a dictionary of properties associated with them. - - Args: - transformation_rules : Instance of TransformationRules holding the data model - only_rdfpath : To consider only properties which have rule `rdfpath` set. Defaults False - - Returns: - Dictionary of classes with a dictionary of properties associated with them. - - !!! note "only_rdfpath" - If only_rdfpath is True, only properties with RuleType.rdfpath will be returned as - a part of the dictionary of properties related to a class. Otherwise, all properties - will be returned. - """ - - class_property_pairs = {} - - for class_, properties in get_classes_with_properties(transformation_rules).items(): - processed_properties = {} - for property_ in properties: - if property_.property_id in processed_properties: - # TODO: use appropriate Warning class from _exceptions.py - # if missing make one ! - warnings.warn( - "Property has been defined more than once! Only first definition will be considered.", stacklevel=2 - ) - continue - - if (only_rdfpath and property_.rule_type == TransformationRuleType.rdfpath) or not only_rdfpath: - processed_properties[property_.property_id] = property_ - class_property_pairs[class_] = processed_properties - - return class_property_pairs - - -def get_class_linkage(transformation_rules: Rules) -> pd.DataFrame: - """Returns a dataframe with the class linkage of the data model. - - Args: - transformation_rules: Instance of TransformationRules holding the data model - - Returns: - Dataframe with the class linkage of the data model - """ - - class_linkage = pd.DataFrame(columns=["source_class", "target_class", "connecting_property", "max_occurrence"]) - for property_ in transformation_rules.properties.values(): - if property_.property_type == "ObjectProperty": - new_row = pd.Series( - { - "source_class": property_.class_id, - "target_class": property_.expected_value_type.suffix, - "connecting_property": property_.property_id, - "max_occurrence": property_.max_count, - "linking_type": "hierarchy" if property_.resource_type_property else "relationship", - } - ) - class_linkage = pd.concat([class_linkage, new_row.to_frame().T], ignore_index=True) - - class_linkage.drop_duplicates(inplace=True) - - return class_linkage - - -def get_class_hierarchy_linkage(rules: Rules) -> pd.DataFrame: - """Remove linkage which is not creating asset hierarchy.""" - class_linkage = get_class_linkage(rules) - return class_linkage[class_linkage.linking_type == "hierarchy"] - - -def get_connected_classes(transformation_rules: Rules) -> set[str]: - """Return a set of classes that are connected to other classes. - - Args: - transformation_rules: Instance of TransformationRules holding the data model - - Returns: - Set of classes that are connected to other classes - """ - class_linkage = get_class_linkage(transformation_rules) - return set(class_linkage.source_class.values).union(set(class_linkage.target_class.values)) - - -def get_disconnected_classes(transformation_rules: Rules) -> set[str]: - """Return a set of classes that are disconnected (i.e. isolated) from other classes. - - Args: - transformation_rules: Instance of TransformationRules holding the data model - - Returns: - Set of classes that are disconnected from other classes - """ - return get_defined_classes(transformation_rules) - get_connected_classes(transformation_rules) - - -def get_symmetric_pairs(transformation_rules: Rules) -> set[tuple[str, str]]: - """Returns a set of pairs of symmetrically linked classes. - - Args: - transformation_rules: Instance of TransformationRules holding the data model - - Returns: - Set of pairs of symmetrically linked classes - """ - - # TODO: Find better name for this method - sym_pairs: set[tuple[str, str]] = set() - - class_linkage = get_class_linkage(transformation_rules) - if class_linkage.empty: - return sym_pairs - - for _, row in class_linkage.iterrows(): - source = row.source_class - target = row.target_class - target_targets = class_linkage[class_linkage.source_class == target].target_class.values - if source in target_targets and (source, target) not in sym_pairs: - sym_pairs.add((source, target)) - return sym_pairs - - -def get_entity_ids(transformation_rules: Rules) -> set[str]: - """Returns a set of entity ids (classes and properties) defined in the data model. - - Args: - transformation_rules: Instance of TransformationRules holding the data model - - Returns: - Set of entity ids (classes and properties) defined in the data model - """ - return set(transformation_rules.classes.keys()).union( - {property_.property_id for property_ in transformation_rules.properties.values()} - ) - - -def to_property_dict(transformation_rules: Rules) -> dict[str, list[Property]]: - """Convert list of properties to a dictionary of lists of properties with property_id as key. - - Args: - transformation_rules: Instance of TransformationRules holding the data model - - Returns: - Dictionary of lists of properties with property_id as key - """ - property_: dict[str, list[Property]] = defaultdict(list) - - for prop in transformation_rules.properties.values(): - if not (prop.property_id and prop.property_name == "*"): - property_[prop.property_id].append(prop) - - return property_ - - -def get_asset_related_properties(properties: list[Property]) -> list[Property]: - """Return properties that are used to define CDF Assets - - Args: - properties: List of properties - - Returns: - List of properties that are used to define CDF Assets - """ - return [prop for prop in properties if "Asset" in prop.cdf_resource_type] - - -def define_class_asset_mapping(transformation_rules: Rules, class_: str) -> dict[str, list[str]]: - """Define mapping between class and asset properties - - Args: - transformation_rules: Instance of TransformationRules holding the data model - class_: Class id for which mapping is to be defined - - Returns: - Dictionary with asset properties as keys and list of class properties as values - """ - mapping_dict: dict[str, list[str]] = {} - - class_properties = to_class_property_pairs(transformation_rules, only_rdfpath=True)[class_] - - for asset_property in get_asset_related_properties(list(class_properties.values())): - for resource_type_property in asset_property.resource_type_property or []: - if resource_type_property not in mapping_dict: - mapping_dict[resource_type_property] = [asset_property.property_id] - else: - mapping_dict[resource_type_property] += [asset_property.property_id] - - return mapping_dict diff --git a/cognite/neat/legacy/rules/examples/Rules-Nordic44-to-graphql.xlsx b/cognite/neat/legacy/rules/examples/Rules-Nordic44-to-graphql.xlsx deleted file mode 100644 index 081bbfe296ea5cbe58fae2e0c5ce7515a57e3dc4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 80404 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 diff --git a/cognite/neat/legacy/rules/examples/Rules-Nordic44.xlsx b/cognite/neat/legacy/rules/examples/Rules-Nordic44.xlsx deleted file mode 100644 index d9e96192c9ff023dcd15284516a61bc06b791f7c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 78798 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 diff --git a/cognite/neat/legacy/rules/examples/__init__.py b/cognite/neat/legacy/rules/examples/__init__.py deleted file mode 100644 index c900dd2bd..000000000 --- a/cognite/neat/legacy/rules/examples/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -from pathlib import Path - -# we should make this a proper package that loads examples -# similar how they handle it in xarray: -# https://github.com/pydata/xarray/blob/main/xarray/tutorial.py -# Currently there are simple paths to the examples which are then easily loaded in the notebooks - -_EXAMPLES = Path(__file__).parent - -power_grid_model = _EXAMPLES / "power-grid-example.xlsx" -power_grid_containers = _EXAMPLES / "power-grid-containers.yaml" -power_grid_data_model = _EXAMPLES / "power-grid-model.yaml" -simple_example = _EXAMPLES / "sheet2cdf-transformation-rules.xlsx" -source_to_solution_mapping = _EXAMPLES / "source-to-solution-mapping-rules.xlsx" -nordic44 = _EXAMPLES / "Rules-Nordic44.xlsx" -nordic44_graphql = _EXAMPLES / "Rules-Nordic44-to-graphql.xlsx" -skos = _EXAMPLES / "skos-rules.xlsx" -wind_energy_ontology = _EXAMPLES / "wind-energy.owl" diff --git a/cognite/neat/legacy/rules/examples/power-grid-containers.yaml b/cognite/neat/legacy/rules/examples/power-grid-containers.yaml deleted file mode 100644 index cc2f3b506..000000000 --- a/cognite/neat/legacy/rules/examples/power-grid-containers.yaml +++ /dev/null @@ -1,124 +0,0 @@ -- externalId: GeographicalRegion - name: GeographicalRegion - properties: - name: - autoIncrement: false - immutable: false - defaultValue: null - description: 'The name that identifies Greographical - - @name name' - name: name - nullable: false - type: - collation: ucs_basic - list: false - type: text - space: workshop - usedFor: node -- externalId: Terminal - name: Terminal - properties: - aliasName: - autoIncrement: false - immutable: false - defaultValue: null - description: 'The alternative name that identifies Substation - - @name aliasName' - name: aliasName - nullable: false - type: - collation: ucs_basic - list: false - type: text - name: - autoIncrement: false - immutable: false - defaultValue: null - description: 'The name that identifies Terminal - - @name name' - name: name - nullable: false - type: - collation: ucs_basic - list: false - type: text - substation: - autoIncrement: false - immutable: false - defaultValue: null - description: 'Substation to which terminal belongs to - - @name substation' - name: substation - nullable: true - type: - container: null - type: direct - list: false - space: workshop - usedFor: node -- externalId: SubGeographicalRegion - name: SubGeographicalRegion - properties: - name: - autoIncrement: false - immutable: false - defaultValue: null - description: 'The name that identifies SubGreographical - - @name name' - name: name - nullable: false - type: - collation: ucs_basic - list: false - type: text - region: - autoIncrement: false - immutable: false - defaultValue: null - description: 'Region to which subgeographical region belongs to - - @name region' - name: region - nullable: true - type: - container: null - type: direct - list: false - space: workshop - usedFor: node -- externalId: Substation - name: Substation - properties: - name: - autoIncrement: false - immutable: false - defaultValue: null - description: 'The name that identifies Substation - - @name name' - name: name - nullable: false - type: - collation: ucs_basic - list: false - type: text - subGeographicalRegion: - autoIncrement: false - immutable: false - defaultValue: null - description: 'The subgeographical region containing the substation - - @name subGeographicalRegion' - name: subGeographicalRegion - nullable: true - type: - container: null - type: direct - list: false - space: workshop - usedFor: node diff --git a/cognite/neat/legacy/rules/examples/power-grid-example.xlsx b/cognite/neat/legacy/rules/examples/power-grid-example.xlsx deleted file mode 100644 index 839f548d22849a8964aa1f77b31d803be6806407..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 77055 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

CxjvemGRZ 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 diff --git a/cognite/neat/legacy/rules/examples/source-to-solution-mapping-rules.xlsx b/cognite/neat/legacy/rules/examples/source-to-solution-mapping-rules.xlsx deleted file mode 100644 index 84723a8f8aa4b9a56ac6467df3d6fea0691b3447..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 79981 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 diff --git a/cognite/neat/legacy/rules/examples/wind-energy.owl b/cognite/neat/legacy/rules/examples/wind-energy.owl deleted file mode 100644 index e708735f2..000000000 --- a/cognite/neat/legacy/rules/examples/wind-energy.owl +++ /dev/null @@ -1,1511 +0,0 @@ - - - - - Cognite - Nikola Vasiljevic - Wind Energy Knowledge Graph - Wind Energy Knowledge Graph - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Represents relation between a CDF Asset (domain agnostic object) - and domain specific object (such as Wind Turbine). - Is CDF Asset Of - Is CDF Asset Of - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - https://docs.cognite.com/cdf/learn/cdf_basics/cdf_basics_datamodel - - https://docs.cognite.com/cdf/learn/cdf_basics/cdf_basics_datamodel - CDF Data Model - CDF Data Type - - - - - - - - - - - - - - - - - - - - - - - - 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Wind Energy Knowledge Graph - Wind Energy Knowledge Graph - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 2 - - - - - 1 - - - - - 1 - - - - - 1 - - - - - 1 - - - - - 1 - - - - - 1 - - - - - 1 - - - - - 1 - - - - - 1 - - - - - 1 - - - - - 1 - - - - - 1 - - - - - 1 - - - - - 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 4.0 - 25.0 - 80.0 - WindTurbine.Vestas-V80 - 67.0 - Vestas-V80 - Vestas-V80 - - - - - - - - - 1.225 - 0.818, 0.806, 0.804, - 0.805, 0.806, 0.807, 0.793, 0.739, 0.709, 0.409, 0.314, 0.249, 0.202, 0.167, 0.14, - 0.118, 0.101, 0.088, 0.076, 0.067, 0.059, 0.052 - 4, 5, 6, 7, 8, 9, - 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25. - - - - - - - - - 1.225 - 66600., 154000., - 282000., 460000., 696000., 996000., 1341000., 1661000., 1866000., 1958000., 1988000., - 1997000., 1999000., 2000000., 2000000., 2000000., 2000000., 2000000., 2000000., - 2000000., 2000000., 2000000. - 4, 5, 6, 7, 8, 9, - 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25. - - - - - - - 123 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/cognite/neat/legacy/rules/exceptions.py b/cognite/neat/legacy/rules/exceptions.py deleted file mode 100644 index 0fc17664f..000000000 --- a/cognite/neat/legacy/rules/exceptions.py +++ /dev/null @@ -1,2972 +0,0 @@ -"""This module contains the definition of errors and warnings raised when dealing -with TransformationRules object. This includes underlying pydantic model, actual transformation rules -handling (such `rdfpath`), and rules loaders, parsers and exporters. - -\nThe errors and warning are grouped by means of error codes:\n -- 0 - 99: errors and warnings raised when dealing with TransformationRules pydantic model -- 100 - 199: errors and warnings raised when parsing actual transformation rules, i.e. `rdfpath` -- 200 - 299: errors and warnings raised when dealing TransformationRules importers -- 300 - 399: errors and warnings raised when dealing TransformationRules parsers -- 400 - 499: errors and warnings raised when dealing TransformationRules exporters - -""" - -from typing import Any - -from cognite.client.data_classes.data_modeling import ContainerId, DataModelId, ViewId -from rdflib import Namespace, URIRef - -from cognite.neat.constants import DEFAULT_DOCS_URL -from cognite.neat.exceptions import NeatException, NeatWarning - -DOCS_BASE_URL = f"{DEFAULT_DOCS_URL}api/exceptions.html#{__name__}" - - -class MultipleExceptions(NeatException): - """This is used to gather multiple errors.""" - - def __init__(self, errors: list[NeatException], verbose: bool = False): - self.errors = errors - self.message = f"Multiple errors occurred: {self.errors}" - if verbose: - self.message += f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - super().__init__(self.message) - - -################################################################################################ -# RULES MODEL REPRESENTATION: 100 - 199 ######################################################## -################################################################################################ - -# Exceptions: - - -class PrefixRegexViolation(NeatException): - """Prefix, which is in the 'Metadata' sheet, does not respect defined regex expression - - Args: - prefix: prefix that raised exception - regex_expression: regex expression against which prefix is validated - verbose: flag that indicates whether to provide enhanced exception message, by default False - - Notes: - Check if prefix in the 'Metadata' sheet contains any illegal characters and - respects the regex expression. - - """ - - type_: str = "PrefixRegexViolation" - code: int = 0 - description: str = "Prefix, which is in the 'Metadata' sheet, does not respect defined regex expression" - example: str = ( - "If prefix is set to 'power grid', while regex expression does not " - "allow spaces, the expression will be violated thus raising this error" - ) - fix: str = ( - "Check if prefix in the 'Metadata' sheet contains any illegal characters and respects the regex expression" - ) - - def __init__(self, prefix: str, regex_expression: str, verbose: bool = False): - self.prefix = prefix - self.regex_expression = regex_expression - - self.message = ( - f"Invalid prefix '{self.prefix}' stored in 'Metadata' sheet, it must obey regex {self.regex_expression}!" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - super().__init__(self.message) - - -class PrefixMissing(NeatException): - """Prefix, which is in the 'Metadata' sheet, is missing. - - Args: - verbose: flag that indicates whether to provide enhanced exception message, by default False - - """ - - type_: str = "PrefixMissing" - code: int = 0 - description: str = "Prefix is missing from the 'Metadata' sheet." - example: str = "There is no prefix in the 'Metadata' sheet." - fix: str = "Specify the prefix if prefix in the 'Metadata' sheet." - - def __init__(self, verbose: bool = False): - self.message = ( - f"Missing prefix stored in 'Metadata' sheet." - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - super().__init__(self.message) - - -class CDFSpaceRegexViolation(NeatException): - """cdfSpaceName, which is in the 'Metadata' sheet, does not respect defined regex expression - - Args: - cdf_space_name: cdf_space_name that raised exception - regex_expression: regex expression against which cdf_space_name is validated - verbose: flag that indicates whether to provide enhanced exception message, by default False - - Notes: - Check if cdfSpaceName in the 'Metadata' sheet contains any illegal characters and - respects the regex expression. - - """ - - type_: str = "CDFSpaceRegexViolation" - code: int = 1 - description: str = "cdfSpaceName, which is in the 'Metadata' sheet, does not respect defined regex expression" - example: str = ( - "If cdfSpaceName is set to 'power grid', while regex expression does not " - "allow spaces, the expression will be violated thus raising this error" - ) - fix: str = ( - "Check if cdfSpaceName in the 'Metadata' sheet " - "contains any illegal characters and respects the regex expression" - ) - - def __init__(self, cdf_space_name: str, regex_expression: str, verbose: bool = False): - self.cdf_space_name = cdf_space_name - self.regex_expression = regex_expression - - self.message = ( - f"Invalid cdfSpaceName '{self.cdf_space_name}' stored in 'Metadata' sheet, " - f"it must obey regex {self.regex_expression}!" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - super().__init__(self.message) - - -class MetadataSheetNamespaceNotValidURL(NeatException): - """namespace, which is in the 'Metadata' sheet, does not respect defined regex expression - - Args: - namespace: namespace that raised exception - verbose: flag that indicates whether to provide enhanced exception message, by default False - - Notes: - Check if `namespace` in the `Metadata` sheet is properly constructed as valid URL - containing only allowed characters. - - """ - - type_: str = "MetadataSheetNamespaceNotValidURL" - code: int = 2 - description: str = "namespace, which is in the 'Metadata' sheet, is not valid URL" - example: str = "If we have 'authority:namespace' as namespace as it is not a valid URL this error will be raised" - fix: str = ( - "Check if 'namespace' in the 'Metadata' sheet is properly " - "constructed as valid URL containing only allowed characters" - ) - - def __init__(self, namespace: str, verbose: bool = False): - self.namespace = namespace - - self.message = ( - f"Invalid namespace '{self.namespace}' stored in 'Metadata' sheet, it must be valid URL!" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - super().__init__(self.message) - - -class MetadataSheetNamespaceNotDefined(NeatException): - """namespace, which is in the 'Metadata' sheet, is not defined - - Args: - namespace: namespace that raised exception - verbose: flag that indicates whether to provide enhanced exception message, by default False - - Notes: - Check if `namespace` in the `Metadata` sheet is properly constructed as valid URL - containing only allowed characters. - - """ - - type_ = "MetadataSheetNamespaceNotDefined" - code: int = 2 - description: str = "namespace, which is in the 'Metadata' sheet, is missing" - example: str = "Example of a valid namespace 'http://www.w3.org/ns/sparql#'" - fix: str = "Define the 'namespace' in the 'Metadata' sheet." - - def __init__(self, verbose: bool = False): - self.message = ( - f"Missing namespace in 'Metadata' sheet." f"\nFor more information visit: {DOCS_BASE_URL}.{self.type_}" - ) - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - super().__init__(self.message) - - -class DataModelIdRegexViolation(NeatException): - """dataModelName, which is in the 'Metadata' sheet, does not respect defined regex expression - - Args: - data_model_name: data_model_name that raised exception - regex_expression: regex expression against which data_model_name is validated - verbose: flag that indicates whether to provide enhanced exception message, by default False - - Notes: - Check if `dataModelName` in the `Metadata` sheet contains any illegal - characters and respects the regex expression - - """ - - type_: str = "DataModelIdRegexViolation" - code: int = 3 - description: str = "external_id, which is in the 'Metadata' sheet, does not respect defined regex expression" - example: str = ( - "If external_id is set to 'power grid data model', while regex expression does not " - "allow spaces, the expression will be violated thus raising this error" - ) - fix: str = ( - "Check if external_id in the 'Metadata' sheet contains any illegal " - "characters and respects the regex expression" - ) - - def __init__(self, data_model_id: str, regex_expression: str, verbose: bool = False): - self.data_model_id = data_model_id - self.regex_expression = regex_expression - - self.message = ( - f"Invalid data model external_id '{self.data_model_id}' stored in 'Metadata' sheet, " - f"it must obey regex {self.regex_expression}!" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - super().__init__(self.message) - - -class VersionRegexViolation(NeatException): - """version, which is in the 'Metadata' sheet, does not respect defined regex expression - - Args: - version: version that raised exception - regex_expression: regex expression against which version is validated - verbose: flag that indicates whether to provide enhanced exception message, by default False - - Notes: - Check if `version` in the `Metadata` sheet contains any illegal - characters and respects the regex expression - - """ - - type_: str = "VersionRegexViolation" - code: int = 4 - description: str = "version, which is in the 'Metadata' sheet, does not respect defined regex expression" - example: str = ( - "If version is set to '1.2.3 alpha4443', while regex expression does not " - "allow spaces, the expression will be violated thus raising this error" - ) - fix: str = ( - "Check if version in the 'Metadata' sheet contains any illegal characters and respects the regex expression" - ) - - def __init__(self, version: str, regex_expression: str, verbose: bool = False): - self.version = version - self.regex_expression = regex_expression - - self.message = ( - f"Invalid version '{self.version}' stored in 'Metadata' sheet, it must obey regex {self.regex_expression}!" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - super().__init__(self.message) - - -class ClassSheetClassIDRegexViolation(NeatException): - """Class ID, which is stored in the column 'Class' in the 'Classes' sheet, does not - respect defined regex expression - - Args: - class_id: class_id that raised exception - regex_expression: regex expression against which class_id is validated - verbose: flag that indicates whether to provide enhanced exception message, by default False - - Notes: - Check definition of class ids in 'Class' column in 'Classes' sheet and - make sure to respect the regex expression by removing any illegal characters - - """ - - type_: str = "ClassSheetClassIDRegexViolation" - code: int = 5 - description: str = ( - "Class ID, which is stored in the column 'Class' in the 'Classes' sheet, " - "does not respect defined regex expression" - ) - example: str = ( - "If class id is set to 'Class 1', while regex expression does not allow spaces," - " the expression will be violated thus raising this error" - ) - fix: str = ( - "Check definition of class ids in 'Class' column in 'Classes' sheet and " - "make sure to respect the regex expression by removing any illegal characters" - ) - - def __init__(self, class_id: str, regex_expression: str, verbose: bool = False): - self.class_id = class_id - self.regex_expression = regex_expression - - self.message = ( - f"Class id '{self.class_id}' stored in 'Class' column in 'Classes' " - f"sheet violates regex {self.regex_expression}!" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - super().__init__(self.message) - - -class ClassIDMissing(NeatException): - """Class ID, which is stored in the column 'Class' in the 'Classes' sheet, is either - missing or did not satisfied regex expression - - Args: - verbose: flag that indicates whether to provide enhanced exception message, by default False - - Notes: - Make sure that class id is provided and respects regex expression - """ - - type_: str = "ClassIDMissing" - code: int = 6 - description: str = ( - "Class ID, which is stored in the column 'Class' in the 'Classes' sheet," - " is either missing or did not satisfied regex expression" - ) - example: str = "" - fix: str = "Make sure that class id is provided and respects regex expression" - - def __init__(self, verbose: bool = False): - self.message = ( - "Class id is missing, it failed validation either because it has" - " not been provided or because it did not respect regex expression!" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - super().__init__(self.message) - - -class PropertiesSheetClassIDRegexViolation(NeatException): - """Class ID, which is stored in the column 'Class' in the 'Properties' sheet, does - not respect defined regex expression - - Args: - verbose: flag that indicates whether to provide enhanced exception message, by default False - class_id: class id that raised exception - regex_expression: regex expression against which class id is checked - - Notes: - Check definition of class ids in `Class` column in `Properties` sheet and make - sure to respect the regex expression by removing any illegal characters - """ - - type_: str = "PropertiesSheetClassIDRegexViolation" - code: int = 7 - description: str = ( - "Class ID, which is stored in the column 'Class' in the 'Properties' sheet, " - "does not respect defined regex expression" - ) - example: str = ( - "If class id is set to 'Class 1', while regex expression does not allow spaces," - " the expression will be violated thus raising this error" - ) - fix: str = ( - "Check definition of class ids in 'Class' column in 'Properties' sheet " - "and make sure to respect the regex expression by removing any illegal characters" - ) - - def __init__(self, class_id: str, regex_expression: str, verbose: bool = False): - self.class_id = class_id - self.regex_expression = regex_expression - - self.message = ( - f"Class id '{self.class_id}' stored in 'Class' column in 'Properties' " - f"sheet violates regex {self.regex_expression}!" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - super().__init__(self.message) - - -class PropertyIDRegexViolation(NeatException): - """Property ID, which is stored in the column 'Property' in the 'Properties' sheet, does - not respect defined regex expression - - Args: - verbose: flag that indicates whether to provide enhanced exception message, by default False - property_id: property id that raised exception - regex_expression: regex expression against which property id is checked - - Notes: - Check definition of class ids in `Property` column in `Properties` sheet and make - sure to respect the regex expression by removing any illegal characters - """ - - type_: str = "PropertyIDRegexViolation" - code: int = 8 - description: str = ( - "Property ID, which is stored in the column 'Property' " - "in the 'Properties' sheet, does not respect defined regex expression" - ) - example: str = ( - "If property id is set to 'property 1', while regex expression does not allow spaces," - " the expression will be violated thus raising this error" - ) - fix: str = ( - "Check definition of property ids in 'Property' column in 'Properties' sheet" - " and make sure to respect the regex expression by removing any illegal characters" - ) - - def __init__(self, property_id: str, regex_expression: str, verbose: bool = False): - self.property_id = property_id - self.regex_expression = regex_expression - - self.message = ( - f"Property id '{self.property_id}' stored in 'Property' " - f"column in 'Properties' sheet violates regex {self.regex_expression}!" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - super().__init__(self.message) - - -class ValueTypeIDRegexViolation(NeatException): - """Value type, which is stored in the column 'Type' in the 'Properties' sheet, does - not respect defined regex expression - - Args: - verbose: flag that indicates whether to provide enhanced exception message, by default False - value_type: value type that raised exception - regex_expression: regex expression against which value type is checked - - Notes: - Check definition of class ids in `Type` column in `Properties` sheet and make - sure to respect the regex expression by removing any illegal characters - """ - - type_: str = "ValueTypeIDRegexViolation" - code: int = 9 - description: str = ( - "Value type, which is stored in the column 'Type' in the 'Properties' sheet, " - "does not respect defined regex expression" - ) - example: str = ( - "If value type is set to 'date time', while regex expression does not" - " allow spaces, the expression will be violated thus raising this error" - ) - fix: str = ( - "Check definition of value types in 'Type' column in 'Properties' sheet" - " and make sure to respect the regex expression by removing any illegal characters" - ) - - def __init__(self, value_type: str, regex_expression: str, verbose: bool = False): - self.value_type = value_type - self.regex_expression = regex_expression - - self.message = ( - f"Value type '{self.value_type}' stored in 'Type' column in " - f"'Properties' sheet violates regex {self.regex_expression}!" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - super().__init__(self.message) - - -class MissingTypeValue(NeatException): - """Value type, which is stored in the column 'Type' in the 'Properties' sheet, is missing - - Args: - verbose: flag that indicates whether to provide enhanced exception message, by default False - - Notes: - Make sure to define value type in `Type` column in `Properties` sheet - """ - - type_: str = "MissingTypeValue" - code: int = 10 - description: str = "Value type, which is stored in the column 'Type' in the 'Properties' sheet, is missing" - example: str = "If value type is not set, this error will be raised" - fix: str = "Make sure to define value type in 'Type' column in 'Properties' sheet" - - def __init__(self, verbose: bool = False): - self.message = ( - "Value type, which is stored in the column 'Type' in the 'Properties' sheet, is missing" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - super().__init__(self.message) - - -class PropertyIDMissing(NeatException): - """Property ID, which is stored in the column 'Property' in the 'Properties' sheet, - is either missing or did not satisfied regex expression - - Args: - verbose: flag that indicates whether to provide enhanced exception message, by default False - - Notes: - Make sure to define value type in `Type` column in `Properties` sheet - """ - - type_: str = "PropertyIDMissing" - code: int = 11 - description: str = ( - "Property ID, which is stored in the column 'Property' in the 'Properties' sheet," - " is either missing or did not satisfied regex expression" - ) - example: str = "" - fix: str = "Make sure that property id is provided and respects regex expression" - - def __init__(self, verbose: bool = False): - self.message = ( - "Property id is missing, validator for property id failed either" - " due to lack of property id or due to not respecting regex expression!" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - super().__init__(self.message) - - -class RuleTypeProvidedButRuleMissing(NeatException): - """This error occurs when transformation rule type is provided but actual - transformation rule is missing - - Args: - verbose: flag that indicates whether to provide enhanced exception message, by default False - property_id: property id which is missing transformation rule - class_id: class id for which property id is defined - rule_type: rule type that is provided for property id - - Notes: - If you provide rule type you must provide rule as well! Otherwise remove rule - type if no transformation rule is needed - """ - - type_: str = "RuleTypeProvidedButRuleMissing" - code: int = 12 - description: str = ( - "This error occurs when transformation rule type is provided but actual transformation rule is missing" - ) - example: str = "" - fix: str = ( - "If you provide rule type you must provide rule as well! " - "Otherwise remove rule type if no transformation rule is needed" - ) - - def __init__(self, property_id: str, class_id: str, rule_type: str, verbose: bool = False): - self.message = ( - f"Rule type '{rule_type}' provided for property '{property_id}' " - f"in class '{class_id}' but rule is not provided!" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - super().__init__(self.message) - - -class PropertyDefinedForUndefinedClass(NeatException): - """Property defined for a class that has not been defined in the 'Classes' sheet - - Args: - property_id: property id that is defined for undefined class - class_id: class id that is undefined - verbose: flag that indicates whether to provide enhanced exception message, by default False - - Notes: - Make sure to define all classes in the 'Classes' sheet before defining properties - for them in the `Properties` sheet - """ - - type_: str = "PropertyDefinedForUndefinedClass" - code: int = 13 - description: str = "Property defined for a class that has not been defined in the 'Classes' sheet" - example: str = ( - "If property 'someProperty' is defined for class 'Class 1' in the 'Properties' sheet, " - "while 'Class 1' has not been defined in the 'Classes' sheet," - " this error will be raised" - ) - fix: str = ( - "Make sure to define all classes in the 'Classes' sheet before defining properties for them" - " in the 'Properties' sheet" - ) - - def __init__(self, property_id: str, class_id: str, verbose: bool = False): - self.property_id = property_id - self.class_id = class_id - - self.message = ( - f"Class <{self.class_id}> to which property {self.property_id}> is being defined" - " is not define in the 'Classes' sheet!" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - super().__init__(self.message) - - -class MetadataSheetMissingOrFailedValidation(NeatException): - """Metadata sheet is missing or it failed validation for one or more fields - - Args: - verbose: flag that indicates whether to provide enhanced exception message, by default False - - Notes: - Make sure to define compliant Metadata sheet before proceeding - """ - - type_: str = "MetadataSheetMissingOrFailedValidation" - code: int = 14 - description: str = "Metadata sheet is missing or it failed validation for one or more fields" - example: str = "" - fix: str = "Make sure to define compliant Metadata sheet before proceeding" - - def __init__(self, verbose: bool = False): - self.message = ( - "Metadata sheet is missing or it failed validation for one or more fields!" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - super().__init__(self.message) - - -class FiledInMetadataSheetMissingOrFailedValidation(NeatException): - """One of the mandatory fields in Metadata sheet is missing or it failed validation - - Args: - verbose: flag that indicates whether to provide enhanced exception message, by default False - - Notes: - Make sure to define compliant field in Metadata sheet before proceeding - """ - - type_: str = "FiledInMetadataSheetMissingOrFailedValidation" - code: int = 15 - description: str = "One of the mandatory fields in Metadata sheet is missing or it failed validation" - example: str = "" - fix: str = "Make sure to define compliant field in Metadata sheet before proceeding" - - def __init__(self, missing_field: str, verbose: bool = False): - self.message = ( - f"Field {missing_field} is missing in the 'Metadata' sheet or it failed validation!" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - super().__init__(self.message) - - -class PrefixesRegexViolation(NeatException): - """Prefix(es), which are in the 'Prefixes' sheet, do(es) not respect defined regex expression - - Args: - prefixes: list of prefixes that violate regex expression - regex_expression: regex expression that is violated - verbose: flag that indicates whether to provide enhanced exception message, by default False - - Notes: - Check if prefixes in the `Prefixes` sheet contains any illegal characters and respects the regex expression - """ - - type_: str = "PrefixesRegexViolation" - code: int = 16 - description: str = "Prefix(es), which are in the 'Prefixes' sheet, do(es) not respect defined regex expression" - example: str = ( - "If prefix is set to 'power grid', while regex expression does not " - "allow spaces, the expression will be violated thus raising this error" - ) - fix: str = ( - "Check if prefixes in the 'Prefixes' sheet contains any illegal characters and respects the regex expression" - ) - - def __init__(self, prefixes: list[str], regex_expression: str, verbose: bool = False): - self.prefixes = prefixes - self.regex_expression = regex_expression - - self.message = ( - f"Invalid prefix(es) {', '.join(self.prefixes)} stored in the 'Prefixes' sheet, " - f"it/they must obey regex {self.regex_expression}!" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - super().__init__(self.message) - - -class PrefixesSheetNamespaceNotValidURL(NeatException): - """Namespace(es), which are/is in the 'Prefixes' sheet, are/is not valid URL(s) - - Args: - namespaces: list of namespaces that raised exception - verbose: flag that indicates whether to provide enhanced exception message, by default False - - Notes: - Check if `namespaces` in the `Prefixes` sheet are properly constructed as valid - URLs containing only allowed characters - """ - - type_: str = "PrefixesSheetNamespaceNotValidURL" - code: int = 17 - description: str = "Namespace(es), which are/is in the 'Prefixes' sheet, are/is not valid URLs" - example: str = "If we have 'authority:namespace' as namespace as it is not a valid URL this error will be raised" - fix: str = ( - "Check if 'namespaces' in the 'Prefixes' sheet are properly " - "constructed as valid URLs containing only allowed characters" - ) - - def __init__(self, namespaces: list[str], verbose: bool = False): - self.namespaces = namespaces - - self.message = ( - f"Invalid namespace(es) {', '.join(self.namespaces)} stored in the 'Prefixes' sheet, " - f"it/they must be valid URLs!" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - super().__init__(self.message) - - -class ValueTypeNotDefinedAsClass(NeatException): - """Expected value type, which is stored in the column 'Type' in the 'Properties' sheet, - is not defined in the 'Classes' sheet. This error occurs when property is defined as - an edge between two classes, of which one is not defined - - Args: - expected_value_type: expected value type that raised exception - property_id: property id that has expected value type that raised exception - class_id: class id for which property is defined - verbose: flag that indicates whether to provide enhanced exception message, by default False - - Notes: - Make sure to define all of the classes in the `Classes` sheet before defining - properties that expect them as value types - """ - - type_: str = "ValueTypeNotDefinedAsClass" - code: int = 18 - description: str = ( - "Expected value type, which is stored in the column 'Type' in the 'Properties'" - " sheet, is not defined in the 'Classes' sheet. " - "This error occurs when property is defined as an edge between two classes, of which one is not defined" - ) - example: str = ( - "We have 'Class1' which has property 'edgeClass1Class2' linking it to 'Class2', thus" - "expected value of 'edgeClass1Class2' is 'Class2'. However, 'Classes' sheet only contains" - " 'Class1', while 'Class2' is not defined. Under this given circumstance, this error will be raised!" - ) - - fix: str = ( - "Make sure to define all of the classes in the 'Classes' sheet before defining " - "properties that expect them as value types" - ) - - def __init__(self, class_id: str, property_id: str, expected_value_type: str, verbose: bool = False): - self.message = ( - f"Property {property_id} defined for class {class_id} has" - f" value type {expected_value_type} which is not defined as a class in the 'Classes' sheet!" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - super().__init__(self.message) - - -class UndefinedObjectsAsExpectedValueTypes(NeatException): - """Expected value types, which are stored in the column 'Type' in the 'Properties' - sheet, are classes that exist in the 'Classes' sheet but for which no properties are defined - in the 'Properties' sheet. - - Args: - undefined_objects: list of undefined objects that raised exception - verbose: flag that indicates whether to provide enhanced exception message, by default False - - Notes: - Make sure to define properties for classes from 'Classes' sheet before defining - properties that expect them as value types - """ - - type_: str = "UndefinedObjectsAsExpectedValueTypes" - code: int = 19 - description: str = ( - "Expected value types, which are stored in the column 'Type' in the 'Properties'" - " sheet, are classes that exist in the 'Classes' sheet but for which no properties are defined " - "in the 'Properties' sheet. " - ) - example: str = ( - "We have 'Class1' which has property 'edgeClass1Class2' linking it to 'Class2', thus" - "expected value of 'edgeClass1Class2' is 'Class2'. " - "Both 'Class1' and 'Class2' are defined in the 'Classes' sheet" - "However, only 'Class1' has properties defined in the 'Properties' sheet, making 'Class2' an undefined object" - " leading to this error being raised!" - ) - - fix: str = ( - "Make sure to define properties for classes from 'Classes' " - "sheet before defining properties that expect them as value types" - ) - - def __init__(self, undefined_objects: list[str], verbose: bool = False): - self.message = ( - f"Following classes {', '.join(undefined_objects)} defined as classes in the 'Classes' sheet" - f" have no properties defined in the 'Properties' sheet or their validation as objects failed!" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - super().__init__(self.message) - - -class ClassSheetParentClassIDRegexViolation(NeatException): - """Parent ID, which is stored in the column 'Parent Class' in the 'Classes' sheet, - does not respect defined regex expression - - Args: - parent_ids: parent_ids that raised exception - regex_expression: regex expression against which parent_id is validated - verbose: flag that indicates whether to provide enhanced exception message, by default False - - Notes: - Check definition of parent ids in `Parent Class` column in `Classes` sheet and - make sure to respect the regex expression by removing any illegal characters - - """ - - type_: str = "ClassSheetParentClassIDRegexViolation" - code: int = 20 - description: str = ( - "Parent ID, which is stored in the column 'Parent Class' in the 'Classes' sheet, " - "does not respect defined regex expression" - ) - example: str = ( - "If parent class is set to 'Class 1', while regex expression does not allow spaces," - " the expression will be violated thus raising this error" - ) - fix: str = ( - "Check definition of class ids in 'Parent Class' column in 'Classes' sheet and " - "make sure to respect the regex expression by removing any illegal characters" - ) - - def __init__(self, parent_ids: list[str], regex_expression: str, verbose: bool = False): - self.parent_ids = parent_ids - self.regex_expression = regex_expression - - self.message = ( - f"Parents ids: [{', '.join(parent_ids or [])}], stored in 'Parent Class' column in 'Classes' " - f"sheet violates regex {self.regex_expression}!" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - - -class MoreThanOneNonAlphanumericCharacter(NeatException): - """This exceptions is raised when doing regex validation of strings which either - represent class ids, property ids, prefix, data model name, that contain more than - one non-alphanumeric character, such as for example '_' or '-'. - - Args: - field_name: filed on which regex validation failed - value: value that failed regex validation - verbose: flag that indicates whether to provide enhanced exception message, by default False - - Notes: - Make sure not to use more than non-alphanumeric character in the row - - """ - - type_: str = "MoreThanOneNonAlphanumericCharacter" - code: int = 21 - description: str = ( - "This exceptions is raised when doing regex validation of strings which either" - "represent class ids, property ids, prefix, data model name, that contain more than" - "one non-alphanumeric character, such as for example '_' or '-'." - ) - example: str = "" - fix: str = "" - - def __init__(self, field_name: str, value: str, verbose: bool = False): - self.field_name = field_name - self.value = value - - self.message = ( - f"Field {field_name} with value {value} contains more than one non-alphanumeric character!" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - super().__init__(self.message) - - -class ViewExternalIdNotDefined(NeatException): - """This exceptions is raised when external id of View is not defined. - - Args: - field_name: filed on which regex validation failed - value: value that failed regex validation - verbose: flag that indicates whether to provide enhanced exception message, by default False - - """ - - type_: str = "ViewExternalIdNotDefined" - code: int = 22 - description: str = "This exceptions is raised when external id of View is not defined" - example: str = "" - fix: str = "" - - def __init__(self, verbose: bool = False): - self.message = ( - f"Missing View external id!" f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - super().__init__(self.message) - - -class SpaceNotDefined(NeatException): - """This exceptions is raised when CDF space name is missing. - - Args: - verbose: flag that indicates whether to provide enhanced exception message, by default False - - """ - - type_: str = "SpaceNotDefined" - code: int = 23 - description: str = "This exceptions is raised when CDF space name is missing" - example: str = "" - fix: str = "" - - def __init__(self, verbose: bool = False): - self.message = ( - f"Missing CDF space name!" f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - super().__init__(self.message) - - -class ViewVersionNotDefined(NeatException): - """This exceptions is raised when View version is not provided. - - Args: - verbose: flag that indicates whether to provide enhanced exception message, by default False - - """ - - type_: str = "ViewVersionNotDefined" - code: int = 24 - description: str = "This exceptions is raised when View version is not provided" - example: str = "" - fix: str = "" - - def __init__(self, verbose: bool = False): - self.message = ( - f"Missing View version!" f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - super().__init__(self.message) - - -class DefaultValueTypeNotProper(NeatException): - """This exceptions is raised when default value type is not proper, i.e. it is not - according to the expected value type set in Rules. - - - Args: - default_value_type: default value type that raised exception - expected_value_type: expected value type that raised exception - verbose: flag that indicates whether to provide enhanced exception message, by default False - - """ - - type_: str = "DefaultValueTypeNotProper" - code: int = 25 - description: str = ( - "This exceptions is raised when default value type is not proper, i.e. it is not " - "according to the expected value type set in Rules." - ) - example: str = "" - fix: str = "" - - def __init__(self, property_id: str, default_value_type: str, expected_value_type: str, verbose: bool = False): - self.default_value_type = default_value_type - self.expected_value_type = expected_value_type - - self.message = ( - f"Default value for property {property_id} is of type {default_value_type} which is different from " - f"the expected value type {expected_value_type}!" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - super().__init__(self.message) - - -class ClassToAssetMappingNotDefined(NeatException): - """This exceptions is raised when deriving class to asset mapping when there is no - mapping available. - - - Args: - class_id: Id of the class that raised exception - verbose: flag that indicates whether to provide enhanced exception message, by default False - - """ - - type_: str = "ClassToAssetMappingNotDefined" - code: int = 26 - description: str = ( - "This exceptions is raised when deriving class to asset mapping when there is no mapping available" - ) - example: str = "" - fix: str = "" - - def __init__(self, class_id: str, verbose: bool = False): - self.class_id = class_id - - self.message = ( - f"Requested serialization from pydantic model instance of class {class_id} is" - " not possible since there is no class to asset mapping available!" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - super().__init__(self.message) - - -class PrefixAlreadyInUse(NeatException): - """This exceptions is raised when trying to update base prefix/space of Rules object - - - Args: - class_id: Id of the class that raised exception - verbose: flag that indicates whether to provide enhanced exception message, by default False - - """ - - type_: str = "PrefixAlreadyInUse" - code: int = 27 - description: str = "This exceptions is raised when trying to update base prefix/space of Rules object" - example: str = "" - fix: str = "" - - def __init__(self, prefix: str, verbose: bool = False): - self.prefix = prefix - - self.message = ( - f"Prefix {prefix} exist in self.prefixes, please use another prefix!" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - super().__init__(self.message) - - -class IncompleteSchema(NeatException): - """This exceptions is raised when schema is not complete, meaning defined properties - are pointing to non-existing classes or value types - - - Args: - missing_classes: list of classes ids that are not defined in the sheet - verbose: flag that indicates whether to provide enhanced exception message, by default False - - """ - - type_: str = "IncompleteSchema" - code: int = 28 - description: str = ( - "This exceptions is raised when schema is not complete, meaning " - "defined properties are pointing to non-existing classes or value types" - ) - example: str = "" - fix: str = "" - - def __init__(self, missing_classes: set, verbose: bool = False): - self.message = ( - f"Classes {missing_classes} are not defined in the Class sheet!" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - super().__init__(self.message) - - -# Warnings: - - -class ClassNameNotProvided(NeatWarning): - """This warning is raised when class name is not provided in the 'Classes' sheet - under 'name' column, which will be then set the class id stored in the 'Class' column - - Args: - verbose: flag that indicates whether to provide enhanced exception message, by default False - class_id: class id that raised warning, and which is used as class name - - Notes: - If you want to have differentiation between the class name and class id, then - provide class name in the `name` column - """ - - type_: str = "ClassNameNotProvided" - code: int = 1 - description: str = ( - "This warning is raised when class name is not provided in the 'Classes' sheet" - "under 'name' column, which will be then set the class id stored in the 'Class' column" - ) - example: str = "" - fix: str = ( - "If you want to have differentiation between the class name" - " and class id, then provide class name in the `name` column" - ) - - # need to have default value set to arguments - # otherwise it will raise TypeError - def __init__(self, class_id: str = "", verbose: bool = False): - self.message = ( - f"Class id {class_id} set as Class name!" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - - -class EntityIDNotDMSCompliant(NeatWarning): - """Warning raise when entity id being class, property or value type is not DMS compliant - - Args: - entity_type: type of entity that raised warning - entity_id: id of entity that raised warning - loc: location of entity in the Transformation Rules sheet that raised warning - verbose: flag that indicates whether to provide enhanced exception message, by default False - - Notes: - DMS ready means that entity id must only use following characters [a-zA-Z0-9_], - where it can only start with letter! Also there are reserved words that cannot be used. - - Reserved words for views: `Query`, `Mutation`, `Subscription`, `String`, `Int32`, `Int64`, `Int`, - `Float32`, `Float64`, `Float`, `Timestamp`, `JSONObject`, `Date`, `Numeric`, `Boolean`, `PageInfo`, - `File`, `Sequence`, `TimeSeries` - - Reserved words for properties: `space`, `externalId`, `createdTime`, `lastUpdatedTime`, - `deletedTime`, `edge_id`, `node_id`, `project_id`, `property_group`, `seq`, `tg_table_name`, `extensions` - - Reserved words for spaces: `space`, `cdf`, `dms`, `pg3`, `shared`, `system`, `node`, `edge` - """ - - type_: str = "EntityIDNotDMSCompliant" - code: int = 2 - description: str = "Warning raise when entity id being class, property or value type is not DMS compliant" - example: str = "" - fix: str = ( - "DMS ready means that entity id must only use following" - " characters [a-zA-Z0-9_], where it can only start with letter!" - ) - - # See ClassNameNotProvided for explanation why default values are set - def __init__(self, entity_type: str = "", entity_id: str = "", loc: str = "", verbose: bool = False): - self.message = ( - f"'{entity_id}' {entity_type.lower()}" - " use character(s) outside of range of allowed characters [a-zA-Z0-9_] or " - f"it starts with non-letter character or it is reserved word! {loc}" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - - -class PropertyRedefined(NeatWarning): - """Warning raise when same property is defined multiple times for same class, this - typically occurs if there are multiple ways to extract certain information from the - NeatGraph. - - Args: - property_id: property id that is redefined - class_id: class id for which property is redefined - loc: location of property redefinition in the 'Properties' sheet of Transformation Rules Excel file - verbose: flag that indicates whether to provide enhanced exception message, by default False - - Notes: - If possible, have only single definition of a particular property for a class, otherwise - transformation rules will not be DMS compliant. - """ - - type_: str = "PropertyRedefined" - code: int = 3 - description: str = "Warning raise when same property is defined multiple times for same class" - example: str = "" - fix: str = "Have only single definition of a particular property for a class" - - # See Warning302 for explanation why default values are set - def __init__(self, property_id: str = "", class_id: str = "", loc: str = "", verbose: bool = False): - self.message = ( - f"Not DMS compliant! Property '{property_id}' for class '{class_id}' redefined! {loc}" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - # hint on a specific web docs page - - -class PropertyNameNotProvided(NeatWarning): - """If property name is not provided in the 'Property' sheet under 'name' column, it - will be set to corresponding value from 'Property' column, thus property id - - Args: - property_id: property id that is set as property name - verbose: flag that indicates whether to provide enhanced exception message, by default False - Notes: - If you want to have different property name then property id, provide it in the 'name' column - """ - - type_: str = "PropertyNameNotProvided" - code: int = 4 - description: str = ( - "If property name is not provided in the 'Property' sheet under 'name' column," - " it will be set to corresponding value from 'Property' column, thus property id" - ) - example: str = "" - fix: str = "If you want to have different property name then property id, provide it in the 'name' column" - - def __init__(self, property_id: str = "", verbose: bool = False): - self.message = ( - f"Property id {property_id} set as Property name!" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - - -class MissingLabel(NeatWarning): - """If property maps to CDF relationship, and it does not have label explicitly stated - under 'Label' column in the 'Property' sheet under 'name' column, it will be set - to corresponding value from 'Property' column, thus property id - - Args: - property_id: property id that which is missing label - verbose: flag that indicates whether to provide enhanced exception message, by default False - - - Notes: - If you want to have control over relationship labels make sure to define one - in the `Label` column in the `Properties` sheet - """ - - type_: str = "MissingLabel" - code: int = 5 - description: str = ( - "If property maps to CDF relationship, and it does not have label explicitly stated under 'Label' column" - " in the 'Property' sheet under 'name' column," - " it will be set to corresponding value from 'Property' column, thus property id" - ) - example: str = "" - fix: str = ( - "If you want to have control over relationship labels make sure to define one" - " in the 'Label' column in the 'Properties' sheet." - ) - - def __init__(self, property_id: str = "", verbose: bool = False): - self.message = ( - f"Property id {property_id} set as CDF relationship label!" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - - -class NoTransformationRules(NeatWarning): - """This warning is raised if there are no transformation rules defined for given - property and class - - Args: - property_id: property id that which is missing transformation rules - class_id: class id for which property is missing transformation rules - verbose: flag that indicates whether to provide enhanced exception message, by default False - - Notes: - One can omit this warning if the Transformation Rules spreadsheet is used solely - for defining the data model and not for performing knowledge graph transformation. - """ - - type_: str = "NoTransformationRules" - code: int = 6 - description: str = ( - "This warning is raised if there are no transformation rules " - "defined in the 'Transformation' sheet for given property" - ) - example: str = "" - fix: str = "No fix is provided for this warning" - - def __init__(self, property_id: str = "", class_id: str = "", verbose: bool = False): - self.message = ( - f"There is no transformation rule configured for class '{class_id}' property '{property_id}'!" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - - -class NamespaceEndingFixed(NeatWarning): - """This warning occurs when namespace does not end with '/' or '#' - - Args: - namespace: namespace that raised warning due to improper ending - verbose: flag that indicates whether to provide enhanced exception message, by default False - - Notes: - Make sure that namespace ends with `/` or `#`, if not it will be fixed by adding - `#` at the end ! - """ - - type_: str = "NamespaceEndingFixed" - code: int = 7 - description: str = "This warning occurs when namespace does not end with '/' or '#'" - example: str = "If namespace is set to http://purl.org/cognite, it will be converted to http://purl.org/cognite#" - fix: str = "Make sure that namespace ends with '/' or '#'" - - def __init__(self, namespace: Namespace, verbose: bool = False): - self.message = ( - f"Namespace {namespace} ending fixed by adding '#' at its end!" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - # hint on a specific web docs page - - -class DataModelIdMissing(NeatWarning): - """This warning occurs when data model name is not provided in 'Metadata' sheet - - Args: - prefix: prefix to which data model name will be set if not provided - verbose: flag that indicates whether to provide enhanced exception message, by default False - - Notes: - Make sure that namespace ends with `/` or `#`, if not it will be fixed by adding - `#` at the end ! - """ - - type_: str = "DataModelIdMissing" - code: int = 8 - description: str = "This warning occurs when data model id is not provided in 'Metadata' sheet" - example: str = "" - fix: str = ( - "Provide data model id by setting value for `external_id`," - " to avoid this warning and otherwise it will default to prefix" - ) - - def __init__(self, prefix: str, verbose: bool = False): - self.message = ( - f"Data model id not provided, defaulting to prefix {prefix}!" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - - -class VersionDotsConvertedToUnderscores(NeatWarning): - """This warning occurs when converting version from dot notation to use of underscores - in order to achieve version format that is accepted by CDF/DMS - - Args: - prefix: prefix to which data model name will be set if not provided - verbose: flag that indicates whether to provide enhanced exception message, by default False - - Notes: - Typically version is expressed in classical form with dots major.minor.patch, while - CDF accepts underscores major_minor_patch, thus this warning occurs when converting - version from dot notation to use of underscores in order to achieve version format - that is accepted by CDF/DMS - """ - - type_: str = "VersionDotsConvertedToUnderscores" - code: int = 9 - description: str = ( - "This warning occurs when converting version from dot notation to use of underscores" - " in order to achieve version format that is accepted by CDF/DMS" - ) - example: str = "If version is provided as 1.2.3, this will be converted to 1_2_3 to be accepted by CDF" - fix: str = "Convert version to underscore notation major_minor_patch" - - def __init__(self, verbose: bool = False): - self.message = ( - "Data model version expressed with '.' which is not acceptable for CDF." - " All '.' are converted to '_'!" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - # hint on a specific web docs page - - -class DefaultValueTypeConverted(NeatWarning): - """This exceptions is warning is raised when default value type is being converted to - the expected value type set in Rules. - - - Args: - default_value_type: default value type that raised exception - expected_value_type: expected value type that raised exception - verbose: flag that indicates whether to provide enhanced exception message, by default False - - Notes: - Make sure not to use more than non-alphanumeric character in the row - - """ - - type_: str = "DefaultValueTypeConverted" - code: int = 10 - description: str = ( - "This exceptions is warning is raised when default value type is being converted to " - "the expected value type set in Rules." - ) - example: str = "" - fix: str = "" - - def __init__( - self, property_id: str = "", default_value_type: str = "", expected_value_type: str = "", verbose: bool = False - ): - self.default_value_type = default_value_type - self.expected_value_type = expected_value_type - - self.message = ( - f"Default value for property {property_id} is of type {default_value_type} " - f"has been converted to the expected value type {expected_value_type}!" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - super().__init__(self.message) - - -class DefaultValueNotList(NeatWarning): - """This exceptions is warning is raised when default value is not a list while it is - expected to be due to set maximum cardinality being different than 1. - - - Args: - verbose: flag that indicates whether to provide enhanced exception message, by default False - - Notes: - Make sure not to use more than non-alphanumeric character in the row - - """ - - type_: str = "DefaultValueNotList" - code: int = 11 - description: str = ( - "This exceptions is warning is raised when default value is not a list while it is " - "expected to be due to set maximum cardinality being different than 1" - ) - example: str = "" - fix: str = "" - - def __init__(self, property_id: str, verbose: bool = False): - self.message = ( - f"Default value for property {property_id} is not a list, " - "while it is expected to be due to set maximum cardinality being different than 1!" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - super().__init__(self.message) - - -################################################################################################ -# RULES PROCESSING: 100 - 199 ################################################################## -################################################################################################ - - -class NotValidRDFPath(NeatException): - """Provided `rdfpath` is not valid, i.e. it cannot be converted to SPARQL query. - - Args: - rdf_path: `rdfpath` that raised exception - verbose: flag that indicates whether to provide enhanced exception message, by default False - - Notes: - Get familiar with `rdfpath` to avoid this exception. - """ - - type_: str = "NotValidRDFPath" - code: int = 100 - description: str = "Provided `rdfpath` is not valid, i.e. it cannot be converted to SPARQL query" - example: str = "" - fix: str = "Get familiar with `rdfpath` and check if provided path is valid!" - - def __init__(self, rdf_path: str, verbose: bool = False): - self.rdf_path = rdf_path - - self.message = ( - f"{self.rdf_path} is not a valid rdfpath!" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - super().__init__(self.message) - - -class NotValidTableLookUp(NeatException): - """Provided `table lookup` is not valid, i.e. it cannot be converted to CDF lookup. - - Args: - table_look_up: `table_look_up`, a part of `rawlookup`, that raised exception - verbose: flag that indicates whether to provide enhanced exception message, by default False - - Notes: - Get familiar with `rawlookup` and `rdfpath` to avoid this exception. - """ - - type_: str = "NotValidTableLookUp" - code: int = 101 - description: str = "Provided table lookup is not valid, i.e. it cannot be converted to CDF lookup" - example: str = "" - fix: str = "Get familiar with RAW look up and RDF paths and check if provided rawlookup is valid" - - def __init__(self, table_look_up: str, verbose: bool = False): - self.table_look_up = table_look_up - - self.message = ( - f"{self.table_look_up} is not a valid table lookup" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - super().__init__(self.message) - - -class NotValidRAWLookUp(NeatException): - """Provided `rawlookup` is not valid, i.e. it cannot be converted to SPARQL query and CDF lookup - - Args: - raw_look_up: `rawlookup` rule that raised exception - verbose: flag that indicates whether to provide enhanced exception message, by default False - - Notes: - Get familiar with `rawlookup` and `rdfpath` to avoid this exception. - """ - - type_: str = "NotValidRAWLookUp" - code: int = 102 - description: str = "Provided rawlookup is not valid, i.e. it cannot be converted to SPARQL query and CDF lookup" - example: str = "" - fix: str = "Get familiar with `rawlookup` and `rdfpath` to avoid this exception" - - def __init__(self, raw_look_up: str, verbose: bool = False): - self.raw_look_up = raw_look_up - - self.message = ( - f"Invalid rawlookup expected traversal | table lookup, got {raw_look_up}" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - super().__init__(self.message) - - -################################################################################################ -# RULES IMPORTERS: 200 - 299 ################################################################### -################################################################################################ -class RulesHasErrors(NeatWarning): - """This warning occurs when generated transformation rules are invalid/incomplete. - - Args: - importer_type: type of importer that is used to generate transformation rules - verbose: flag that indicates whether to provide enhanced exception message, by default False - - Notes: - Generated transformation rules are not guaranteed to be valid and complete. - Go through the generated report file and fix the errors and warnings. - """ - - type_: str = "GeneratedTransformationRulesHasErrors" - code: int = 200 - description: str = ( - "This warning occurs when transformation rules generated using an importer are invalid/incomplete." - ) - example: str = "" - fix: str = "Go through the generated report file and fix the warnings in generated Transformation Rules." - - def __init__(self, importer_type: str = "OWL ontology", verbose: bool = False): - self.message = ( - f"Rules generated using {importer_type} are invalid!" - " Consult generated validation report for details on the errors and fix them" - " before using the rules file." - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - - -class RulesHasWarnings(NeatWarning): - """This warning occurs when th generated transformation rules are invalid/incomplete. - - Args: - importer_type: type of importer that is used to generate transformation rules - verbose: flag that indicates whether to provide enhanced exception message, by default False - - Notes: - OWL ontology is not guaranteed to contain all information needed to generate - transformation rules. In such cases, transformation rules generated from OWL ontology - will be incomplete and invalid. Go through the generated report file and fix the warnings - """ - - type_: str = "GeneratedTransformationRulesHasWarnings" - code: int = 201 - description: str = ( - "This warning occurs when transformation rules generated using an importer are invalid/incomplete." - ) - example: str = "" - fix: str = "Go through the generated report file and fix the warnings in generated Transformation Rules." - - def __init__(self, importer_type: str = "OWL ontology", verbose: bool = False): - self.message = ( - f"Rules generated using {importer_type} raised warnings!" - " Consult generated validation report for details on the warnings and optionally fix them" - " before using the rules file." - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - - -class GraphClassNameCollision(NeatWarning): - """This warning occurs when graph contains instances of classes with same name, but - belonging to different namespaces. - - Args: - verbose: flag that indicates whether to provide enhanced exception message, by default False - - Notes: - Since the RDF graph is fully flexible and based on URIs, it is possible to have - instances of classes with same name, but belonging to different namespaces. This - warning is raised when such collision occurs. - """ - - type_: str = "GraphClassNameCollision" - code: int = 202 - description: str = ( - "This warning occurs when graph contains instances of classes with same name, but" - " belonging to different namespaces." - ) - example: str = "" - fix: str = "Be caution when reviewing the generated transformation rules." - - def __init__(self, class_name: str, verbose: bool = False): - self.message = f"Class name collision detected in the graph for class name {class_name}!" - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - - -class GraphClassPropertyMultiValueTypes(NeatWarning): - """This warning occurs when a same property is define for two object/classes where - its expected value type is different in one definition, e.g. acts as an edge, while in - other definition acts as and attribute - - Args: - class_id: class id that raised warning due to multi type definition - property_id: property id that raised warning due to multi type definition - types: list of types of property - verbose: flag that indicates whether to provide enhanced exception message, by default False - - Notes: - If a property takes different value types for different objects, simply define - new property. It is bad practice to have multi type property! - """ - - type_: str = "GraphClassPropertyMultiValueTypes" - code: int = 203 - description: str = ( - "This warning occurs when a same property is define for two object/classes where" - " its expected value type is different in one definition, e.g. acts as an edge, while in " - "other definition acts as and attribute" - ) - example: str = "" - fix: str = "If a property takes different value types for different objects, simply define new property" - - def __init__( - self, class_name: str = "", property_name: str = "", types: list[str] | None = None, verbose: bool = False - ): - self.message = ( - "It is bad practice to have multi type property! " - f"Currently property '{property_name}' for class {class_name} has" - f" multi type property: {', '.join(types or [])}" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - - -class GraphClassPropertyMultiOccurrence(NeatWarning): - """This warning occurs when there is multiple different occurrences of the same property - across various class instances. - - Args: - class_id: class id that raised warning due to multi type definition - property_id: property id that raised warning due to multi type definition - occurrences: list of property occurrences - verbose: flag that indicates whether to provide enhanced exception message, by default False - - Notes: - If a property takes different value types for different objects, simply define - new property. It is bad practice to have multi type property! - """ - - type_: str = "GraphClassPropertyMultiOccurrence" - code: int = 204 - description: str = ( - "This warning occurs when there is multiple different occurrences of the same property " - " across various class instances" - ) - example: str = "" - fix: str = "There" - - def __init__(self, class_name: str = "", property_name: str = "", verbose: bool = False): - self.message = ( - f"Currently property '{property_name}' for class {class_name} has multi occurrences" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - - -################################################################################################ -# RULES PARSERS: 300 - 399 ##################################################################### -################################################################################################ -class SourceObjectDoesNotProduceMandatorySheets(NeatException): - """Given object (e.g., Excel file) does not produce one or more mandatory sheets - - Args: - missing_mandatory_sheets: set of missing mandatory sheets - verbose: flag that indicates whether to provide enhanced exception message, by default False - - Notes: - This exception is raised when source object converted to is missing one or more mandatory sheets. - The mandatory sheets are: `Metadata`, `Classes`, `Properties`. - """ - - type_: str = "SourceObjectDoesProduceMandatorySheets" - code: int = 300 - description: str = "Given Excel file is missing one or more mandatory sheets" - example: str = "An Excel file is missing sheet named 'Metadata'" - fix: str = "Make sure that Excel file contains all mandatory sheets, i.e. 'Metadata', 'Classes', 'Properties'" - - def __init__(self, missing_mandatory_sheets: set[str], verbose: bool = False): - self.missing_fields = missing_mandatory_sheets - - self.message = ( - "Given Excel file is not compliant Transformation Rules file." - f" It is missing mandatory sheets: {', '.join(missing_mandatory_sheets)}." - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - super().__init__(self.message) - - -class UnableToDownloadExcelFile(NeatException): - """This error is raised during loading of byte representation of a Excel file from - Github, when given file cannot be downloaded. - - Args: - filepath: file path to Excel file that cannot be downloaded from Github - loc: URL of Excel file from Github repository - reason: reason why file cannot be downloaded (e.g., Forbidden, Not Found, etc.) - verbose: flag that indicates whether to provide enhanced exception message, by default False - - Notes: - Make sure you provided correct parameters to download Excel file from github repository. - """ - - type_: str = "UnableToDownloadExcelFile" - code: int = 301 - description: str = ( - "This error is raised during loading of byte representation of" - " a Excel file from Github when given file cannot be downloaded." - ) - example: str = "" - fix: str = "Make sure you provided correct parameters to download Excel file from github repository" - - def __init__(self, filepath: str, loc: str, reason: str, verbose: bool = False): - self.message = ( - f"File '{filepath}' from '{loc}' cannot be downloaded! Reason: {reason}" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - super().__init__(self.message) - - -class NotExcelFile(NeatException): - """This error is raised during loading of byte representation of a file from Github - into `openpyxl` `Workbook` object in case when byte representation is not Excel file. - - Args: - filepath: file path to Excel file that cannot be downloaded from Github - loc: URL of Excel file from Github repository - verbose: flag that indicates whether to provide enhanced exception message, by default False - - Notes: - Make sure you that byte representation of a file is Excel file. - """ - - type_: str = "NotExcelFile" - code: int = 302 - description: str = ( - "This error is raised during loading of byte representation of a file from Github" - " into `openpyxl` `Workbook` object in case when byte representation is not Excel file." - ) - example: str = "" - fix: str = "Make sure you that byte representation of a file is Excel file!" - - def __init__(self, filepath: str, loc: str, verbose: bool = False): - self.message = ( - f"File '{filepath}' from '{loc}' is not a valid excel file!" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - super().__init__(self.message) - - -class MetadataSheetMissingMandatoryFields(NeatException): - """Metadata sheet, which is part of Transformation Rules Excel file, is missing - mandatory rows (i.e., fields) - - Args: - missing_fields: Fields/rows that are missing in Metadata sheet - verbose: flag that indicates whether to provide enhanced exception message, by default False - """ - - type_: str = "MetadataSheetMissingMandatoryFields" - code: int = 303 - description: str = "Metadata sheet, which is part of Transformation Rules Excel file, is missing mandatory rows" - example: str = "" - fix: str = "" - - def __init__(self, missing_fields: set[str], verbose: bool = False): - self.missing_fields = missing_fields - - self.message = ( - f"Metadata sheet is missing following mandatory fields: {', '.join(missing_fields)}" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - super().__init__(self.message) - - -class ClassesSheetMissingMandatoryColumns(NeatException): - """Classes sheet, which is a mandatory part of Transformation Rules Excel file, is - missing mandatory columns at row 2 - - Args: - missing_fields: Fields/columns that are missing in Classes sheet - verbose: flag that indicates whether to provide enhanced exception message, by default False - """ - - type_: str = "ClassesSheetMissingMandatoryColumns" - code: int = 304 - description: str = ( - "Classes sheet, which is a mandatory part of Transformation Rules Excel file, " - "is missing mandatory columns at row 2" - ) - example: str = "" - fix: str = "" - - def __init__(self, missing_fields: set[str], verbose: bool = False): - self.missing_fields = missing_fields - - self.message = ( - f"Classes sheet is missing following mandatory columns: {', '.join(missing_fields)} at row 2" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - super().__init__(self.message) - - -class PropertiesSheetMissingMandatoryColumns(NeatException): - """Properties sheet, which is a mandatory part of Transformation Rules Excel file, is - missing mandatory columns at row 2 - - Args: - missing_fields: Fields/columns that are missing in Properties sheet - verbose: flag that indicates whether to provide enhanced exception message, by default False - """ - - type_: str = "PropertiesSheetMissingMandatoryColumns" - code: int = 305 - description: str = ( - "Properties sheet, which is a mandatory part of Transformation Rules Excel file, " - "is missing mandatory columns at row 2" - ) - example: str = "" - fix: str = "" - - def __init__(self, missing_fields: set[str], verbose: bool = False): - self.missing_fields = missing_fields - - self.message = ( - f"Properties sheet is missing following mandatory columns: {', '.join(missing_fields)} at row 2" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - super().__init__(self.message) - - -class PrefixesSheetMissingMandatoryColumns(NeatException): - """Prefixes sheet, which is part of Transformation Rules Excel file, is missing - mandatory columns at row 1 - - Args: - missing_fields: Fields/columns that are missing in Prefixes sheet - verbose: flag that indicates whether to provide enhanced exception message, by default False - """ - - type_: str = "PrefixesSheetMissingMandatoryColumns" - code: int = 306 - description: str = ( - "Prefixes sheet, which is part of Transformation Rules Excel file, is missing mandatory columns at row 1" - ) - example: str = "" - fix: str = "" - - def __init__(self, missing_fields: set[str], verbose: bool = False): - self.missing_fields = missing_fields - - self.message = ( - f"Prefixes sheet is missing following mandatory columns: {', '.join(missing_fields)} at row 1" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - super().__init__(self.message) - - -class InstancesSheetMissingMandatoryColumns(NeatException): - """Instances sheet, which is part of Transformation Rules Excel file, is missing - mandatory columns at row 1 - - Args: - missing_fields: Fields/columns that are missing in Instances sheet - verbose: flag that indicates whether to provide enhanced exception message, by default False - """ - - type_: str = "InstancesSheetMissingMandatoryColumns" - code: int = 307 - description: str = ( - "Instances sheet, which is part of Transformation Rules Excel file, is missing mandatory columns at row 1" - ) - example: str = "" - fix: str = "" - - def __init__(self, missing_fields: set[str], verbose: bool = False): - self.missing_fields = missing_fields - - self.message = ( - f"Instances sheet is missing following mandatory columns: {', '.join(missing_fields)} at row 1" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - super().__init__(self.message) - - -# Warnings -class MissingDataModelPrefixOrNamespace(NeatWarning): - """Prefix and/or namespace are missing in the 'Metadata' sheet - - Args: - verbose: flag that indicates whether to provide enhanced exception message, by default False - - Notes: - Add missing prefix and/or namespace in the 'Metadata' sheet - """ - - type_: str = "MissingDataModelPrefixOrNamespace" - code: int = 300 - description: str = "Either prefix or namespace or both are missing in the 'Metadata' sheet" - example: str = "" - fix: str = "Add missing prefix and/or namespace in the 'Metadata' sheet" - - def __init__(self, verbose: bool = False): - self.message = ( - "Instances sheet is present but prefix and/or namespace are missing in 'Metadata' sheet." - "Instances sheet will not be processed!" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - # hint on a specific web docs page - - -################################################################################################ -# RULES EXPORTERS 400-499####################################################################### -################################################################################################ - - -class EntitiesContainNonDMSCompliantCharacters(NeatException): - """This error is raised during export of Transformation Rules to DMS schema when - entities (e.g., types and fields) ids contain non DMS compliant characters. - - Args: - report: report of entities that contain non DMS compliant characters - verbose: flag that indicates whether to provide enhanced exception message, by default False - - Notes: - Make sure to check validation report of Transformation Rules and fix DMS related exceptions. - """ - - type_: str = "EntitiesContainNonDMSCompliantCharacters" - code: int = 400 - description: str = ( - "This error is raised during export of Transformation Rules to" - " DMS schema when entities contain non DMS compliant characters." - ) - example: str = "" - fix: str = "Make sure to check validation report of Transformation Rules and fix DMS related exceptions." - - def __init__(self, report: str = "", verbose: bool = False): - self.message = ( - f"Following entities contain non DMS compliant characters: {report}" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - super().__init__(self.message) - - -class PropertiesDefinedMultipleTimes(NeatException): - """This error is raised during export of Transformation Rules to DMS schema when - when properties are defined multiple times for the same class. - - Args: - report: report on properties which are defined multiple times - verbose: flag that indicates whether to provide enhanced exception message, by default False - - Notes: - Make sure to check validation report of Transformation Rules and fix DMS related warnings. - """ - - type_: str = "PropertiesDefinedMultipleTimes" - code: int = 401 - description: str = ( - "This error is raised during export of Transformation Rules to " - "DMS schema when properties are defined multiple times for the same class." - ) - example: str = "" - fix: str = "Make sure to check validation report of Transformation Rules and fix DMS related warnings." - - def __init__(self, report: str = "", verbose: bool = False): - self.message = ( - f"Following properties defined multiple times for the same class(es): {report}" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - super().__init__(self.message) - - -class PropertyDefinitionsNotForSameProperty(NeatException): - """This error is raised if property definitions are not for linked to the same - property id when exporting rules to ontological representation. - - Args: - verbose: flag that indicates whether to provide enhanced exception message, by default False - """ - - type_: str = "PropertyDefinitionsNotForSameProperty" - code: int = 402 - description: str = "This error is raised if property definitions are not for linked to the same property id" - example: str = "" - fix: str = "" - - def __init__(self, verbose: bool = False): - self.message = ( - "All definitions should have the same property_id! Aborting." - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - super().__init__(self.message) - - -class FieldValueOfUnknownType(NeatException): - """This error is raised when generating in-memory pydantic model from Transformation - Rules from model, when field definitions are not provided as dictionary of field names - ('str') and their types ('tuple' or 'dict'). - - Args: - field: field name that raised exception due to unknown type - definition: definition of field that raised exception - verbose: flag that indicates whether to provide enhanced exception message, by default False - """ - - type_: str = "FieldValueOfUnknownType" - code: int = 403 - description: str = ( - "This error is raised when generating in-memory pydantic model" - " from Transformation Rules from model, when field definitions are not" - " provided as dictionary of field names ('str') and their types ('tuple' or 'dict')." - ) - example: str = "" - fix: str = "" - - def __init__(self, field: str, definition: Any, verbose: bool = False): - self.message = ( - f"Field {field} has definition of type {type(definition)}" - " which is not acceptable! Only definition in form of dict or tuple is acceptable!" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - super().__init__(self.message) - - -class MissingInstanceTriples(NeatException): - """This error is raised when queried RDF class instance does not return any triples that define it. - - Args: - id_: instance id - verbose: flag that indicates whether to provide enhanced exception message, by default False - """ - - type_: str = "MissingInstanceTriples" - code: int = 404 - description: str = ( - "This error is raised when queried RDF class instance " " does not return any triples that define it." - ) - example: str = "" - fix: str = "Make sure that RDF class instance holds necessary triples that define it." - - def __init__(self, id_: str | URIRef, verbose: bool = False): - self.message = ( - f"Instance {id_} does not contain triples that would define it!" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - super().__init__(self.message) - - -class PropertyRequiredButNotProvided(NeatException): - """This error is raised when instantiating in-memory pydantic model from graph class - instance which is missing required property. - - Args: - id_: instance id - verbose: flag that indicates whether to provide enhanced exception message, by default False - """ - - type_: str = "PropertyRequiredButNotProvided" - code: int = 405 - description: str = ( - "This error is raised when instantiating in-memory pydantic model" - " from graph class instance which is missing required property." - ) - example: str = "" - fix: str = "Either make field optional or add missing property to graph instance." - - def __init__(self, property: str, id_: str | URIRef, verbose: bool = False): - self.message = ( - f"Property {property} is not present in graph instance {id_}!" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - super().__init__(self.message) - - -class DataModelOrItsComponentsAlreadyExist(NeatException): - """This error is raised when attempting to create data model which already exist in DMS - - Args: - existing_data_model: external_id of model that already exist in DMS - existing_containers: set of external_ids of containers that already exist in DMS - existing_views: set of external_ids of views that already exist in DMS - verbose: flag that indicates whether to provide enhanced exception message, by default False - - Notes: - Remove existing data model and underlying views and/or containers, or bump - version of data model and views and/ir optionally delete containers. - """ - - type_: str = "DataModelOrItsComponentsAlreadyExist" - code: int = 406 - description: str = "This error is raised when attempting to create data model which already exist in DMS." - example: str = "" - fix: str = ( - "Remove existing data model and underlying views and/or containers, or bump " - "version of data model and views and optionally delete containers." - ) - - def __init__( - self, - existing_spaces: set[str] | None, - existing_data_model: DataModelId | None, - existing_containers: set[str], - existing_views: set[str], - verbose: bool = False, - ): - self.existing_spaces = existing_spaces - self.existing_data_model = existing_data_model - self.existing_containers = existing_containers - self.existing_views = existing_views - - self.message = "Aborting data model creation!" - if self.existing_spaces: - self.message += ( - f"\nSpaces {self.existing_spaces} already exists in DMS! Delete them or skip their creation! " - ) - if self.existing_data_model: - self.message += ( - f"\nData model {self.existing_data_model} already exists in DMS! Delete it first or bump its version! " - ) - if self.existing_views: - self.message += ( - f"\nViews {self.existing_views} already exist in DMS! Delete them first or bump their versions! " - ) - if self.existing_containers: - self.message += f"\nContainers {self.existing_containers} already exist in DMS! Delete them first! " - - self.message += ( - "\nTo remove existing data model and its components, use `self.remove_data_model(client)` method." - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - super().__init__(self.message) - - -class InstancePropertiesNotMatchingViewProperties(NeatException): - """This error is raised when an instance of a class has properties which are not - defined in the DMS container - - Args: - class_name: class name of instance that raised exception - class_properties: list of mandatory properties of class - container_properties: list of properties of container - verbose: flag that indicates whether to provide enhanced exception message, by default False - - Notes: - Make sure that all properties of a class are defined in the DMS container. - """ - - type_: str = "InstancePropertiesNotMatchingViewProperties" - code: int = 407 - description: str = "Instance of a class has properties which are not defined in the DMS view" - example: str = "" - fix: str = "Make sure that all properties of a class are defined in the DMS view" - - def __init__(self, class_name: str, class_properties: list[str], view_properties: list[str], verbose: bool = False): - self.message = ( - f"Instance of class {class_name} has properties {class_properties}" - f" while DMS view {class_name} has properties {view_properties}!" - f" Cannot create instance in DMS as properties do not match!" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - super().__init__(self.message) - - -class ContainerPropertyValueTypeRedefinition(NeatException): - """This error is raised when building up container where a property being redefined - with different value type - - Args: - container_id: container id that raised exception - property_id: container property id that raised exception - value_type: value type of container property that raised exception - verbose: flag that indicates whether to provide enhanced exception message, by default False - - Notes: - Make sure that when redefining property in container, value type is the same - """ - - type_: str = "ContainerPropertyValueTypeRedefinition" - code: int = 408 - description: str = "Container property value type is being redefined" - example: str = "" - fix: str = "Make sure that when redefining property in container, value type remains the same" - - def __init__( - self, - container_id: str, - property_id: str, - current_value_type: str, - redefined_value_type: str, - loc: str, - verbose: bool = False, - ): - self.message = ( - f"Container {container_id} property {property_id}" - f" value type {current_value_type} redefined to {redefined_value_type}!" - f"{loc}" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - super().__init__(self.message) - - -class ViewPropertyRedefinition(NeatException): - """This error is raised when building up views where a property being redefined differently - - Args: - view_id: view id that raised exception - property_id: view property id that raised exception - verbose: flag that indicates whether to provide enhanced exception message, by default False - - Notes: - Avoid redefining property in the same view - """ - - type_: str = "ViewPropertyRedefinition" - code: int = 409 - description: str = "View property is being redefined in the same view but differently" - example: str = "" - fix: str = "Avoid redefining property in the same view" - - def __init__( - self, - view_id: str, - property_id: str, - loc: str, - verbose: bool = False, - ): - self.message = ( - f"View {view_id} property {property_id} has been redefined in the same view!" - f"{loc}" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - super().__init__(self.message) - - -# Warnings -class OntologyMultiTypeProperty(NeatWarning): - """This warning occurs when a same property is define for two object/classes where - its expected value type is different in one definition, e.g. acts as an edge, while in - other definition acts as and attribute - - Args: - property_id: property id that raised warning due to multi type definition - types: list of types of property - verbose: flag that indicates whether to provide enhanced exception message, by default False - - Notes: - If a property takes different value types for different objects, simply define - new property. It is bad practice to have multi type property! - """ - - type_: str = "OntologyMultiTypeProperty" - code: int = 400 - description: str = ( - "This warning occurs when a same property is define for two object/classes where" - " its expected value type is different in one definition, e.g. acts as an edge, while in " - "other definition acts as and attribute" - ) - example: str = "" - fix: str = "If a property takes different value types for different objects, simply define new property" - - def __init__(self, property_id: str = "", types: list[str] | None = None, verbose: bool = False): - self.message = ( - "It is bad practice to have multi type property! " - f"Currently property '{property_id}' is defined as multi type property: {', '.join(types or [])}" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - - -class OntologyMultiRangeProperty(NeatWarning): - """This warning occurs when a property takes range of values which consists of union - of multiple value types - - Args: - property_id: property id that raised warning due to multi range definition - range_of_values: list of ranges that property takes - verbose: flag that indicates whether to provide enhanced exception message, by default False - - Notes: - If a property takes different range of values, simply define new property. - """ - - type_: str = "OntologyMultiRangeProperty" - code: int = 401 - description: str = ( - "This warning occurs when a property takes range of values which consists of union of multiple value types." - ) - example: str = "" - fix: str = "If a property takes different range of values, simply define new property" - - def __init__(self, property_id: str = "", range_of_values: list[str] | None = None, verbose: bool = False): - self.message = ( - "It is bad practice to have property that take various range of values! " - f"Currently property '{property_id}' has multiple ranges: {', '.join(range_of_values or [])}" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - - -class OntologyMultiDomainProperty(NeatWarning): - """This warning occurs when a property is reused for more than one classes - - Args: - property_id: property id that raised warning due to reuse definition - classes: list of classes that use the same property - verbose: flag that indicates whether to provide enhanced exception message, by default False - - Notes: - No need to fix this, but make sure that property type is consistent across different - classes and that ideally takes the same range of values - """ - - type_: str = "OntologyMultiDomainProperty" - code: int = 402 - description: str = "This warning occurs when a property is reused for more than one classes." - example: str = "" - fix: str = ( - "No need to fix this, but make sure that property type is consistent" - " across different classes and that ideally takes the same range of values" - ) - - def __init__(self, property_id: str = "", classes: list[str] | None = None, verbose: bool = False): - self.message = ( - f"Currently property '{property_id}' is defined for multiple classes: {', '.join(classes or [])}" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - - -class OntologyMultiLabeledProperty(NeatWarning): - """This warning occurs when a property is given multiple labels, typically if the - same property is defined for different classes but different name is given - - Args: - property_id: property id that raised warning due to multiple labels - names: list of names of property - verbose: flag that indicates whether to provide enhanced exception message, by default False - - Notes: - This would be automatically fixed by taking the first label (aka name) of the property. - """ - - type_: str = "OntologyMultiLabeledProperty" - code: int = 403 - description: str = ( - "This warning occurs when a property is given multiple labels," - " typically if the same property is defined for different " - "classes but different name is given." - ) - example: str = "" - fix: str = "This would be automatically fixed by taking the first label (aka name) of the property." - - def __init__(self, property_id: str = "", names: list[str] | None = None, verbose: bool = False): - self.message = ( - "Property should have single preferred label (human readable name)." - f"Currently property '{property_id}' has multiple preferred labels: {', '.join(names or [])} !" - f"Only the first name, i.e. '{names[0] if names else ''}' will be considered!" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - - -class OntologyMultiDefinitionProperty(NeatWarning): - """This warning occurs when a property is given multiple human readable definitions, - typically if the same property is defined for different classes where each definition - is different. - - Args: - property_id: property id that raised warning due to multiple definitions - verbose: flag that indicates whether to provide enhanced exception message, by default False - - Notes: - This would be automatically fixed by concatenating all definitions. - """ - - type_: str = "OntologyMultiDefinitionProperty" - code: int = 404 - description: str = ( - "This warning occurs when a property is given multiple human readable definitions," - " typically if the same property is defined for different " - "classes where each definition is different." - ) - example: str = "" - fix: str = "This would be automatically fixed by concatenating all definitions." - - def __init__(self, property_id: str, verbose: bool = False): - self.message = ( - f"Multiple definitions (aka comments) of property '{property_id}' detected." - " Definitions will be concatenated." - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - - -class FieldNotFoundInInstance(NeatWarning): - """This warning occurs when a property, associated to the pydantic field, is not found in the instance. - The missing field will be removed, which might lead to failure of the pydantic model validation if - the field/property is mandatory. - - Args: - id_: instance id that raised warning due to missing field - verbose: flag that indicates whether to provide enhanced exception message, by default False - - Notes: - If property/field is mandatory make sure that instances contain all mandatory fields. - Otherwise, no need to fix this warning. - """ - - type_: str = "FieldNotFoundInInstance" - code: int = 405 - description: str = ( - "This warning occurs when a property, associated to the pydantic field, is not found in the instance." - "The missing field will be removed, which might lead to failure of the pydantic model validation if" - " the field/property is mandatory." - ) - example: str = "" - fix: str = ( - "If property/field is mandatory make sure that instances contain all mandatory fields." - "Otherwise, no need to fix this warning." - ) - - def __init__(self, id_: str | URIRef = "", field_name: str = "", verbose: bool = False): - self.message = ( - f"Field {field_name} is missing in the instance {id_}." - " If this field is mandatory, the validation of the pydantic model will fail!" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - - -class FieldContainsMoreThanOneValue(NeatWarning): - """This warning occurs when a property, associated to the pydantic field, contains - more than one value (i.e. list of values), while it is defined as single value field. - As consequence, only the first value will be considered! - - Args: - field_name: field name that raised warning due to multiple values - no_of_values: number of values that field contains - verbose: flag that indicates whether to provide enhanced exception message, by default False - - Notes: - If a property takes more than one value, define it as list of values in TransformationRules. - To do this do not bound its `max_count` to 1, either leave it blank or set it to >1. - """ - - type_: str = "FieldContainsMoreThanOneValue" - code: int = 406 - description: str = ( - "This warning occurs when a property, associated to the pydantic field, contains" - " more than one value (i.e. list of values), while it is defined as single value field." - " As consequence, only the first value will be considered!" - ) - example: str = "" - fix: str = ( - "If a property takes more than one value, define it as list of values in TransformationRules." - "To do this do not bound its `max_count` to 1, either leave it blank or set it to >1." - ) - - def __init__(self, field_name: str = "", no_of_values: int | None = None, verbose: bool = False): - self.message = ( - f"Field {field_name} is defined as single value property in TransformationRules," - f" but it contains {no_of_values} values!" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - - -class ContainerPropertyTypeUnsupported(NeatWarning): - """This warning occurs when a property type is not supported by the container. - Currently only `DatatypeProperty` and `ObjectProperty` are supported, which - translate to `attribute` and `edge` respectively. - - Args: - property_id: property id that raised warning due to unsupported type - unsupported_type: unsupported property type - verbose: flag that indicates whether to provide enhanced exception message, by default False - - Notes: - Contact the NEAT support team if the warning is raised. - """ - - type_: str = "ContainerPropertyTypeUnsupported" - code: int = 407 - description: str = ( - "This warning occurs when a property type is not supported by the container." - " Currently only `DatatypeProperty` and `ObjectProperty` are supported, which" - " translate to `attribute` and `edge` respectively." - ) - example: str = "" - fix: str = "Contact NEAT support team." - - def __init__(self, property_id: str = "", unsupported_type: str = "", verbose: bool = False): - self.message = ( - f"Property {property_id} has unsupported type {unsupported_type}!" - "Only DatatypeProperty and ObjectProperty are supported!" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - - -class ViewPropertyTypeUnsupported(NeatWarning): - """This warning occurs when a TransformationRule property translates to unsupported - DMS view property. Currently only attributes, edges 1-1 and edges 1-n are supported. - - Args: - property_id: property id that raised warning due to unsupported type - verbose: flag that indicates whether to provide enhanced exception message, by default False - - Notes: - Contact the NEAT support team if the warning is raised. - """ - - type_: str = "ViewPropertyTypeUnsupported" - code: int = 408 - description: str = ( - "This warning occurs when a TransformationRule property translates to unsupported DMS view property." - " Currently only attributes, edges 1-1 and edges 1-n are supported." - ) - example: str = "" - fix: str = "Contact NEAT support team." - - def __init__(self, property_id: str = "", verbose: bool = False): - self.message = ( - f"Property {property_id} translates to unsupported!" - " Currently only attributes, edges 1-1 and edges 1-n are supported." - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - - -class ContainersAlreadyExist(NeatWarning): - """This warning occurs when attempting to create containers which already exist in DMS. - - Args: - container_ids: set of container ids that already exist in DMS - verbose: flag that indicates whether to provide enhanced exception message, by default False - - Notes: - If you need to create containers remove existing once and try again. - """ - - type_: str = "ContainersAlreadyExist" - code: int = 409 - description: str = "This warning occurs when attempting to create containers which already exist in DMS." - example: str = "" - fix: str = "Remove existing containers and try again." - - def __init__(self, container_ids: set[ContainerId] | None = None, space: str = "", verbose: bool = False): - self.message = ( - f"Containers {container_ids or set()} already exist in space {space}. " - "Since update of containers can cause issues, " - "remove them first prior data model creation!" - "Aborting containers creation!" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - - -class ViewsAlreadyExist(NeatWarning): - """This warning occurs when attempting to create views which already exist in DMS. - - Args: - views_ids: set of view ids that already exist in DMS - version: version of data model/views that already exist in DMS - verbose: flag that indicates whether to provide enhanced exception message, by default False - - Notes: - If you need to create views remove existing once and try again or update - data model version. - """ - - type_: str = "ViewsAlreadyExist" - code: int = 410 - description: str = "This warning occurs when attempting to create views which already exist in DMS." - example: str = "" - fix: str = "Remove existing views and try again or update version of data model." - - def __init__(self, views_ids: set[ViewId] | None = None, version: str = "", space: str = "", verbose: bool = False): - self.message = ( - f"Views {views_ids or set()} version {version} already exist in space {space}. " - "Since update of views raise issues, " - "remove them first prior data model creation or update version of data model!" - "Aborting views creation!" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - - -class DataModelAlreadyExist(NeatWarning): - """This warning occurs when attempting to create data model which already exist in DMS. - - Args: - data_model_id: data model id that already exist in DMS - version: version of data model that already exist in DMS - verbose: flag that indicates whether to provide enhanced exception message, by default False - - Notes: - Remove existing data model and try again or update its version - """ - - type_: str = "DataModelAlreadyExist" - code: int = 411 - description: str = "This warning occurs when attempting to create data model which already exist in DMS." - example: str = "" - fix: str = "Remove existing data model and try again or update its version." - - def __init__(self, data_model_id: str = "", version: str = "", space: str = "", verbose: bool = False): - self.message = ( - f"Data model {data_model_id} version {version} already exist in space {space}. " - "Since update of data model can raise issues, " - "remove it first or update its version!" - "Aborting data model creation!" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" - - -class EdgeConditionUnmet(NeatWarning): - """This warning occurs when attempting to create an edge but not all conditions are met. - - Args: - edge: data model id that already exist in DMS - verbose: flag that indicates whether to provide enhanced exception message, by default False - """ - - type_: str = "EdgeConditionUnmet" - code: int = 412 - description: str = "This warning occurs when attempting to create an edge, but the conditions are not met." - example: str = "" - fix: str = "Check if the edge is valid and that the lenght of the external_id is < 255" - - def __init__(self, edge: str, verbose: bool = False): - self.message = ( - f"Ignoring edge {edge} as its format is not valid" - f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}" - ) - - if verbose: - self.message += f"\nDescription: {self.description}" - self.message += f"\nExample: {self.example}" - self.message += f"\nFix: {self.fix}" diff --git a/cognite/neat/legacy/rules/exporters/__init__.py b/cognite/neat/legacy/rules/exporters/__init__.py deleted file mode 100644 index e1835095f..000000000 --- a/cognite/neat/legacy/rules/exporters/__init__.py +++ /dev/null @@ -1,20 +0,0 @@ -from ._rules2dms import DMSExporter, DMSSchemaComponents -from ._rules2excel import ExcelExporter -from ._rules2graphql import GraphQLSchemaExporter -from ._rules2ontology import OWLExporter, SemanticDataModelExporter, SHACLExporter -from ._rules2triples import TripleExporter - -# Deprecated -from ._rules2triples import TripleExporter as Rules2Triples - -__all__ = [ - "ExcelExporter", - "OWLExporter", - "SHACLExporter", - "SemanticDataModelExporter", - "GraphQLSchemaExporter", - "DMSExporter", - "DMSSchemaComponents", - "Rules2Triples", - "TripleExporter", -] diff --git a/cognite/neat/legacy/rules/exporters/_base.py b/cognite/neat/legacy/rules/exporters/_base.py deleted file mode 100644 index 163ba3118..000000000 --- a/cognite/neat/legacy/rules/exporters/_base.py +++ /dev/null @@ -1,45 +0,0 @@ -import sys -from abc import ABC, abstractmethod -from pathlib import Path -from typing import Generic, TypeVar - -from cognite.neat.legacy.rules.models.raw_rules import RawRules -from cognite.neat.legacy.rules.models.rules import Rules - -if sys.version_info >= (3, 11): - from typing import Self -else: - from typing_extensions import Self - -T_Export = TypeVar("T_Export") - - -class BaseExporter(ABC, Generic[T_Export]): - def __init__(self, rules: Rules, report: str | None = None): - self.rules = rules - self.report = report - - @classmethod - def from_rules(cls, rules: RawRules | Rules) -> Self: - if isinstance(rules, Rules): - return cls(rules) - elif isinstance(rules, RawRules): - validated_rules = rules.to_rules(skip_validation=True) - report = rules.validate_rules() - return cls(validated_rules, report) - - raise ValueError(f"Expected RawRules or Rules, got {type(rules)}") - - def export_to_file(self, filepath: Path, report_path: Path | None = None) -> None: - self._export_to_file(filepath) - if self.report: - report_path = report_path or filepath.parent / f"{filepath.stem}_validation_report.txt" - report_path.write_text(self.report) - - @abstractmethod - def _export_to_file(self, filepath: Path) -> None: - raise NotImplementedError - - @abstractmethod - def export(self) -> T_Export: - raise NotImplementedError diff --git a/cognite/neat/legacy/rules/exporters/_core/__init__.py b/cognite/neat/legacy/rules/exporters/_core/__init__.py deleted file mode 100644 index 51b6b0f3b..000000000 --- a/cognite/neat/legacy/rules/exporters/_core/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -"""Exporters dealing with CDF Core""" - -from .rules2labels import get_labels - -__all__ = ["get_labels"] diff --git a/cognite/neat/legacy/rules/exporters/_core/rules2labels.py b/cognite/neat/legacy/rules/exporters/_core/rules2labels.py deleted file mode 100644 index de4e8f6f7..000000000 --- a/cognite/neat/legacy/rules/exporters/_core/rules2labels.py +++ /dev/null @@ -1,24 +0,0 @@ -from cognite.neat.legacy.rules.models.rules import Rules - - -def get_labels(transformation_rules: Rules) -> set[str]: - """ - Return CDF labels for classes and relationships. - - Args: - transformation_rules: The transformation rules to extract labels from. - - Returns: - Set of CDF labels - """ - class_labels = {class_.class_id for class_ in transformation_rules.classes.values()} - - property_labels = {property_.property_id for property_ in transformation_rules.properties.values()} - - relationship_labels = { - str(rule.label) - for rule in transformation_rules.properties.values() - if "Relationship" in rule.cdf_resource_type and rule.label - } - - return class_labels.union(relationship_labels).union(property_labels) diff --git a/cognite/neat/legacy/rules/exporters/_rules2dms.py b/cognite/neat/legacy/rules/exporters/_rules2dms.py deleted file mode 100644 index 3ef02080c..000000000 --- a/cognite/neat/legacy/rules/exporters/_rules2dms.py +++ /dev/null @@ -1,885 +0,0 @@ -"""Exports rules to CDF Data Model Storage (DMS) through cognite-sdk.""" - -import dataclasses -import sys -import warnings -from dataclasses import dataclass -from pathlib import Path -from typing import ClassVar, Literal, cast, no_type_check - -import yaml -from cognite.client.data_classes.data_modeling.data_types import ListablePropertyType - -from cognite.neat.legacy.rules.models.value_types import ValueTypeMapping - -if sys.version_info >= (3, 11): - from typing import Self -else: - from typing_extensions import Self - -from cognite.client import CogniteClient -from cognite.client import data_modeling as dm -from cognite.client.data_classes.data_modeling import ( - ContainerApply, - ContainerApplyList, - ContainerId, - ContainerProperty, - DataModelApply, - DataModelId, - DirectRelation, - DirectRelationReference, - MappedPropertyApply, - PropertyType, - SpaceApply, - ViewApply, - ViewId, -) -from cognite.client.data_classes.data_modeling.views import ( - ConnectionDefinitionApply, - SingleHopConnectionDefinitionApply, -) -from cognite.client.exceptions import CogniteAPIError -from pydantic import BaseModel, ConfigDict, field_validator - -from cognite.neat.legacy.rules import exceptions -from cognite.neat.legacy.rules.exporters._base import BaseExporter -from cognite.neat.legacy.rules.exporters._validation import are_entity_names_dms_compliant -from cognite.neat.legacy.rules.models._base import ContainerEntity, EntityTypes, ParentClass -from cognite.neat.legacy.rules.models.rules import Class, Property, Rules -from cognite.neat.utils.auxiliary import generate_exception_report - -if sys.version_info < (3, 11): - from exceptiongroup import ExceptionGroup - - -@dataclass -class EmptyPropertyType(PropertyType): - _type = "No property" - - -@dataclass -class DMSSchema: - data_model: DataModelApply - containers: ContainerApplyList - - -class DMSExporter(BaseExporter[DMSSchema]): - """Class for exporting transformation rules object to CDF Data Model Storage (DMS). - - Args: - rules: Transformation rules object. - data_model_id: The id of the data model to be created. - container_policy: How to create/reuse existing containers. - existing_model: In the case of updating an existing model, this is the existing model. - report: Report. This is used when the exporter object is created from RawRules - - !!! note "Container policy" - Here is more information about the different container policies: - - `create`: assumes no containers exist in CDF, will attempt to create them - - `reuse`: assumes containers exists in CDF, will attempt to only re-use them - - `extend`: will re-use existing, extend and/or create only missing containers - - `optimize`: create containers of size's prescribed by NEAT's optimization algorithm - """ - - def __init__( - self, - rules: Rules, - data_model_id: dm.DataModelId | None = None, - container_policy: Literal["create", "reuse", "extend", "optimize"] = "create", - existing_model: dm.DataModel[dm.View] | None = None, - report: str | None = None, - ): - super().__init__(rules, report) - self.data_model_id = data_model_id - self.container_policy = container_policy - if container_policy == "extend" and existing_model is None: - raise ValueError("Container policy is extend-existing, but no existing model is provided") - if container_policy != "create": - raise NotImplementedError("Only one-to-one-view container policy is currently supported") - self.existing_model = existing_model - - def _export_to_file(self, filepath: Path) -> None: - if filepath.suffix not in {".yaml", ".yml"}: - warnings.warn("File extension is not .yaml, adding it to the file name", stacklevel=2) - filepath = filepath.with_suffix(".yaml") - schema = self.export() - filepath.write_text( - yaml.safe_dump( - { - "data_models": [schema.data_model.dump(camel_case=True)], - "containers": schema.containers.dump(camel_case=True), - } - ) - ) - - def export(self) -> DMSSchema: - model = DMSSchemaComponents.from_rules(self.rules, self.data_model_id) - return DMSSchema( - data_model=DataModelApply( - space=model.space, - external_id=model.external_id, - version=model.version, - description=model.description, - name=model.name, - views=list(model.views.values()), - ), - containers=ContainerApplyList(model.containers.values()), - ) - - -class DMSSchemaComponents(BaseModel): - """ - DMS Schema Components pydantic class used to create space(s), containers, views and data model in CDF. - - This can be used to create a data model in CDF from rules. - - Args: - space: Name of the space to place the resulting data model. - external_id: External id of the data model. - version: Version of the data model. - description: Description of the data model. - name: Name of the data model. - containers: Containers connected to the data model. - views: Views connected to the data model. - - """ - - space: str - external_id: str - version: str - description: str | None = None - name: str | None = None - containers: dict[str, ContainerApply] - views: dict[str, ViewApply] - - model_config: ClassVar[ConfigDict] = ConfigDict( - populate_by_name=True, str_strip_whitespace=True, arbitrary_types_allowed=True, strict=False, extra="allow" - ) - - @field_validator("views", mode="after") - def remove_empty_views(cls, value): - """This validator removes views that do not have any properties or implements.""" - for view_id, view in list(value.items()): - if not view.properties and not view.implements: - del value[view_id] - - return value - - @property - def spaces(self): - return set( - [container.space for container in self.containers.values()] - + [self.space] - + [view.space for view in self.views.values()] - ) - - @classmethod - def from_rules( - cls, rules: Rules, data_model_id: dm.DataModelId | None = None, set_expected_source: bool = True - ) -> Self: - """Generates a DataModel class instance from a Rules instance. - - Args: - rules: instance of Rules. - data_model_id: The id of the data model to be created. - set_expected_source: Whether to set the expected source on views with direct relation properties. - - Returns: - Instance of DataModel. - """ - if data_model_id and data_model_id.space: - # update space in rules to match space - rules.update_space(data_model_id.space) - - if data_model_id and data_model_id.version: - # update version in rules to match version - rules.update_version(data_model_id.version) - - if data_model_id and data_model_id.external_id: - # update data model name to match external_id - rules.metadata.suffix = data_model_id.external_id - - names_compliant, name_warnings = are_entity_names_dms_compliant(rules, return_report=True) - if not names_compliant: - raise exceptions.EntitiesContainNonDMSCompliantCharacters(report=generate_exception_report(name_warnings)) - - if rules.metadata.external_id is None: - raise exceptions.DataModelIdMissing(prefix=rules.metadata.space) - - return cls( - space=rules.metadata.space, - external_id=rules.metadata.external_id, - version=rules.metadata.version, - description=rules.metadata.description, - name=rules.metadata.name, - containers=cls.containers_from_rules(rules), - views=cls.views_from_rules(rules), - ) - - @classmethod - def containers_from_rules(cls, rules: Rules) -> dict[str, ContainerApply]: - """Create a dictionary of ContainerApply instances from a Rules instance. - - Args: - rules: instance of Rules.` - - Returns: - Dictionary of ContainerApply instances. - """ - - containers: dict[str, ContainerApply] = {} - errors: list = [] - - for row, property_ in rules.properties.items(): - if property_.container: - container_property_id: str = cast(str, property_.container_property) - container_id = property_.container.id - - api_container = cls.as_api_container(property_.container) - api_container_property = cls.as_api_container_property(property_) - - # scenario: adding new container to the data model for the first time - if not isinstance(api_container_property.type, EmptyPropertyType) and container_id not in containers: - containers[container_id] = api_container - containers[container_id].properties[container_property_id] = api_container_property - - # scenario: adding new property to an existing container - elif ( - not isinstance(api_container_property.type, EmptyPropertyType) - and container_property_id not in containers[container_id].properties - ): - containers[container_id].properties[container_property_id] = api_container_property - - # scenario: property is redefined checking for potential sub-scenarios - elif ( - not isinstance(api_container_property.type, EmptyPropertyType) - and container_property_id in containers[container_id].properties - ): - existing_property = containers[container_id].properties[container_property_id] - - # scenario: property is redefined with a different value type -> raise error - if not isinstance(existing_property.type, type(api_container_property.type)): - errors.append( - exceptions.ContainerPropertyValueTypeRedefinition( - container_id=container_id, - property_id=container_property_id, - current_value_type=str(existing_property.type), - redefined_value_type=str(api_container_property.type), - loc=f"[Properties/Type/{row}]", - ) - ) - - # scenario: default value is redefined -> set default value to None - if ( - not isinstance(existing_property.type, DirectRelation) - and not isinstance(api_container_property.type, DirectRelation) - and existing_property.default_value != api_container_property.default_value - ): - containers[container_id].properties[container_property_id] = dataclasses.replace( - existing_property, default_value=None - ) - - # scenario: property holds multiple values -> set is_list to True - if ( - not isinstance(existing_property.type, DirectRelation) - and not isinstance(api_container_property.type, DirectRelation) - and isinstance(existing_property.type, ListablePropertyType) - and isinstance(api_container_property.type, ListablePropertyType) - and existing_property.type.is_list != api_container_property.type.is_list - ): - type_ = containers[container_id].properties[container_property_id].type - if isinstance(type_, ListablePropertyType): - type_.is_list = True - - if errors: - raise ExceptionGroup("Properties value types have been redefined! This is prohibited! Aborting!", errors) - - return containers - - @classmethod - def as_api_container_property(cls, property_: Property) -> ContainerProperty: - is_one_to_many = property_.max_count != 1 - - # Literal, i.e. Node attribute - if property_.property_type is EntityTypes.data_property: - property_type = cast(ValueTypeMapping, property_.expected_value_type.mapping).dms - return ContainerProperty( - type=property_type(is_list=is_one_to_many), - nullable=property_.min_count == 0, - default_value=property_.default, - name=property_.property_name, - description=property_.description, - ) - - # URIRef, i.e. edge 1-1 , aka direct relation between Nodes - elif property_.property_type is EntityTypes.object_property and not is_one_to_many: - return ContainerProperty( - type=DirectRelation(), - nullable=True, - name=property_.property_name, - description=property_.description, - ) - - # return type=None if property cannot be created - else: - return ContainerProperty(type=EmptyPropertyType()) - - @classmethod - def as_api_container(cls, container: ContainerEntity) -> ContainerApply: - return ContainerApply( - space=container.space, - external_id=container.external_id, - description=container.description, - name=container.name, - # properties are added later - properties={}, - ) - - @classmethod - def views_from_rules(cls, rules: Rules) -> dict[str, ViewApply]: - """Generates a dictionary of ViewApply instances from a Rules instance. - - Args: - rule: Instance of Rules. - space: Name of the space to place the views. - - Returns: - Dictionary of ViewApply instances. - """ - views: dict[str, ViewApply] = { - f"{rules.metadata.space}:{class_.class_id}": cls.as_api_view( - class_, rules.metadata.space, rules.metadata.version - ) - for class_ in rules.classes.values() - } - errors: list = [] - - # Create views from property-class definitions - for row, property_ in rules.properties.items(): - view_property = cls.as_api_view_property(property_, rules.metadata.space) - id_ = f"{rules.metadata.space}:{property_.class_id}" - - # scenario: view exist but property does not so it is added - if view_property and (property_.property_id not in cast(dict, views[id_].properties)): - cast(dict, views[id_].properties)[property_.property_id] = view_property - - # scenario: view exist, property exists but it is differently defined -> raise error - # type: ignore - elif ( - view_property - and property_.property_id in cast(dict, views[id_].properties) - and view_property is not cast(dict, views[id_].properties)[property_.property_id] - ): - errors.append( - exceptions.ViewPropertyRedefinition( - view_id=id_, - property_id=cast(str, property_.property_id), - loc=f"[Properties/Property/{row}]", - ) - ) - - if errors: - raise ExceptionGroup("View properties have been redefined! This is prohibited! Aborting!", errors) - - return views - - @classmethod - def as_api_view(cls, class_: Class, space: str, version: str) -> ViewApply: - return ViewApply( - space=space, - external_id=class_.class_id, - version=version, - name=class_.class_name, - description=class_.description, - properties={}, - implements=( - [parent_class.view_id for parent_class in cast(list[ParentClass], class_.parent_class)] - if class_.parent_class - else None - ), - ) - - @classmethod - def as_api_view_property( - cls, property_: Property, space: str - ) -> MappedPropertyApply | ConnectionDefinitionApply | None: - property_.container = cast(ContainerEntity, property_.container) - property_.container_property = cast(str, property_.container_property) - if property_.property_type is EntityTypes.data_property: - return MappedPropertyApply( - container=ContainerId(space=property_.container.space, external_id=property_.container.external_id), - container_property_identifier=property_.container_property, - name=property_.property_name, - description=property_.description, - ) - - # edge 1-1 == directRelation - elif property_.property_type is EntityTypes.object_property and property_.max_count == 1: - return MappedPropertyApply( - container=ContainerId(space=property_.container.space, external_id=property_.container.external_id), - container_property_identifier=property_.container_property, - name=property_.property_name, - description=property_.description, - source=ViewId( - space=property_.expected_value_type.space, - external_id=property_.expected_value_type.external_id, - version=property_.expected_value_type.version, - ), - ) - - # edge 1-many == EDGE, this does not have container! type is here source ViewId ?! - elif property_.property_type is EntityTypes.object_property and property_.max_count != 1: - if property_.container and property_.expected_value_type.space != property_.container.space: - type_ = DirectRelationReference( - space=property_.container.space, - external_id=f"{property_.container.suffix}.{property_.container_property}", - ) - else: - type_ = DirectRelationReference( - space=space, external_id=f"{property_.class_id}.{property_.property_id}" - ) - - return SingleHopConnectionDefinitionApply( - type=type_, - # Here we create ViewID to the container that the edge is pointing to. - source=ViewId( - space=property_.expected_value_type.space, - external_id=property_.expected_value_type.external_id, - version=property_.expected_value_type.version, - ), - direction="outwards", - name=property_.property_name, - description=property_.description, - ) - else: - return None - - def find_existing_spaces(self, client: CogniteClient) -> set[str]: - """Checks if the spaces exist in CDF. - - Args: - client: Cognite client. - - Returns: - External ids of spaces which are part of DMS Schema components that already exist in CDF. - """ - - return set(client.data_modeling.spaces.retrieve(list(self.spaces)).as_ids()) - - def find_existing_containers(self, client: CogniteClient) -> set[str]: - """Checks if the containers exist in CDF. - - Args: - client: Cognite client. - - Returns: - External ids of containers which are part of DMS Schema components that already exist in CDF. - """ - - return { - f"{id_.space}:{id_.external_id}" - for id_ in client.data_modeling.containers.retrieve( - [container.as_id() for container in self.containers.values()] - ).as_ids() - } - - def find_existing_views(self, client: CogniteClient) -> set[str]: - """Checks if the views exist in CDF. - - Args: - client: Cognite client. - - Returns: - External ids of views which are part of DMS Schema components that already exist in CDF. - """ - - return { - f"{id_.space}:{id_.external_id}" - for id_ in client.data_modeling.views.retrieve([view.as_id() for view in self.views.values()]).as_ids() - } - - def find_existing_data_model(self, client: CogniteClient) -> DataModelId | None: - """Checks if the data model exists in CDF. - - Args: - client: Cognite client. - - Returns: - True if the data model exists, False otherwise. - """ - if model := client.data_modeling.data_models.retrieve((self.space, self.external_id, self.version)): - cdf_data_model = model.latest_version() - return cdf_data_model.as_id() - return None - - # mypy unsatisfied with overload , tried all combination and gave up - @no_type_check - def to_cdf( - self, - client: CogniteClient, - components_to_create: set | None = None, - existing_component_handling: Literal["fail", "skip", "update"] = "fail", - multi_space_components_create: bool = False, - return_report: bool = False, - ) -> None | tuple[dict[str, list], dict[str, list]]: - """Write the the data model to CDF. - - Args: - client: Connected Cognite client. - components_to_create: Which components to create. Takes set - existing_component_handling: How to handle existing components. Takes Literal["fail", "skip", "update"] - multi_space_comp_create: Whether to create only components belonging to the data model space, - or also additionally components outside of the data model space. Default is False. - - !!! note "Multi Space DMS Schema Components" - If multi_space_components_create is set to True, the components will be created - in all spaces used or defined in `Rules`. If set to False, the - components will only be created in the space defined in `Rules` metadata. - - !!! note "Component Creation Policy" - Here is more information about the different component creation policies - configured through components_to_create argument: - - - `all`: all components of the data model will be created, meaning space, containers, views and data model - - `data model`: only the data model will be created - - `view`: only views will be created - - `container`: only the containers will be created - - for creation of containers beyond the data model space, set `multi_space_components_create` argument - to True. - - - !!! note "Existing Component Handling Policy" - Here is more information about the different existing component handling policies: - - - `fail`: if any component of the data model (DMS schema) already exists - - `skip`: skip DMS components that exist - - `update`: create DMS components that do not exist and update those that do exist - - `update` policy is currently not implemented ! - - """ - - logs, errors = {}, {} - - components_to_create = components_to_create or {"all"} - - existing_spaces = self.find_existing_spaces(client) - existing_containers = self.find_existing_containers(client) - existing_views = self.find_existing_views(client) - existing_data_model = self.find_existing_data_model(client) - - if ( - existing_spaces or existing_containers or existing_data_model or existing_views - ) and existing_component_handling == "fail": - raise exceptions.DataModelOrItsComponentsAlreadyExist( - existing_spaces, - existing_data_model, - existing_containers, - existing_views, - ) - - if "space" in components_to_create or "all" in components_to_create: - logs["space"], errors["space"] = self.create_space( - client, - existing_spaces, - existing_component_handling == "update", - multi_space_components_create, - ) - if "container" in components_to_create or "all" in components_to_create: - logs["container"], errors["container"] = self.create_containers( - client, - existing_containers, - existing_component_handling == "update", - multi_space_components_create, - ) - - if "view" in components_to_create or "all" in components_to_create: - logs["view"], errors["view"] = self.create_views( - client, existing_views, existing_component_handling == "update" - ) - if "data model" in components_to_create or "all" in components_to_create: - logs["data model"], errors["data model"] = self.create_data_model( - client, existing_data_model, existing_component_handling == "update" - ) - - if return_report: - return logs, errors - - def create_space( - self, - client: CogniteClient, - existing_spaces: set, - update: bool = False, - multi_space_components_create: bool = False, - ) -> tuple[list, list]: - logs, errors = [], [] - - spaces_to_create = ( - self.spaces - existing_spaces - if multi_space_components_create - else ((self.spaces - existing_spaces) & {self.space}) - ) - - spaces_to_update = existing_spaces if multi_space_components_create else (existing_spaces & {self.space}) - - if spaces_to_create: - try: - _ = client.data_modeling.spaces.apply([SpaceApply(space=space) for space in spaces_to_create]) - logs.append(f"Created space {spaces_to_create}") - except CogniteAPIError as e: - errors.append(f"Failed to create space {spaces_to_create}! Reason: {e.message}") - - if update and spaces_to_update: - try: - _ = client.data_modeling.spaces.apply([SpaceApply(space=space) for space in spaces_to_update]) - logs.append(f"Updated space {spaces_to_update}") - except CogniteAPIError as e: - errors.append(f"Failed to update spaces {spaces_to_update}! Reason: {e.message}") - - return logs, errors - - def create_containers( - self, - client: CogniteClient, - existing_containers: set, - update: bool = False, - multi_space_components_create: bool = False, - ) -> tuple[list, list]: - logs, errors = [], [] - - containers_to_create = ( - set(self.containers.keys()) - existing_containers - if multi_space_components_create - else {k for k in (set(self.containers.keys()) - existing_containers) if k.split(":")[0] == self.space} - ) - - containers_to_update = ( - existing_containers - if multi_space_components_create - else {k for k in existing_containers if k.split(":")[0] == self.space} - ) - - if containers_to_create: - try: - _ = client.data_modeling.containers.apply([self.containers[id_] for id_ in containers_to_create]) - logs.append(f"Created container {containers_to_create}") - except CogniteAPIError as e: - errors.append(f"Failed to create containers {containers_to_create}! Reason: {e.message}") - - if update and containers_to_update: - try: - _ = client.data_modeling.containers.apply([self.containers[id_] for id_ in containers_to_update]) - logs.append(f"Updated containers {containers_to_update}") - except CogniteAPIError as e: - errors.append(f"Failed to update containers {containers_to_update}! Reason: {e.message}") - - return logs, errors - - def create_views( - self, - client: CogniteClient, - existing_views: set, - update: bool = False, - ) -> tuple[list, list]: - logs, errors = [], [] - - non_existing_views = set(self.views.keys()) - existing_views - if non_existing_views: - try: - _ = client.data_modeling.views.apply([self.views[id_] for id_ in non_existing_views]) - logs.append(f"Created views {non_existing_views}") - except CogniteAPIError as e: - errors.append(f"Failed to update views {non_existing_views}! Reason: {e.message}") - - if update and existing_views: - try: - _ = client.data_modeling.views.apply([self.views[id_] for id_ in existing_views]) - logs.append(f"Updated views {existing_views}") - except CogniteAPIError as e: - errors.append(f"Failed to update views {existing_views}! Reason: {e.message}") - - return logs, errors - - def create_data_model( - self, - client: CogniteClient, - existing_data_model: DataModelId | None = None, - update: bool = False, - ) -> tuple[list, list]: - logs, errors = [], [] - - if not existing_data_model: - try: - _ = client.data_modeling.data_models.apply( - DataModelApply( - name=self.name, - description=self.description, - space=self.space, - external_id=self.external_id, - version=self.version, - views=[view.as_id() for view in self.views.values()], - ) - ) - logs.append(f"Created data model {{{self.space}:{self.external_id}/{self.version}}}") - except CogniteAPIError as e: - errors.append( - f"Failed to create data model {{{self.space}:{self.external_id}/{self.version}}}!" - f" Reason: {e.message}" - ) - elif update: - try: - _ = client.data_modeling.data_models.apply( - DataModelApply( - name=self.name, - description=self.description, - space=self.space, - external_id=self.external_id, - version=self.version, - views=[view.as_id() for view in self.views.values()], - ) - ) - logs.append("Updated data model " f"{{{self.space}:{self.external_id}/{self.version}}}") - except CogniteAPIError as e: - errors.append( - "Failed to update data model" - f" {{{self.space}:{self.external_id}/{self.version}}}! Reason: {e.message}" - ) - - else: - logs.append(f"Skipped update of data model {{{self.space}:{self.external_id}/{self.version}}}!") - - return logs, errors - - @no_type_check - def remove( - self, - client: CogniteClient, - components_to_remove: set | None = None, - multi_space_components_removal: bool = False, - return_report: bool = False, - ) -> None | tuple[dict[str, list], dict[str, list]]: - """Remove DMS schema components from CDF. - - Args: - client: Connected Cognite client. - components_to_remove: Which components to remove. Takes set - multi_space_components_removal: Whether to remove components in multiple spaces, - or only in the space of the data model. Default is False. - - !!! note "Component Creation Policy" - Here is more information about the different component creation policies: - - `all`: all components of the data model will be created, meaning space, containers, views and data model - - `data model`: only the data model will be created - - `view`: only the views will be created - - `container`: only the containers will be created - - - !!! note "Multi Space DMS Schema Components" - If multi_space_components_removal is set to True, the components will be removed - in all spaces used or defined in `Rules`. If set to False, the - components will only be removed in the space defined in `Rules` metadata. - """ - - logs, errors = {}, {} - - components_to_remove = components_to_remove or {"all"} - - if "data model" in components_to_remove or "all" in components_to_remove: - logs["data model"], errors["data model"] = self.remove_data_model(client) - if "view" in components_to_remove or "all" in components_to_remove: - logs["view"], errors["view"] = self.remove_views(client) - if "container" in components_to_remove or "all" in components_to_remove: - logs["container"], errors["container"] = self.remove_containers(client, multi_space_components_removal) - if "space" in components_to_remove or "all" in components_to_remove: - logs["space"], errors["space"] = self.remove_spaces(client, multi_space_components_removal) - - if return_report: - return logs, errors - - def remove_data_model(self, client: CogniteClient) -> tuple[list, list]: - logs, errors = [], [] - - if client.data_modeling.data_models.retrieve((self.space, self.external_id, self.version)): - try: - _ = client.data_modeling.data_models.delete((self.space, self.external_id, self.version)) - logs.append(f"Removed data model {{{self.space}:{self.external_id}/{self.version}}}") - except CogniteAPIError as e: - errors.append( - f"Failed to remove data model " - f"{{{self.space}:{self.external_id}/{self.version}}}! Reason: {e.message}" - ) - else: - logs.append("No Data Model to remove") - - return logs, errors - - def remove_views(self, client: CogniteClient) -> tuple[list, list]: - logs, errors = [], [] - - if existing_views := self.find_existing_views(client): - try: - _ = client.data_modeling.views.delete([self.views[id_].as_id() for id_ in existing_views]) - logs.append(f"Removed views {existing_views}!") - except CogniteAPIError as e: - errors.append(f"Failed to remove views {existing_views}! Reason: {e.message}") - - else: - logs.append("No Views to remove!") - - return logs, errors - - def remove_containers( - self, client: CogniteClient, multi_space_components_removal: bool = False - ) -> tuple[list, list]: - logs, errors = [], [] - - existing_containers = self.find_existing_containers(client) - existing_container_in_rules_space = {k for k in existing_containers if k.split(":")[0] == self.space} - - if existing_containers and multi_space_components_removal: - try: - _ = client.data_modeling.containers.delete( - [self.containers[id_].as_id() for id_ in existing_containers] - ) - logs.append(f"Removed containers {existing_containers}!") - except CogniteAPIError as e: - errors.append(f"Failed to remove containers {existing_containers}! Reason: {e.message}") - - elif existing_container_in_rules_space and not multi_space_components_removal: - try: - _ = client.data_modeling.containers.delete( - [self.containers[id_].as_id() for id_ in existing_container_in_rules_space] - ) - logs.append(f"Removed containers {existing_container_in_rules_space}!") - except CogniteAPIError as e: - errors.append(f"Failed to remove containers {existing_container_in_rules_space}! Reason: {e.message}") - - else: - logs.append("No Containers to remove") - - return logs, errors - - def remove_spaces(self, client: CogniteClient, multi_space_components_removal: bool = False) -> tuple[list, list]: - logs, errors = [], [] - existing_spaces = self.find_existing_spaces(client) - - if existing_spaces and multi_space_components_removal: - for space in existing_spaces: - try: - _ = client.data_modeling.spaces.delete(space) - logs.append(f"Removed spaces {space}!") - except CogniteAPIError as e: - errors.append(f"Failed to remove {space}! Reason: {e.message}") - - elif self.space in existing_spaces and not multi_space_components_removal: - try: - _ = client.data_modeling.spaces.delete(self.space) - logs.append(f"Removed space {self.space}!") - except CogniteAPIError as e: - errors.append(f"Failed to remove space {self.space}! Reason: {e.message}") - - else: - logs.append("No Spaces to remove") - - return logs, errors diff --git a/cognite/neat/legacy/rules/exporters/_rules2excel.py b/cognite/neat/legacy/rules/exporters/_rules2excel.py deleted file mode 100644 index 2e6ced733..000000000 --- a/cognite/neat/legacy/rules/exporters/_rules2excel.py +++ /dev/null @@ -1,213 +0,0 @@ -from pathlib import Path -from typing import cast - -from openpyxl import Workbook -from openpyxl.cell import Cell -from openpyxl.styles import Alignment, Border, Font, NamedStyle, PatternFill, Side -from openpyxl.worksheet.worksheet import Worksheet - -from cognite.neat.legacy.rules.models._base import EntityTypes - -from ._base import BaseExporter - - -class ExcelExporter(BaseExporter[Workbook]): - """Class for exporting transformation rules object to excel file.""" - - def _export_to_file(self, filepath: Path) -> None: - """Exports transformation rules to excel file.""" - data = self.export() - try: - data.save(filepath) - finally: - data.close() - - def export(self) -> Workbook: - """Generates workbook from transformation rules.""" - data = Workbook() - # Remove default sheet named "Sheet" - data.remove(data["Sheet"]) - # Serialize the rules to the excel file - - # map rules metadata to excel - metadata = self.rules.metadata - metadata_sheet = data.create_sheet("Metadata") - - # add each metadata property to the sheet as a row - metadata_sheet.append(["prefix", metadata.prefix]) - metadata_sheet.append(["namespace", metadata.namespace]) - metadata_sheet.append(["external_id", metadata.suffix]) - metadata_sheet.append(["title", metadata.name]) - metadata_sheet.append(["description", metadata.description]) - metadata_sheet.append(["version", metadata.version]) - metadata_sheet.append( - [ - "creator", - ( - ",".join(metadata.creator if isinstance(metadata.creator, list) else [metadata.creator]) - if metadata.creator - else "" - ), - ] - ) - metadata_sheet.append( - [ - "contributor", - ( - ",".join(metadata.contributor if isinstance(metadata.contributor, list) else [metadata.contributor]) - if metadata.contributor - else "" - ), - ] - ) - metadata_sheet.append(["created", metadata.created]) - metadata_sheet.append(["updated", metadata.updated]) - metadata_sheet.append(["rights", metadata.rights]) - - # map classes to excel sheet named "Classes" and add each class as a row - classes_sheet = data.create_sheet("Classes") - - classes_sheet.append(["Solution model", "", "", "Knowledge acquisition", "", "", ""]) - classes_sheet.merge_cells("A1:C1") - classes_sheet.merge_cells("D1:G1") - classes_sheet.append( - ["Class", "Description", "Parent Class", "Source", "Source Entity Name", "Match Type", "Comment"] - ) # A ... # G - - for class_ in self.rules.classes.values(): - classes_sheet.append( - [ - class_.class_id, - class_.description, - ( - ",".join([parent_class.versioned_id for parent_class in class_.parent_class]) - if class_.parent_class - else None - ), - str(class_.source), - class_.source_entity_name, - class_.match_type, - class_.comment, - ] - ) - - # map properties to excel sheet named "Properties" and add each property as a row - properties_sheet = data.create_sheet("Properties") - properties_sheet.append( - [ - "Solution model", # A - "", # B - "", # C - "", # D - "", # E - "", # F - "Solution classic CDF resources", # G - "", # H - "", # I - "", # J - "", # K - "", # L - "Transformation rules", # M - "", # N - "Knowledge acquisition", # O - "", # P - "", # Q - "", # R - ] - ) - properties_sheet.merge_cells("A1:F1") - properties_sheet.merge_cells("G1:L1") - properties_sheet.merge_cells("M1:N1") - properties_sheet.merge_cells("O1:R1") - properties_sheet.append( - [ - "Class", # A - "Property", # B - "Description", # C - "Type", # D - "Min Count", # E - "Max Count", # F - "Resource Type", # G - "Resource Type Property", # H - "Relationship Source Type", # I - "Relationship Target Type", # J - "Relationship Label", # K - "Relationship ExternalID Rule", # L - "Rule Type", # M - "Rule", # N - "Source", # O - "Source Entity Name", # P - "Match Type", # Q - "Comment", # R - ] - ) - - for property_ in self.rules.properties.values(): - properties_sheet.append( - [ - property_.class_id, # A - property_.property_id, # B - property_.description, # C - ( - property_.expected_value_type.versioned_id - if property_.property_type == EntityTypes.object_property - else property_.expected_value_type.suffix - ), # D - property_.min_count, # E - property_.max_count, # F - ",".join(property_.cdf_resource_type) if property_.cdf_resource_type else "", # G - ",".join(property_.resource_type_property) if property_.resource_type_property else "", # H - property_.source_type, # I - property_.target_type, # J - property_.label, # K - property_.relationship_external_id_rule, # L - property_.rule_type, # M - property_.rule, # N - str(property_.source), # O - property_.source_entity_name, # P - property_.match_type, # Q - property_.comment, # R - ] - ) - - prefixes_sheet = data.create_sheet("Prefixes") - prefixes_sheet.append(["Prefix", "URI"]) # A # B - - for prefix, uri in self.rules.prefixes.items(): - prefixes_sheet.append([prefix, uri]) # A # B - - return self._set_header_style(data) - - @staticmethod - def _set_header_style(data: Workbook): - """Sets the header style for all sheets in the self.workbook""" - style = NamedStyle(name="header style") - style.font = Font(bold=True, size=16) - side = Side(style="thin", color="000000") - style.border = Border(left=side, right=side, top=side, bottom=side) - data.add_named_style(style) - - for sheet in data.sheetnames: - if sheet == "Metadata": - continue - if sheet == "Classes" or sheet == "Properties": - sheet_obj = cast(Worksheet, data[sheet]) - if sheet == "Classes": - sheet_obj.freeze_panes = "A3" - else: - sheet_obj.freeze_panes = "D3" - - for cell in sheet_obj[1]: - cell = cast(Cell, cell) # type: ignore[index] - cell.style = style - cell.fill = PatternFill("solid", start_color="D5B2CF") - cell.alignment = Alignment(horizontal="center", vertical="center") - for cell in sheet_obj[2]: - cell = cast(Cell, cell) # type: ignore[index] - cell.style = style - cell.fill = PatternFill("solid", start_color="D5DBD5") - cell.alignment = Alignment(horizontal="center", vertical="center") - adjusted_width = (len(str(cell.value)) + 5) * 1.2 - cast(Worksheet, data[sheet]).column_dimensions[cell.column_letter].width = adjusted_width - - return data diff --git a/cognite/neat/legacy/rules/exporters/_rules2graphql.py b/cognite/neat/legacy/rules/exporters/_rules2graphql.py deleted file mode 100644 index a73b183b2..000000000 --- a/cognite/neat/legacy/rules/exporters/_rules2graphql.py +++ /dev/null @@ -1,183 +0,0 @@ -import sys -import warnings -from dataclasses import dataclass -from pathlib import Path -from typing import TYPE_CHECKING - -if sys.version_info >= (3, 11): - from typing import Self -else: - from typing_extensions import Self - -from cognite.neat.legacy.rules import exceptions -from cognite.neat.legacy.rules.analysis import to_class_property_pairs -from cognite.neat.legacy.rules.exporters._validation import are_entity_names_dms_compliant, are_properties_redefined -from cognite.neat.legacy.rules.models.rules import Rules -from cognite.neat.legacy.rules.models.value_types import XSD_VALUE_TYPE_MAPPINGS -from cognite.neat.utils.auxiliary import generate_exception_report - -from ._base import BaseExporter - -if TYPE_CHECKING: - from jinja2 import Template - - -class GraphQLSchemaExporter(BaseExporter[str]): - def _export_to_file(self, filepath: Path): - if filepath.suffix != ".graphql": - warnings.warn("File extension is not .graphql, adding it to the file name", stacklevel=2) - filepath = filepath.with_suffix(".graphql") - filepath.write_text(self.export()) - - def export(self) -> str: - return GraphQLSchema.from_rules(self.rules).schema - - -_TYPE = ( - "{% include 'type_header' %}type {{ class_definition.class_id }} {{'{'}}" - "{%-for property_definition in class_properties%}" - "{% include 'field' %}" - "{% endfor %}\n}\n" -) - -_TYPE_HEADER = ( - "{%- if header %}" - "{%- if class_definition.description and class_definition.class_name %}" - '"""\n{{class_definition.description}}\n@name {{ class_definition.class_name }}\n"""\n{##}\n' - "{%- elif class_definition.description %}" - '\n"""\n{{class_definition.description}}\n"""\n' - "{%- endif %}" - "{%- endif %}" -) - - -_FIELD = ( - "{% include 'field_header' %}\n" - " {{ property_definition.property_id }}: " - "{%-if property_definition.property_type == 'DatatypeProperty'%}" - "{% include 'attribute_occurrence' %}" - "{%-else%}" - "{% include 'edge_occurrence' %}" - "{%- endif -%}" -) - -_FIELD_HEADER = ( - "{%- if header %}" - "{%- if property_definition.description and property_definition.property_name %}" - '\n """\n {{property_definition.description}}' - "\n @name {{ property_definition.property_name }}" - '\n """\n ' - "{%- elif property_definition.description %}" - '\n """\n {{property_definition.description}}' - '\n """\n ' - "{%- endif %}" - "{%- endif %}" -) - -_ATTRIBUTE_OCCURRENCE = ( - "{%-if property_definition.min_count and property_definition.max_count == 1%}" - " {% include 'value_type' %}!" - "{%-elif property_definition.min_count and property_definition.max_count != 1%}" - " [{% include 'value_type' %}!]!" - "{%-elif property_definition.max_count != 1%}" - " [{% include 'value_type' %}!]!" - "{%-else%}{% include 'value_type' %}" - "{%- endif -%}" -) - -_EDGE_OCCURRENCE = ( - "{%-if not(property_definition.min_count and property_definition.max_count == 1)%}" - " [{% include 'value_type' %}]" - "{%-else%}" - " {% include 'value_type' %}" - "{%- endif -%}" -) - -_FIELD_VALUE_TYPE = """{{property_definition.expected_value_type.mapping.graphql - if property_definition.expected_value_type.suffix in value_type_mapping - else property_definition.expected_value_type.suffix}}""" - - -@dataclass -class GraphQLSchema: - """ - Represents a GraphQL schema. - - This can be used to generate a GraphQL schema from TransformationRules. - - Args: - schema: The GraphQL schema. - - """ - - schema: str - - @classmethod - def from_rules(cls, transformation_rules: Rules, verbose: bool = False) -> Self: - """ - Generates a GraphQL schema from TransformationRules. - - Args: - transformation_rules: The TransformationRules to generate a GraphQL schema from. - verbose: Whether to include descriptions and names in the schema. - - Returns: - A GraphQLSchema instance. - """ - names_compliant, name_warnings = are_entity_names_dms_compliant(transformation_rules, return_report=True) - if not names_compliant: - raise exceptions.EntitiesContainNonDMSCompliantCharacters(report=generate_exception_report(name_warnings)) - - properties_redefined, redefinition_warnings = are_properties_redefined(transformation_rules, return_report=True) - if properties_redefined: - raise exceptions.PropertiesDefinedMultipleTimes(report=generate_exception_report(redefinition_warnings)) - - return cls(schema=cls.generate_schema(transformation_rules, verbose)) - - @staticmethod - def generate_schema(transformation_rules: Rules, verbose: bool) -> str: - """ - Generates a GraphQL schema from TransformationRules. - - Args: - transformation_rules: Instance of TransformationRules to generate a GraphQL schema from. - verbose: Whether to include descriptions and names in the schema. - - Returns: - A GraphQL schema. - """ - class_properties = to_class_property_pairs(transformation_rules) - - type_definitions = [] - - for class_id in class_properties: - parameters = { - "class_definition": transformation_rules.classes[class_id], - "class_properties": list(class_properties[class_id].values()), - "value_type_mapping": XSD_VALUE_TYPE_MAPPINGS, - "header": verbose, - } - - type_definitions.append(GraphQLSchema.template().render(parameters)) - - return "\n\n".join(type_definitions) - - @staticmethod - def template() -> "Template": - from jinja2 import DictLoader, Environment, Template - - template: Template = Environment( - loader=DictLoader( - { - "type_header": _TYPE_HEADER, - "type": _TYPE, - "field_header": _FIELD_HEADER, - "field": _FIELD, - "value_type": _FIELD_VALUE_TYPE, - "edge_occurrence": _EDGE_OCCURRENCE, - "attribute_occurrence": _ATTRIBUTE_OCCURRENCE, - } - ), - cache_size=1000, - ).get_template("type") - return template diff --git a/cognite/neat/legacy/rules/exporters/_rules2ontology.py b/cognite/neat/legacy/rules/exporters/_rules2ontology.py deleted file mode 100644 index 32e6de1f1..000000000 --- a/cognite/neat/legacy/rules/exporters/_rules2ontology.py +++ /dev/null @@ -1,524 +0,0 @@ -import sys -import warnings -from abc import ABC -from pathlib import Path -from typing import ClassVar - -from pydantic import BaseModel, ConfigDict, ValidationInfo, field_validator -from rdflib import DCTERMS, OWL, RDF, RDFS, XSD, BNode, Graph, Literal, Namespace, URIRef -from rdflib.collection import Collection as GraphCollection - -from cognite.neat.legacy.rules import exceptions -from cognite.neat.legacy.rules.analysis import to_class_property_pairs, to_property_dict -from cognite.neat.legacy.rules.exporters._base import BaseExporter -from cognite.neat.legacy.rules.exporters._validation import are_properties_redefined -from cognite.neat.legacy.rules.models.rules import Class, Metadata, Property, Rules -from cognite.neat.legacy.rules.models.value_types import XSD_VALUE_TYPE_MAPPINGS -from cognite.neat.utils.auxiliary import generate_exception_report -from cognite.neat.utils.rdf_ import remove_namespace_from_uri - -if sys.version_info >= (3, 11): - from typing import Self -else: - from typing_extensions import Self - - -class GraphExporter(BaseExporter[Graph], ABC): - def _export_to_file(self, filepath: Path) -> None: - self.export().serialize(destination=filepath) - - -class OWLExporter(GraphExporter): - def export(self) -> Graph: - return Ontology.from_rules(self.rules).as_owl() - - -class SHACLExporter(GraphExporter): - def export(self) -> Graph: - return Ontology.from_rules(self.rules).as_shacl() - - -class SemanticDataModelExporter(GraphExporter): - def export(self) -> Graph: - return Ontology.from_rules(self.rules).as_semantic_data_model() - - -class OntologyModel(BaseModel): - model_config: ClassVar[ConfigDict] = ConfigDict(arbitrary_types_allowed=True, strict=False, extra="allow") - - -class Ontology(OntologyModel): - """ - Represents an ontology. Thi class is used to generate an OWL ontology from a set of transformation rules. - - Args: - properties: A list of OWL properties. - classes: A list of OWL classes. - shapes: A list of SHACL node shapes. - metadata: Metadata about the ontology. - prefixes: A dictionary of prefixes and namespaces. - """ - - properties: list["OWLProperty"] - classes: list["OWLClass"] - shapes: list["SHACLNodeShape"] - metadata: "OWLMetadata" - prefixes: dict[str, Namespace] - - @classmethod - def from_rules(cls, rules: Rules) -> Self: - """ - Generates an ontology from a set of transformation rules. - - Args: - transformation_rules: Instance of TransformationRules. - - Returns: - An instance of Ontology. - """ - properties_redefined, redefinition_warnings = are_properties_redefined(rules, return_report=True) - if properties_redefined: - raise exceptions.PropertiesDefinedMultipleTimes(report=generate_exception_report(redefinition_warnings)) - - if rules.prefixes is None: - raise exceptions.PrefixMissing() - - if rules.metadata.namespace is None: - raise exceptions.MissingDataModelPrefixOrNamespace() - - return cls( - properties=[ - OWLProperty.from_list_of_properties(definition, rules.metadata.namespace) - for definition in to_property_dict(rules).values() - ], - classes=[ - OWLClass.from_class(definition, rules.metadata.namespace, rules.prefixes) - for definition in rules.classes.values() - ], - shapes=[ - SHACLNodeShape.from_rules( - rules.classes[class_], - list(properties.values()), - rules.metadata.namespace, - ) - for class_, properties in to_class_property_pairs(rules).items() - ], - metadata=OWLMetadata(**rules.metadata.model_dump()), - prefixes=rules.prefixes, - ) - - def as_shacl(self) -> Graph: - """ - Generates a SHACL graph from the ontology. - - Returns: - A SHACL graph. - """ - - shacl = Graph() - shacl.bind(self.metadata.prefix, self.metadata.namespace) - for prefix, namespace in self.prefixes.items(): - shacl.bind(prefix, namespace) - - for shape in self.shapes: - for triple in shape.triples: - shacl.add(triple) # type: ignore[arg-type] - - return shacl - - def as_owl(self) -> Graph: - """ - Generates an OWL graph from the ontology. - - Returns: - An OWL graph. - """ - owl = Graph() - owl.bind(self.metadata.prefix, self.metadata.namespace) - for prefix, namespace in self.prefixes.items(): - owl.bind(prefix, namespace) - - if self.metadata.namespace is None: - raise exceptions.MetadataSheetNamespaceNotDefined() - - owl.add((URIRef(self.metadata.namespace), RDF.type, OWL.Ontology)) - for property_ in self.properties: - for triple in property_.triples: - owl.add(triple) # type: ignore[arg-type] - - for class_ in self.classes: - for triple in class_.triples: - owl.add(triple) # type: ignore[arg-type] - - for triple in self.metadata.triples: - owl.add(triple) # type: ignore[arg-type] - - return owl - - def as_semantic_data_model(self) -> Graph: - return self.as_owl() + self.as_shacl() - - @property - def owl_triples(self) -> list[tuple]: - return list(self.as_owl()) - - @property - def shacl_triples(self) -> list[tuple]: - return list(self.as_shacl()) - - @property - def triples(self) -> list[tuple]: - return self.owl_triples + self.shacl_triples - - @property - def ontology(self) -> str: - return self.as_owl().serialize() - - @property - def constraints(self) -> str: - return self.as_shacl().serialize() - - @property - def semantic_data_model(self) -> str: - return (self.as_owl() + self.as_shacl()).serialize() - - -class OWLMetadata(Metadata): - @property - def triples(self) -> list[tuple]: - # Mandatory triples originating from Metadata mandatory fields - if self.namespace is None: - raise exceptions.MetadataSheetNamespaceNotDefined() - triples: list[tuple] = [ - (URIRef(self.namespace), DCTERMS.hasVersion, Literal(self.version)), - (URIRef(self.namespace), OWL.versionInfo, Literal(self.version)), - (URIRef(self.namespace), RDFS.label, Literal(self.name)), - (URIRef(self.namespace), DCTERMS.title, Literal(self.name)), - (URIRef(self.namespace), DCTERMS.created, Literal(self.created, datatype=XSD.dateTime)), - (URIRef(self.namespace), DCTERMS.description, Literal(self.description)), - ] - if isinstance(self.creator, list): - triples.extend([(URIRef(self.namespace), DCTERMS.creator, Literal(creator)) for creator in self.creator]) - else: - triples.append((URIRef(self.namespace), DCTERMS.creator, Literal(self.creator))) - - # Optional triples originating from Metadata optional fields - if self.updated: - triples.append((URIRef(self.namespace), DCTERMS.modified, Literal(self.updated, datatype=XSD.dateTime))) - if self.rights: - triples.append((URIRef(self.namespace), DCTERMS.rights, Literal(self.rights))) - - if self.contributor and isinstance(self.contributor, list): - triples.extend( - [ - (URIRef(self.namespace), DCTERMS.contributor, Literal(contributor)) - for contributor in self.contributor - ] - ) - elif self.contributor: - triples.append((URIRef(self.namespace), DCTERMS.contributor, Literal(self.contributor))) - - return triples - - -class OWLClass(OntologyModel): - id_: URIRef - type_: URIRef = OWL.Class - label: str | None - comment: str | None - sub_class_of: list[URIRef] | None - namespace: Namespace - - @classmethod - def from_class(cls, definition: Class, namespace: Namespace, prefixes: dict) -> Self: - if definition.parent_class and isinstance(definition.parent_class, list): - sub_class_of = [] - for parent_class in definition.parent_class: - try: - sub_class_of.append(prefixes[parent_class.prefix][parent_class.suffix]) - except KeyError: - sub_class_of.append(namespace[parent_class.suffix]) - else: - sub_class_of = None - - return cls( - id_=namespace[definition.class_id], - label=definition.class_name, - comment=definition.description, - sub_class_of=sub_class_of, - namespace=namespace, - ) - - @property - def type_triples(self) -> list[tuple]: - return [(self.id_, RDF.type, self.type_)] - - @property - def label_triples(self) -> list[tuple]: - return [(self.id_, RDFS.label, Literal(self.label))] - - @property - def comment_triples(self) -> list[tuple]: - return [(self.id_, RDFS.comment, Literal(self.comment))] - - @property - def subclass_triples(self) -> list[tuple]: - if self.sub_class_of: - return [(self.id_, RDFS.subClassOf, sub_class_of) for sub_class_of in self.sub_class_of] - else: - return [] - - @property - def triples(self) -> list[tuple]: - return self.type_triples + self.label_triples + self.comment_triples + self.subclass_triples - - -class OWLProperty(OntologyModel): - id_: URIRef - type_: set[URIRef] - label: set[str] - comment: set[str] - domain: set[URIRef] - range_: set[URIRef] - namespace: Namespace - - @staticmethod - def same_property_id(definitions: list[Property]) -> bool: - return len({definition.property_id for definition in definitions}) == 1 - - @classmethod - def from_list_of_properties(cls, definitions: list[Property], namespace: Namespace) -> "OWLProperty": - """Here list of properties is a list of properties with the same id, but different definitions.""" - - if not cls.same_property_id(definitions): - raise exceptions.PropertyDefinitionsNotForSameProperty() - - owl_property = cls.model_construct( - id_=namespace[definitions[0].property_id], - namespace=namespace, - label=set(), - comment=set(), - domain=set(), - range_=set(), - type_=set(), - ) - for definition in definitions: - owl_property.type_.add(OWL[definition.property_type]) - owl_property.range_.add( - XSD[definition.expected_value_type.suffix] - if definition.expected_value_type.suffix in XSD_VALUE_TYPE_MAPPINGS - else namespace[definition.expected_value_type.suffix] - ) - owl_property.domain.add(namespace[definition.class_id]) - - if definition.property_name: - owl_property.label.add(definition.property_name) - if definition.description: - owl_property.comment.add(definition.description) - - return owl_property - - @field_validator("type_") - def is_multi_type(cls, v, info: ValidationInfo): - if len(v) > 1: - warnings.warn( - exceptions.OntologyMultiTypeProperty( - remove_namespace_from_uri(info.data["id_"]), [remove_namespace_from_uri(t) for t in v] - ).message, - category=exceptions.OntologyMultiTypeProperty, - stacklevel=2, - ) - return v - - @field_validator("range_") - def is_multi_range(cls, v, info: ValidationInfo): - if len(v) > 1: - warnings.warn( - exceptions.OntologyMultiRangeProperty( - remove_namespace_from_uri(info.data["id_"]), [remove_namespace_from_uri(t) for t in v] - ).message, - category=exceptions.OntologyMultiRangeProperty, - stacklevel=2, - ) - return v - - @field_validator("domain") - def is_multi_domain(cls, v, info: ValidationInfo): - if len(v) > 1: - warnings.warn( - exceptions.OntologyMultiDomainProperty( - remove_namespace_from_uri(info.data["id_"]), [remove_namespace_from_uri(t) for t in v] - ).message, - category=exceptions.OntologyMultiDomainProperty, - stacklevel=2, - ) - return v - - @field_validator("label") - def has_multi_name(cls, v, info: ValidationInfo): - if len(v) > 1: - warnings.warn( - exceptions.OntologyMultiLabeledProperty(remove_namespace_from_uri(info.data["id_"]), v).message, - category=exceptions.OntologyMultiLabeledProperty, - stacklevel=2, - ) - return v - - @field_validator("comment") - def has_multi_comment(cls, v, info: ValidationInfo): - if len(v) > 1: - warnings.warn( - exceptions.OntologyMultiDefinitionProperty(remove_namespace_from_uri(info.data["id_"])).message, - category=exceptions.OntologyMultiDefinitionProperty, - stacklevel=2, - ) - return v - - @property - def domain_triples(self) -> list[tuple]: - triples: list[tuple] = [] - if len(self.domain) == 1: - triples.append((self.id_, RDFS.domain, next(iter(self.domain)))) - else: - _graph = Graph() - b_union = BNode() - b_domain = BNode() - _graph.add((self.id_, RDFS.domain, b_domain)) - _graph.add((b_domain, OWL.unionOf, b_union)) - _graph.add((b_domain, RDF.type, OWL.Class)) - _ = GraphCollection(_graph, b_union, list(self.domain)) - triples.extend(list(_graph)) - return triples - - @property - def range_triples(self) -> list[tuple]: - triples: list[tuple] = [] - if len(self.range_) == 1: - triples.append((self.id_, RDFS.range, next(iter(self.range_)))) - else: - _graph = Graph() - b_union = BNode() - b_range = BNode() - _graph.add((self.id_, RDFS.range, b_range)) - _graph.add((b_range, OWL.unionOf, b_union)) - _graph.add((b_range, RDF.type, OWL.Class)) - _graph.add((b_range, OWL.unionOf, b_union)) - _graph.add((b_range, RDF.type, OWL.Class)) - _ = GraphCollection(_graph, b_union, list(self.range_)) - triples.extend(list(_graph)) - return triples - - @property - def type_triples(self) -> list[tuple]: - return [(self.id_, RDF.type, type_) for type_ in self.type_] - - @property - def label_triples(self) -> list[tuple]: - label = list(filter(None, self.label)) - return [(self.id_, RDFS.label, Literal(label[0] if label else self.id_))] - - @property - def comment_triples(self) -> list[tuple]: - return [(self.id_, RDFS.comment, Literal("\n".join(filter(None, self.comment))))] - - @property - def triples(self) -> list[tuple]: - return self.type_triples + self.label_triples + self.comment_triples + self.domain_triples + self.range_triples - - -SHACL = Namespace("http://www.w3.org/ns/shacl#") - - -class SHACLNodeShape(OntologyModel): - id_: URIRef - type_: URIRef = SHACL.NodeShape - target_class: URIRef - property_shapes: list["SHACLPropertyShape"] - namespace: Namespace - - @property - def type_triples(self) -> list[tuple]: - return [(self.id_, RDF.type, self.type_)] - - @property - def target_class_triples(self) -> list[tuple]: - return [(self.id_, SHACL.targetClass, self.target_class)] - - @property - def property_shapes_triples(self) -> list[tuple]: - triples: list[tuple] = [] - for property_shape in self.property_shapes: - triples.append((self.id_, SHACL.property, property_shape.id_)) - triples.extend(property_shape.triples) - return triples - - @property - def triples(self) -> list[tuple]: - return self.type_triples + self.target_class_triples + self.property_shapes_triples - - @classmethod - def from_rules( - cls, class_definition: Class, property_definitions: list[Property], namespace: Namespace - ) -> "SHACLNodeShape": - return cls( - id_=namespace[f"{class_definition.class_id}Shape"], - target_class=namespace[class_definition.class_id], - property_shapes=[SHACLPropertyShape.from_property(prop, namespace) for prop in property_definitions], - namespace=namespace, - ) - - -class SHACLPropertyShape(OntologyModel): - id_: BNode - type_: URIRef = SHACL.property - path: URIRef # URIRef to property in OWL - node_kind: URIRef # SHACL.IRI or SHACL.Literal - expected_value_type: URIRef - min_count: int | None - max_count: int | None - namespace: Namespace - - @property - def path_triples(self) -> list[tuple]: - return [(self.id_, SHACL.path, self.path)] - - @property - def node_kind_triples(self) -> list[tuple]: - triples: list[tuple] = [(self.id_, SHACL.nodeKind, self.node_kind)] - - if self.node_kind == SHACL.Literal: - triples.append((self.id_, SHACL.datatype, self.expected_value_type)) - else: - triples.append((self.id_, SHACL.node, self.expected_value_type)) - - return triples - - @property - def cardinality_triples(self) -> list[tuple]: - triples: list[tuple] = [] - if self.min_count: - triples.append((self.id_, SHACL.minCount, Literal(self.min_count))) - if self.max_count: - triples.append((self.id_, SHACL.maxCount, Literal(self.max_count))) - - return triples - - @property - def triples(self) -> list[tuple]: - return self.path_triples + self.node_kind_triples + self.cardinality_triples - - @classmethod - def from_property(cls, definition: Property, namespace: Namespace) -> "SHACLPropertyShape": - return cls( - id_=BNode(), - path=namespace[definition.property_id], - node_kind=SHACL.IRI if definition.property_type == "ObjectProperty" else SHACL.Literal, - expected_value_type=( - namespace[f"{definition.expected_value_type.suffix}Shape"] - if definition.property_type == "ObjectProperty" - else XSD[definition.expected_value_type.suffix] - ), - min_count=definition.min_count, - max_count=definition.max_count, - namespace=namespace, - ) diff --git a/cognite/neat/legacy/rules/exporters/_rules2pydantic_models.py b/cognite/neat/legacy/rules/exporters/_rules2pydantic_models.py deleted file mode 100644 index 958ced8b7..000000000 --- a/cognite/neat/legacy/rules/exporters/_rules2pydantic_models.py +++ /dev/null @@ -1,748 +0,0 @@ -import re -import sys -import warnings -from collections.abc import Iterable -from datetime import date, datetime -from typing import Any, TypeAlias, cast - -from cognite.client.data_classes import Asset, Relationship -from cognite.client.data_classes.data_modeling import EdgeApply, MappedPropertyApply, NodeApply, NodeOrEdgeData, ViewId -from cognite.client.data_classes.data_modeling.views import SingleHopConnectionDefinitionApply, ViewApply -from pydantic import BaseModel, ConfigDict, Field, create_model -from pydantic._internal._model_construction import ModelMetaclass -from rdflib import Graph, URIRef - -from cognite.neat.legacy.graph.loaders.core.rdf_to_assets import NeatMetadataKeys -from cognite.neat.legacy.graph.transformations.query_generator.sparql import build_construct_query, triples2dictionary -from cognite.neat.legacy.rules import exceptions -from cognite.neat.legacy.rules.analysis import define_class_asset_mapping, to_class_property_pairs -from cognite.neat.legacy.rules.exporters._rules2dms import DMSSchemaComponents -from cognite.neat.legacy.rules.exporters._validation import are_entity_names_dms_compliant -from cognite.neat.legacy.rules.models.rules import Property, Rules -from cognite.neat.legacy.rules.models.value_types import ValueTypeMapping -from cognite.neat.utils.auxiliary import create_sha256_hash, generate_exception_report - -if sys.version_info >= (3, 11): - from datetime import UTC -else: - from datetime import timezone - - UTC = timezone.utc - -EdgeOneToOne: TypeAlias = str # type: ignore[valid-type] -EdgeOneToMany: TypeAlias = list[str] # type: ignore[valid-type] - - -def default_model_configuration( - external_id: str | None = None, - name: str | None = None, - description: str | None = None, - space: str | None = None, - version: str | None = None, -) -> ConfigDict: - return ConfigDict( - populate_by_name=True, - str_strip_whitespace=True, - arbitrary_types_allowed=True, - strict=False, - extra="allow", - json_schema_extra={ - "title": name, - "description": description, - "external_id": external_id, - "name": name, - "space": space, - "version": version, - }, - ) - - -def default_class_methods(): - return [ - from_dict, - from_graph, - to_asset, - to_relationship, - to_node, - to_edge, - get_field_description, - get_field_name, - ] - - -def default_class_property_methods(): - return [model_name, model_external_id, model_description, attributes, edges_one_to_one, edges_one_to_many] - - -def rules_to_pydantic_models( - rules: Rules, - methods: list | None = None, - add_extra_fields: bool = False, -) -> dict[str, ModelMetaclass]: - """ - Generate pydantic models from rules. - - Args: - rules: Rules to generate pydantic models from - methods: List of methods to register for pydantic models, - by default None meaning defaulting to base neat methods. - add_extra_fields: Flag indicating to add extra fields to pydantic models, by default False - - Returns: - Dictionary containing pydantic models with class ids as key and pydantic model as value - containing properties defined for the given class(es) in the rules. - - - !!! note "Default NEAT methods" - Default NEAT methods which are added to the generated pydantic models are: - - - `get_field_description`: Returns description of the field if one exists. - - `get_field_name`: Returns name of the field if one exists. - - `model_name`: Returns the name of the model if one exists. - - `model_external_id`: Returns the external id of the model if one exists. - - `model_description`: Returns the description of the model if one exists. - - `from_graph`: Creates model instance from class instance stored in RDF graph. - - `to_asset`: Creates CDF Asset instance from model instance. - - `to_node`: Creates DMS node from model instance. - - `to_edge`: Creates DMS edge from model instance. - - `attributes`: Returns list of node attributes. - - `edges_one_to_one`: Returns list of node edges one to one. - - `edges_one_to_many`: Returns list of node edges one to many. - - - !!! note "Limitations" - Currently this will take only unique properties and those which column rule_type - is set to rdfpath, hence only_rdfpath = True. This means that at the moment - we do not support UNION, i.e. ability to handle multiple rdfpaths for the same - property. This is needed option and should be added in the second version of the exporter. - - !!! note "Classes and Properties must be DMS Compliant" - Rules must be DMS compliant, i.e. class and property ids must obey the following regex: - r`(^[a-zA-Z][a-zA-Z0-9_]{0,253}[a-zA-Z0-9]?$)` and must not contain reserved keywords. - - Classes reserved keywords: `Query`, `Mutation`, `Subscription`, `String`, `Int32`, - `Int64`, `Int`, `Float32`, `Float64`, `Float`,`Timestamp`, `JSONObject`, `Date`, - `Numeric`, `Boolean`, `PageInfo`, `File`, `Sequence`, `TimeSeries` - - Properties reserved keywords: `space`, `externalId`, `createdTime`, `lastUpdatedTime`, - `deletedTime`, `edge_id` `node_id`, `project_id`, `property_group`, `seq`, `tg_table_name`, `extensions` - """ - - names_compliant, name_warnings = are_entity_names_dms_compliant(rules, return_report=True) - if not names_compliant: - raise exceptions.EntitiesContainNonDMSCompliantCharacters(report=generate_exception_report(name_warnings)) - - if methods is None: - methods = default_class_methods() + default_class_property_methods() - - class_property_pairs = to_class_property_pairs(rules, only_rdfpath=True) - - models: dict[str, ModelMetaclass] = {} - for class_, properties in class_property_pairs.items(): - # generate fields from define properties - fields = _properties_to_pydantic_fields(properties) - - if add_extra_fields: - # store default class to relationship mapping field - # which is used by the `to_relationship` method - fields["class_to_asset_mapping"] = ( - dict[str, list[str]], - Field( - define_class_asset_mapping(rules, class_), - description="This is a helper field used for generating CDF Asset out of model instance", - ), - ) - - model = _dictionary_to_pydantic_model( - model_id=class_, - model_fields_definition=fields, - model_name=rules.classes[class_].class_name, - model_description=rules.classes[class_].description, - model_methods=methods, - space=rules.metadata.prefix, - version=rules.metadata.version, - ) - - models[class_] = model - - return models - - -def _properties_to_pydantic_fields( - properties: dict[str, Property], -) -> dict[str, tuple[EdgeOneToMany | EdgeOneToOne | type | list[type], Any]]: - """Turns definition of properties into pydantic fields. - - Parameters - ---------- - properties : dict[str, Property] - Dictionary of properties - - Returns - ------- - dict[str, tuple[EdgeOneToMany | EdgeOneToOne | type | list[type], Any]] - Dictionary of pydantic fields - """ - - fields: dict[str, tuple[EdgeOneToMany | EdgeOneToOne | type | list[type], Any]] - - fields = {"external_id": (str, Field(..., alias="external_id"))} - - for property_id, property_ in properties.items(): - field_type = _define_field_type(property_) - field_definition: dict = { - "alias": property_.property_name, - "description": property_.description if property_.description else None, - # keys below will be available under json_schema_extra - "property_type": field_type.__name__ if field_type in [EdgeOneToOne, EdgeOneToMany] else "NodeAttribute", - "property_value_type": property_.expected_value_type.suffix, - "property_name": property_.property_name, - "property_id": property_.property_id, - } - - if field_type.__name__ in [EdgeOneToMany.__name__, list.__name__]: - field_definition["min_length"] = property_.min_count - field_definition["max_length"] = property_.max_count - - if not property_.is_mandatory and not property_.default: - field_definition["default"] = None - elif property_.default: - field_definition["default"] = property_.default - - fields[property_id] = ( - field_type, - Field(**field_definition), # type: ignore[pydantic-field] - ) - return fields - - -def _define_field_type(property_: Property): - if property_.property_type == "ObjectProperty" and property_.max_count == 1: - return EdgeOneToOne - elif property_.property_type == "ObjectProperty": - return EdgeOneToMany - elif property_.property_type == "DatatypeProperty" and property_.max_count == 1: - return cast(ValueTypeMapping, property_.expected_value_type.mapping).python - else: - inner_type = cast(ValueTypeMapping, property_.expected_value_type.mapping).python - return list[inner_type] # type: ignore[valid-type] - - -def _dictionary_to_pydantic_model( - model_id: str, - model_fields_definition: dict, - space: str | None = None, - version: str | None = None, - model_name: str | None = None, - model_description: str | None = None, - model_configuration: ConfigDict | None = None, - model_methods: list | None = None, - validators: list | None = None, -) -> type[BaseModel]: - """Generates pydantic model from dictionary containing definition of fields. - Additionally, it adds methods to the model and validators. - - Parameters - ---------- - model_name : str - Name of the model, typically an id of the class - model_fields_definition : dict - Dictionary containing definition of fields - model_configuration : ConfigDict, optional - Configuration of pydantic model, by default None - methods : list, optional - Methods that work on fields once model is instantiated, by default None - property_attributes : list, optional - Property attributed that work on model fields once model is instantiated, by default None - validators : list, optional - Any custom validators to be added in addition to base pydantic ones, by default None - - Returns - ------- - type[BaseModel] - Pydantic model - """ - - if not model_configuration: - model_configuration = default_model_configuration( - external_id=model_id, space=space, version=version, name=model_name, description=model_description - ) - - fields: dict[str, tuple | type[BaseModel]] = {} - - for field_name, value in model_fields_definition.items(): - if isinstance(value, tuple): - fields[field_name] = value - # Nested classes - elif isinstance(value, dict): - fields[field_name] = (_dictionary_to_pydantic_model(f"{model_id}_{field_name}", value), ...) - else: - raise exceptions.FieldValueOfUnknownType(field_name, value) - - model = create_model(model_id, __config__=model_configuration, **fields) # type: ignore[call-overload] - - if model_methods: - for method in model_methods: - try: - setattr(model, method.__name__, method) - except AttributeError: - try: - setattr(model, method.fget.__name__, method) - except AttributeError: - setattr(model, method.__func__.fget.__name__, method) - - # any additional validators to be added - if validators: - ... - - return model - - -@classmethod # type: ignore -@property -def attributes(cls) -> list[str]: - return [ - field - for field in cls.model_fields - if (schema := cls.model_fields[field].json_schema_extra) and schema.get("property_type") == "NodeAttribute" - ] - - -@classmethod # type: ignore -@property -def edges_one_to_one(cls) -> list[str]: - return [ - field - for field in cls.model_fields - if (schema := cls.model_fields[field].json_schema_extra) and schema.get("property_type") == "EdgeOneToOne" - ] - - -@classmethod # type: ignore -@property -def edges_one_to_many(cls) -> list[str]: - return [ - field - for field in cls.model_fields - if (schema := cls.model_fields[field].json_schema_extra) and schema.get("property_type") == "EdgeOneToMany" - ] - - -# Define methods that work on model instance -@classmethod # type: ignore[misc] -def from_dict(cls, dictionary: dict[str, list[str] | str]): - # wrangle results to dict - args: dict[str, list[Any] | Any] = {} - for field in cls.model_fields.values(): - # if field is not required and not in result, skip - if not field.is_required() and field.alias not in dictionary: - continue - - # if field is required and not in result, raise error - if field.is_required() and field.alias not in dictionary: - raise exceptions.PropertyRequiredButNotProvided(field.alias, cast(str, dictionary["external_id"])) - - # flatten result if field is not edge or list of values - if field.annotation.__name__ not in [EdgeOneToMany.__name__, list.__name__]: - if isinstance(dictionary[field.alias], list) and len(dictionary[field.alias]) > 1: - warnings.warn( - exceptions.FieldContainsMoreThanOneValue( - field.alias, - len(dictionary[field.alias]), - ).message, - category=exceptions.FieldContainsMoreThanOneValue, - stacklevel=2, - ) - - args[field.alias] = dictionary[field.alias][0] - else: - args[field.alias] = dictionary[field.alias] - - return cls(**args) - - -# Define methods that work on model instance -@classmethod # type: ignore[misc] -def from_graph( - cls, - graph: Graph, - transformation_rules: Rules, - external_id: URIRef, - incomplete_instance_allowed: bool = True, -): - """Method that creates model instance from class instance stored in graph. - - Args: - graph: Graph containing triples of class instance - transformation_rules: Transformation rules - external_id: External id of class instance to be used to instantiate associated pydantic model - incomplete_instance_allowed: Flag allowing incomplete instances to be queried. Defaults to True. - - Raises: - exceptions.MissingInstanceTriples: _description_ - exceptions.PropertyRequiredButNotProvided: _description_ - - Returns: - Pydantic model instance - """ - - # build sparql query for given object - class_ = cls.__name__ - - # here properties_optional is set to True in order to also return - # incomplete class instances so we catch them and raise exceptions - sparql_construct_query = build_construct_query( - graph, - class_, - transformation_rules, - class_instances=[external_id], - properties_optional=incomplete_instance_allowed, - ) - # In the docs, a construct query is said to return triple - # Not sure if the triple will be URIRef or Literal - query_result = cast(Iterable[tuple[URIRef, URIRef, str | URIRef]], graph.query(sparql_construct_query)) - - dictionary = triples2dictionary(query_result)[external_id] - - if not dictionary: - raise exceptions.MissingInstanceTriples(external_id) - - return cls.from_dict(dictionary) - - -# define methods that creates asset out of model id (default) -def to_asset( - self, - data_set_id: int, - add_system_metadata: bool = True, - metadata_keys: NeatMetadataKeys | None = None, - add_labels: bool = True, -) -> Asset: - """Convert model instance to asset. - - Parameters - ---------- - add_system_metadata : bool, optional - Flag indicating to add or not system/neat metadata, by default True - metadata_keys : NeatMetadataKeys | None, optional - Definition of system/neat metadata, by default None - add_labels : bool, optional - To add or not labels to asset, by default True - data_set_id : int, optional - Data set id to which asset belongs to, by default None - - Returns - ------- - Asset - Asset instance - """ - # Needs copy otherwise modifications impact all instances - if not self.class_to_asset_mapping: - raise exceptions.ClassToAssetMappingNotDefined(self.__class__.__name__) - - class_instance_dictionary = self.model_dump(by_alias=True) - adapted_mapping_config = _adapt_mapping_config_by_instance( - self.external_id, class_instance_dictionary, self.class_to_asset_mapping - ) - asset = _class_to_asset_instance_dictionary(class_instance_dictionary, adapted_mapping_config) - - # set default metadata keys if not provided - metadata_keys = NeatMetadataKeys() if metadata_keys is None else metadata_keys - - # add system metadata - if add_system_metadata: - _add_system_metadata(self, metadata_keys, asset) - - if add_labels: - asset["labels"] = [asset["metadata"][metadata_keys.type], "non-historic"] - - return Asset(**asset, data_set_id=data_set_id) - - -def _add_system_metadata(self, metadata_keys: NeatMetadataKeys, asset: dict): - asset["metadata"][metadata_keys.type] = self.__class__.__name__ - asset["metadata"][metadata_keys.identifier] = self.external_id - now = str(datetime.now(UTC)) - asset["metadata"][metadata_keys.start_time] = now - asset["metadata"][metadata_keys.update_time] = now - asset["metadata"][metadata_keys.active] = "true" - - -def _adapt_mapping_config_by_instance(external_id, class_instance_dictionary, mapping_config): - adapted_mapping_config = {} - for asset_field, class_properties in mapping_config.items(): - if asset_field != "metadata": - # We are selecting first property that is available in the graph - # and add it to corresponding asset field and exit loop - for property_ in class_properties: - if class_instance_dictionary.get(property_, None): - adapted_mapping_config[asset_field] = property_ - break - - elif metadata_keys := [ - property_ for property_ in class_properties if class_instance_dictionary.get(property_, None) - ]: - adapted_mapping_config["metadata"] = metadata_keys - - # Raise warnings for fields that will miss in asset - for asset_field in mapping_config: - if asset_field not in adapted_mapping_config: - warnings.warn( - exceptions.FieldNotFoundInInstance(external_id, asset_field).message, - category=exceptions.FieldNotFoundInInstance, - stacklevel=2, - ) - - return adapted_mapping_config - - -def _class_to_asset_instance_dictionary(class_instance_dictionary, mapping_config): - for key, values in mapping_config.items(): - if key != "metadata": - mapping_config[key] = class_instance_dictionary.get(values, None) - else: - mapping_config[key] = {value: class_instance_dictionary.get(value, None) for value in values} - - return mapping_config - - -def to_relationship(self, transformation_rules: Rules) -> Relationship: - """Creates relationship instance from model instance.""" - raise NotImplementedError() - - -def to_node( - self, data_model_or_view_id: DMSSchemaComponents | ViewId | None = None, add_class_prefix: bool = False -) -> NodeApply: - """Creates DMS node from the instance of pydantic model. - - Args: - data_model_or_view_id: Instance of DataModel or ViewID. Defaults to None. - add_class_prefix: Whether to add class id (i.e.model name) prefix to external_id of View. Defaults to False. - - Returns: - Instance of NodeApply containing node information. - - - !!! note "Default Behavior" - If no DataModel or ViewID is passed, then the default behavior is to create node - using View information which is by default stored under `model_json_schema` attribute of pydantic model. - !!! note "Limitations" - Currently adding class prefix is only possible if the node is created if ViewID is passed or - if pydantic model already contains View information (which default behavior). - """ - - if isinstance(data_model_or_view_id, DMSSchemaComponents): - return _to_node_using_data_model(self, data_model_or_view_id, add_class_prefix) - elif isinstance(data_model_or_view_id, ViewId): - if not data_model_or_view_id.space: - raise exceptions.SpaceNotDefined() - if not data_model_or_view_id.external_id: - raise exceptions.ViewExternalIdNotDefined() - if not data_model_or_view_id.version: - raise exceptions.ViewVersionNotDefined() - return _to_node_using_view_id(self, data_model_or_view_id) - else: - space = self.model_json_schema().get("space", None) - external_id = self.model_json_schema().get("external_id", None) - version = self.model_json_schema().get("version", None) - - if not space: - raise exceptions.SpaceNotDefined() - if not external_id: - raise exceptions.ViewExternalIdNotDefined() - if not version: - raise exceptions.ViewVersionNotDefined() - - return _to_node_using_view_id(self, ViewId(space, external_id, version)) - - -def _to_node_using_view_id(self, view_id: ViewId) -> NodeApply: - attributes: dict = { - attribute: ( - getattr(self, attribute).isoformat() - if isinstance(getattr(self, attribute), date) - else getattr(self, attribute) - ) - for attribute in self.attributes - } - - edges_one_to_one: dict = {} - - for edge in self.edges_one_to_one: - if external_id := getattr(self, edge): - edges_one_to_one[edge] = { - "space": self.model_json_schema()["space"], - "externalId": external_id, - } - - return NodeApply( - space=self.model_json_schema()["space"], - external_id=self.external_id, - sources=[ - NodeOrEdgeData( - source=view_id, - properties=attributes | edges_one_to_one, - ) - ], - ) - - -def _to_node_using_data_model(self, data_model, add_class_prefix) -> NodeApply: - view_id: str = f"{self.model_json_schema()['space']}:{type(self).__name__}" - - if not set(self.attributes + self.edges_one_to_one).issubset(set(data_model.views[view_id].properties.keys())): - raise exceptions.InstancePropertiesNotMatchingViewProperties( - self.__class__.__name__, - self.attributes + self.edges_one_to_one + self.edges_one_to_many, - list(data_model.views[view_id].properties.keys()), - ) - - attributes: dict = { - attribute: ( - getattr(self, attribute).isoformat() - if isinstance(getattr(self, attribute), date) - else getattr(self, attribute) - ) - for attribute in self.attributes - } - if add_class_prefix: - edges_one_to_one: dict = {} - dm_view = data_model.views[view_id] - if dm_view.properties: - for edge in self.edges_one_to_one: - mapped_property = dm_view.properties[edge] - if isinstance(mapped_property, MappedPropertyApply): - object_view = mapped_property.source - if object_view: - object_class_name = object_view.external_id - - if external_id := getattr(self, edge): - edges_one_to_one[edge] = { - "space": data_model.views[view_id].space, - "externalId": add_class_prefix_to_xid( - class_name=object_class_name, - external_id=external_id, - ), - } - else: - edges_one_to_one = {} - - for edge in self.edges_one_to_one: - if external_id := getattr(self, edge): - edges_one_to_one[edge] = { - "space": self.model_json_schema()["space"], - "externalId": external_id, - } - - return NodeApply( - space=data_model.views[view_id].space, - external_id=self.external_id, - sources=[ - NodeOrEdgeData( - source=data_model.views[view_id].as_id(), - properties=attributes | edges_one_to_one, - ) - ], - ) - - -def to_edge(self, data_model: DMSSchemaComponents, add_class_prefix: bool = False) -> list[EdgeApply]: - """Creates DMS edge from pydantic model.""" - edges: list[EdgeApply] = [] - - def is_external_id_valid(external_id: str) -> bool: - # should match "^[^\x00]{1,255}$" and not be None or none - if external_id == "None" or external_id == "none": - return False - return bool(re.match(r"^[^\x00]{1,255}$", external_id)) - - class_name: str = type(self).__name__ - view_id: str = f"{self.model_json_schema()['space']}:{class_name}" - - for edge_one_to_many in self.edges_one_to_many: - edge_type_id = f"{class_name}.{edge_one_to_many}" - if end_node_ids := getattr(self, edge_one_to_many): - for end_node_id in end_node_ids: - if not is_external_id_valid(end_node_id): - warnings.warn( - message=exceptions.EdgeConditionUnmet(edge_one_to_many).message, - category=exceptions.EdgeConditionUnmet, - stacklevel=2, - ) - end_node_external_id = end_node_id - if add_class_prefix: - end_node_class_name = _get_end_node_class_name(data_model.views[view_id], edge_one_to_many) - if end_node_class_name: - end_node_external_id = add_class_prefix_to_xid(end_node_class_name, end_node_id) - else: - warnings.warn( - message=exceptions.EdgeConditionUnmet(edge_one_to_many).message, - category=exceptions.EdgeConditionUnmet, - stacklevel=2, - ) - - external_id = f"{self.external_id}.{edge_one_to_many}.{end_node_external_id}" - edge = EdgeApply( - space=data_model.views[view_id].space, - external_id=external_id if len(external_id) < 256 else create_sha256_hash(external_id), - type=(data_model.views[view_id].space, edge_type_id), - start_node=(data_model.views[view_id].space, self.external_id), - end_node=(data_model.views[view_id].space, end_node_external_id), - ) - edges.append(edge) - return edges - - -def _get_end_node_class_name(view: ViewApply, edge: str) -> str | None: - """Get the class name of the end node of an edge.""" - if view.properties is None: - return None - mapped_instance = view.properties[edge] - if isinstance(mapped_instance, SingleHopConnectionDefinitionApply) and mapped_instance.source: - return mapped_instance.source.external_id - return None - - -@classmethod # type: ignore -@property -def model_name(cls) -> str | None: - """Returns the name of the model if one exists""" - return cls.model_json_schema().get("class_name", None) - - -@classmethod # type: ignore -@property -def model_external_id(cls) -> str | None: - """Returns the external id of the model if one exists""" - return cls.model_json_schema().get("class_id", None) - - -@classmethod # type: ignore -@property -def model_description(cls) -> str | None: - """Returns the description of the model if one exists""" - return cls.model_json_schema().get("description", None) - - -@classmethod # type: ignore -def get_field_description(cls, field_id: str) -> str | None: - """Returns description of the field if one exists""" - if field_id in cls.model_fields: - return cls.model_fields[field_id].description - else: - return None - - -@classmethod # type: ignore -def get_field_name(cls, field_id: str) -> str | None: - """Returns name of the field if one exists""" - if field_id in cls.model_fields and cls.model_fields[field_id].json_schema_extra: - if "property_name" in cls.model_fields[field_id].json_schema_extra: - return cls.model_fields[field_id].json_schema_extra["property_name"] - return None - - -def add_class_prefix_to_xid(class_name: str, external_id: str) -> str: - """Adds class name as prefix to the external_id""" - return f"{class_name}_{external_id}" diff --git a/cognite/neat/legacy/rules/exporters/_rules2rules.py b/cognite/neat/legacy/rules/exporters/_rules2rules.py deleted file mode 100644 index de2587f0d..000000000 --- a/cognite/neat/legacy/rules/exporters/_rules2rules.py +++ /dev/null @@ -1,105 +0,0 @@ -"""This module provides set of methods that perform conversion of TransformationRules -to TransformationRules for purpose of for example: - -- subsetting the data model to only include desired classes and their properties -- converting classes/properties ids/names to DMS compliant format -""" - -import logging -import re -import warnings -from typing import Any - -from cognite.neat.legacy.rules.analysis import get_defined_classes -from cognite.neat.legacy.rules.models.rules import Rules - - -def subset_rules(rules: Rules, desired_classes: set, skip_validation: bool = False) -> Rules: - """ - Subset transformation rules to only include desired classes and their properties. - - Args: - transformation_rules: Instance of TransformationRules to subset - desired_classes: Desired classes to include in the reduced data model - skip_validation: Whether to skip underlying pydantic validation, by default False - - Returns: - Instance of TransformationRules - - !!! note "Skipping Validation" - It is fine to skip validation since we are deriving the reduced data model from data - model (i.e. TransformationRules) which has already been validated. - - """ - - defined_classes = get_defined_classes(rules) - possible_classes = defined_classes.intersection(desired_classes) - impossible_classes = desired_classes - possible_classes - - if not possible_classes: - logging.error("None of the desired classes are defined in the data model!") - raise ValueError("None of the desired classes are defined in the data model!") - - if impossible_classes: - logging.warning(f"Could not find the following classes defined in the data model: {impossible_classes}") - warnings.warn( - f"Could not find the following classes defined in the data model: {impossible_classes}", stacklevel=2 - ) - - reduced_data_model: dict[str, Any] = { - "metadata": rules.metadata.model_copy(), - "prefixes": (rules.prefixes or {}).copy(), - "classes": {}, - "properties": {}, - "instances": (rules.instances or []).copy(), - } - - logging.info(f"Reducing data model to only include the following classes: {possible_classes}") - for class_ in possible_classes: - reduced_data_model["classes"][class_] = rules.classes[class_] - - for id_, property_definition in rules.properties.items(): - if property_definition.class_id in possible_classes: - reduced_data_model["properties"][id_] = property_definition - - if skip_validation: - return Rules.model_construct(**reduced_data_model) - else: - return Rules(**reduced_data_model) - - -def to_dms_compliant_rules(rules: Rules) -> Rules: - raise NotImplementedError() - - -# to be used for conversion to DMS compliant format -def to_dms_name(name: str, entity_type: str, fix_casing: bool = False) -> str: - """ - Repairs an entity name to conform to GraphQL naming convention - >>> repair_name("wind-speed", "property") - 'windspeed' - >>> repair_name("Wind.Speed", "property", True) - 'windSpeed' - >>> repair_name("windSpeed", "class", True) - 'WindSpeed' - >>> repair_name("22windSpeed", "class") - '_22windSpeed' - """ - - # Remove any non GraphQL compliant characters - repaired_string = re.sub(r"[^_a-zA-Z0-9]", "", name) - - # Name must start with a letter or underscore - if repaired_string[0].isdigit(): - repaired_string = f"_{repaired_string}" - - if not fix_casing: - return repaired_string - # Property names must be camelCase - if entity_type == "property" and repaired_string[0].isupper(): - return repaired_string[0].lower() + repaired_string[1:] - # Class names must be PascalCase - elif entity_type == "class" and repaired_string[0].islower(): - return repaired_string[0].upper() + repaired_string[1:] - else: - return repaired_string diff --git a/cognite/neat/legacy/rules/exporters/_rules2triples.py b/cognite/neat/legacy/rules/exporters/_rules2triples.py deleted file mode 100644 index b78765a90..000000000 --- a/cognite/neat/legacy/rules/exporters/_rules2triples.py +++ /dev/null @@ -1,38 +0,0 @@ -from pathlib import Path - -from rdflib.term import Node - -from cognite.neat.legacy.rules.models.rules import Rules - -from ._base import BaseExporter - - -class TripleExporter(BaseExporter[list[tuple[Node, Node, Node]]]): - """ - Exporter for transformation rules instances sheet to RDF triples - """ - - def _export_to_file(self, filepath: Path) -> None: - raise NotImplementedError("Export to file not implemented") - - def export(self) -> list[tuple[Node, Node, Node]]: - return get_instances_as_triples(self.rules) - - -def get_instances_as_triples(transformation_rules: Rules) -> list[tuple[Node, Node, Node]]: - """ - Converts transformation rules instances sheet to RDF triples - - Args: - transformation_rules: An instance of TransformationRules pydantic class - - Returns: - List of triples provided as tuples - - """ - if transformation_rules.instances: - return [ - (instance.instance, instance.property_, instance.value) # type: ignore[misc] - for instance in transformation_rules.instances - ] - return [] diff --git a/cognite/neat/legacy/rules/exporters/_validation.py b/cognite/neat/legacy/rules/exporters/_validation.py deleted file mode 100644 index 70ed7371d..000000000 --- a/cognite/neat/legacy/rules/exporters/_validation.py +++ /dev/null @@ -1,146 +0,0 @@ -import re -import warnings -from typing import Literal, overload - -from cognite.neat.exceptions import wrangle_warnings -from cognite.neat.legacy.rules import exceptions -from cognite.neat.legacy.rules.models.rules import ( - Rules, - dms_property_id_compliance_regex, - value_id_compliance_regex, - view_id_compliance_regex, -) - - -@overload -def are_entity_names_dms_compliant( - transformation_rules: Rules, return_report: Literal[True] -) -> tuple[bool, list[dict]]: ... - - -@overload -def are_entity_names_dms_compliant(transformation_rules: Rules, return_report: Literal[False] = False) -> bool: ... - - -def are_entity_names_dms_compliant( - transformation_rules: Rules, return_report: bool = False -) -> bool | tuple[bool, list[dict]]: - """Check if data model definitions are valid.""" - - flag: bool = True - with warnings.catch_warnings(record=True) as validation_warnings: - for class_ in transformation_rules.classes.values(): - if not re.match(view_id_compliance_regex, class_.class_id): - warnings.warn( - exceptions.EntityIDNotDMSCompliant( - "Class", class_.class_id, f"[Classes/Class/{class_.class_id}]" - ).message, - category=exceptions.EntityIDNotDMSCompliant, - stacklevel=2, - ) - flag = False - - for row, property_ in transformation_rules.properties.items(): - # check class id which would resolve as view/container id - if not re.match(view_id_compliance_regex, property_.class_id): - warnings.warn( - exceptions.EntityIDNotDMSCompliant( - "Class", property_.class_id, f"[Properties/Class/{row}]" - ).message, - category=exceptions.EntityIDNotDMSCompliant, - stacklevel=2, - ) - flag = False - - # check property id which would resolve as view/container id - if not re.match(dms_property_id_compliance_regex, property_.property_id): - warnings.warn( - exceptions.EntityIDNotDMSCompliant( - "Property", property_.property_id, f"[Properties/Property/{row}]" - ).message, - category=exceptions.EntityIDNotDMSCompliant, - stacklevel=2, - ) - flag = False - - # check container external id - if property_.container and not re.match(view_id_compliance_regex, property_.container.external_id): - warnings.warn( - exceptions.EntityIDNotDMSCompliant( - "Container", property_.container.external_id, f"[Properties/Container/{row}]" - ).message, - category=exceptions.EntityIDNotDMSCompliant, - stacklevel=2, - ) - flag = False - - # check container property external id - if property_.container_property and not re.match( - dms_property_id_compliance_regex, property_.container_property - ): - warnings.warn( - exceptions.EntityIDNotDMSCompliant( - "Container Property", property_.container_property, f"[Properties/Container Property/{row}]" - ).message, - category=exceptions.EntityIDNotDMSCompliant, - stacklevel=2, - ) - flag = False - - # expected value type, as it is case sensitive should be ok - if not re.match(value_id_compliance_regex, property_.expected_value_type.external_id): - warnings.warn( - exceptions.EntityIDNotDMSCompliant( - "Value type", property_.expected_value_type.external_id, f"[Properties/Type/{row}]" - ).message, - category=exceptions.EntityIDNotDMSCompliant, - stacklevel=2, - ) - flag = False - - if return_report: - return flag, wrangle_warnings(validation_warnings) - else: - return flag - - -@overload -def are_properties_redefined(transformation_rules: Rules, return_report: Literal[True]) -> tuple[bool, list[dict]]: ... - - -@overload -def are_properties_redefined(transformation_rules: Rules, return_report: Literal[False] = False) -> bool: ... - - -def are_properties_redefined( - transformation_rules: Rules, return_report: bool = False -) -> bool | tuple[bool, list[dict]]: - flag: bool = False - with warnings.catch_warnings(record=True) as validation_warnings: - analyzed_properties = {} - for property_ in transformation_rules.properties.values(): - if property_.property_id not in analyzed_properties: - analyzed_properties[property_.property_id] = [property_.class_id] - elif property_.class_id in analyzed_properties[property_.property_id]: - flag = True - warnings.warn( - exceptions.PropertyRedefined(property_.property_id, property_.class_id).message, - category=exceptions.EntityIDNotDMSCompliant, - stacklevel=2, - ) - - else: - analyzed_properties[property_.property_id].append(property_.class_id) - - if return_report: - return flag, wrangle_warnings(validation_warnings) - else: - return flag - - -def property_ids_camel_case_compliant(transformation_rules) -> bool | tuple[bool, list[dict]]: - raise NotImplementedError() - - -def class_id_pascal_case_compliant(transformation_rules) -> bool | tuple[bool, list[dict]]: - raise NotImplementedError() diff --git a/cognite/neat/legacy/rules/importers/__init__.py b/cognite/neat/legacy/rules/importers/__init__.py deleted file mode 100644 index a5562143c..000000000 --- a/cognite/neat/legacy/rules/importers/__init__.py +++ /dev/null @@ -1,22 +0,0 @@ -from ._base import BaseImporter -from ._dict2rules import ArbitraryDictImporter -from ._dms2rules import DMSImporter -from ._graph2rules import GraphImporter -from ._json2rules import ArbitraryJSONImporter -from ._owl2rules import OWLImporter -from ._spreadsheet2rules import ExcelImporter, GoogleSheetImporter -from ._xsd2rules import XSDImporter -from ._yaml2rules import ArbitraryYAMLImporter - -__all__ = [ - "BaseImporter", - "ArbitraryDictImporter", - "ArbitraryJSONImporter", - "ArbitraryYAMLImporter", - "DMSImporter", - "OWLImporter", - "XSDImporter", - "GraphImporter", - "ExcelImporter", - "GoogleSheetImporter", -] diff --git a/cognite/neat/legacy/rules/importers/_base.py b/cognite/neat/legacy/rules/importers/_base.py deleted file mode 100644 index d92f8be55..000000000 --- a/cognite/neat/legacy/rules/importers/_base.py +++ /dev/null @@ -1,66 +0,0 @@ -import getpass -from abc import ABC, abstractmethod -from datetime import datetime - -import pandas as pd -from pydantic_core import ErrorDetails - -from cognite.neat.legacy.rules.models.raw_rules import RawRules -from cognite.neat.legacy.rules.models.rules import Rules - - -class BaseImporter(ABC): - """ - BaseImporter class which all importers inherit from. - """ - - @abstractmethod - def to_tables(self) -> dict[str, pd.DataFrame]: - """Creates raw tables from the data.""" - raise NotImplementedError - - def to_raw_rules(self) -> RawRules: - """Creates `RawRules` object from the data.""" - - tables = self.to_tables() - - return RawRules.from_tables(tables=tables, importer_type=self.__class__.__name__) - - def to_rules( - self, - return_report: bool = False, - skip_validation: bool = False, - validators_to_skip: set[str] | None = None, - ) -> tuple[Rules | None, list[ErrorDetails] | None, list | None] | Rules: - """ - Creates `Rules` object from the data. - - Args: - return_report: To return validation report. Defaults to False. - skip_validation: Bypasses Rules validation. Defaults to False. - validators_to_skip: List of validators to skip. Defaults to None. - - Returns: - Instance of `Rules`, which can be validated, not validated based on - `skip_validation` flag, or partially validated if `validators_to_skip` is set, - and optional list of errors and warnings if - `return_report` is set to True. - - !!! Note "Skip Validation - `skip_validation` flag should be only used for purpose when `Rules` object - is exported to an Excel file. Do not use this flag for any other purpose! - """ - - raw_rules = self.to_raw_rules() - - return raw_rules.to_rules(return_report, skip_validation, validators_to_skip) - - def _default_metadata(self): - return { - "prefix": "neat", - "version": "0.1.0", - "title": "Neat Imported Data Model", - "created": datetime.now().replace(microsecond=0).isoformat(), - "creator": getpass.getuser(), - "description": f"Imported using {type(self).__name__}", - } diff --git a/cognite/neat/legacy/rules/importers/_dict2rules.py b/cognite/neat/legacy/rules/importers/_dict2rules.py deleted file mode 100644 index c93c545fb..000000000 --- a/cognite/neat/legacy/rules/importers/_dict2rules.py +++ /dev/null @@ -1,158 +0,0 @@ -from datetime import datetime, timezone -from typing import Any, Literal - -import pandas as pd - -from cognite.neat.legacy.rules.models.tables import Tables - -from ._base import BaseImporter - - -class ArbitraryDictImporter(BaseImporter): - """ - Importer for an arbitrary dictionary. - - This importer infers the data model from the dictionary based on the shape of the data. - - Args: - data: dictionary containing Rules definitions. - relationship_direction: Direction of relationships, either "parent-to-child" or "child-to-parent". Dictionaries - are nested with children nested inside parents. This option determines whether the resulting rules - will have an edge from parents to children or from children to parents. - """ - - def __init__( - self, - data: dict[str, Any], - relationship_direction: Literal["parent-to-child", "child-to-parent"] = "parent-to-child", - ): - self.data = data - self.relationship_direction = relationship_direction - - def to_tables(self) -> dict[str, pd.DataFrame]: - metadata = pd.Series( - dict( - title="OpenAPI to DM transformation rules", - description="OpenAPI to DM transformation rules", - version="0.1", - creator="Cognite", - created=datetime.now(timezone.utc).replace(microsecond=0).isoformat(), - namespace="http://purl.org/cognite/neat#", - prefix="neat", - data_model_name="OpenAPI", - cdf_space_name="OpenAPI", - ) - ).reset_index() - finder = _TripleFinder(self.relationship_direction) - finder.find_triples(self.data) - - return { - Tables.metadata: metadata, - Tables.classes: pd.DataFrame(finder.classes).T, - Tables.properties: pd.DataFrame(finder.properties).T, - } - - -class _TripleFinder: - def __init__(self, relationship_direction: Literal["parent-to-child", "child-to-parent"]) -> None: - self.classes: dict[str, dict[str, Any]] = {} - self.properties: dict[str, dict[str, Any]] = {} - self.relationship_direction = relationship_direction - - def find_triples(self, data: dict[str, Any]) -> None: - self._convert_dict_to_classes_and_props(data) - - def _convert_dict_to_classes_and_props( - self, data: dict, parent_property_name: str | None = None, grand_parent_property_name: str | None = None - ) -> None: - if isinstance(data, dict) and len(data) == 0: - return - elif isinstance(data, dict) and parent_property_name is None: - for key, value in data.items(): - self._convert_dict_to_classes_and_props(value, key) - elif isinstance(data, dict): - self.add_class(parent_property_name, "missing", grand_parent_property_name, is_list=False) - for key, value in data.items(): - self._convert_dict_to_classes_and_props(value, key, parent_property_name) - elif isinstance(data, list): - if parent_property_name is not None and grand_parent_property_name is not None: - data_type = self._get_list_type(data, parent_property_name) - self.add_property(grand_parent_property_name, parent_property_name, data_type, "missing", is_list=True) - for item in data: - self._convert_dict_to_classes_and_props(item, parent_property_name, grand_parent_property_name) - elif isinstance(data, bool | int | float | str) and parent_property_name is not None: - data_type = self._get_primitive_data_type(data) - self.add_property(grand_parent_property_name, parent_property_name, data_type, "missing") - else: - raise ValueError(f"Unknown type {type(data)}") - - def add_class( - self, class_name: str, description: str = "", parent_class_name: str | None = None, is_list: bool = False - ): - if class_name in self.classes: - return - class_ = {"Class": class_name, "description": description} - if parent_class_name: - if self.relationship_direction == "child-to-parent": - self.add_property(class_name, "parent", parent_class_name, "missing", is_list=False) - elif self.relationship_direction == "parent-to-child": - self.add_property(parent_class_name, class_name, class_name, "missing", is_list) - else: - raise ValueError(f"Unknown relationship direction {self.relationship_direction}") - self.classes[class_name] = class_ - - def add_property( - self, - class_name: str, - property_name: str, - property_type: str, - description: str = "missing", - is_list: bool = False, - ): - if class_name + property_name in self.properties: - return - prop = dict( - class_id=class_name, - property_id=property_name, - property_name=property_name, - property_type="ObjectProperty", - description=description, - expected_value_type=property_type, - max_count=1 if not is_list else None, - cdf_resource_type="Asset", - resource_type_property="Asset", - rule_type="rdfpath", - rule=f"neat:{class_name}(neat:{property_name})", - label="linked to", - ) - self.properties[class_name + property_name] = prop - - @staticmethod - def _get_primitive_data_type(data: Any, errors: Literal["raise", "empty"] = "raise") -> str: - data_type = type(data) - if data_type is bool: - return "boolean" - elif data_type is int: - return "integer" - elif data_type is float: - return "float" - elif data_type is str and not pd.isna(pd.to_datetime(data, errors="coerce")): - return "dateTime" - elif data_type is str: - return "string" - - if errors == "empty": - return "" - else: - raise ValueError(f"Unknown primitive type {data_type}") - - @classmethod - def _get_list_type(cls, data: list[Any], class_name: str) -> str: - if isinstance(data[0], dict): - return class_name - - data_types = {cls._get_primitive_data_type(item, "empty") for item in data} - if "" in data_types or len(data_types) > 1: - # Fallback to string - return "string" - return data_types.pop() diff --git a/cognite/neat/legacy/rules/importers/_dms2rules.py b/cognite/neat/legacy/rules/importers/_dms2rules.py deleted file mode 100644 index 2e73feb5a..000000000 --- a/cognite/neat/legacy/rules/importers/_dms2rules.py +++ /dev/null @@ -1,194 +0,0 @@ -import sys -from collections.abc import Sequence -from datetime import datetime -from typing import Any, cast - -import pandas as pd -from cognite.client import CogniteClient -from cognite.client.data_classes.data_modeling import ( - DataModel, - DirectRelation, - EdgeConnection, - MappedProperty, - SingleHopConnectionDefinition, - View, -) -from cognite.client.data_classes.data_modeling.data_types import ListablePropertyType -from cognite.client.data_classes.data_modeling.ids import DataModelIdentifier, ViewId - -from cognite.neat.legacy.rules.models.tables import Tables -from cognite.neat.legacy.rules.models.value_types import DMS_VALUE_TYPE_MAPPINGS, XSD_VALUE_TYPE_MAPPINGS - -from ._base import BaseImporter - -if sys.version_info >= (3, 11): - from typing import Self -else: - from typing_extensions import Self - - -class DMSImporter(BaseImporter): - """ - Converts a Data Model Storage (DMS) data model to a set of transformation rules. - - Args: - views: List of views to convert to transformation rules. - """ - - def __init__(self, views: Sequence[View] | DataModel[View], metadata: dict[str, str | float] | None = None): - if isinstance(views, DataModel): - self.views = views.views - else: - self.views = list(views) - - if metadata is None: - self.metadata = self._default_metadata() - if len(self.views) == 1: - self.metadata["version"] = self.views[0].version - self.metadata["prefix"] = self.views[0].space - else: - self.metadata = metadata - - if isinstance(views, DataModel): - if views.name: - self.metadata["title"] = views.name - if views.description: - self.metadata["description"] = views.description - if views.space: - self.metadata["prefix"] = views.space - if views.external_id: - self.metadata["suffix"] = views.external_id - if views.version: - self.metadata["version"] = views.version - - @classmethod - def from_cdf(cls, client: CogniteClient, data_model: DataModelIdentifier) -> Self: - """ - Converts a Data Model Storage (DMS) data model to a set of transformation rules. - - Args: - client: Cognite client to use for fetching data models. - data_model: List of data models to convert to transformation rules. - - !!! Note - Beware that `DataModelIdentifier` is just type hint that you cannot instantiate - directly, e.g. `id = DataModelIdentifier(space=, external_id, version)` will fail. - Instead, provide `data_models` as a list of three element tuples, - e.g. `[(space, external_id, version)]`, or two element tuples, - e.g. `[(space, external_id)]`, where `space` represents CDF space name, - `external_id` represents data model external ID, and `version` - represents data model version. If `version` is not provided, whatever is - the first version CDF returns it will give you that one. - - """ - data_model = client.data_modeling.data_models.retrieve(data_model, inline_views=True)[0] - - # Avoid duplicate views (same view can be used by multiple data models) - views_by_id: dict[ViewId, View] = {} - for view in data_model.views: - views_by_id[view.as_id()] = view - - if metadata := cls._to_metadata(data_model): - return cls(list(views_by_id.values()), metadata) - else: - return cls(list(views_by_id.values())) - - def to_tables(self) -> dict[str, pd.DataFrame]: - classes: list[dict[str, str | float]] = [] - properties: list[dict[str, str | float]] = [] - for view in self.views: - class_id = view.external_id - classes.append( - { - "Class": class_id, - "Name": view.name or float("nan"), - "Description": view.description or float("nan"), - } - ) - for prop_id, prop in view.properties.items(): - if isinstance(prop, MappedProperty): - # Edge 1-1 - if isinstance(prop.type, DirectRelation): - type_ = cast(ViewId, prop.source).external_id - else: - type_ = cast( - str, DMS_VALUE_TYPE_MAPPINGS.get(type(prop.type), XSD_VALUE_TYPE_MAPPINGS["string"]).xsd - ) - - default_value = prop.default_value - name = prop.name or prop_id - description = prop.description or float("nan") - - # Edge 1-many - elif isinstance(prop, EdgeConnection): - type_ = prop.source.external_id - default_value = None - name = prop.name or prop_id - description = prop.description or float("nan") - else: - raise NotImplementedError(f"Property type {type(prop)} not supported") - - max_count: str | float = "1" - if isinstance(prop, SingleHopConnectionDefinition) or ( - isinstance(prop, MappedProperty) - and isinstance(prop.type, ListablePropertyType) - and prop.type.is_list - ): - max_count = float("nan") - - min_count: str | float = "1" - if isinstance(prop, SingleHopConnectionDefinition) or ( - isinstance(prop, MappedProperty) and prop.nullable - ): - min_count = "0" - - properties.append( - { - "Class": class_id, - "Property": prop_id, - "Name": name, - "Description": description, - "Type": type_, - "Default": cast(Any, default_value), # fixes issues with mypy - "Min Count": min_count, - "Max Count": max_count, - "Rule Type": "rdfpath", - "Rule": f"cim:{class_id}(cim:{prop_id})", - } - ) - - return { - Tables.metadata: pd.Series(self.metadata).to_frame("value").reset_index(), - Tables.classes: pd.DataFrame(classes), - Tables.properties: pd.DataFrame(properties), - } - - @staticmethod - def _to_metadata(data_model: DataModel) -> dict: - mapping = { - "space": "cdf_space_name", - "external_id": "data_model_name", - "version": "version", - "description": "description", - "created_time": "created", - "last_updated_time": "updated", - "name": "title", - } - - metadata = {mapping.get(k, k): v for k, v in data_model.to_pandas().value.to_dict().items() if k in mapping} - - metadata["prefix"] = metadata["data_model_name"] - metadata["creator"] = "Unknown" - - if "created" in metadata: - metadata["created"] = datetime.utcfromtimestamp(metadata["created"] / 1e3) - if "updated" in metadata: - metadata["updated"] = datetime.utcfromtimestamp(metadata["updated"] / 1e3) - - return metadata - - def _repr_html_(self) -> str: - """Pretty display of the DMSImporter object in a Notebook""" - dump = self.metadata - dump["views_count"] = len(self.views) - return pd.Series(dump).to_frame("value")._repr_html_() # type: ignore[operator] diff --git a/cognite/neat/legacy/rules/importers/_graph2rules.py b/cognite/neat/legacy/rules/importers/_graph2rules.py deleted file mode 100644 index 6f008fc4c..000000000 --- a/cognite/neat/legacy/rules/importers/_graph2rules.py +++ /dev/null @@ -1,308 +0,0 @@ -"""This module performs importing of graph to TransformationRules pydantic class. -In more details, it traverses the graph and abstracts class and properties, basically -generating a list of rules based on which nodes that form the graph are made. -""" - -import warnings -from datetime import datetime -from typing import cast - -import pandas as pd -from rdflib import Graph, Literal, Namespace, URIRef - -from cognite.neat.constants import get_default_prefixes -from cognite.neat.legacy.rules import exceptions -from cognite.neat.legacy.rules.exporters._rules2rules import to_dms_name -from cognite.neat.legacy.rules.models.tables import Tables -from cognite.neat.utils.rdf_ import get_namespace, remove_namespace_from_uri, uri_to_short_form - -from ._base import BaseImporter - - -class GraphImporter(BaseImporter): - """ - Convert RDF graph, containing nodes and edges, to tables/ transformation rules / Excel file. - - Args: - graph: RDF graph to be imported - max_number_of_instance: Max number of instances to be analyzed for each class in RDF graph - - - !!! Note - Due to high degree of flexibility of RDF graphs, the RDF graph is not guaranteed to be - converted to a complete and/or valid `Rules` object. Therefore, it is recommended to - call method `to_raw_rules` to get the raw rules which one should export to Excel file - using `exporter.ExcelExporter` and then manually edit the Excel file by checking - validation report file produced by the exporter. - - """ - - def __init__(self, graph: Graph, max_number_of_instance: int = -1): - self.graph = graph - self.max_number_of_instance = max_number_of_instance - - def to_tables(self) -> dict[str, pd.DataFrame]: - data_model, prefixes = _graph_to_data_model_dict(self.graph, self.max_number_of_instance) - - return { - Tables.metadata: _parse_metadata_df(), - Tables.classes: _parse_classes_df(data_model, prefixes), - Tables.properties: _parse_properties_df(data_model, prefixes), - Tables.prefixes: _parse_prefixes_df(prefixes), - } - - -def _create_default_properties_parsing_config() -> dict[str, tuple[str, ...]]: - # TODO: these are to be read from Property pydantic model - return { - "header": ( - "Class", - "Property", - "Description", - "Type", - "Min Count", - "Max Count", - "Rule Type", - "Rule", - "Source", - "Source Entity Name", - "Match Type", - "Comment", - ) - } - - -def _create_default_classes_parsing_config() -> dict[str, tuple[str, ...]]: - # TODO: these are to be read from Class pydantic model - return {"header": ("Class", "Description", "Parent Class", "Source", "Source Entity Name", "Match Type", "Comment")} - - -def _parse_prefixes_df(prefixes: dict[str, Namespace]) -> pd.DataFrame: - return pd.DataFrame.from_dict({"Prefix": list(prefixes.keys()), "URI": [str(uri) for uri in prefixes.values()]}) - - -def _parse_metadata_df() -> pd.DataFrame: - clean_list = { - "namespace": "http://purl.org/cognite/neat/", - "prefix": "playground", - "external_id": "neat", - "version": "1.0.0", - "isCurrentVersion": True, - "created": datetime.utcnow(), - "updated": datetime.utcnow(), - "title": "RDF Graph Inferred Data Model", - "description": "This data model has been inferred with NEAT", - "creator": "NEAT", - "contributor": "NEAT", - "rights": "Unknown rights of usage", - "license": "Unknown license", - } - return pd.DataFrame(list(clean_list.items()), columns=["Key", "Value"]) - - -def _parse_classes_df(data_model: dict, prefixes: dict, parsing_config: dict | None = None) -> pd.DataFrame: - if parsing_config is None: - parsing_config = _create_default_classes_parsing_config() - - class_rows = [] - - for class_ in data_model: - sanitized_class = to_dms_name(class_, "class") - class_rows.append( - [ - sanitized_class, - None, - None, - str(prefixes[data_model[class_]["uri"].split(":")[0]]) + class_, - class_, - "exact", - "Parsed from RDF graph", - ] - ) - - return pd.DataFrame(class_rows, columns=parsing_config["header"]) - - -def _parse_properties_df(data_model: dict, prefixes: dict, parsing_config: dict | None = None) -> pd.DataFrame: - if parsing_config is None: - parsing_config = _create_default_properties_parsing_config() - - property_rows = [] - - for class_ in data_model: - sanitized_class = to_dms_name(class_, "class") - for property_ in data_model[class_]["properties"]: - for type_ in data_model[class_]["properties"][property_]["value_type"]: - sanitized_property = to_dms_name(property_, "property") - - max_count = max(data_model[class_]["properties"][property_]["occurrence"]) - - property_rows.append( - [ - sanitized_class, - sanitized_property, - None, - to_dms_name(type_, "value-type"), - 0, # setting min count to 0 to be more flexible (all properties are optional) - None if max_count > 1 else 1, - "rdfpath", - f'{data_model[class_]["uri"]}({data_model[class_]["properties"][property_]["uri"]})', - str(prefixes[data_model[class_]["properties"][property_]["uri"].split(":")[0]]) + property_, - property_, - "exact", - "Parsed from RDF graph", - ] - ) - - return pd.DataFrame(property_rows, columns=parsing_config["header"]) - - -def _graph_to_data_model_dict(graph: Graph, max_number_of_instance: int = -1) -> tuple[dict, dict]: - """Convert RDF graph to dictionary defining data model and prefixes of the graph - - Args: - graph: RDF graph to be converted to TransformationRules object - max_number_of_instance: Max number of instances to be considered for each class - - Returns: - Tuple of data model and prefixes of the graph - """ - data_model: dict[str, dict] = {} - - prefixes: dict[str, Namespace] = get_default_prefixes() - - for class_ in _get_class_ids(graph): - _add_uri_namespace_to_prefixes(class_, prefixes) - class_name = remove_namespace_from_uri(class_) - - if class_name in data_model: - warnings.warn( - exceptions.GraphClassNameCollision(class_name=class_name).message, - category=exceptions.GraphClassNameCollision, - stacklevel=2, - ) - class_name = f"{class_name}_{len(data_model)+1}" - - data_model[class_name] = {"properties": {}, "uri": uri_to_short_form(class_, prefixes)} - - for instance in _get_class_instance_ids(graph, class_, max_number_of_instance): - for property_, occurrence, data_type, object_type in _define_instance_properties(graph, instance): - property_name = remove_namespace_from_uri(property_) - _add_uri_namespace_to_prefixes(property_, prefixes) - - type_ = data_type if data_type else object_type - - # this is to skip rdf:type property - if not type_: - continue - - type_name = remove_namespace_from_uri(type_) - _add_uri_namespace_to_prefixes(type_, prefixes) - - if property_name not in data_model[class_name]["properties"]: - data_model[class_name]["properties"][property_name] = { - "occurrence": {occurrence.value}, - "value_type": {type_name: {"uri": uri_to_short_form(type_, prefixes)}}, - "uri": uri_to_short_form(property_, prefixes), - } - - elif type_name not in data_model[class_name]["properties"][property_name]["value_type"]: - data_model[class_name]["properties"][property_name]["value_type"][type_name] = { - "uri": uri_to_short_form(type_, prefixes) - } - warnings.warn( - exceptions.GraphClassPropertyMultiValueTypes( - class_name=class_name, - property_name=property_name, - types=list(data_model[class_name]["properties"][property_name]["value_type"].keys()), - ).message, - category=exceptions.GraphClassPropertyMultiValueTypes, - stacklevel=3, - ) - - elif occurrence.value not in data_model[class_name]["properties"][property_name]["occurrence"]: - data_model[class_name]["properties"][property_name]["occurrence"].add(occurrence.value) - - warnings.warn( - exceptions.GraphClassPropertyMultiOccurrence( - class_name=class_name, property_name=property_name - ).message, - category=exceptions.GraphClassPropertyMultiOccurrence, - stacklevel=3, - ) - else: - continue - - return data_model, prefixes - - -def _add_uri_namespace_to_prefixes(URI: URIRef, prefixes: dict[str, Namespace]): - """Add URI to prefixes dict if not already present - - Args: - URI: URI from which namespace is being extracted - prefixes: Dict of prefixes and namespaces - """ - if Namespace(get_namespace(URI)) not in prefixes.values(): - prefixes[f"prefix-{len(prefixes)+1}"] = Namespace(get_namespace(URI)) - - -def _get_class_ids(graph: Graph) -> list[URIRef]: - """Get instances ids for a given class - - Args: - graph: Graph containing class instances - class_: Class for which instances are to be found - namespace: Namespace of given class (to avoid writing long URIs) - limit: Max number of instances to return, by default -1 meaning all instances - - Returns: - List of class instance URIs - """ - - query_statement = """SELECT ?class (count(?s) as ?instances ) - WHERE { ?s a ?class . } - group by ?class order by DESC(?instances)""" - - return [cast(tuple[URIRef, int], res)[0] for res in list(graph.query(query_statement))] - - -def _get_class_instance_ids(graph: Graph, class_id: URIRef, max_number_of_instance: int = -1) -> list[URIRef]: - """Get instances ids for a given class - - Args: - graph: Graph containing class instances - class_id: Class id for which instances are to be found - - Returns: - List of class instance URIs - """ - - query_statement = "SELECT DISTINCT ?subject WHERE { ?subject a .}".replace("class", class_id) - if max_number_of_instance > 0: - query_statement += f" LIMIT {max_number_of_instance}" - return [cast(tuple[URIRef], res)[0] for res in list(graph.query(query_statement))] - - -def _define_instance_properties( - graph: Graph, instance_id: URIRef -) -> list[tuple[URIRef, Literal, URIRef | None, None | URIRef]]: - """Get properties of a given instance - - Args: - graph: Graph containing class instances - instance_id: Instance id for which properties are to be found and defined - - Returns: - List of properties of a given instance - """ - query_statement = """SELECT ?property (count(?property) as ?occurrence) ?dataType ?objectType - WHERE { ?property ?value . - BIND(datatype(?value) AS ?dataType) - OPTIONAL {?value rdf:type ?objectType .} - } - GROUP BY ?property ?dataType ?objectType""" - - results = graph.query(query_statement.replace("instance_id", instance_id)) - - return [cast(tuple[URIRef, Literal, URIRef | None, None | URIRef], res) for res in list(results)] diff --git a/cognite/neat/legacy/rules/importers/_json2rules.py b/cognite/neat/legacy/rules/importers/_json2rules.py deleted file mode 100644 index c215ffe88..000000000 --- a/cognite/neat/legacy/rules/importers/_json2rules.py +++ /dev/null @@ -1,39 +0,0 @@ -import json -from pathlib import Path -from typing import Literal - -from ._dict2rules import ArbitraryDictImporter - - -class ArbitraryJSONImporter(ArbitraryDictImporter): - """ - Importer for data given in a JSON file or string. - - This importer infers the data model from the JSON string based on the shape of the data. - - Args: - json_path_or_str: Path to file with JSON or a JSON string. - relationship_direction: Direction of relationships, either "parent-to-child" or "child-to-parent". JSON - files are nested with children nested inside parents. This option determines whether the resulting rules - will have an edge from parents to children or from children to parents. - - """ - - def __init__( - self, - json_path_or_str: Path, - relationship_direction: Literal["parent-to-child", "child-to-parent"] = "parent-to-child", - ): - if isinstance(json_path_or_str, str): - data = json.loads(json_path_or_str) - super().__init__(data, relationship_direction) - elif isinstance(json_path_or_str, Path): - if not json_path_or_str.exists(): - raise ValueError(f"File {json_path_or_str} does not exist") - if json_path_or_str.suffix != ".json": - raise ValueError(f"File {json_path_or_str} is not a JSON file") - self.json_path = json_path_or_str - data = json.loads(json_path_or_str.read_text()) - super().__init__(data, relationship_direction) - else: - raise TypeError(f"Expected Path or str, got {type(json_path_or_str)}") diff --git a/cognite/neat/legacy/rules/importers/_owl2rules/__init__.py b/cognite/neat/legacy/rules/importers/_owl2rules/__init__.py deleted file mode 100644 index c1644faf6..000000000 --- a/cognite/neat/legacy/rules/importers/_owl2rules/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from ._owl2rules import OWLImporter - -__all__ = ["OWLImporter"] diff --git a/cognite/neat/legacy/rules/importers/_owl2rules/_owl2classes.py b/cognite/neat/legacy/rules/importers/_owl2rules/_owl2classes.py deleted file mode 100644 index ff4b0b442..000000000 --- a/cognite/neat/legacy/rules/importers/_owl2rules/_owl2classes.py +++ /dev/null @@ -1,239 +0,0 @@ -from typing import cast - -import numpy as np -import pandas as pd -from rdflib import OWL, Graph - -from cognite.neat.utils.rdf_ import remove_namespace_from_uri - - -def parse_owl_classes(graph: Graph, make_compliant: bool = False, language: str = "en") -> pd.DataFrame: - """Parse owl classes from graph to pandas dataframe. - - Args: - graph: Graph containing owl classes - make_compliant: Flag for generating compliant classes, by default False - language: Language to use for parsing, by default "en" - - Returns: - Dataframe containing owl classes - - !!! note "make_compliant" - If `make_compliant` is set to True, in presence of errors, default values will be used instead. - This makes the method very opinionated, but results in a compliant classes. - """ - - query = """ - SELECT ?class ?name ?description ?parentClass ?deprecated ?deprecationDate - ?replacedBy ?source ?sourceEntity ?match ?comment - WHERE { - ?class a owl:Class . - OPTIONAL {?class rdfs:subClassOf ?parentClass }. - OPTIONAL {?class rdfs:label ?name }. - OPTIONAL {?class rdfs:comment ?description} . - OPTIONAL {?class owl:deprecated ?deprecated} . - FILTER (!isBlank(?class)) - FILTER (!bound(?parentClass) || !isBlank(?parentClass)) - FILTER (!bound(?name) || LANG(?name) = "" || LANGMATCHES(LANG(?name), "en")) - FILTER (!bound(?description) || LANG(?description) = "" || LANGMATCHES(LANG(?description), "en")) - } - """ - - # create raw dataframe - - raw_df = _parse_raw_dataframe(cast(list[tuple], list(graph.query(query.replace("en", language))))) - if raw_df.empty: - return pd.concat([raw_df, pd.DataFrame([len(raw_df) * [""]])], ignore_index=True) - - # group values and clean up - processed_df = _clean_up_classes(raw_df) - - # make compliant - if make_compliant: - processed_df = make_classes_compliant(processed_df) - - # Make Parent Class list elements into string joined with comma - processed_df["Parent Class"] = processed_df["Parent Class"].apply( - lambda x: ", ".join(x) if isinstance(x, list) and x else None - ) - - return processed_df - - -def _parse_raw_dataframe(query_results: list[tuple]) -> pd.DataFrame: - df = pd.DataFrame( - query_results, - columns=[ - "Class", - "Name", - "Description", - "Parent Class", - "Deprecated", - "Deprecation Date", - "Replaced By", - "Source", - "Source Entity Name", - "Match", - "Comment", - ], - ) - if df.empty: - return df - - # # remove NaNs - df.replace(np.nan, "", regex=True, inplace=True) - - df.Source = df.Class - df.Class = df.Class.apply(lambda x: remove_namespace_from_uri(x)) - df["Source Entity Name"] = df.Class - df["Match"] = len(df) * ["exact"] - df["Parent Class"] = df["Parent Class"].apply(lambda x: remove_namespace_from_uri(x)) - - return df - - -def _clean_up_classes(df: pd.DataFrame) -> pd.DataFrame: - clean_list = [ - { - "Class": class_, - "Name": group_df["Name"].unique()[0], - "Description": "\n".join(list(group_df.Description.unique())), - "Parent Class": ", ".join(list(group_df["Parent Class"].unique())), - "Deprecated": group_df.Deprecated.unique()[0], - "Deprecation Date": group_df["Deprecation Date"].unique()[0], - "Replaced By": group_df["Replaced By"].unique()[0], - "Source": group_df["Source"].unique()[0], - "Source Entity Name": group_df["Name"].unique()[0], - "Match Type": group_df["Match"].unique()[0], - "Comment": group_df["Comment"].unique()[0], - } - for class_, group_df in df.groupby("Class") - ] - - df = pd.DataFrame(clean_list) - - # bring NaNs back - df.replace("", None, inplace=True) - - # split Parent Class column back into list - df["Parent Class"] = df["Parent Class"].apply(lambda x: x.split(", ") if isinstance(x, str) else None) - - return df - - -def make_classes_compliant(classes: pd.DataFrame) -> pd.DataFrame: - """Make classes compliant. - - Returns: - Dataframe containing compliant classes - - !!! note "About the compliant classes" - The compliant classes are based on the OWL base ontology, but adapted to NEAT and use in CDF. - One thing to note is that this method would not be able to fix issues with class ids which - are not compliant with the CDF naming convention. For example, if a class id contains a space, - starts with a number, etc. This will cause issues when trying to create the class in CDF. - """ - - # Replace empty or non-string values in "Match Type" column with "exact" - classes["Match Type"] = classes["Match Type"].fillna("exact") - classes["Match Type"] = classes["Match Type"].apply( - lambda x: "exact" if not isinstance(x, str) or len(x) == 0 else x - ) - - # Replace empty or non-string values in "Comment" column with a default value - classes["Comment"] = classes["Comment"].fillna("Imported from Ontology by NEAT") - classes["Comment"] = classes["Comment"].apply( - lambda x: "Imported from Ontology by NEAT" if not isinstance(x, str) or len(x) == 0 else x - ) - - # Replace empty or non-boolean values in "Deprecated" column with False - classes["Deprecated"] = classes["Deprecated"].fillna(False) - classes["Deprecated"] = classes["Deprecated"].apply(lambda x: False if not isinstance(x, bool) else x) - - # Add _object_property_class, _data_type_property_class, _thing_class to the dataframe - classes = pd.concat( - [classes, pd.DataFrame([_object_property_class(), _data_type_property_class(), _thing_class()])], - ignore_index=True, - ) - - # Reduce length of elements in the "Description" column to 1024 characters - classes["Description"] = classes["Description"].apply(lambda x: x[:1024] if isinstance(x, str) else None) - - # Add missing parent classes to the dataframe - classes = pd.concat( - [classes, pd.DataFrame(_add_parent_class(classes))], - ignore_index=True, - ) - - return classes - - -def _object_property_class() -> dict: - return { - "Class": "ObjectProperty", - "Name": None, - "Description": "The class of object properties.", - "Parent Class": None, - "Source": OWL.ObjectProperty, - "Match Type": "exact", - "Comment": "Added by NEAT based on owl:ObjectProperty but adapted to NEAT and use in CDF.", - } - - -def _data_type_property_class() -> dict: - return { - "Class": "DatatypeProperty", - "Name": None, - "Description": "The class of data properties.", - "Parent Class": None, - "Source": OWL.DatatypeProperty, - "Match Type": "exact", - "Comment": "Added by NEAT based on owl:DatatypeProperty but adapted to NEAT and use in CDF.", - } - - -def _thing_class() -> dict: - return { - "Class": "ThingContainer", - "Name": None, - "Description": "The class of holding class individuals.", - "Parent Class": None, - "Source": OWL.Thing, - "Match Type": "exact", - "Comment": ( - "Added by NEAT. " - "Imported from OWL base ontology, it is meant for use as a default" - " value type for object properties which miss a declared range." - ), - } - - -def _add_parent_class(df: pd.DataFrame) -> list[dict]: - parent_set = { - item - for sublist in df["Parent Class"].tolist() - if sublist - for item in sublist - if item != "" and item is not None - } - class_set = set(df["Class"].tolist()) - - rows = [] - for missing_parent_class in parent_set.difference(class_set): - rows += [ - { - "Class": missing_parent_class, - "Name": None, - "Description": None, - "Parent Class": None, - "Source": None, - "Match Type": None, - "Comment": ( - "Added by NEAT. " - "This is a parent class that is missing in the ontology. " - "It is added by NEAT to make the ontology compliant with CDF." - ), - } - ] - - return rows diff --git a/cognite/neat/legacy/rules/importers/_owl2rules/_owl2metadata.py b/cognite/neat/legacy/rules/importers/_owl2rules/_owl2metadata.py deleted file mode 100644 index 4e2397625..000000000 --- a/cognite/neat/legacy/rules/importers/_owl2rules/_owl2metadata.py +++ /dev/null @@ -1,260 +0,0 @@ -import datetime -import re - -import pandas as pd -from rdflib import Graph, Namespace - -from cognite.neat.legacy.rules.models.rules import ( - cdf_space_compliance_regex, - data_model_id_compliance_regex, - prefix_compliance_regex, - version_compliance_regex, -) -from cognite.neat.utils.collection_ import remove_none_elements_from_set -from cognite.neat.utils.rdf_ import convert_rdflib_content - - -def parse_owl_metadata(graph: Graph, make_compliant: bool = False) -> pd.DataFrame: - """Parse owl metadata from graph to pandas dataframe. - - Args: - graph: Graph containing owl metadata - make_compliant: Flag for generating compliant metadata, by default False - - Returns: - Dataframe containing owl metadata - - !!! note "make_compliant" - If `make_compliant` is set to True, in presence of errors, default values will be used instead. - This makes the method very opinionated, but results in a compliant metadata. - - - """ - # TODO: Move dataframe to dict representation - - query = """SELECT ?namespace ?prefix ?dataModelName ?cdfSpaceName ?version ?isCurrentVersion - ?created ?updated ?title ?description ?creator ?contributor ?rights ?license - WHERE { - ?namespace a owl:Ontology . - OPTIONAL {?namespace owl:versionInfo ?version }. - OPTIONAL {?namespace dcterms:creator ?creator }. - OPTIONAL {?namespace dcterms:title|rdfs:label|skos:prefLabel ?title }. - OPTIONAL {?namespace dcterms:contributor ?contributor }. - OPTIONAL {?namespace dcterms:modified ?updated }. - OPTIONAL {?namespace dcterms:created ?created }. - OPTIONAL {?namespace dcterms:description ?description }. - - OPTIONAL {?namespace dcterms:rights|dc:rights ?rights }. - - OPTIONAL {?namespace dcterms:license|dc:license ?license }. - FILTER (!isBlank(?namespace)) - FILTER (!bound(?description) || LANG(?description) = "" || LANGMATCHES(LANG(?description), "en")) - FILTER (!bound(?title) || LANG(?title) = "" || LANGMATCHES(LANG(?title), "en")) - } - """ - - results = [{item for item in sublist} for sublist in list(zip(*graph.query(query), strict=True))] - - clean_list = convert_rdflib_content( - { - "namespace": Namespace(results[0].pop()), - "prefix": results[1].pop(), - "dataModelName": results[2].pop(), - "cdfSpaceName": results[3].pop(), - "version": results[4].pop(), - "isCurrentVersion": results[5].pop(), - "created": results[6].pop(), - "updated": results[7].pop(), - "title": results[8].pop(), - "description": results[9].pop(), - "creator": ( - ", ".join(remove_none_elements_from_set(results[10])) - if remove_none_elements_from_set(results[10]) - else None - ), - "contributor": ( - ", ".join(remove_none_elements_from_set(results[11])) - if remove_none_elements_from_set(results[11]) - else None - ), - "rights": results[12].pop(), - "license": results[13].pop(), - } - ) - - if make_compliant: - clean_list.pop("created") - return pd.DataFrame(list(make_metadata_compliant(clean_list).items()), columns=["Key", "Value"]) - - return pd.DataFrame(list(clean_list.items()), columns=["Key", "Value"]) - - -def make_metadata_compliant(metadata: dict) -> dict: - """Attempts to fix errors in metadata, otherwise defaults to values that will pass validation. - - Args: - metadata: Dictionary containing metadata - - Returns: - Dictionary containing metadata with fixed errors - """ - - metadata = fix_namespace(metadata, default=Namespace("http://purl.org/cognite/neat#")) - metadata = fix_prefix(metadata) - metadata = fix_dataModelName(metadata) - metadata = fix_cdfSpaceName(metadata) - metadata = fix_version(metadata) - metadata = fix_isCurrentVersion(metadata) - metadata = fix_date(metadata, date_type="created", default=datetime.datetime.now().replace(microsecond=0)) - metadata = fix_date(metadata, date_type="updated", default=datetime.datetime.now().replace(microsecond=0)) - metadata = fix_title(metadata) - metadata = fix_description(metadata) - metadata = fix_author(metadata, "creator") - metadata = fix_author(metadata, "contributor", "Cognite") - metadata = fix_rights(metadata) - metadata = fix_license(metadata) - - return metadata - - -def fix_license(metadata: dict, default: str = "Unknown license") -> dict: - if license := metadata.get("license", None): - if not isinstance(license, str): - metadata["license"] = default - elif isinstance(license, str) and len(license) == 0: - metadata["license"] = default - else: - metadata["license"] = default - return metadata - - -def fix_rights(metadata: dict, default: str = "Unknown rights") -> dict: - if rights := metadata.get("rights", None): - if not isinstance(rights, str): - metadata["rights"] = default - elif isinstance(rights, str) and len(rights) == 0: - metadata["rights"] = default - else: - metadata["rights"] = default - return metadata - - -def fix_author(metadata: dict, author_type: str = "creator", default: str = "NEAT") -> dict: - if author := metadata.get(author_type, None): - if not isinstance(author, str) or isinstance(author, list): - metadata[author_type] = default - elif isinstance(author, str) and len(author) == 0: - metadata[author_type] = default - else: - metadata[author_type] = default - return metadata - - -def fix_description(metadata: dict, default: str = "This model has been inferred from OWL ontology") -> dict: - if description := metadata.get("description", None): - if not isinstance(description, str) or len(description) == 0: - metadata["description"] = default - elif isinstance(description, str) and len(description) > 1024: - metadata["description"] = metadata["description"][:1024] - else: - metadata["description"] = default - return metadata - - -def fix_cdfSpaceName(metadata: dict, default: str = "playground") -> dict: - if space := metadata.get("cdfSpaceName", None): - if not isinstance(space, str) or not re.match(cdf_space_compliance_regex, space): - metadata["cdfSpaceName"] = default - else: - metadata["cdfSpaceName"] = default - return metadata - - -def fix_dataModelName(metadata: dict, default: str = "neat") -> dict: - if data_model_name := metadata.get("dataModelName", None): - if not isinstance(data_model_name, str) or not re.match(data_model_id_compliance_regex, data_model_name): - metadata["dataModelName"] = default - else: - metadata["dataModelName"] = default - return metadata - - -def fix_prefix(metadata: dict, default: str = "neat") -> dict: - if prefix := metadata.get("prefix", None): - if not isinstance(prefix, str) or not re.match(prefix_compliance_regex, prefix): - metadata["prefix"] = default - else: - metadata["prefix"] = default - return metadata - - -def fix_namespace(metadata: dict, default: Namespace) -> dict: - if namespace := metadata.get("namespace", None): - if not isinstance(namespace, Namespace): - try: - metadata["namespace"] = Namespace(namespace) - except Exception: - metadata["namespace"] = default - else: - metadata["namespace"] = default - - return metadata - - -def fix_date( - metadata: dict, - date_type: str, - default: datetime.datetime, -) -> dict: - if date := metadata.get(date_type, None): - try: - if isinstance(date, datetime.datetime): - pass - elif isinstance(date, datetime.date): - metadata[date_type] = datetime.datetime.combine(metadata[date_type], datetime.datetime.min.time()) - elif isinstance(date, str): - metadata[date_type] = datetime.datetime.strptime(metadata[date_type], "%Y-%m-%dT%H:%M:%SZ") - else: - metadata[date_type] = default - except Exception: - metadata[date_type] = default - else: - metadata[date_type] = default - - return metadata - - -def fix_version(metadata: dict, default: str = "1.0.0") -> dict: - if version := metadata.get("version", None): - if not re.match(version_compliance_regex, version): - metadata["version"] = default - else: - metadata["version"] = default - - return metadata - - -def fix_isCurrentVersion(metadata: dict, default: bool = True) -> dict: - if isCurrentVersion := metadata.get("isCurrentVersion", None): - if not isinstance(isCurrentVersion, bool): - metadata["isCurrentVersion"] = default - else: - metadata["isCurrentVersion"] = default - - return metadata - - -def fix_title(metadata: dict, default: str = "OWL Inferred Data Model") -> dict: - if title := metadata.get("title", None): - if not isinstance(title, str): - metadata["title"] = default - elif isinstance(title, str) and len(title) == 0: - metadata["title"] = default - elif isinstance(title, str) and len(title) > 255: - metadata["title"] = metadata["title"][:255] - else: - pass - else: - metadata["title"] = default - - return metadata diff --git a/cognite/neat/legacy/rules/importers/_owl2rules/_owl2properties.py b/cognite/neat/legacy/rules/importers/_owl2rules/_owl2properties.py deleted file mode 100644 index 15ecdc361..000000000 --- a/cognite/neat/legacy/rules/importers/_owl2rules/_owl2properties.py +++ /dev/null @@ -1,217 +0,0 @@ -from typing import cast - -import numpy as np -import pandas as pd -from rdflib import Graph - -from cognite.neat.utils.rdf_ import remove_namespace_from_uri - -from ._owl2classes import _data_type_property_class, _object_property_class, _thing_class - - -def parse_owl_properties(graph: Graph, make_compliant: bool = False, language: str = "en") -> pd.DataFrame: - """Get all properties from the OWL ontology - - Parameters - ---------- - graph : Graph - Graph to query - parsing_config : dict, optional - Configuration for parsing the dataframe, by default None - - Returns - ------- - pd.DataFrame - Dataframe with columns: class, property, name, ... - """ - - query = """ - - SELECT ?class ?property ?name ?description ?type ?minCount ?maxCount - ?deprecated ?deprecationDate ?replacedBy ?source ?sourceEntity - ?match ?comment ?propertyType - WHERE { - ?property a ?propertyType. - FILTER (?propertyType IN (owl:ObjectProperty, owl:DatatypeProperty ) ) - OPTIONAL {?property rdfs:domain ?class }. - OPTIONAL {?property rdfs:range ?type }. - OPTIONAL {?property rdfs:label ?name }. - OPTIONAL {?property rdfs:comment ?description} . - OPTIONAL {?property owl:maxCardinality ?maxCount} . - OPTIONAL {?property owl:minCardinality ?minCount} . - FILTER (!isBlank(?property)) - FILTER (!bound(?type) || !isBlank(?type)) - FILTER (!bound(?class) || !isBlank(?class)) - FILTER (!bound(?name) || LANG(?name) = "" || LANGMATCHES(LANG(?name), "en")) - FILTER (!bound(?description) || LANG(?description) = "" || LANGMATCHES(LANG(?description), "en")) - OPTIONAL {?property owl:deprecated ?deprecated} . - } - """ - - raw_df = _parse_raw_dataframe(cast(list[tuple], list(graph.query(query.replace("en", language))))) - if raw_df.empty: - return pd.concat([raw_df, pd.DataFrame([len(raw_df) * [""]])], ignore_index=True) - - # group values and clean up - processed_df = _clean_up_properties(raw_df) - - # make compliant - if make_compliant: - processed_df = make_properties_compliant(processed_df) - - # drop column _property_type, which was a helper column: - processed_df.drop(columns=["_property_type"], inplace=True) - - return processed_df - - -def _parse_raw_dataframe(query_results: list[tuple]) -> pd.DataFrame: - df = pd.DataFrame( - query_results, - columns=[ - "Class", - "Property", - "Name", - "Description", - "Type", - "Min Count", - "Max Count", - "Deprecated", - "Deprecation Date", - "Replaced By", - "Source", - "Source Entity Name", - "Match Type", - "Comment", - "_property_type", - ], - ) - if df.empty: - return df - - df.replace(np.nan, "", regex=True, inplace=True) - - df.Source = df.Property - df.Class = df.Class.apply(lambda x: remove_namespace_from_uri(x)) - df.Property = df.Property.apply(lambda x: remove_namespace_from_uri(x)) - df.Type = df.Type.apply(lambda x: remove_namespace_from_uri(x)) - df["Source Entity Name"] = df.Property - df["Match Type"] = len(df) * ["exact"] - df["_property_type"] = df["_property_type"].apply(lambda x: remove_namespace_from_uri(x)) - - return df - - -def _clean_up_properties(df: pd.DataFrame) -> pd.DataFrame: - class_grouped_dfs = df.groupby("Class") - - clean_list = [] - - for class_, class_grouped_df in class_grouped_dfs: - property_grouped_dfs = class_grouped_df.groupby("Property") - for property_, property_grouped_df in property_grouped_dfs: - clean_list += [ - { - "Class": class_, - "Property": property_, - "Name": property_grouped_df["Name"].unique()[0], - "Description": "\n".join(list(property_grouped_df.Description.unique()))[:1024], - "Type": property_grouped_df.Type.unique()[0], - "Min Count": property_grouped_df["Min Count"].unique()[0], - "Max Count": property_grouped_df["Max Count"].unique()[0], - "Deprecated": property_grouped_df.Deprecated.unique()[0], - "Deprecation Date": property_grouped_df["Deprecation Date"].unique()[0], - "Replaced By": property_grouped_df["Replaced By"].unique()[0], - "Source": property_grouped_df["Source"].unique()[0], - "Source Entity Name": property_grouped_df["Source Entity Name"].unique()[0], - "Match Type": property_grouped_df["Match Type"].unique()[0], - "Comment": property_grouped_df["Comment"].unique()[0], - "_property_type": property_grouped_df["_property_type"].unique()[0], - } - ] - - df = pd.DataFrame(clean_list) - df.replace("", None, inplace=True) - - return df - - -def make_properties_compliant(properties: pd.DataFrame) -> pd.DataFrame: - # default to None if "Min Count" is not specified - properties["Min Count"] = properties["Min Count"].apply(lambda x: None if not isinstance(x, int) or x == "" else x) - - # default to None if "Max Count" is not specified - properties["Max Count"] = properties["Max Count"].apply(lambda x: 1 if not isinstance(x, int) or x == "" else x) - - # Replace empty or non-string values in "Match Type" column with "exact" - properties["Match Type"] = properties["Match Type"].fillna("exact") - properties["Match Type"] = properties["Match Type"].apply( - lambda x: "exact" if not isinstance(x, str) or len(x) == 0 else x - ) - - # Replace empty or non-string values in "Comment" column with a default value - properties["Comment"] = properties["Comment"].fillna("Imported from Ontology by NEAT") - properties["Comment"] = properties["Comment"].apply( - lambda x: "Imported from Ontology by NEAT" if not isinstance(x, str) or len(x) == 0 else x - ) - - # Replace empty or non-boolean values in "Deprecated" column with False - properties["Deprecated"] = properties["Deprecated"].fillna(False) - properties["Deprecated"] = properties["Deprecated"].apply(lambda x: False if not isinstance(x, bool) else x) - - # Reduce length of elements in the "Description" column to 1024 characters - properties["Description"] = properties["Description"].apply(lambda x: x[:1024] if isinstance(x, str) else None) - - # fixes and additions - properties = fix_dangling_properties(properties) - properties = fix_missing_property_value_type(properties) - - return properties - - -def fix_dangling_properties(properties: pd.DataFrame) -> pd.DataFrame: - """This method fixes properties which are missing a domain definition in the ontology. - - Args: - properties: Dataframe containing properties - - Returns: - Dataframe containing properties with fixed domain - """ - domain = { - "ObjectProperty": _object_property_class()["Class"], - "DatatypeProperty": _data_type_property_class()["Class"], - } - - # apply missing range - properties["Class"] = properties.apply( - lambda row: domain[row._property_type] - if row._property_type == "ObjectProperty" and pd.isna(row["Class"]) - else domain["DatatypeProperty"] - if pd.isna(row["Class"]) - else row["Class"], - axis=1, - ) - return properties - - -def fix_missing_property_value_type(properties: pd.DataFrame) -> pd.DataFrame: - """This method fixes properties which are missing a range definition in the ontology. - - Args: - properties: Dataframe containing properties - - Returns: - Dataframe containing properties with fixed range - """ - # apply missing range - properties["Type"] = properties.apply( - lambda row: _thing_class()["Class"] - if row._property_type == "ObjectProperty" and pd.isna(row["Type"]) - else "string" - if pd.isna(row["Type"]) - else row["Type"], - axis=1, - ) - - return properties diff --git a/cognite/neat/legacy/rules/importers/_owl2rules/_owl2rules.py b/cognite/neat/legacy/rules/importers/_owl2rules/_owl2rules.py deleted file mode 100644 index 21a80d82a..000000000 --- a/cognite/neat/legacy/rules/importers/_owl2rules/_owl2rules.py +++ /dev/null @@ -1,290 +0,0 @@ -"""This module performs importing of various formats to one of serializations for which -there are loaders to TransformationRules pydantic class.""" - -# TODO: if this module grows too big, split it into several files and place under ./converter directory - -from pathlib import Path - -import pandas as pd -from pydantic_core import ErrorDetails -from rdflib import DC, DCTERMS, OWL, RDF, RDFS, SKOS, Graph - -from cognite.neat.legacy.rules.importers._base import BaseImporter -from cognite.neat.legacy.rules.models.raw_rules import RawRules -from cognite.neat.legacy.rules.models.rules import Rules -from cognite.neat.legacy.rules.models.tables import Tables -from cognite.neat.legacy.rules.models.value_types import XSD_VALUE_TYPE_MAPPINGS - -from ._owl2classes import parse_owl_classes -from ._owl2metadata import parse_owl_metadata -from ._owl2properties import parse_owl_properties - - -class OWLImporter(BaseImporter): - """Convert OWL ontology to tables/ transformation rules / Excel file. - - Args: - owl_filepath: Path to OWL ontology - - !!! Note - OWL Ontologies typically lacks some information that is required for making a complete - data model. This means that the methods .to_rules() will typically fail. Instead, it is recommended - that you use the .to_spreadsheet() method to generate an Excel file, and then manually add the missing - information to the Excel file. The Excel file can then be converted to a `Rules` object. - - Alternatively, one can set the `make_compliant` parameter to True to allow neat to attempt to make - the imported rules compliant by adding default values for missing information, attaching dangling - properties to default containers based on the property type, etc. One has to be aware - that NEAT will be opinionated about how to make the ontology compliant, and that the resulting - rules may not be what you expect. - - """ - - def __init__(self, owl_filepath: Path): - self.owl_filepath = owl_filepath - - def to_tables(self, make_compliant: bool = False) -> dict[str, pd.DataFrame]: - graph = Graph() - try: - graph.parse(self.owl_filepath) - except Exception as e: - raise Exception(f"Could not parse owl file: {e}") from e - - # bind key namespaces - graph.bind("owl", OWL) - graph.bind("rdf", RDF) - graph.bind("rdfs", RDFS) - graph.bind("dcterms", DCTERMS) - graph.bind("dc", DC) - graph.bind("skos", SKOS) - - tables: dict[str, pd.DataFrame] = { - Tables.metadata: parse_owl_metadata(graph, make_compliant=make_compliant), - Tables.classes: parse_owl_classes(graph, make_compliant=make_compliant), - Tables.properties: parse_owl_properties(graph, make_compliant=make_compliant), - } - - if make_compliant: - tables = make_tables_compliant(tables) - # add sorting of classes and properties prior exporting - - tables[Tables.classes] = tables[Tables.classes].sort_values(by=["Class"]) - tables[Tables.properties] = tables[Tables.properties].sort_values(by=["Class", "Property"]) - - return tables - - def to_raw_rules(self, make_compliant: bool = False) -> RawRules: - """Creates `RawRules` object from the data.""" - - tables = self.to_tables(make_compliant=make_compliant) - - return RawRules.from_tables(tables=tables, importer_type=self.__class__.__name__) - - def to_rules( - self, - return_report: bool = False, - skip_validation: bool = False, - validators_to_skip: set[str] | None = None, - make_compliant: bool = False, - ) -> tuple[Rules | None, list[ErrorDetails] | None, list | None] | Rules: - """ - Creates `Rules` object from the data. - - Args: - return_report: To return validation report. Defaults to False. - skip_validation: Bypasses Rules validation. Defaults to False. - validators_to_skip: List of validators to skip. Defaults to None. - make_compliant: Flag for generating compliant rules, by default False - - Returns: - Instance of `Rules`, which can be validated, not validated based on - `skip_validation` flag, or partially validated if `validators_to_skip` is set, - and optional list of errors and warnings if - `return_report` is set to True. - - !!! Note "Skip Validation - `skip_validation` flag should be only used for purpose when `Rules` object - is exported to an Excel file. Do not use this flag for any other purpose! - """ - - raw_rules = self.to_raw_rules(make_compliant=make_compliant) - - return raw_rules.to_rules(return_report, skip_validation, validators_to_skip) - - -def make_tables_compliant(tables: dict[str, pd.DataFrame]) -> dict[str, pd.DataFrame]: - tables = _add_missing_classes(tables) - tables = _add_missing_value_types(tables) - tables = _add_properties_to_dangling_classes(tables) - tables = _add_entity_type_property(tables) - - return tables - - -def _add_missing_classes(tables: dict[str, pd.DataFrame]) -> dict[str, pd.DataFrame]: - """Add missing classes to containers. - - Args: - tables: imported tables from owl ontology - - Returns: - Updated tables with missing classes added to containers - """ - - missing_classes = set(tables[Tables.properties].Class.to_list()) - set(tables[Tables.classes].Class.to_list()) - - rows = [] - for class_ in missing_classes: - rows += [ - { - "Class": class_, - "Name": None, - "Description": None, - "Parent Class": None, - "Deprecated": False, - "Deprecation Date": None, - "Replaced By": None, - "Source": None, - "Source Entity Name": None, - "Match Type": None, - "Comment": ( - "Added by NEAT. " - "This is a class that a domain of a property but was not defined in the ontology. " - "It is added by NEAT to make the ontology compliant with CDF." - ), - } - ] - - if rows: - tables[Tables.classes] = pd.concat( - [tables[Tables.classes], pd.DataFrame(rows)], - ignore_index=True, - ) - - return tables - - -def _add_missing_value_types(tables: dict[str, pd.DataFrame]) -> dict[str, pd.DataFrame]: - """Add properties to classes that do not have any properties defined to them - - Args: - tables: imported tables from owl ontology - - Returns: - Updated tables with missing properties added to containers - """ - - xsd_types = set(XSD_VALUE_TYPE_MAPPINGS.keys()) - referred_types = set(tables[Tables.properties]["Type"].to_list()) - defined_classes = set(tables[Tables.classes]["Class"].to_list()) - - rows = [] - for class_ in referred_types.difference(defined_classes).difference(xsd_types): - rows += [ - { - "Class": class_, - "Name": None, - "Description": None, - "Parent Class": None, - "Deprecated": False, - "Deprecation Date": None, - "Replaced By": None, - "Source": None, - "Source Entity Name": None, - "Match Type": None, - "Comment": ( - "Added by NEAT. " - "This is a class that a domain of a property but was not defined in the ontology. " - "It is added by NEAT to make the ontology compliant with CDF." - ), - } - ] - - if rows: - tables[Tables.classes] = pd.concat( - [tables[Tables.classes], pd.DataFrame(rows)], - ignore_index=True, - ) - - return tables - - -def _add_properties_to_dangling_classes( - tables: dict[str, pd.DataFrame], properties_to_add: list[str] | None = None -) -> dict[str, pd.DataFrame]: - """Add properties to classes that do not have any properties defined to them - - Args: - tables: imported tables from owl ontology - - Returns: - Updated tables with missing properties added to containers - """ - - if properties_to_add is None: - properties_to_add = ["label"] - undefined_classes = set(tables[Tables.classes].Class.to_list()) - set(tables[Tables.properties].Class.to_list()) - - rows = [] - for class_ in undefined_classes: - for property_ in properties_to_add: - rows += [ - { - "Class": class_, - "Property": property_, - "Name": property_, - "Description": None, - "Type": "string", - "Min Count": None, - "Max Count": 1, - "Deprecated": False, - "Deprecation Date": None, - "Replaced By": None, - "Source": None, - "Source Entity Name": None, - "Match Type": None, - "Comment": "Added by NEAT. Default property to make the ontology compliant with CDF.", - } - ] - - if rows: - tables[Tables.properties] = pd.concat( - [tables[Tables.properties], pd.DataFrame(rows)], - ignore_index=True, - ) - - return tables - - -def _add_entity_type_property(tables: dict[str, pd.DataFrame]) -> dict[str, pd.DataFrame]: - missing_entity_type = set( - tables[Tables.properties].groupby("Class").filter(lambda x: "entityType" not in x.Property.to_list()).Class - ) - - rows = [] - for class_ in missing_entity_type: - rows += [ - { - "Class": class_, - "Property": "entityType", - "Name": "entityType", - "Description": None, - "Type": "string", - "Min Count": None, - "Max Count": 1, - "Deprecated": False, - "Deprecation Date": None, - "Replaced By": None, - "Source": None, - "Source Entity Name": None, - "Match Type": None, - "Comment": "Added by NEAT. Default property added to make the ontology compliant with CDF.", - } - ] - - if rows: - tables[Tables.properties] = pd.concat( - [tables[Tables.properties], pd.DataFrame(rows)], - ignore_index=True, - ) - return tables diff --git a/cognite/neat/legacy/rules/importers/_spreadsheet2rules.py b/cognite/neat/legacy/rules/importers/_spreadsheet2rules.py deleted file mode 100644 index 621df7552..000000000 --- a/cognite/neat/legacy/rules/importers/_spreadsheet2rules.py +++ /dev/null @@ -1,45 +0,0 @@ -"""This module performs importing of graph to TransformationRules pydantic class. -In more details, it traverses the graph and abstracts class and properties, basically -generating a list of rules based on which nodes that form the graph are made. -""" - -from pathlib import Path - -import pandas as pd -from openpyxl import Workbook, load_workbook - -from cognite.neat.utils.auxiliary import local_import - -from ._base import BaseImporter - - -class ExcelImporter(BaseImporter): - def __init__(self, filepath: Path): - self.filepath = filepath - - def to_tables(self) -> dict[str, pd.DataFrame]: - workbook: Workbook = load_workbook(self.filepath) - - return { - sheet_name: pd.read_excel( - self.filepath, - sheet_name=sheet_name, - header=None if sheet_name == "Metadata" else 0, - skiprows=1 if sheet_name in ["Classes", "Properties", "Instances"] else None, - ) - for sheet_name in workbook.sheetnames - } - - -class GoogleSheetImporter(BaseImporter): - def __init__(self, sheet_id: str): - self.sheet_id = sheet_id - - def to_tables(self) -> dict[str, pd.DataFrame]: - local_import("gspread", "google") - import gspread # type: ignore[import] - - client_google = gspread.service_account() - google_sheet = client_google.open_by_key(self.sheet_id) - - return {worksheet.title: pd.DataFrame(worksheet.get_all_records()) for worksheet in google_sheet.worksheets()} diff --git a/cognite/neat/legacy/rules/importers/_xsd2rules.py b/cognite/neat/legacy/rules/importers/_xsd2rules.py deleted file mode 100644 index b37b87c86..000000000 --- a/cognite/neat/legacy/rules/importers/_xsd2rules.py +++ /dev/null @@ -1,20 +0,0 @@ -from pathlib import Path - -import pandas as pd - -from ._base import BaseImporter - - -class XSDImporter(BaseImporter): - """ - Importer for XSD (XML Schema) files. - - Args: - xml_directory: Path to directory containing XSD files. - """ - - def __init__(self, xsd_directory: Path): - self.xsd_directory = xsd_directory - - def to_tables(self) -> dict[str, pd.DataFrame]: - raise NotImplementedError diff --git a/cognite/neat/legacy/rules/importers/_yaml2rules.py b/cognite/neat/legacy/rules/importers/_yaml2rules.py deleted file mode 100644 index 7e3ebf7fc..000000000 --- a/cognite/neat/legacy/rules/importers/_yaml2rules.py +++ /dev/null @@ -1,39 +0,0 @@ -from pathlib import Path -from typing import Literal - -import yaml - -from ._dict2rules import ArbitraryDictImporter - - -class ArbitraryYAMLImporter(ArbitraryDictImporter): - """ - Importer for data given in a YAML file or string. - - This importer infers the data model from the YAML string based on the shape of the data. - - Args: - yaml_path_or_str: Path to file with YAML. - relationship_direction: Direction of relationships, either "parent-to-child" or "child-to-parent". JSON - files are nested with children nested inside parents. This option determines whether the resulting rules - will have an edge from parents to children or from children to parents. - """ - - def __init__( - self, - yaml_path_or_str: Path, - relationship_direction: Literal["parent-to-child", "child-to-parent"] = "parent-to-child", - ): - if isinstance(yaml_path_or_str, str): - data = yaml.safe_load(yaml_path_or_str) - super().__init__(data, relationship_direction) - elif isinstance(yaml_path_or_str, Path): - if not yaml_path_or_str.exists(): - raise ValueError(f"File {yaml_path_or_str} does not exist") - if yaml_path_or_str.suffix != ".json": - raise ValueError(f"File {yaml_path_or_str} is not a JSON file") - self.json_path = yaml_path_or_str - data = yaml.safe_load(yaml_path_or_str.read_text()) - super().__init__(data, relationship_direction) - else: - raise TypeError(f"Expected Path or str, got {type(yaml_path_or_str)}") diff --git a/cognite/neat/legacy/rules/models/__init__.py b/cognite/neat/legacy/rules/models/__init__.py deleted file mode 100644 index c03b7cf8d..000000000 --- a/cognite/neat/legacy/rules/models/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from .raw_rules import RawRules -from .rules import Rules -from .tables import Tables - -__all__ = ["Tables", "Rules", "RawRules"] diff --git a/cognite/neat/legacy/rules/models/_base.py b/cognite/neat/legacy/rules/models/_base.py deleted file mode 100644 index f0102d548..000000000 --- a/cognite/neat/legacy/rules/models/_base.py +++ /dev/null @@ -1,151 +0,0 @@ -import re -import sys -from typing import ClassVar - -from cognite.client.data_classes.data_modeling import ViewId -from pydantic import BaseModel, ConfigDict, Field -from rdflib import Literal, URIRef - -if sys.version_info >= (3, 11): - from enum import StrEnum - from typing import Self -else: - from backports.strenum import StrEnum - from typing_extensions import Self - - -class EntityTypes(StrEnum): - subject = "subject" - predicate = "predicate" - object = "object" - class_ = "class" - property_ = "property" - object_property = "ObjectProperty" - data_property = "DatatypeProperty" - annotation_property = "AnnotationProperty" - data_value_type = "data_value_type" - object_value_type = "object_value_type" - view = "view" - container = "container" - undefined = "undefined" - - -# ALLOWED -ALLOWED_PATTERN = r"[^a-zA-Z0-9-_.]" - -# REGEX expressions -PREFIX_REGEX = r"[a-zA-Z]+[a-zA-Z0-9-_.]*[a-zA-Z0-9]+" -SUFFIX_REGEX = r"[a-zA-Z0-9-_.]+[a-zA-Z0-9]|[-_.]*[a-zA-Z0-9]+" -VERSION_REGEX = r"[a-zA-Z0-9]([.a-zA-Z0-9_-]{0,41}[a-zA-Z0-9])" - -ENTITY_ID_REGEX = rf"{PREFIX_REGEX}:({SUFFIX_REGEX})" -ENTITY_ID_REGEX_COMPILED = re.compile(rf"^(?P{PREFIX_REGEX}):(?P{SUFFIX_REGEX})$") -VERSIONED_ENTITY_REGEX_COMPILED = re.compile( - rf"^(?P{PREFIX_REGEX}):(?P{SUFFIX_REGEX})\(version=(?P{VERSION_REGEX})\)$" -) - -CLASS_ID_REGEX = rf"(?P<{EntityTypes.class_}>{ENTITY_ID_REGEX})" -CLASS_ID_REGEX_COMPILED = re.compile(rf"^{CLASS_ID_REGEX}$") - -PROPERTY_ID_REGEX = rf"\((?P<{EntityTypes.property_}>{ENTITY_ID_REGEX})\)" -VERSION_ID_REGEX = rf"\(version=(?P{VERSION_REGEX})\)" - - -class Entity(BaseModel): - """Entity is a class or property in OWL/RDF sense.""" - - model_config: ClassVar[ConfigDict] = ConfigDict(use_enum_values=True) - prefix: str - suffix: str - type_: EntityTypes = Field(default=EntityTypes.undefined) - name: str | None = None - description: str | None = None - version: str | None = None - - @property - def id(self) -> str: - return f"{self.prefix}:{self.suffix}" - - @property - def versioned_id(self) -> str: - if self.version: - return f"{self.prefix}:{self.suffix}(version={self.version})" - else: - return self.id - - @property - def space(self) -> str: - """Returns entity space in CDF.""" - return self.prefix - - @property - def external_id(self) -> str: - """Returns entity external id in CDF.""" - return self.suffix - - def __repr__(self): - return self.id - - @classmethod - def from_string(cls, entity_string: str, base_prefix: str | None = None, **kwargs) -> Self: - if result := VERSIONED_ENTITY_REGEX_COMPILED.match(entity_string): - return cls( - prefix=result.group("prefix"), - suffix=result.group("suffix"), - name=result.group("suffix"), - version=result.group("version"), - **kwargs, - ) - elif result := ENTITY_ID_REGEX_COMPILED.match(entity_string): - return cls( - prefix=result.group("prefix"), suffix=result.group("suffix"), name=result.group("suffix"), **kwargs - ) - elif base_prefix and re.match(SUFFIX_REGEX, entity_string) and re.match(PREFIX_REGEX, base_prefix): - return cls(prefix=base_prefix, suffix=entity_string, name=entity_string, **kwargs) - else: - raise ValueError(f"{cls.__name__} is expected to be prefix:suffix, got {entity_string}") - - @classmethod - def from_list(cls, entity_strings: list[str], base_prefix: str | None = None, **kwargs) -> list[Self]: - return [ - cls.from_string(entity_string=entity_string, base_prefix=base_prefix, **kwargs) - for entity_string in entity_strings - ] - - -class ParentClass(Entity): - type_: EntityTypes = EntityTypes.class_ - - @property - def view_id(self) -> ViewId: - return ViewId(space=self.space, external_id=self.external_id, version=self.version) - - @classmethod - def from_view_id(cls, view_id: ViewId) -> Self: - return cls(prefix=view_id.space, suffix=view_id.external_id, version=view_id.version) - - -class ContainerEntity(Entity): - type_: EntityTypes = EntityTypes.container - - -class ViewEntity(Entity): - type_: EntityTypes = EntityTypes.view - - -class Triple(BaseModel): - model_config: ClassVar[ConfigDict] = ConfigDict( - populate_by_name=True, arbitrary_types_allowed=True, strict=False, extra="allow" - ) - - subject: str | URIRef | Entity - predicate: str | URIRef | Entity - object: str | URIRef | Literal | Entity | None = None - optional: bool = Field( - description="Indicates whether a triple is optional, used when building SPARQL query", - default=False, - ) - - @classmethod - def from_rdflib_triple(cls, triple: tuple[URIRef, URIRef, URIRef | Literal]) -> Self: - return cls(subject=triple[0], predicate=triple[1], object=triple[2]) diff --git a/cognite/neat/legacy/rules/models/raw_rules.py b/cognite/neat/legacy/rules/models/raw_rules.py deleted file mode 100644 index 6f122d224..000000000 --- a/cognite/neat/legacy/rules/models/raw_rules.py +++ /dev/null @@ -1,316 +0,0 @@ -import logging -import warnings -from collections.abc import Hashable -from typing import Any, cast, no_type_check -from warnings import warn - -import pandas as pd -from pydantic import field_validator -from pydantic_core import ErrorDetails, ValidationError -from rdflib import Namespace - -from cognite.neat.constants import get_default_prefixes -from cognite.neat.exceptions import wrangle_warnings - -# rules model and model components: -from cognite.neat.legacy.rules.models.rules import ( - Class, - Metadata, - Property, - RuleModel, - Rules, -) -from cognite.neat.legacy.rules.models.tables import Tables - -# importers: -from cognite.neat.rules import exceptions -from cognite.neat.utils.auxiliary import generate_exception_report - -__all__ = ["RawRules"] - - -class RawRules(RuleModel): - """ - RawRules represent invalidated form of Rules, which is a core concept in `neat`. - RawRules are used as staging area for Rules, and are often used when importing rules - from sources other than Excel rules, e.g. from a json schema or owl ontology. Often - these sources are not validated, and are missing information to be able to create - a valid Rules object. - - Args: - Metadata: Data model metadata - classes: Classes defined in the data model - properties: Class properties defined in the data model with accompanying transformation rules - to transform data from source to target representation - prefixes: Prefixes used in the data model. Defaults to internal set of prefixes - instances: Instances defined in the data model. Defaults to None - """ - - Metadata: pd.DataFrame - Classes: pd.DataFrame - Properties: pd.DataFrame - Prefixes: pd.DataFrame = pd.DataFrame() - Instances: pd.DataFrame = pd.DataFrame() - importer_type: str = "RawTablesImporter" - - @field_validator("Metadata") - def has_metadata_mandatory_rows(cls, v: pd.DataFrame): - given_rows = set(v.iloc[:, 0].values) - mandatory_rows = Metadata.mandatory_fields() - mandatory_rows_alias = Metadata.mandatory_fields(use_alias=True) - - if not (mandatory_rows.issubset(given_rows) or mandatory_rows_alias.issubset(given_rows)): - missing_rows = mandatory_rows_alias.difference(given_rows) - raise exceptions.MetadataSheetMissingMandatoryFields(missing_rows).to_pydantic_custom_error() - return v - - @field_validator("Classes") - def has_classes_mandatory_columns(cls, v): - given_columns = set(v.columns) - mandatory_columns = Class.mandatory_fields() - mandatory_columns_alias = Class.mandatory_fields(use_alias=True) - - if not (mandatory_columns.issubset(given_columns) or mandatory_columns_alias.issubset(given_columns)): - missing_columns = mandatory_columns_alias.difference(given_columns) - raise exceptions.ClassesSheetMissingMandatoryColumns(missing_columns).to_pydantic_custom_error() - return v - - @field_validator("Properties") - def has_properties_mandatory_columns(cls, v): - given_columns = set(v.columns) - mandatory_columns = Property.mandatory_fields() - mandatory_columns_alias = Property.mandatory_fields(use_alias=True) - - if not (mandatory_columns.issubset(given_columns) or mandatory_columns_alias.issubset(given_columns)): - missing_columns = mandatory_columns_alias.difference(given_columns) - raise exceptions.PropertiesSheetMissingMandatoryColumns(missing_columns).to_pydantic_custom_error() - return v - - @field_validator("Prefixes") - def has_prefixes_mandatory_columns(cls, v): - given_columns = set(v.columns) - mandatory_columns = {"Prefix", "URI"} - - if not mandatory_columns.issubset(given_columns): - missing_columns = mandatory_columns.difference(given_columns) - raise exceptions.PrefixesSheetMissingMandatoryColumns(missing_columns).to_pydantic_custom_error() - return v - - @field_validator("Instances") - def has_instances_mandatory_columns(cls, v): - given_columns = set(v.columns) - mandatory_columns = {"Instance", "Property", "Value"} - - if not mandatory_columns.issubset(given_columns): - missing_columns = mandatory_columns.difference(given_columns) - raise exceptions.InstancesSheetMissingMandatoryColumns(missing_columns).to_pydantic_custom_error() - return v - - @staticmethod - def _drop_non_string_columns(df: pd.DataFrame) -> pd.DataFrame: - """Drop non-string columns as this can cause issue when loading rules - - Args: - df: data frame - - Returns: - dataframe with removed non string columns - """ - columns = [column for column in df.columns[df.columns.notna()] if isinstance(column, str)] - - return df[columns] - - # mypy complains "RawRules" has incompatible type "**dict[str, DataFrame]"; expected "set[str]" , which is wrong! - @no_type_check - @classmethod - def from_tables(cls, tables: dict[str, pd.DataFrame], importer_type: str = "RawTablesImporter") -> "RawRules": - """Create RawRules from raw tables. - - Args: - tables: Tables to be converted to RawRules - - Returns: - Instance of RawRules - """ - - expected_tables = cls.mandatory_fields() - - # Validate raw tables - if missing_tables := (expected_tables - set(tables)): - raise exceptions.SourceObjectDoesNotProduceMandatorySheets(missing_tables) - - tables_dict: dict[str, pd.DataFrame] = { - Tables.metadata: tables[Tables.metadata], - Tables.classes: cls._drop_non_string_columns(tables[Tables.classes]), - Tables.properties: cls._drop_non_string_columns(tables[Tables.properties]), - } - - if Tables.prefixes in tables: - tables_dict[Tables.prefixes] = cls._drop_non_string_columns(tables[Tables.prefixes]) - if Tables.instances in tables: - tables_dict[Tables.instances] = cls._drop_non_string_columns(tables[Tables.instances]) - - return cls( - **tables_dict, - importer_type=importer_type, - ) - - # mypy unsatisfied with overload , tried all combination and gave up - @no_type_check - def to_rules( - self, - return_report: bool = False, - skip_validation: bool = False, - validators_to_skip: set[str] | None = None, - ) -> tuple[Rules | None, list[ErrorDetails] | None, list | None] | Rules: - """Validates RawRules instances and returns Rules instance. - - Args: - return_report: To return validation report. Defaults to False. - skip_validation: Bypasses Rules validation. Defaults to False. - validators_to_skip: List of validators to skip. Defaults to None. - - Returns: - Instance of `Rules`, which can be validated or not validated based on - `skip_validation` flag, and optional list of errors and warnings if - `return_report` is set to True. - - !!! Note - `skip_validation` flag should be only used for purpose when `Rules` object - is exported to an Excel file. Do not use this flag for any other purpose! - """ - - rules_dict = _raw_tables_to_rules_dict(self, validators_to_skip) - if skip_validation: - return _to_invalidated_rules(rules_dict) - else: - return _to_validated_rules(rules_dict, return_report) - - def validate_rules(self) -> str | None: - _, errors, warnings_ = self.to_rules(return_report=True, skip_validation=False) - - report = "" - if errors: - warnings.warn( - exceptions.RulesHasErrors(importer_type=self.importer_type).message, - category=exceptions.RulesHasErrors, - stacklevel=2, - ) - report = generate_exception_report(cast(list[ErrorDetails], errors), "Errors") - - if warnings_: - warnings.warn( - exceptions.RulesHasWarnings(importer_type=self.importer_type).message, - category=exceptions.RulesHasWarnings, - stacklevel=2, - ) - report += generate_exception_report(cast(list[dict], warnings_), "Warnings") - - return report if report else None - - -def _to_validated_rules( - rules_dict: dict, return_report: bool = False -) -> tuple[Rules | None, list[ErrorDetails] | None, list[dict] | None] | Rules: - validation_warnings = [] - try: - with warnings.catch_warnings(record=True) as validation_warnings: - rules = Rules(**rules_dict) - return (rules, None, wrangle_warnings(validation_warnings)) if return_report else rules - - except ValidationError as e: - validation_errors = e.errors() - if return_report: - return None, validation_errors, wrangle_warnings(validation_warnings) - else: - raise e - - -def _to_invalidated_rules(rules_dict: dict) -> Rules: - args = { - "metadata": Metadata.model_construct(**rules_dict["metadata"]), - "classes": { - class_: Class.model_construct(**definition) for class_, definition in rules_dict["classes"].items() - }, - "properties": { - property_: Property.model_construct(**definition) - for property_, definition in rules_dict["properties"].items() - }, - "prefixes": rules_dict["prefixes"], - } - - return cast(Rules, Rules.model_construct(**args)) - - -def _raw_tables_to_rules_dict(raw_tables: RawRules, validators_to_skip: set | None = None) -> dict[str, Any]: - """Converts raw tables to a dictionary of rules.""" - rules_dict: dict[str, Any] = { - "metadata": _metadata_table2dict(raw_tables.Metadata), - "classes": _classes_table2dict(raw_tables.Classes), - "properties": _properties_table2dict(raw_tables.Properties), - "prefixes": ( - get_default_prefixes() if raw_tables.Prefixes.empty else _prefixes_table2dict(raw_tables.Prefixes) - ), - } - - rules_dict["instances"] = ( - None - if raw_tables.Instances.empty - else _instances_table2dict(raw_tables.Instances, rules_dict["metadata"], rules_dict["prefixes"]) - ) - - if validators_to_skip: - rules_dict["validators_to_skip"] = validators_to_skip - rules_dict["metadata"]["validators_to_skip"] = validators_to_skip - for class_ in rules_dict["classes"].keys(): - rules_dict["classes"][class_]["validators_to_skip"] = validators_to_skip - for property_ in rules_dict["properties"].keys(): - rules_dict["properties"][property_]["validators_to_skip"] = validators_to_skip - - return rules_dict - - -def _metadata_table2dict(meta_df: pd.DataFrame) -> dict[str, Any]: - assert len(meta_df.columns) == 2 - col1, col2 = meta_df.columns - metadata_dict = dict(zip(meta_df[col1], meta_df[col2], strict=True)) - metadata_dict["source"] = meta_df.source if "source" in dir(meta_df) else None - if "namespace" in metadata_dict: - metadata_dict["namespace"] = Namespace(metadata_dict["namespace"]) - return metadata_dict - - -def _classes_table2dict( - classes_df: pd.DataFrame, -) -> dict[Any | None, dict[Hashable, Any]]: - return {class_.get("Class"): class_ for class_ in classes_df.to_dict(orient="records")} - - -def _properties_table2dict( - properties_df: pd.DataFrame, -) -> dict[str, dict[Hashable, Any]]: - return {f"row {i+3}": property_ for i, property_ in enumerate(properties_df.to_dict(orient="records"))} - - -def _prefixes_table2dict(prefix_df: pd.DataFrame) -> dict[str, Namespace]: - return {row["Prefix"]: Namespace(row["URI"]) for i, row in prefix_df.iterrows()} - - -def _instances_table2dict( - instances_df: pd.DataFrame, metadata: dict[str, Any], prefixes: dict[str, Namespace] -) -> list[dict] | None: - if ("prefix" not in metadata and "namespace" not in metadata) or "namespace" not in metadata: - logging.warning(exceptions.MissingDataModelPrefixOrNamespace().message) - warn(exceptions.MissingDataModelPrefixOrNamespace().message, stacklevel=2) - return None - - prefix = metadata["prefix"] if "prefix" in metadata else metadata["space"] - prefixes[prefix] = metadata["namespace"] - - instances = [] - for _, row in instances_df.iterrows(): - row_as_dict = row.to_dict() - row_as_dict["namespace"] = metadata["namespace"] - row_as_dict["prefixes"] = prefixes - instances.append(row_as_dict) - return instances diff --git a/cognite/neat/legacy/rules/models/rdfpath.py b/cognite/neat/legacy/rules/models/rdfpath.py deleted file mode 100644 index cbb67e295..000000000 --- a/cognite/neat/legacy/rules/models/rdfpath.py +++ /dev/null @@ -1,237 +0,0 @@ -""" """ - -import re -import sys -from collections import Counter -from typing import Literal - -from pydantic import BaseModel, field_validator - -from cognite.neat.legacy.rules import exceptions - -from ._base import ( - CLASS_ID_REGEX, - CLASS_ID_REGEX_COMPILED, - ENTITY_ID_REGEX, - PROPERTY_ID_REGEX, - SUFFIX_REGEX, - Entity, - EntityTypes, -) - -if sys.version_info >= (3, 11): - from enum import StrEnum - from typing import Self -else: - from backports.strenum import StrEnum - from typing_extensions import Self - - -class TransformationRuleType(StrEnum): - rdfpath = "rdfpath" - rawlookup = "rawlookup" - sparql = "sparql" - - -class Lookup(StrEnum): - table = "table" - key = "key" - value = "value" # type: ignore - - -# traversal direction -DIRECTION_REGEX = r"(?P(->|<-))" - -# steps -STEP_REGEX = rf"((->|<-){CLASS_ID_REGEX}({PROPERTY_ID_REGEX})?)" -STEP_REGEX_COMPILED = re.compile(STEP_REGEX) -STEP_CLASS_REGEX_COMPILED = re.compile(rf"(^{DIRECTION_REGEX}{CLASS_ID_REGEX})$") -STEP_CLASS_AND_PROPERTY_REGEX_COMPILED = re.compile(rf"(^{DIRECTION_REGEX}{CLASS_ID_REGEX}{PROPERTY_ID_REGEX}$)") - - -_traversal = "traversal" -ORIGIN_REGEX = rf"(?P{ENTITY_ID_REGEX})" - -HOP_REGEX_COMPILED = re.compile(rf"^{ORIGIN_REGEX}(?P<{_traversal}>{STEP_REGEX}+)$") - -# grabbing specific property for a class, property can be either object, annotation or data property -SINGLE_PROPERTY_REGEX_COMPILED = re.compile(rf"^{CLASS_ID_REGEX}{PROPERTY_ID_REGEX}$") - -# grabbing all properties for a class -ALL_PROPERTIES_REGEX_COMPILED = re.compile(rf"^{CLASS_ID_REGEX}\(\*\)$") - -ALL_TRAVERSAL_REGEX_COMPILED = ( - rf"({CLASS_ID_REGEX}\(\*\)|{CLASS_ID_REGEX}{PROPERTY_ID_REGEX}|{ORIGIN_REGEX}(?P<{_traversal}>{STEP_REGEX}+))" -) - -TABLE_REGEX_COMPILED = re.compile( - rf"^(?P<{Lookup.table}>{SUFFIX_REGEX})\((?P<{Lookup.key}>{SUFFIX_REGEX}),\s*(?P<{Lookup.value}>{SUFFIX_REGEX})\)$" -) - - -StepDirection = Literal["source", "target", "origin"] -_direction_by_symbol: dict[str, StepDirection] = {"->": "target", "<-": "source"} - - -class Step(BaseModel): - class_: Entity - property: Entity | None = None # only terminal step has property - direction: StepDirection - - @classmethod - def from_string(cls, raw: str, **kwargs) -> Self: - if result := STEP_CLASS_AND_PROPERTY_REGEX_COMPILED.match(raw): - return cls( - class_=Entity.from_string(result.group(EntityTypes.class_), type_="class"), - property=Entity.from_string(result.group(EntityTypes.property_), type_="property"), - direction=_direction_by_symbol[result.group("direction")], - **kwargs, - ) - elif result := STEP_CLASS_REGEX_COMPILED.match(raw): - return cls( - class_=Entity.from_string(result.group(EntityTypes.class_)), - direction=_direction_by_symbol[result.group("direction")], - ) # type: ignore - msg = f"Invalid step {raw}, expected in one of the following forms:" - msg += " ->prefix:suffix, <-prefix:suffix, ->prefix:suffix(prefix:suffix) or <-prefix:suffix(prefix:suffix)" - raise ValueError(msg) - - -class Traversal(BaseModel): - class_: Entity - - -class SingleProperty(Traversal): - property: Entity - - @classmethod - def from_string(cls, class_: str, property_: str) -> Self: - return cls( - class_=Entity.from_string(class_, type_="class"), property=Entity.from_string(property_, type_="property") - ) - - -class AllReferences(Traversal): - @classmethod - def from_string(cls, class_: str) -> Self: - return cls(class_=Entity.from_string(class_, type_="class")) - - -class AllProperties(Traversal): - @classmethod - def from_string(cls, class_: str) -> Self: - return cls(class_=Entity.from_string(class_, type_="class")) - - -class Origin(BaseModel): - class_: Entity - - @field_validator("class_", mode="before") - def process_if_string(cls, value): - return Entity.from_string(value) if isinstance(value, str) else value - - -class Hop(Traversal): - """Multi or single hop traversal through graph""" - - traversal: list[Step] - - @classmethod - def from_string(cls, class_: str, traversal: str | list[Step]) -> Self: - return cls( - class_=Entity.from_string(class_), - traversal=( - [Step.from_string(result[0]) for result in STEP_REGEX_COMPILED.findall(traversal)] - if isinstance(traversal, str) - else traversal - ), - ) - - -class TableLookup(BaseModel): - name: str - key: str - value: str - - -class Rule(BaseModel): - pass - - -class Query(BaseModel): - query: str - - -class RDFPath(Rule): - traversal: Traversal | Query - - -class RawLookup(RDFPath): - table: TableLookup - - -class SPARQLQuery(RDFPath): - traversal: Query - - -def parse_traversal(raw: str) -> AllReferences | AllProperties | SingleProperty | Hop: - if result := CLASS_ID_REGEX_COMPILED.match(raw): - return AllReferences.from_string(class_=result.group(EntityTypes.class_)) - elif result := ALL_PROPERTIES_REGEX_COMPILED.match(raw): - return AllProperties.from_string(class_=result.group(EntityTypes.class_)) - elif result := SINGLE_PROPERTY_REGEX_COMPILED.match(raw): - return SingleProperty.from_string( - class_=result.group(EntityTypes.class_), property_=result.group(EntityTypes.property_) - ) - elif result := HOP_REGEX_COMPILED.match(raw): - return Hop.from_string(class_=result.group("origin"), traversal=result.group(_traversal)) - else: - raise exceptions.NotValidRDFPath(raw).to_pydantic_custom_error() - - -def parse_table_lookup(raw: str) -> TableLookup: - if result := TABLE_REGEX_COMPILED.match(raw): - return TableLookup( - name=result.group(Lookup.table), key=result.group(Lookup.key), value=result.group(Lookup.value) - ) - raise exceptions.NotValidTableLookUp(raw).to_pydantic_custom_error() - - -def parse_rule(rule_raw: str, rule_type: TransformationRuleType | None) -> RDFPath: - match rule_type: - case TransformationRuleType.rdfpath: - rule_raw = rule_raw.replace(" ", "") - return RDFPath(traversal=parse_traversal(rule_raw)) - case TransformationRuleType.rawlookup: - rule_raw = rule_raw.replace(" ", "") - if Counter(rule_raw).get("|") != 1: - raise exceptions.NotValidRAWLookUp(rule_raw).to_pydantic_custom_error() - traversal, table_lookup = rule_raw.split("|") - return RawLookup(traversal=parse_traversal(traversal), table=parse_table_lookup(table_lookup)) - case TransformationRuleType.sparql: - return SPARQLQuery(traversal=Query(query=rule_raw)) - case None: - raise ValueError("Rule type must be specified") - - -def is_valid_rule(rule_type: TransformationRuleType, rule_raw: str) -> bool: - is_valid_rule = {TransformationRuleType.rdfpath: is_rdfpath, TransformationRuleType.rawlookup: is_rawlookup}[ - rule_type - ] - return is_valid_rule(rule_raw) - - -def is_rdfpath(raw: str) -> bool: - try: - parse_traversal(raw) - except ValueError: - return False - return True - - -def is_rawlookup(raw: str) -> bool: - try: - parse_rule(raw, TransformationRuleType.rawlookup) - except ValueError: - return False - return True diff --git a/cognite/neat/legacy/rules/models/rules.py b/cognite/neat/legacy/rules/models/rules.py deleted file mode 100644 index 9799fcf64..000000000 --- a/cognite/neat/legacy/rules/models/rules.py +++ /dev/null @@ -1,1289 +0,0 @@ -"""This module contains the definition of `TransformationRules` pydantic model and all -its sub-models and validators. -""" - -from __future__ import annotations - -import math -import re -import sys -import warnings -from collections.abc import ItemsView, Iterator, KeysView, ValuesView -from datetime import datetime -from functools import wraps -from typing import Any, ClassVar, Generic, TypeAlias, TypeVar, cast - -import pandas as pd -from pydantic import ( - BaseModel, - ConfigDict, - Field, - HttpUrl, - TypeAdapter, - ValidationError, - constr, - field_validator, - model_validator, - validator, -) -from pydantic.fields import FieldInfo -from rdflib import XSD, Literal, Namespace, URIRef - -from cognite.neat.constants import get_default_prefixes -from cognite.neat.legacy.rules import exceptions -from cognite.neat.legacy.rules.models._base import ( - ENTITY_ID_REGEX_COMPILED, - VERSIONED_ENTITY_REGEX_COMPILED, - ContainerEntity, - EntityTypes, - ParentClass, -) -from cognite.neat.legacy.rules.models.rdfpath import ( - AllReferences, - Entity, - Hop, - RawLookup, - SingleProperty, - SPARQLQuery, - TransformationRuleType, - Traversal, - parse_rule, -) -from cognite.neat.legacy.rules.models.value_types import ( - XSD_VALUE_TYPE_MAPPINGS, - ValueType, -) - -if sys.version_info >= (3, 11): - from typing import Self -else: - from typing_extensions import Self - -__all__ = [ - "Class", - "Classes", - "Instance", - "Metadata", - "Prefixes", - "Property", - "Properties", - "Resource", - "Rules", -] - -METADATA_VALUE_MAX_LENGTH = 5120 - - -def replace_nan_floats_with_default(values: dict, model_fields: dict[str, FieldInfo]) -> dict: - output = {} - for field_name, value in values.items(): - is_nan_float = isinstance(value, float) and math.isnan(value) - if not is_nan_float: - output[field_name] = value.strip() if isinstance(value, str) else value - continue - if field_name in model_fields: - output[field_name] = model_fields[field_name].default - else: - # field_name may be an alias - source_name = next( - (name for name, field in model_fields.items() if field.alias == field_name), - None, - ) - if source_name: - output[field_name] = model_fields[source_name].default - else: - # Just pass it through if it is not an alias. - output[field_name] = value.strip() if isinstance(value, str) else value - return output - - -def skip_field_validator(validators_field): - def decorator(func): - @wraps(func) - def wrapper(cls, value, values): - if isinstance(values, dict): - to_skip = values.get(validators_field, set()) - else: - try: - to_skip = values.data.get(validators_field, set()) - except Exception: - to_skip = set() - - if "all" in to_skip or func.__name__ in to_skip: - return value - return func(cls, value, values) - - return wrapper - - return decorator - - -def skip_model_validator(validators_field): - def decorator(func): - @wraps(func) - def wrapper(self): - to_skip = getattr(self, validators_field, set()) - if "all" in to_skip or func.__name__ in to_skip: - return self - - return func(self) - - return wrapper - - return decorator - - -class RuleModel(BaseModel): - model_config: ClassVar[ConfigDict] = ConfigDict( - populate_by_name=True, - str_strip_whitespace=True, - arbitrary_types_allowed=True, - strict=False, - extra="allow", - ) - validators_to_skip: set[str] = Field(default_factory=set, exclude=True) - - @classmethod - def mandatory_fields(cls, use_alias=False) -> set[str]: - """Returns a set of mandatory fields for the model.""" - return _get_required_fields(cls, use_alias) - - -def _get_required_fields(model: type[BaseModel], use_alias: bool = False) -> set[str]: - """Get required fields from a pydantic model. - - Parameters - ---------- - model : type[BaseModel] - Pydantic data model - use_alias : bool, optional - Whether to return field alias name, by default False - - Returns - ------- - list[str] - List of required fields - """ - required_fields = set() - for name, field in model.model_fields.items(): - if not field.is_required(): - continue - - alias = getattr(field, "alias", None) - if use_alias and alias: - required_fields.add(alias) - else: - required_fields.add(name) - return required_fields - - -class URL(BaseModel): - url: HttpUrl - - -######################################################################################## -### These highly depend on CDF API endpoint limitations we need to keep them updated ### -######################################################################################## -Description: TypeAlias = constr(min_length=1, max_length=1024) # type: ignore[valid-type] - -more_than_one_none_alphanumerics_regex = r"([_-]{2,})" - -prefix_compliance_regex = r"^([a-zA-Z]+)([a-zA-Z0-9]*[_-]{0,1}[a-zA-Z0-9_-]*)([a-zA-Z0-9]*)$" -data_model_id_compliance_regex = r"^[a-zA-Z]([a-zA-Z0-9_]{0,253}[a-zA-Z0-9])?$" -cdf_space_compliance_regex = ( - r"(?!^(space|cdf|dms|pg3|shared|system|node|edge)$)(^[a-zA-Z][a-zA-Z0-9_-]{0,41}[a-zA-Z0-9]?$)" -) - -view_id_compliance_regex = ( - r"(?!^(Query|Mutation|Subscription|String|Int32|Int64|Int|Float32|Float64|Float|" - r"Timestamp|JSONObject|Date|Numeric|Boolean|PageInfo|File|Sequence|TimeSeries)$)" - r"(^[a-zA-Z][a-zA-Z0-9_]{0,253}[a-zA-Z0-9]?$)" -) - -value_id_compliance_regex = r"(^[a-zA-Z][a-zA-Z0-9_]{0,253}[a-zA-Z0-9]?$)" - -dms_property_id_compliance_regex = ( - r"(?!^(space|externalId|createdTime|lastUpdatedTime|deletedTime|edge_id|" - r"node_id|project_id|property_group|seq|tg_table_name|extensions)$)" - r"(^[a-zA-Z][a-zA-Z0-9_]{0,253}[a-zA-Z0-9]?$)" -) - - -class_id_compliance_regex = r"(?!^(Class|class)$)(^[a-zA-Z][a-zA-Z0-9._-]{0,253}[a-zA-Z0-9]?$)" -property_id_compliance_regex = r"^(\*)|(?!^(Property|property)$)(^[a-zA-Z][a-zA-Z0-9._-]{0,253}[a-zA-Z0-9]?$)" - -version_compliance_regex = r"^[a-zA-Z0-9]([.a-zA-Z0-9_-]{0,41}[a-zA-Z0-9])?$" -######################################################################################## -######################################################################################## - - -Prefix: TypeAlias = str -ExternalId: TypeAlias = str - - -class Metadata(RuleModel): - """ - Metadata model for data model - - Args: - prefix: This is used as prefix for generation of RDF OWL/SHACL data model representation - suffix: Suffix is used as the data model external id when resolving rules as CDF data model - namespace: This is used as RDF namespace for generation of RDF OWL/SHACL data model representation and/or for - generation of RDF graphs - title: This is used as data model name in CDF, or as a data model title in RDF - version: This is used as RDF and CDF data model version - created: This is used as RDF data model creation date for generation of RDF OWL/SHACL data model representation - updated: This is used as RDF data model update date for generation of RDF OWL/SHACL data model representation - description: This is used as RDF data model description for generation of RDF - OWL/SHACL data model representation - creator: This is used as RDF data model creator for generation of RDF OWL/SHACL data model representation - contributor: This is used as RDF data model contributor for generation of - RDF OWL/SHACL data model representation - rights: This is used as RDF data model rights for generation of RDF OWL/SHACL data model representation - """ - - prefix: Prefix = Field( - alias="space", - description=( - "This is used as prefix for generation of RDF OWL/SHACL data model representation" - " and/or as CDF space name to which model is intend to be stored" - ), - ) - - suffix: ExternalId | None = Field( - description=( - "Suffix is used as the data model external id when resolving rules as CDF data model" - " This field is optional and if not provided it will be generated from prefix." - ), - alias="external_id", - default=None, - min_length=1, - max_length=255, - ) - - namespace: Namespace | None = Field( - description="This is used as RDF namespace for generation of RDF OWL/SHACL data model representation " - "and/or for generation of RDF graphs.", - min_length=1, - max_length=2048, - default=None, - ) - - version: str = Field(min_length=1, max_length=43) - title: str | None = Field(alias="name", min_length=1, max_length=255, default=None) - - description: Description | None = None - - created: datetime = Field(default_factory=lambda: datetime.utcnow()) - updated: datetime = Field(default_factory=lambda: datetime.utcnow()) - - creator: str | list[str] | None = None - contributor: str | list[str] | None = None - rights: str | None = "Restricted for Internal Use of Cognite" - license: str | None = "Proprietary License" - - @field_validator("contributor", "contributor", "description", "rights", mode="before") - def replace_float_nan_with_default(cls, value, info): - if isinstance(value, float) and math.isnan(value): - return cls.model_fields[info.field_name].default - return value - - @field_validator("version", mode="before") - def convert_to_string(cls, value): - return str(value) - - @validator("prefix", always=True) - @skip_field_validator("validators_to_skip") - def is_prefix_compliant(cls, value, values): - if re.search(more_than_one_none_alphanumerics_regex, value): - raise exceptions.MoreThanOneNonAlphanumericCharacter("prefix", value).to_pydantic_custom_error() - if not re.match(cdf_space_compliance_regex, value): - raise exceptions.PrefixRegexViolation(value, cdf_space_compliance_regex).to_pydantic_custom_error() - else: - return value - - @validator("suffix", always=True) - @skip_field_validator("validators_to_skip") - def set_suffix_if_none(cls, value, values): - if value is not None: - return value - warnings.warn( - exceptions.DataModelIdMissing(values["prefix"].replace("-", "_")).message, - category=exceptions.DataModelIdMissing, - stacklevel=2, - ) - return values["prefix"].replace("-", "_") - - @validator("suffix", always=True) - @skip_field_validator("validators_to_skip") - def is_suffix_compliant(cls, value, values): - if re.search(more_than_one_none_alphanumerics_regex, value): - raise exceptions.MoreThanOneNonAlphanumericCharacter("suffix", value).to_pydantic_custom_error() - if not re.match(data_model_id_compliance_regex, value): - raise exceptions.DataModelIdRegexViolation(value, data_model_id_compliance_regex).to_pydantic_custom_error() - else: - return value - - @validator("namespace", always=True) - @skip_field_validator("validators_to_skip") - def set_namespace_if_none(cls, value, values): - if value is None: - suffix = f"/{values['suffix']}" if values["prefix"] != values["suffix"] else "" - return Namespace(f"http://purl.org/cognite/{values['prefix']}{suffix}#") - try: - return Namespace(TypeAdapter(HttpUrl).validate_python(value)) - except ValidationError as e: - raise exceptions.MetadataSheetNamespaceNotValidURL(value).to_pydantic_custom_error() from e - - @validator("namespace", always=True) - @skip_field_validator("validators_to_skip") - def fix_namespace_ending(cls, value, values): - if value.endswith("#") or value.endswith("/"): - return value - warnings.warn( - exceptions.NamespaceEndingFixed(value).message, - category=exceptions.NamespaceEndingFixed, - stacklevel=2, - ) - return Namespace(f"{value}#") - - @validator("title", always=True) - @skip_field_validator("validators_to_skip") - def set_title_if_none(cls, value, values): - if value is not None: - return value - elif values["suffix"]: - return values["suffix"] - else: - return values["prefix"] - - @validator("creator", always=True) - @skip_field_validator("validators_to_skip") - def set_creator_if_none(cls, value, values): - if value is not None: - return value - else: - return ["neat"] - - @validator("contributor", always=True) - @skip_field_validator("validators_to_skip") - def set_contributor_if_none(cls, value, values): - if value is not None: - return value - else: - return ["Cognite"] - - @validator("version", always=True) - @skip_field_validator("validators_to_skip") - def is_version_compliant(cls, value, values): - if not re.match(version_compliance_regex, value): - raise exceptions.VersionRegexViolation(value, version_compliance_regex).to_pydantic_custom_error() - else: - return value - - @field_validator("creator", "contributor", mode="before") - def to_list_if_comma(cls, value, values): - if isinstance(value, str): - if value: - return value.replace(", ", ",").split(",") - if cls.model_fields[values.field_name].default is None: - return None - return value - - @property - def space(self) -> str: - """Returns data model space.""" - return cast(str, self.prefix) - - @property - def external_id(self) -> str: - """Returns data model external.""" - return cast(str, self.suffix) - - @property - def name(self) -> str: - """Returns data model name.""" - return cast(str, self.title) - - def to_pandas(self) -> pd.Series: - """Converts Metadata to pandas Series.""" - return pd.Series(self.model_dump()) - - def _repr_html_(self) -> str: - """Returns HTML representation of Metadata.""" - return self.to_pandas().to_frame("value")._repr_html_() # type: ignore[operator] - - -class Resource(RuleModel): - """ - Base class for resources that constitute data model (i.e., classes, properties) - - Args: - description: The description of the resource. - cdf_resource_type: The CDF resource type to which resource resolves to - deprecated: Whether the resource is deprecated or not. - deprecation_date: The date when the resource was deprecated. - replaced_by: The resource that replaced this resource. - source: Source of information for given resource - source_entity_name: The name of the source entity that is closest to the resource being described. - match_type: The match type of the resource being described and the source entity. - comment: Additional comment about mapping between the resource being described and the source entity. - - """ - - # Solution model - description: Description | None = Field(alias="Description", default=None) - - # Solution CDF resource, it is not needed when working with FDM, this is only for - # Classic CDF data model - cdf_resource_type: list[str] | str | None = Field(alias="Resource Type", default=None) - - # Advance data modeling: Keeping track if Resource got deprecated or not - deprecated: bool = Field(default=False) - deprecation_date: datetime | None = Field(alias="deprecationDate", default=None) - replaced_by: str | None = Field(alias="replacedBy", default=None) - - # Advance data modeling: Relation to existing resources for purpose of mapping - source: HttpUrl | None = Field( - alias="Source", - description=( - "Source of information for given entity, e.g. https://www.entsoe.eu/digital/common-information-model/" - ), - default=None, - ) - source_entity_name: str | None = Field( - alias="Source Entity Name", - description="Closest entity in source, e.g. Substation", - default=None, - ) - match_type: str | None = Field( - alias="Match Type", - description="Type of match between source entity and one being defined", - default=None, - ) - comment: str | None = Field(alias="Comment", description="Comment about mapping", default=None) - - @model_validator(mode="before") - def replace_float_nan_with_default(cls, values: dict) -> dict: - return replace_nan_floats_with_default(values, cls.model_fields) - - -T_Resource = TypeVar("T_Resource", bound=Resource) - - -class ResourceDict(BaseModel, Generic[T_Resource]): - data: dict[str, T_Resource] = Field(default_factory=dict) - - def __getitem__(self, item: str) -> T_Resource: - return self.data[item] - - def __setitem__(self, key: str, value: T_Resource): - self.data[key] = value - - def __contains__(self, item: str) -> bool: - return item in self.data - - def __len__(self) -> int: - return len(self.data) - - def __iter__(self) -> Iterator[str]: # type: ignore[override] - return iter(self.data) - - def values(self) -> ValuesView[T_Resource]: - return self.data.values() - - def keys(self) -> KeysView[str]: - return self.data.keys() - - def items(self) -> ItemsView[str, T_Resource]: - return self.data.items() - - def to_pandas(self, drop_na_columns: bool = True, include: list[str] | None = None) -> pd.DataFrame: - """Converts ResourceDict to pandas DataFrame.""" - df = pd.DataFrame([class_.model_dump() for class_ in self.data.values()]) - if drop_na_columns: - df = df.dropna(axis=1, how="all") - if include is not None: - df = df[include] - return df - - def groupby(self, by: str) -> dict[str, ResourceDict[T_Resource]]: - """Groups ResourceDict by given column(s).""" - groups: dict[str, ResourceDict[T_Resource]] = {} - for key, resource in self.data.items(): - value = getattr(resource, by) - if value not in groups: - groups[value] = ResourceDict() - groups[value][key] = resource - return groups - - def _repr_html_(self) -> str: - """Returns HTML representation of ResourceDict.""" - return self.to_pandas(drop_na_columns=True)._repr_html_() # type: ignore[operator] - - -class Class(Resource): - """ - Base class for all classes that are part of the data model. - - Args: - class_id: The class ID of the class. - class_name: The name of the class. - parent_class: The parent class of the class. - """ - - class_id: ExternalId = Field(alias="Class", min_length=1, max_length=255) - class_name: ExternalId | None = Field(alias="Name", default=None, min_length=1, max_length=255) - # Solution model - parent_class: list[ParentClass] | None = Field(alias="Parent Class", default=None) - # Todo: Remove? Does not seem to be used anywhere - filter_: str | None = Field(alias="Filter", default=None, min_length=1) - - @model_validator(mode="before") - def replace_nan_floats_with_default(cls, values: dict) -> dict: - return replace_nan_floats_with_default(values, cls.model_fields) - - @validator("class_id", always=True) - @skip_field_validator("validators_to_skip") - def is_class_id_compliant(cls, value, values): - if re.search(more_than_one_none_alphanumerics_regex, value): - raise exceptions.MoreThanOneNonAlphanumericCharacter("class_id", value).to_pydantic_custom_error() - if not re.match(class_id_compliance_regex, value): - raise exceptions.ClassSheetClassIDRegexViolation( - value, class_id_compliance_regex - ).to_pydantic_custom_error() - else: - return value - - @validator("class_name", always=True) - def set_class_name_if_none(cls, value, values): - if value is None: - if "class_id" not in values: - raise exceptions.ClassIDMissing().to_pydantic_custom_error() - warnings.warn( - exceptions.ClassNameNotProvided(values["class_id"]).message, - category=exceptions.ClassNameNotProvided, - stacklevel=2, - ) - value = values["class_id"] - return value - - @field_validator("parent_class", mode="before") - @skip_field_validator("validators_to_skip") - def parent_class_to_list_of_entities(cls, value, values): - if isinstance(value, str) and value: - parent_classes = [] - for v in value.replace(", ", ",").split(","): - if ENTITY_ID_REGEX_COMPILED.match(v) or VERSIONED_ENTITY_REGEX_COMPILED.match(v): - parent_classes.append(ParentClass.from_string(entity_string=v)) - else: - # if all fails defaults "neat" object which ends up being updated to proper - # prefix and version upon completion of Rules validation - parent_classes.append(ParentClass(prefix="undefined", suffix=v, name=v)) - - return parent_classes - else: - return None - - @field_validator("parent_class", mode="after") - @skip_field_validator("validators_to_skip") - def is_parent_class_id_compliant(cls, value, values): - if isinstance(value, list): - if illegal_ids := [v for v in value if re.search(more_than_one_none_alphanumerics_regex, v.suffix)]: - raise exceptions.MoreThanOneNonAlphanumericCharacter( - "parent_class", ", ".join(illegal_ids) - ).to_pydantic_custom_error() - if illegal_ids := [v for v in value if not re.match(class_id_compliance_regex, v.suffix)]: - for v in illegal_ids: - print(v.id) - raise exceptions.ClassSheetParentClassIDRegexViolation( - illegal_ids, class_id_compliance_regex - ).to_pydantic_custom_error() - return value - - -class Classes(ResourceDict[Class]): - """This represents a collection of classes that are part of the data model.""" - - ... - - -class Property(Resource): - """ - A property is a characteristic of a class. It is a named attribute of a class that describes a range of values - or a relationship to another class. - - Args: - class_id: Class ID to which property belongs - property_id: Property ID of the property - property_name: Property name. Defaults to property_id - expected_value_type: Expected value type of the property - min_count: Minimum count of the property values. Defaults to 0 - max_count: Maximum count of the property values. Defaults to None - default: Default value of the property - property_type: Property type (DatatypeProperty/attribute or ObjectProperty/edge/relationship) - cdf_resource_type: CDF resource to under which property will be resolved to (e.g., Asset) - resource_type_property: To what property of CDF resource given property resolves to (e.g., Asset name) - source_type: In case if property resolves as CDF relationship, this argument indicates - relationship source type (defaults to Asset) - target_type: In case if property resolves as CDF relationship, this argument - indicates relationship target type (defaults to Asset) - label: CDF Label used for relationship. Defaults to property_id - relationship_external_id_rule: Rule to use when generating CDF relationship externalId - rule_type: Rule type for the transformation from source to target representation - of knowledge graph. Defaults to None (no transformation) - rule: Actual rule for the transformation from source to target representation of - knowledge graph. Defaults to None (no transformation) - skip_rule: Flag indicating if rule should be skipped when performing - knowledge graph transformations. Defaults to False - - """ - - # Solution model - class_id: ExternalId = Field(alias="Class", min_length=1, max_length=255) - property_id: ExternalId = Field(alias="Property", min_length=1, max_length=255) - property_name: ExternalId | None = Field(alias="Name", default=None, min_length=1, max_length=255) - expected_value_type: ValueType = Field(alias="Type") - min_count: int | None = Field(alias="Min Count", default=0) - max_count: int | None = Field(alias="Max Count", default=None) - default: Any | None = Field(alias="Default", default=None) - - # OWL property - property_type: str = EntityTypes.data_property - - # Core CDF resources (Asset, Relationship, and Labels) - resource_type_property: list[str] | None = Field( - alias="Resource Type Property", - default=None, - description="This is what property to resolve to in CDF resource, for " - "example f cdf_resource_type is 'Asset', then this could" - "be 'name' or 'description'. Note you can specify " - "multiple properties ['name', 'metadata'] which would store" - "this property twice in CDF, once as 'name' and once as 'metadata", - ) - source_type: str = Field(alias="Relationship Source Type", default="Asset") - target_type: str = Field(alias="Relationship Target Type", default="Asset") - label: str | None = Field(alias="Relationship Label", default=None) - relationship_external_id_rule: str | None = Field(alias="Relationship ExternalID Rule", default=None) - # Specialization of cdf_resource_type to allow definition of both - # Asset and Relationship at the same time - cdf_resource_type: list[str] = Field( - alias="Resource Type", - default_factory=list, - description="This is typically 'Asset' or 'Relationship'", - ) - - # Transformation rule (domain to solution) - rule_type: TransformationRuleType | None = Field(alias="Rule Type", default=None) - rule: str | AllReferences | SingleProperty | Hop | RawLookup | SPARQLQuery | Traversal | None = Field( - alias="Rule", default=None - ) - skip_rule: bool = Field(alias="Skip", default=False) - - # Container-specific things, only used for advance modeling or auto-filled by neat - container: ContainerEntity | None = Field(alias="Container", default=None) - container_property: str | None = Field(alias="Container Property", default=None) - index: bool | None = Field(alias="Index", default=False) - constraints: str | None = Field(alias="Constraints", default=None, min_length=1) - - @property - def is_raw_lookup(self) -> bool: - return self.rule_type == TransformationRuleType.rawlookup - - @model_validator(mode="before") - def replace_float_nan_with_default(cls, values: dict) -> dict: - return replace_nan_floats_with_default(values, cls.model_fields) - - @field_validator("container", mode="before") - def container_string_to_entity(cls, value): - if not value: - return value - - if ENTITY_ID_REGEX_COMPILED.match(value) or VERSIONED_ENTITY_REGEX_COMPILED.match(value): - return ContainerEntity.from_string(entity_string=value) - else: - return ContainerEntity(prefix="undefined", suffix=value, name=value) - - @field_validator("expected_value_type", mode="before") - def expected_value_type_string_to_entity(cls, value): - # handle simple types - if value in XSD_VALUE_TYPE_MAPPINGS.keys(): - return XSD_VALUE_TYPE_MAPPINGS[value] - - # complex types correspond to relations to other classes - if ENTITY_ID_REGEX_COMPILED.match(value) or VERSIONED_ENTITY_REGEX_COMPILED.match(value): - return ValueType.from_string(entity_string=value, type_=EntityTypes.object_value_type, mapping=None) - else: - return ValueType( - prefix="undefined", - suffix=value, - name=value, - type_=EntityTypes.object_value_type, - mapping=None, - ) - # return ValueType( - - @validator("class_id", always=True) - @skip_field_validator("validators_to_skip") - def is_class_id_compliant(cls, value, values): - if re.search(more_than_one_none_alphanumerics_regex, value): - raise exceptions.MoreThanOneNonAlphanumericCharacter("class_id", value).to_pydantic_custom_error() - if not re.match(class_id_compliance_regex, value): - raise exceptions.PropertiesSheetClassIDRegexViolation( - value, class_id_compliance_regex - ).to_pydantic_custom_error() - else: - return value - - @validator("property_id", always=True) - @skip_field_validator("validators_to_skip") - def is_property_id_compliant(cls, value, values): - if re.search(more_than_one_none_alphanumerics_regex, value): - raise exceptions.MoreThanOneNonAlphanumericCharacter("property_id", value).to_pydantic_custom_error() - if not re.match(property_id_compliance_regex, value): - raise exceptions.PropertyIDRegexViolation(value, property_id_compliance_regex).to_pydantic_custom_error() - else: - return value - - @validator("expected_value_type", always=True) - @skip_field_validator("validators_to_skip") - def is_expected_value_type_compliant(cls, value, values): - if re.search(more_than_one_none_alphanumerics_regex, value.suffix): - raise exceptions.MoreThanOneNonAlphanumericCharacter( - "expected_value_type", value - ).to_pydantic_custom_error() - if not re.match(class_id_compliance_regex, value.suffix): - raise exceptions.ValueTypeIDRegexViolation(value, class_id_compliance_regex).to_pydantic_custom_error() - else: - return value - - @validator("rule_type", pre=True) - def to_lowercase(cls, value): - return value.casefold() if value else value - - @validator("skip_rule", pre=True) - def from_string(cls, value): - if isinstance(value, str): - return value.casefold() in {"true", "skip", "yes", "y"} - return value - - @validator("rule") - @skip_field_validator("validators_to_skip") - def is_valid_rule(cls, value, values): - if rule_type := values.get("rule_type"): - if not value: - raise exceptions.RuleTypeProvidedButRuleMissing( - values["property_id"], values["class_id"], values["rule_type"] - ).to_pydantic_custom_error() - _ = parse_rule(value, rule_type) - return value - - @validator("resource_type_property", pre=True) - def split_str(cls, v): - if v: - return [v.strip() for v in v.split(",")] if "," in v else [v] - - @field_validator("cdf_resource_type", mode="before") - def to_list_if_comma(cls, value, info): - if isinstance(value, str): - if value: - return value.replace(", ", ",").split(",") - if cls.model_fields[info.field_name].default is None: - return None - return value - - # Setters - # TODO: configure setters to only run if field_validators are successful, otherwise do not run them! - @property - def is_mandatory(self) -> bool: - return self.min_count != 0 - - @model_validator(mode="after") - def set_property_type(self): - if self.expected_value_type.type_ == EntityTypes.data_value_type: - self.property_type = EntityTypes.data_property - else: - self.property_type = EntityTypes.object_property - return self - - @model_validator(mode="after") - def set_container_if_missing(self): - if not self.container and ( - self.expected_value_type.type_ == EntityTypes.data_value_type or self.max_count == 1 - ): - self.container = ContainerEntity(prefix="undefined", suffix=self.class_id, name=self.class_id) - return self - - @model_validator(mode="after") - def set_container_property_if_missing(self): - if not self.container_property and ( - self.expected_value_type.type_ == EntityTypes.data_value_type or self.max_count == 1 - ): - self.container_property = self.property_id - return self - - @model_validator(mode="after") - def set_property_name_if_none(self): - if self.property_name is None: - warnings.warn( - exceptions.PropertyNameNotProvided(self.property_id).message, - category=exceptions.PropertyNameNotProvided, - stacklevel=2, - ) - self.property_name = self.property_id - return self - - @model_validator(mode="after") - @skip_model_validator("validators_to_skip") - def set_relationship_label(self): - if self.label is None: - warnings.warn( - exceptions.MissingLabel(self.property_id).message, - category=exceptions.MissingLabel, - stacklevel=2, - ) - self.label = self.property_id - return self - - @model_validator(mode="after") - @skip_model_validator("validators_to_skip") - def set_skip_rule(self): - if self.rule_type is None: - warnings.warn( - exceptions.NoTransformationRules(class_id=self.class_id, property_id=self.property_id).message, - category=exceptions.NoTransformationRules, - stacklevel=2, - ) - self.skip_rule = True - else: - self.skip_rule = False - return self - - @model_validator(mode="after") - def set_default_as_list(self): - if ( - self.property_type == "DatatypeProperty" - and self.default - and self.max_count - and self.max_count != 1 - and not isinstance(self.default, list) - ): - warnings.warn( - exceptions.DefaultValueNotList(self.property_id).message, - category=exceptions.DefaultValueNotList, - stacklevel=2, - ) - if isinstance(self.default, str): - if self.default: - self.default = self.default.replace(", ", ",").split(",") - else: - self.default = [self.default] - return self - - @model_validator(mode="after") - @skip_model_validator("validators_to_skip") - def is_default_value_type_proper(self): - if self.property_type == "DatatypeProperty" and self.default: - default_value = self.default[0] if isinstance(self.default, list) else self.default - - if type(default_value) is not self.expected_value_type.python: - try: - if isinstance(self.default, list): - updated_list = [] - for value in self.default: - updated_list.append(self.expected_value_type.python(value)) - self.default = updated_list - else: - self.default = self.expected_value_type.python(self.default) - - except Exception: - exceptions.DefaultValueTypeNotProper( - self.property_id, - type(self.default), - self.expected_value_type.python, - ) - return self - - -class Properties(ResourceDict[Property]): - """This represents a collection of properties that are part of the data model.""" - - ... - - -class Prefixes(RuleModel): - """ - Class deals with prefixes used in the data model and data model instances - - Args: - prefixes: Dict of prefixes - """ - - prefixes: dict[str, Namespace] = get_default_prefixes() - - -class Instance(RuleModel): - """ - Class deals with RDF triple that defines data model instances of data, represented - as a single row in the `Instances` sheet of the Excel file. - - Args: - instance: URI of the instance - property_: URI of the property - value: value of the property - namespace: namespace of the instance - prefixes: prefixes of the instance - - !!! note "Warning" - Use of the `Instances` sheet is not recommended, instead if you need additional - triples in your graph use Graph Capturing Sheet instead! - - See - [`extract_graph_from_sheet`](../graph/extractors.md#cognite.neat.graph.extractors.extract_graph_from_sheet) - for more details. - """ - - instance: URIRef | None = Field(alias="Instance", default=None) - property_: URIRef | None = Field(alias="Property", default=None) - value: Literal | URIRef | None = Field(alias="Value", default=None) - namespace: Namespace - prefixes: dict[str, Namespace] - - @staticmethod - def get_value(value, prefixes) -> URIRef | Literal: - try: - url = URL(url=value).url - return URIRef(str(url)) - except ValidationError: - try: - entity = Entity.from_string(value) - return URIRef(prefixes[entity.prefix][entity.suffix]) - except ValueError: - return value - - @model_validator(mode="before") - def convert_values(cls, values: dict): - # we expect to read Excel sheet which contains naming convention of column - # 'Instance', 'Property', 'Value', if that's not the case we should raise error - if not {"Instance", "Property", "Value"}.issubset(set(values.keys())): - raise TypeError("We only support inputs from the transformation rule Excel sheet!!!") - - namespace = values["namespace"] - prefixes = values["prefixes"] - - values["Instance"] = cls.get_value(values["Instance"], prefixes) - values["Instance"] = ( - values["Instance"] if isinstance(values["Instance"], URIRef) else URIRef(namespace[values["Instance"]]) - ) - - values["Property"] = cls.get_value(values["Property"], prefixes) - values["Property"] = ( - values["Property"] if isinstance(values["Property"], URIRef) else URIRef(namespace[values["Property"]]) - ) - - if isinstance(values["Value"], str): - values["Value"] = cls.get_value(values["Value"], prefixes) - if not isinstance(values["Value"], URIRef): - datatype = ( - XSD.integer - if cls.isint(values["Value"]) - else XSD.float - if cls.isfloat(values["Value"]) - else XSD.string - ) - values["Value"] = Literal(values["Value"], datatype=datatype) - elif isinstance(values["Value"], float): - values["Value"] = Literal(values["Value"], datatype=XSD.float) - elif isinstance(values["Value"], int): - values["Value"] = Literal(values["Value"], datatype=XSD.integer) - elif isinstance(values["Value"], bool): - values["Value"] = Literal(values["Value"], datatype=XSD.boolean) - elif isinstance(values["Value"], datetime): - values["Value"] = Literal(values["Value"], datatype=XSD.dateTime) - else: - values["Value"] = Literal(values["Value"], datatype=XSD.string) - - return values - - @staticmethod - def isfloat(x): - try: - _ = float(x) - except (TypeError, ValueError): - return False - else: - return True - - @staticmethod - def isint(x): - try: - a = float(x) - b = int(a) - except (TypeError, ValueError): - return False - else: - return a == b - - -class Rules(RuleModel): - """ - Rules is a core concept in `neat`. This represents fusion of data model - definitions and (optionally) the transformation rules used to transform the data/graph - from the source representation to the target representation defined by the data model. - The rules are defined in a Excel sheet and then parsed into a `Rules` object. The - `Rules` object is then used to generate data model and the`RDF` graph made of data - model instances. - - Args: - metadata: Data model metadata - classes: Classes defined in the data model - properties: Class properties defined in the data model with accompanying transformation rules - to transform data from source to target representation - prefixes: Prefixes used in the data model. Defaults to internal prefixes - instances: Instances defined in the data model. Defaults to None - validators_to_skip: List of validators to skip. Defaults to [] - - !!! note "Importers" - Neat supports importing data from different sources. See the importers section for more details. - - !!! note "Exporters" - Neat supports exporting data to different sources. See the exporters section for more details. - - !!! note "validators_to_skip" use this only if you are sure what you are doing - """ - - metadata: Metadata - classes: Classes - properties: Properties - prefixes: dict[str, Namespace] = get_default_prefixes() - instances: list[Instance] = Field(default_factory=list) - - @property - def raw_tables(self) -> list[str]: - return list( - { - parse_rule(rule.rule, TransformationRuleType.rawlookup).table.name # type: ignore[arg-type, attr-defined] - for rule in self.properties.values() - if rule.is_raw_lookup - } - ) - - @field_validator("instances", mode="before") - def none_as_empty_list(cls, value): - if value is None: - return [] - return value - - @field_validator("classes", mode="before") - def dict_to_classes_obj(cls, value: dict | Classes) -> Classes: - if not isinstance(value, dict): - return value - dict_of_classes = TypeAdapter(dict[str, Class]).validate_python(value) - return Classes(data=dict_of_classes) - - @field_validator("properties", mode="before") - def dict_to_properties_obj(cls, value: dict | Properties) -> Properties: - if not isinstance(value, dict): - return value - dict_of_properties = TypeAdapter(dict[str, Property]).validate_python(value) - return Properties(data=dict_of_properties) - - @model_validator(mode="after") - @skip_model_validator("validators_to_skip") - def update_prefix_version_entities(self) -> Self: - version = self.metadata.version - prefix = self.metadata.prefix - - # update expected_value_types - for id_ in self.properties.keys(): - # only update version of expected value type which are part of this data model - if ( - not self.properties[id_].expected_value_type.version - and self.properties[id_].expected_value_type.prefix == "undefined" - ): - self.properties[id_].expected_value_type.version = version - - if self.properties[id_].expected_value_type.prefix == "undefined": - self.properties[id_].expected_value_type.prefix = prefix - - # update container - for id_ in self.properties.keys(): - # only update version of expected value type which are part of this data model - if ( - self.properties[id_].container - and cast(ContainerEntity, self.properties[id_].container).prefix == "undefined" - ): - cast(ContainerEntity, self.properties[id_].container).prefix = prefix - - # update parent classes - for id_ in self.classes.keys(): - if self.classes[id_].parent_class: - for parent_class in cast(list[ParentClass], self.classes[id_].parent_class): - if parent_class.prefix == "undefined": - parent_class.prefix = prefix - if not parent_class.version: - parent_class.version = version - - return self - - @model_validator(mode="after") - @skip_model_validator("validators_to_skip") - def update_container_description_and_name(self): - for id_ in self.properties.keys(): - if ( - self.properties[id_].container - and self.properties[id_].container.external_id in self.classes - and self.properties[id_].container.space == self.metadata.space - ): - self.properties[id_].container.description = self.classes[ - self.properties[id_].container.external_id - ].description - - self.properties[id_].container.name = self.classes[ - self.properties[id_].container.external_id - ].class_name - return self - - @model_validator(mode="after") - @skip_model_validator("validators_to_skip") - def add_missing_classes(self): - for property_ in self.properties.values(): - if property_.class_id not in self.classes: - self.classes[property_.class_id] = Class( - class_id=property_.class_id, - class_name=property_.class_id, - comment="This class was automatically added by neat", - ) - return self - - def update_prefix(self, prefix: str): - if prefix == self.metadata.prefix: - warnings.warn("Prefix is already in use, no changes made!", stacklevel=2) - elif prefix in self.prefixes.keys(): - raise exceptions.PrefixAlreadyInUse(prefix).to_pydantic_custom_error() - elif not re.match(cdf_space_compliance_regex, prefix): - raise exceptions.PrefixRegexViolation(prefix, cdf_space_compliance_regex).to_pydantic_custom_error() - else: - old_prefix = self.metadata.prefix - self.metadata.prefix = prefix - - # update entity ids for expected_value_types and containers - for id_ in self.properties.keys(): - if self.properties[id_].expected_value_type.prefix == old_prefix: - self.properties[id_].expected_value_type.prefix = prefix - - if ( - self.properties[id_].container - and cast(ContainerEntity, self.properties[id_].container).prefix == old_prefix - ): - cast(ContainerEntity, self.properties[id_].container).prefix = prefix - - # update parent classes - for id_ in self.classes.keys(): - if self.classes[id_].parent_class: - for parent_class in cast(list[ParentClass], self.classes[id_].parent_class): - if parent_class.prefix == old_prefix: - parent_class.prefix = prefix - - # update prefixes - self.prefixes[prefix] = self.prefixes.pop(old_prefix) - - def update_space(self, space: str): - "Convenience method for updating prefix more intuitive to CDF users" - return self.update_prefix(space) - - def update_version(self, version: str): - if version == self.metadata.version: - warnings.warn("Version is already in use, no changes made!", stacklevel=2) - elif not re.match(version_compliance_regex, version): - raise exceptions.VersionRegexViolation(version, version_compliance_regex).to_pydantic_custom_error() - else: - old_version = self.metadata.version - self.metadata.version = version - for id_ in self.properties.keys(): - if ( - self.properties[id_].expected_value_type.prefix == self.metadata.prefix - and self.properties[id_].expected_value_type.version == old_version - ): - self.properties[id_].expected_value_type.version = version - - for id_ in self.classes.keys(): - if self.classes[id_].parent_class: - for parent_class in cast(list[ParentClass], self.classes[id_].parent_class): - if parent_class.prefix == self.metadata.prefix and parent_class.version == old_version: - parent_class.version = version - - @validator("prefixes") - @skip_field_validator("validators_to_skip") - def are_prefixes_compliant(cls, value, values): - if ill_formed_prefixes := [ - prefix for prefix, _ in value.items() if re.search(more_than_one_none_alphanumerics_regex, prefix) - ]: - raise exceptions.MoreThanOneNonAlphanumericCharacter( - "prefixes", ", ".join(ill_formed_prefixes) - ).to_pydantic_custom_error() - if ill_formed_prefixes := [ - prefix for prefix, _ in value.items() if not re.match(prefix_compliance_regex, prefix) - ]: - raise exceptions.PrefixesRegexViolation( - ill_formed_prefixes, prefix_compliance_regex - ).to_pydantic_custom_error() - else: - return value - - @validator("prefixes") - @skip_field_validator("validators_to_skip") - def are_namespaces_compliant(cls, value, values): - ill_formed_namespaces = [] - for _, namespace in value.items(): - try: - _ = URL(url=namespace).url - except ValueError: - ill_formed_namespaces += namespace - - if ill_formed_namespaces: - raise exceptions.PrefixesSheetNamespaceNotValidURL(ill_formed_namespaces).to_pydantic_custom_error() - else: - return value - - @validator("prefixes") - @skip_field_validator("validators_to_skip") - def add_data_model_prefix_namespace(cls, value, values): - if "metadata" not in values: - raise exceptions.MetadataSheetMissingOrFailedValidation().to_pydantic_custom_error() - if "prefix" not in values["metadata"].dict(): - raise exceptions.FiledInMetadataSheetMissingOrFailedValidation( - missing_field="prefix" - ).to_pydantic_custom_error() - if "namespace" not in values["metadata"].dict(): - raise exceptions.FiledInMetadataSheetMissingOrFailedValidation( - missing_field="namespace" - ).to_pydantic_custom_error() - - value[values["metadata"].prefix] = values["metadata"].namespace - return value - - @property - def space(self) -> str: - """Returns data model space.""" - return cast(str, self.metadata.prefix) - - @property - def external_id(self) -> str: - """Returns data model external.""" - return cast(str, self.metadata.suffix) - - @property - def name(self) -> str: - """Returns data model name.""" - return cast(str, self.metadata.title) - - def _repr_html_(self) -> str: - """Pretty display of the TransformationRules object in a Notebook""" - dump = self.metadata.model_dump(by_alias=True) - for key in ["creator", "contributor"]: - dump[key] = ", ".join(dump[key]) if isinstance(dump[key], list) else dump[key] - dump["class_count"] = len(self.classes) - dump["property_count"] = len(self.properties) - dump["instance_count"] = len(self.instances) - return pd.Series(dump).to_frame("value")._repr_html_() # type: ignore[operator] diff --git a/cognite/neat/legacy/rules/models/tables.py b/cognite/neat/legacy/rules/models/tables.py deleted file mode 100644 index 76a93cf3e..000000000 --- a/cognite/neat/legacy/rules/models/tables.py +++ /dev/null @@ -1,9 +0,0 @@ -__all__ = ["Tables"] - - -class Tables: - prefixes = "Prefixes" - properties = "Properties" - classes = "Classes" - metadata = "Metadata" - instances = "Instances" diff --git a/cognite/neat/legacy/rules/models/value_types.py b/cognite/neat/legacy/rules/models/value_types.py deleted file mode 100644 index a66c734e2..000000000 --- a/cognite/neat/legacy/rules/models/value_types.py +++ /dev/null @@ -1,118 +0,0 @@ -from __future__ import annotations - -from datetime import date, datetime -from typing import cast - -from cognite.client.data_classes.data_modeling import ( - Boolean, - Date, - FileReference, - Float64, - Int32, - Int64, - Json, - PropertyType, - SequenceReference, - Text, - TimeSeriesReference, - Timestamp, -) -from pydantic import BaseModel - -from cognite.neat.legacy.rules.models._base import Entity, EntityTypes - - -class ValueTypeMapping(BaseModel): - """Mapping between XSD, Python, DMS and Graphql types.""" - - xsd: str - python: type - dms: type - graphql: str - - -class ValueType(Entity): - """Value type is a data/object type defined as a child of Entity model.""" - - mapping: ValueTypeMapping | None = None - - @property - def python(self) -> type | None: - """Returns the Python type for a given value type.""" - if self.type_ == EntityTypes.data_value_type: - return cast(ValueTypeMapping, self.mapping).python - else: - return None - - @property - def xsd(self) -> str | None: - """Returns the XSD type for a given value type.""" - if self.type_ == EntityTypes.data_value_type: - return cast(ValueTypeMapping, self.mapping).xsd - else: - return None - - @property - def dms(self) -> type | None: - """Returns the DMS type for a given value type.""" - if self.type_ == EntityTypes.data_value_type: - return cast(ValueTypeMapping, self.mapping).dms - else: - return None - - @property - def graphql(self) -> str | None: - """Returns the Graphql type for a given value type.""" - if self.type_ == EntityTypes.data_value_type: - return cast(ValueTypeMapping, self.mapping).graphql - else: - return None - - -_DATA_TYPES: list[dict] = [ - {"name": "boolean", "python": bool, "GraphQL": "Boolean", "dms": Boolean}, - {"name": "float", "python": float, "GraphQL": "Float", "dms": Float64}, - {"name": "double", "python": float, "GraphQL": "Float", "dms": Float64}, - {"name": "integer", "python": int, "GraphQL": "Int", "dms": Int32}, - {"name": "nonPositiveInteger", "python": int, "GraphQL": "Int", "dms": Int32}, - {"name": "nonNegativeInteger", "python": int, "GraphQL": "Int", "dms": Int32}, - {"name": "negativeInteger", "python": int, "GraphQL": "Int", "dms": Int32}, - {"name": "long", "python": int, "GraphQL": "Int", "dms": Int64}, - {"name": "string", "python": str, "GraphQL": "String", "dms": Text}, - {"name": "langString", "python": str, "GraphQL": "String", "dms": Text}, - {"name": "anyURI", "python": str, "GraphQL": "String", "dms": Text}, - {"name": "normalizedString", "python": str, "GraphQL": "String", "dms": Text}, - {"name": "token", "python": str, "GraphQL": "String", "dms": Text}, - {"name": "PlainLiteral", "python": str, "GraphQL": "String", "dms": Text}, - # Graphql does not have a datetime/date type this is CDF specific - {"name": "dateTime", "python": datetime, "GraphQL": "Timestamp", "dms": Timestamp}, - {"name": "dateTimeStamp", "python": datetime, "GraphQL": "Timestamp", "dms": Timestamp}, - {"name": "date", "python": date, "GraphQL": "String", "dms": Date}, - # CDF specific types, not in XSD - {"name": "timeseries", "python": TimeSeriesReference, "GraphQL": "TimeSeries", "dms": TimeSeriesReference}, - {"name": "file", "python": FileReference, "GraphQL": "File", "dms": FileReference}, - {"name": "sequence", "python": SequenceReference, "GraphQL": "Sequence", "dms": SequenceReference}, - {"name": "json", "python": Json, "GraphQL": "Json", "dms": Json}, -] - -XSD_VALUE_TYPE_MAPPINGS: dict[str, ValueType] = { - data_type["name"]: ValueType( - prefix="xsd", - suffix=cast(str, data_type["name"]), - name=cast(str, data_type["name"]), - type_=EntityTypes.data_value_type, - mapping=ValueTypeMapping( - xsd=data_type["name"], - python=data_type["python"], - dms=data_type["dms"], - graphql=data_type["GraphQL"], - ), - ) - for data_type in _DATA_TYPES -} - - -DMS_VALUE_TYPE_MAPPINGS: dict[type[PropertyType], ValueType] = {} -for value_type in XSD_VALUE_TYPE_MAPPINGS.values(): - if value_type.dms not in DMS_VALUE_TYPE_MAPPINGS: - DMS_VALUE_TYPE_MAPPINGS[cast(type[PropertyType], value_type.dms)] = cast(ValueType, value_type) diff --git a/cognite/neat/legacy/workflows/examples/Export_DMS/workflow.yaml b/cognite/neat/legacy/workflows/examples/Export_DMS/workflow.yaml deleted file mode 100644 index 3f79a3d14..000000000 --- a/cognite/neat/legacy/workflows/examples/Export_DMS/workflow.yaml +++ /dev/null @@ -1,89 +0,0 @@ -configs: [] -description: null -implementation_module: null -name: Export DMS -steps: -- complex_configs: {} - configs: {} - description: null - enabled: true - id: step_861205 - label: Upload Rules Spreadsheet - max_retries: 0 - method: null - params: - file_type: rules - retry_delay: 3 - stype: file_uploader - system_component_id: null - transition_to: - - step_295479 - trigger: true - ui_config: - pos_x: 629 - pos_y: 57 -- complex_configs: {} - configs: - File name: '' - Report formatter: BasicHTML - Role: infer - description: null - enabled: true - id: step_295479 - label: Validate - max_retries: 0 - method: ExcelToRules - params: {} - retry_delay: 3 - stype: stdstep - system_component_id: null - transition_to: - - step_50885 - trigger: false - ui_config: - pos_x: 629 - pos_y: 161 -- complex_configs: - Components: - containers: true - data_models: true - spaces: true - views: true - configs: - Dry run: 'False' - Existing component handling: update - Multi-space components create: 'True' - description: null - enabled: true - id: step_50885 - label: Export Data Model to CDF - max_retries: 0 - method: RulesToDMS - params: {} - retry_delay: 3 - stype: stdstep - system_component_id: null - transition_to: - - step_171560 - trigger: false - ui_config: - pos_x: 629 - pos_y: 243 -- complex_configs: {} - configs: {} - description: null - enabled: true - id: step_171560 - label: Export Transformations - max_retries: 0 - method: RulesToCDFTransformations - params: {} - retry_delay: 3 - stype: stdstep - system_component_id: null - transition_to: [] - trigger: false - ui_config: - pos_x: 629 - pos_y: 342 -system_components: [] diff --git a/cognite/neat/legacy/workflows/examples/Export_Rules_to_Ontology/workflow.yaml b/cognite/neat/legacy/workflows/examples/Export_Rules_to_Ontology/workflow.yaml deleted file mode 100644 index d0448bcb4..000000000 --- a/cognite/neat/legacy/workflows/examples/Export_Rules_to_Ontology/workflow.yaml +++ /dev/null @@ -1,152 +0,0 @@ -configs: [] -description: null -implementation_module: null -name: Export Rules to Ontology -steps: - - complex_configs: {} - configs: {} - description: null - enabled: true - id: step_trigger - label: Workflow Trigger - max_retries: 0 - method: null - params: {} - retry_delay: 3 - stype: http_trigger - system_component_id: null - transition_to: - - step_import_excel_rules - trigger: true - ui_config: - pos_x: 480 - pos_y: 45 - - complex_configs: {} - configs: - file_name: skos-rules.xlsx - validation_report_file: rules_validation_report.txt - validation_report_storage_dir: rules_validation_report - version: "" - description: null - enabled: true - id: step_import_excel_rules - label: Import Excel Rules - max_retries: 0 - method: ImportExcelToRules - params: {} - retry_delay: 3 - stype: stdstep - system_component_id: null - transition_to: - - step_export_rules_to_ontology - trigger: false - ui_config: - pos_x: 481 - pos_y: 120 - - complex_configs: {} - configs: - ontology_file_path: staging/ontology.ttl - description: null - enabled: true - id: step_export_rules_to_ontology - label: Export Rules To Ontology - max_retries: 0 - method: ExportRulesToOntology - params: {} - retry_delay: 3 - stype: stdstep - system_component_id: null - transition_to: - - step_export_rules_to_shacle - trigger: false - ui_config: - pos_x: 478 - pos_y: 191 - - complex_configs: {} - configs: - db_server_api_root_url: "" - disk_store_dir: ontology-graph-store - graph_name: source - init_procedure: reset - sparql_query_url: "" - sparql_update_url: "" - store_type: oxigraph - description: null - enabled: true - id: step_configure_graph_store - label: Configure Graph Store - max_retries: 0 - method: ConfigureGraphStore - params: {} - retry_delay: 3 - stype: stdstep - system_component_id: null - transition_to: - - step_load_ontology_to_graph_store - trigger: false - ui_config: - pos_x: 479 - pos_y: 383 - - complex_configs: {} - configs: - add_base_iri: "True" - file_path: staging/ontology.ttl - mime_type: text/turtle - description: null - enabled: true - id: step_load_ontology_to_graph_store - label: Load Ontology To Graph Store - max_retries: 0 - method: ExtractGraphFromRdfFile - params: {} - retry_delay: 3 - stype: stdstep - system_component_id: null - transition_to: - - step_load_shacl_to_graph - trigger: false - ui_config: - pos_x: 479 - pos_y: 455 - - complex_configs: {} - configs: - file_name: shacl.ttl - shacl_file_path: staging/shacl.ttl - storage_dir: staging - description: null - enabled: true - id: step_export_rules_to_shacle - label: Export Rules to Shacl - max_retries: 0 - method: ExportRulesToSHACL - params: {} - retry_delay: 3 - stype: stdstep - system_component_id: null - transition_to: - - step_configure_graph_store - trigger: false - ui_config: - pos_x: 480 - pos_y: 291 - - complex_configs: {} - configs: - add_base_iri: "True" - file_path: staging/shacl.ttl - mime_type: text/turtle - description: null - enabled: true - id: step_load_shacl_to_graph - label: Load SHACL to Graph Store - max_retries: 0 - method: ExtractGraphFromRdfFile - params: {} - retry_delay: 3 - stype: stdstep - system_component_id: null - transition_to: [] - trigger: false - ui_config: - pos_x: 475 - pos_y: 542 -system_components: [] diff --git a/cognite/neat/legacy/workflows/examples/Extract_DEXPI_Graph_and_Export_Rules/workflow.yaml b/cognite/neat/legacy/workflows/examples/Extract_DEXPI_Graph_and_Export_Rules/workflow.yaml deleted file mode 100644 index 79e08e1fd..000000000 --- a/cognite/neat/legacy/workflows/examples/Extract_DEXPI_Graph_and_Export_Rules/workflow.yaml +++ /dev/null @@ -1,139 +0,0 @@ -configs: [] -description: null -implementation_module: null -name: Extract DEXPI Graph and Export Rules -steps: - - complex_configs: {} - configs: {} - description: null - enabled: true - id: step_trigger - label: Workflow Trigger - max_retries: 0 - method: null - params: {} - retry_delay: 3 - stype: http_trigger - system_component_id: null - transition_to: - - step_128344 - - step_configure_graph_store - trigger: true - ui_config: - pos_x: 352 - pos_y: 106 - - complex_configs: {} - configs: - db_server_api_root_url: "" - disk_store_dir: dexpi-graph - graph_name: source - init_procedure: reset - sparql_query_url: "" - sparql_update_url: "" - store_type: oxigraph - description: null - enabled: true - id: step_configure_graph_store - label: Graph Store Config - max_retries: 0 - method: ConfigureGraphStore - params: {} - retry_delay: 3 - stype: stdstep - system_component_id: null - transition_to: - - step_download_dexpi_file - trigger: false - ui_config: - pos_x: 353 - pos_y: 176 - - complex_configs: {} - configs: - base_namespace: http://purl.org/cognite/neat# - file_path: source-graphs/depxi_example.xml - description: null - enabled: true - id: step_load_ttl - label: Extract Graph from DEXPI file - max_retries: 0 - method: ExtractGraphFromDexpiFile - params: {} - retry_delay: 3 - stype: stdstep - system_component_id: null - transition_to: - - infer_rules - trigger: false - ui_config: - pos_x: 354 - pos_y: 344 - - complex_configs: {} - configs: - api_url: https://raw.githubusercontent.com/cognitedata/neat/main/tests/data/depxi_example.xml - auth_mode: none - http_method: GET - output_file_path: source-graphs/depxi_example.xml - password: "" - response_destination: file - token: "" - username: "" - description: null - enabled: true - id: step_download_dexpi_file - label: Download DISC DEXPI example - max_retries: 0 - method: DownloadDataFromRestApiToFile - params: {} - retry_delay: 3 - stype: stdstep - system_component_id: null - transition_to: - - step_load_ttl - trigger: false - ui_config: - pos_x: 354 - pos_y: 250 - - complex_configs: {} - configs: - excel_file_path: rules/inferred-rules.xlsx - file_name: inferred_transformations.xlsx - max_number_of_instances: "-1" - storage_dir: staging - description: null - enabled: true - id: infer_rules - label: Import/infer Rules from Graph - max_retries: 0 - method: ImportGraphToRules - params: {} - retry_delay: 3 - stype: stdstep - system_component_id: null - transition_to: - - import_inferred_rules - trigger: false - ui_config: - pos_x: 356 - pos_y: 442 - - complex_configs: {} - configs: - file_name: inferred-rules.xlsx - validation_report_file: rules_validation_report.txt - validation_report_storage_dir: rules_validation_report - version: "" - description: null - enabled: true - id: import_inferred_rules - label: Import Inferred Rules - max_retries: 0 - method: ImportExcelToRules - params: {} - retry_delay: 3 - stype: stdstep - system_component_id: null - transition_to: [] - trigger: false - ui_config: - pos_x: 356 - pos_y: 537 -system_components: [] diff --git a/cognite/neat/legacy/workflows/examples/Extract_RDF_Graph_and_Generate_Assets/workflow.yaml b/cognite/neat/legacy/workflows/examples/Extract_RDF_Graph_and_Generate_Assets/workflow.yaml deleted file mode 100644 index 944f121b5..000000000 --- a/cognite/neat/legacy/workflows/examples/Extract_RDF_Graph_and_Generate_Assets/workflow.yaml +++ /dev/null @@ -1,270 +0,0 @@ -configs: [] -description: null -implementation_module: null -name: Extract RDF Graph and Generate Assets -steps: - - complex_configs: {} - configs: {} - description: null - enabled: true - id: step_http_trigger - label: Process trigger - max_retries: 0 - method: null - params: - workflow_start_method: persistent_blocking - retry_delay: 3 - stype: http_trigger - system_component_id: null - transition_to: - - step_rules_loader - trigger: true - ui_config: - pos_x: 454 - pos_y: 77 - - complex_configs: {} - configs: - file_name: Rules-Nordic44.xlsx - validation_report_file: rules_validation_report.txt - validation_report_storage_dir: rules_validation_report - version: "" - description: null - enabled: true - id: step_rules_loader - label: Rules loader - max_retries: 0 - method: ImportExcelToRules - params: {} - retry_delay: 3 - stype: stdstep - system_component_id: null - transition_to: - - step_234135 - - step_configure_source_graph_store - trigger: false - ui_config: - pos_x: 452 - pos_y: 167 - - complex_configs: {} - configs: - add_base_iri: "True" - file_path: source-graphs/Knowledge-Graph-Nordic44-dirty.xml - mime_type: application/rdf+xml - description: null - enabled: true - id: step_graph_loader - label: Extract Graph from RDF File - max_retries: 0 - method: ExtractGraphFromRdfFile - params: {} - retry_delay: 3 - stype: stdstep - system_component_id: null - transition_to: - - step_transform - trigger: false - ui_config: - pos_x: 453 - pos_y: 418 - - complex_configs: {} - configs: - cdf_lookup_database: "" - description: null - enabled: true - id: step_transform - label: Transform graph - max_retries: 0 - method: TransformSourceToSolutionGraph - params: {} - retry_delay: 3 - stype: stdstep - system_component_id: null - transition_to: [] - trigger: false - ui_config: - pos_x: 453 - pos_y: 500 - - complex_configs: {} - configs: - asset_external_id_prefix: "" - assets_cleanup_type: full - data_set_id: "2626756768281823" - description: null - enabled: true - id: step_generate_assets - label: Generate cdf assets - max_retries: 0 - method: GenerateAssetsFromGraph - params: {} - retry_delay: 3 - stype: stdstep - system_component_id: null - transition_to: - - step_generate_relationships - trigger: false - ui_config: - pos_x: 455 - pos_y: 571 - - complex_configs: {} - configs: - data_set_id: "2626756768281823" - relationship_external_id_prefix: "" - description: null - enabled: true - id: step_generate_relationships - label: Generate relationships - max_retries: 0 - method: GenerateRelationshipsFromGraph - params: {} - retry_delay: 3 - stype: stdstep - system_component_id: null - transition_to: [] - trigger: false - ui_config: - pos_x: 456 - pos_y: 633 - - complex_configs: {} - configs: - db_server_api_root_url: "" - disk_store_dir: nordic44-graph-store - graph_name: source - init_procedure: reset - sparql_query_url: "" - sparql_update_url: "" - store_type: oxigraph - description: null - enabled: true - id: step_configure_source_graph_store - label: Configure source graph store - max_retries: 0 - method: ConfigureGraphStore - params: {} - retry_delay: 3 - stype: stdstep - system_component_id: null - transition_to: - - step_configure_solution_graph_store - trigger: false - ui_config: - pos_x: 451 - pos_y: 245 - - complex_configs: {} - configs: - db_server_api_root_url: "" - disk_store_dir: solution-graph-store-4 - graph_name: solution - init_procedure: reset - sparql_query_url: "" - sparql_update_url: "" - store_type: oxigraph - description: null - enabled: true - id: step_configure_solution_graph_store - label: Configure solution graph store - max_retries: 0 - method: ConfigureGraphStore - params: {} - retry_delay: 3 - stype: stdstep - system_component_id: null - transition_to: - - step_graph_loader - trigger: false - ui_config: - pos_x: 454 - pos_y: 334 -system_components: - - description: null - id: grid_management_system - label: Grid management system - transition_to: - - rdf_xml_file - ui_config: - pos_x: 171 - pos_y: 6 - - description: null - id: rdf_xml_file - label: RDF XML File - transition_to: - - graph_db_store - ui_config: - pos_x: 170 - pos_y: 103 - - description: null - id: graph_db_store - label: OxiGraph source graph_store - transition_to: - - transformer - ui_config: - pos_x: 240 - pos_y: 177 - - description: null - id: in_memmory_store - label: In-memory source graph_store - transition_to: [] - ui_config: - pos_x: 73 - pos_y: 175 - - description: null - id: transformation_rules - label: Transformation rules - transition_to: - - transformer - ui_config: - pos_x: 413 - pos_y: 195 - - description: null - id: cdf_raw_table - label: Cdf raw lookup table - transition_to: - - transformer - ui_config: - pos_x: 586 - pos_y: 195 - - description: null - id: transformer - label: Transformer - transition_to: - - solution_graph - ui_config: - pos_x: 237 - pos_y: 329 - - description: null - id: solution_graph - label: Solution graph - transition_to: - - cdf_classic_exporter - ui_config: - pos_x: 237 - pos_y: 405 - - description: null - id: cdf_classic_exporter - label: CDF Classic exporter - transition_to: - - cdf_classic - ui_config: - pos_x: 181 - pos_y: 498 - - description: null - id: cdf_fdm_exporter - label: CDF FDM exporter - transition_to: - - cdf_fdm - ui_config: - pos_x: 345 - pos_y: 498 - - description: null - id: cdf_classic - label: CDF Classic (Asset,Relationships) - transition_to: [] - ui_config: - pos_x: 182 - pos_y: 586 - - description: null - id: cdf_fdm - label: CDF FDM - transition_to: [] - ui_config: - pos_x: 347 - pos_y: 586 diff --git a/cognite/neat/legacy/workflows/examples/Import_DMS/workflow.yaml b/cognite/neat/legacy/workflows/examples/Import_DMS/workflow.yaml deleted file mode 100644 index dd62471c5..000000000 --- a/cognite/neat/legacy/workflows/examples/Import_DMS/workflow.yaml +++ /dev/null @@ -1,65 +0,0 @@ -configs: [] -description: null -implementation_module: null -name: Import DMS -steps: -- complex_configs: {} - configs: - Data model id: playground_nordic44:nordic44 - Report formatter: BasicHTML - Role: information_architect - description: null - enabled: true - id: step_725987 - label: Import DMS - max_retries: 0 - method: DMSToRules - params: {} - retry_delay: 3 - stype: stdstep - system_component_id: null - transition_to: - - step_419138 - trigger: false - ui_config: - pos_x: 541 - pos_y: 84 -- complex_configs: {} - configs: - Styling: default - description: null - enabled: true - id: step_419138 - label: Create Excel Spreadsheet - max_retries: 0 - method: RulesToExcel - params: {} - retry_delay: 3 - stype: stdstep - system_component_id: null - transition_to: [] - trigger: false - ui_config: - pos_x: 540 - pos_y: 193 -- complex_configs: {} - configs: {} - description: null - enabled: true - id: step_431484 - label: Trigger Workflow - max_retries: 0 - method: null - params: - sync: 'false' - workflow_name: '' - retry_delay: 3 - stype: http_trigger - system_component_id: null - transition_to: - - step_725987 - trigger: true - ui_config: - pos_x: 541 - pos_y: -13 -system_components: [] diff --git a/cognite/neat/legacy/workflows/examples/Ontology_to_Data_Model/workflow.yaml b/cognite/neat/legacy/workflows/examples/Ontology_to_Data_Model/workflow.yaml deleted file mode 100644 index 16a2e52d0..000000000 --- a/cognite/neat/legacy/workflows/examples/Ontology_to_Data_Model/workflow.yaml +++ /dev/null @@ -1,116 +0,0 @@ -configs: [] -description: null -implementation_module: null -name: Ontology to Data Model -steps: - - complex_configs: {} - configs: {} - description: null - enabled: true - id: step_trigger - label: Trigger - max_retries: 0 - method: null - params: {} - retry_delay: 3 - stype: http_trigger - system_component_id: null - transition_to: - - step_128344 - - dowload_ontology - trigger: true - ui_config: - pos_x: 524 - pos_y: 259 - - complex_configs: {} - configs: - excel_file_path: rules/LIS-14-rules.xlsx - make_compliant: "True" - ontology_file_path: staging/LIS-14.ttl - description: null - enabled: true - id: onotology2rules - label: Import Ontology to Rules - max_retries: 0 - method: ImportOntologyToRules - params: {} - retry_delay: 3 - stype: stdstep - system_component_id: null - transition_to: - - load_generated_rules - - import_generated_rules - trigger: false - ui_config: - pos_x: 524 - pos_y: 403 - - complex_configs: {} - configs: - file_name: LIS-14-rules.xlsx - validation_report_file: rules_validation_report.txt - validation_report_storage_dir: rules_validation_report - version: "" - description: null - enabled: true - id: import_generated_rules - label: Import Generated Rules - max_retries: 0 - method: ImportExcelToRules - params: {} - retry_delay: 3 - stype: stdstep - system_component_id: null - transition_to: - - step_59017 - - step_829062 - - export_rules_to_graphql_schema - trigger: false - ui_config: - pos_x: 524 - pos_y: 490 - - complex_configs: {} - configs: - api_url: https://rds.posccaesar.org/ontology/lis14/ont/core/2.0/LIS-14.ttl - auth_mode: none - http_method: GET - output_file_path: staging/LIS-14.ttl - password: "" - response_destination: file - token: "" - username: "" - description: null - enabled: true - id: dowload_ontology - label: Download Ontology - max_retries: 0 - method: DownloadDataFromRestApiToFile - params: {} - retry_delay: 3 - stype: stdstep - system_component_id: null - transition_to: - - onotology2rules - trigger: false - ui_config: - pos_x: 524 - pos_y: 333 - - complex_configs: {} - configs: - file_name: "" - storage_dir: staging - description: null - enabled: true - id: export_rules_to_graphql_schema - label: Export Rules to GraphQL Schema - max_retries: 0 - method: ExportRulesToGraphQLSchema - params: {} - retry_delay: 3 - stype: stdstep - system_component_id: null - transition_to: [] - trigger: false - ui_config: - pos_x: 525 - pos_y: 577 -system_components: [] diff --git a/cognite/neat/legacy/workflows/examples/Validate_Rules/workflow.yaml b/cognite/neat/legacy/workflows/examples/Validate_Rules/workflow.yaml deleted file mode 100644 index 1158c0fba..000000000 --- a/cognite/neat/legacy/workflows/examples/Validate_Rules/workflow.yaml +++ /dev/null @@ -1,67 +0,0 @@ -configs: [] -description: null -implementation_module: null -name: Validate Rules -steps: -- complex_configs: {} - configs: - File name: '' - Report formatter: BasicHTML - Role: infer - description: null - enabled: true - id: step_validate_rules - label: Validate Rules - max_retries: 0 - method: ExcelToRules - params: {} - retry_delay: 3 - stype: stdstep - system_component_id: null - transition_to: - - step_942973 - - step_715590 - trigger: false - ui_config: - pos_x: 558 - pos_y: 97 -- complex_configs: {} - configs: {} - description: null - enabled: true - id: step_upload_rules - label: Upload Rules Spreadsheets - max_retries: 0 - method: null - params: - file_type: rules - retry_delay: 3 - stype: file_uploader - system_component_id: null - transition_to: - - step_31642 - - step_validate_rules - trigger: true - ui_config: - pos_x: 558 - pos_y: -14 -- complex_configs: {} - configs: - Output role format: input - Styling: default - description: null - enabled: true - id: step_715590 - label: Convert Rules - max_retries: 0 - method: RulesToExcel - params: {} - retry_delay: 3 - stype: stdstep - system_component_id: null - transition_to: [] - trigger: false - ui_config: - pos_x: 558 - pos_y: 217 -system_components: [] diff --git a/cognite/neat/legacy/workflows/examples/Validate_Solution_Model/workflow.yaml b/cognite/neat/legacy/workflows/examples/Validate_Solution_Model/workflow.yaml deleted file mode 100644 index f15436e73..000000000 --- a/cognite/neat/legacy/workflows/examples/Validate_Solution_Model/workflow.yaml +++ /dev/null @@ -1,64 +0,0 @@ -configs: [] -description: null -implementation_module: null -name: Validate Solution Model -steps: -- complex_configs: {} - configs: {} - description: null - enabled: true - id: step_769298 - label: Upload File - max_retries: 0 - method: null - params: - file_type: rules - retry_delay: 3 - stype: file_uploader - system_component_id: null - transition_to: - - step_399494 - trigger: true - ui_config: - pos_x: 627 - pos_y: -19 -- complex_configs: {} - configs: - File name: '' - Report formatter: BasicHTML - Role: infer - description: null - enabled: true - id: step_399494 - label: Import Excel - max_retries: 0 - method: ExcelToRules - params: {} - retry_delay: 3 - stype: stdstep - system_component_id: null - transition_to: - - step_273233 - trigger: false - ui_config: - pos_x: 627 - pos_y: 66 -- complex_configs: {} - configs: - Report Formatter: BasicHTML - description: null - enabled: true - id: step_273233 - label: Validate Against CDF - max_retries: 0 - method: ValidateRulesAgainstCDF - params: {} - retry_delay: 3 - stype: stdstep - system_component_id: null - transition_to: [] - trigger: false - ui_config: - pos_x: 627 - pos_y: 154 -system_components: [] diff --git a/cognite/neat/legacy/workflows/examples/Visualize_Data_Model_Using_Mock_Graph/workflow.yaml b/cognite/neat/legacy/workflows/examples/Visualize_Data_Model_Using_Mock_Graph/workflow.yaml deleted file mode 100644 index 47bb42bd1..000000000 --- a/cognite/neat/legacy/workflows/examples/Visualize_Data_Model_Using_Mock_Graph/workflow.yaml +++ /dev/null @@ -1,95 +0,0 @@ -configs: [] -description: null -implementation_module: null -name: Visualize Data Model Using Mock Graph -steps: - - complex_configs: {} - configs: - File name: information-architect-david.xlsx - Report formatter: BasicHTML - Role: infer - description: null - enabled: true - id: step_verify_rules - label: Verify Rules - max_retries: 0 - method: ExcelToRules - params: {} - retry_delay: 3 - stype: stdstep - system_component_id: null - transition_to: - - step_configure_graph_store - trigger: false - ui_config: - pos_x: 507 - pos_y: 250 - - complex_configs: {} - configs: - Disk storage directory: mock-graph-store - Graph: source - Graph store type: oxigraph - GraphDB API root URL: "" - Init procedure: reset - Query URL: "" - Update URL: "" - description: null - enabled: true - id: step_configure_graph_store - label: Configure Graph Store - max_retries: 0 - method: GraphStoreConfiguration - params: {} - retry_delay: 3 - stype: stdstep - system_component_id: null - transition_to: - - step_166101 - - step_mock_graph_generation - trigger: false - ui_config: - pos_x: 506 - pos_y: 314 - - complex_configs: {} - configs: - Class count: - '{"WindTurbine" : 1, "WindFarm" : 1, "OffshoreSubstation" : 1, - "DistributionSubstation" : 1, "OnshoreSubstation" : 1, "ArrayCable" : - 1, "ExportCable" : 1, "Transmission" : 1, "DistributionLine" : 1, "Meter" - : 1, "ElectricCarCharger" : 1}' - Graph: source - description: null - enabled: true - id: step_mock_graph_generation - label: Generate Mock Graph - max_retries: 0 - method: GraphFromMockData - params: {} - retry_delay: 3 - stype: stdstep - system_component_id: null - transition_to: [] - trigger: false - ui_config: - pos_x: 506 - pos_y: 390 - - complex_configs: {} - configs: {} - description: null - enabled: true - id: step_upload_rules - label: Upload Rules - max_retries: 0 - method: null - params: - file_type: rules - retry_delay: 3 - stype: file_uploader - system_component_id: null - transition_to: - - step_verify_rules - trigger: true - ui_config: - pos_x: 507 - pos_y: 177 -system_components: [] diff --git a/cognite/neat/legacy/workflows/examples/Visualize_Semantic_Data_Model/workflow.yaml b/cognite/neat/legacy/workflows/examples/Visualize_Semantic_Data_Model/workflow.yaml deleted file mode 100644 index 88a006c3f..000000000 --- a/cognite/neat/legacy/workflows/examples/Visualize_Semantic_Data_Model/workflow.yaml +++ /dev/null @@ -1,111 +0,0 @@ -configs: [] -description: null -implementation_module: null -name: Visualize Semantic Data Model -steps: -- complex_configs: {} - configs: {} - description: null - enabled: true - id: step_upload_excel_rules - label: Upload Excel Rules - max_retries: 0 - method: null - params: - file_type: rules - retry_delay: 3 - stype: file_uploader - system_component_id: null - transition_to: - - step_421523 - - step_excel_to_rules - trigger: true - ui_config: - pos_x: 498 - pos_y: 149 -- complex_configs: {} - configs: - File name: '' - Report formatter: BasicHTML - Role: infer - description: null - enabled: true - id: step_excel_to_rules - label: Import and Validate Rules - max_retries: 0 - method: ExcelToRules - params: {} - retry_delay: 3 - stype: stdstep - system_component_id: null - transition_to: - - step_rules_to_ontology - trigger: false - ui_config: - pos_x: 497 - pos_y: 234 -- complex_configs: {} - configs: - File path: staging/semantic-data-model.ttl - description: null - enabled: true - id: step_rules_to_ontology - label: Rules to Ontology - max_retries: 0 - method: RulesToSemanticDataModel - params: {} - retry_delay: 3 - stype: stdstep - system_component_id: null - transition_to: - - step_configure_graph_store - trigger: false - ui_config: - pos_x: 497 - pos_y: 323 -- complex_configs: {} - configs: - Disk storage directory: semantic-data-model - Graph: source - Graph store type: oxigraph - GraphDB API root URL: '' - Init procedure: reset - Query URL: '' - Update URL: '' - description: null - enabled: true - id: step_configure_graph_store - label: Configure Graph Store - max_retries: 0 - method: GraphStoreConfiguration - params: {} - retry_delay: 3 - stype: stdstep - system_component_id: null - transition_to: - - step_load_semantic_data_model - trigger: false - ui_config: - pos_x: 495 - pos_y: 399 -- complex_configs: {} - configs: - Add base URI: 'False' - File path: staging/semantic-data-model.ttl - MIME type: text/turtle - description: null - enabled: true - id: step_load_semantic_data_model - label: Load Semantic Data Model - max_retries: 0 - method: GraphFromRdfFile - params: {} - retry_delay: 3 - stype: stdstep - system_component_id: null - transition_to: [] - trigger: false - ui_config: - pos_x: 495 - pos_y: 476 -system_components: [] diff --git a/cognite/neat/workflows/migration/__init__.py b/cognite/neat/workflows/migration/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/cognite/neat/workflows/migration/steps.py b/cognite/neat/workflows/migration/steps.py deleted file mode 100644 index 67d61e3d7..000000000 --- a/cognite/neat/workflows/migration/steps.py +++ /dev/null @@ -1,91 +0,0 @@ -from pathlib import Path - -import yaml - -# Migration mapping - -# LoadTransformationRules to ImportExcelToRules -# InstancesFromRdfFileToSourceGraph to ExtractGraphFromRdfFile -# InstancesFromRulesToSolutionGraph to ExtractGraphFromRulesInstanceSheet -# GraphCapturingSheetToGraph to ExtractGraphFromGraphCapturingSheet -# GenerateMockGraph to ExtractGraphFromMockGraph -# InstancesFromJsonToGraph to ExtractGraphFromJsonFile -# InstancesFromAvevaPiAF to ExtractGraphFromAvevaPiAssetFramework -# DexpiToGraph to ExtractGraphFromDexpiFile -# GenerateCDFAssetsFromGraph to GenerateAssetsFromGraph -# GenerateCDFRelationshipsFromGraph to GenerateRelationshipsFromGraph -# GenerateCDFNodesAndEdgesFromGraph to GenerateNodesAndEdgesFromGraph -# UploadCDFAssets to LoadAssetsToCDF -# UploadCDFRelationships to LoadRelationshipsToCDF -# UploadCDFNodes to LoadNodesToCDF -# UploadCDFEdges to LoadEdgesToCDF -# CreateCDFLabels to LoadLabelsToCDF -# OpenApiToRules to `ImportOpenApiToRules -# ArbitraryJsonYamlToRules to ImportArbitraryJsonYamlToRules -# GraphToRules to ImportGraphToRules -# OntologyToRules to ImportOntologyToRules -# GraphQLSchemaFromRules to ExportGraphQLSchemaFromRules -# OntologyFromRules to ExportOntologyFromRules -# SHACLFromRules to ExportSHACLFromRules -# GraphCaptureSpreadsheetFromRules to ExportRulesToGraphCapturingSheet -# ExcelFromRules to ExportRulesToExcel - -# Define a dictionary that maps old step names to new ones -step_rename_mapping = { - "LoadTransformationRules": "ImportExcelToRules", - "InstancesFromRdfFileToSourceGraph": "ExtractGraphFromRdfFile", - "InstancesFromRulesToSolutionGraph": "ExtractGraphFromRulesInstanceSheet", - "GraphCapturingSheetToGraph": "ExtractGraphFromGraphCapturingSheet", - "GenerateMockGraph": "ExtractGraphFromMockGraph", - "InstancesFromJsonToGraph": "ExtractGraphFromJsonFile", - "InstancesFromAvevaPiAF": "ExtractGraphFromAvevaPiAssetFramework", - "DexpiToGraph": "ExtractGraphFromDexpiFile", - "GenerateCDFAssetsFromGraph": "GenerateAssetsFromGraph", - "GenerateCDFRelationshipsFromGraph": "GenerateRelationshipsFromGraph", - "GenerateCDFNodesAndEdgesFromGraph": "GenerateNodesAndEdgesFromGraph", - "UploadCDFAssets": "LoadAssetsToCDF", - "UploadCDFRelationships": "LoadRelationshipsToCDF", - "UploadCDFNodes": "LoadNodesToCDF", - "UploadCDFEdges": "LoadEdgesToCDF", - "CreateCDFLabels": "LoadLabelsToCDF", - "OpenApiToRules": "ImportOpenApiToRules", - "ArbitraryJsonYamlToRules": "ImportArbitraryJsonYamlToRules", - "GraphToRules": "ImportGraphToRules", - "OntologyToRules": "ImportOntologyToRules", - "GraphQLSchemaFromRules": "ExportGraphQLSchemaFromRules", - "OntologyFromRules": "ExportOntologyFromRules", - "SHACLFromRules": "ExportSHACLFromRules", - "GraphCaptureSpreadsheetFromRules": "ExportRulesToGraphCapturingSheet", - "ExcelFromRules": "ExportRulesToExcel", -} - - -def rename_workflow_steps(workflow_path: Path, dry_run: bool) -> str: - # Load the YAML file - data = yaml.safe_load(workflow_path.read_text()) - # Replace old step names with new ones - for step in data["steps"]: - if step["method"] in step_rename_mapping: - print(f"Renaming step {step['method']} to {step_rename_mapping[step['method']]}") - step["method"] = step_rename_mapping[step["method"]] - - # Save the updated YAML file - with workflow_path.open("w") as file: - yaml.safe_dump(data, file) - return "ok" - - -def migrate_all_workflow_names(workflows_path: str, dry_run: bool) -> str: - # Get all workflow files - workflow_files = Path(workflows_path).rglob("workflow.yaml") - # Migrate each workflow file - for workflow_file in workflow_files: - print(f"Migrating {workflow_file}") - rename_workflow_steps(workflow_file, dry_run) - return "ok" - - -if __name__ == "__main__": - # print current directory - print("The current directory is", Path.cwd()) - migrate_all_workflow_names("data/workflows", True) diff --git a/cognite/neat/workflows/migration/wf_manifests.py b/cognite/neat/workflows/migration/wf_manifests.py deleted file mode 100644 index cf3a40f77..000000000 --- a/cognite/neat/workflows/migration/wf_manifests.py +++ /dev/null @@ -1,33 +0,0 @@ -import logging -import os -from pathlib import Path - -import yaml - - -def migrate_wf_manifest(wf_store_path: Path): - """Migrate workflow manifests . Changed name of element from groups -> system_components""" - migrated_files = [] - wf_store_path = wf_store_path / "workflows" - for wf_module_name in os.listdir(wf_store_path): - wf_module_full_path = wf_store_path / wf_module_name - - if wf_module_full_path.is_dir(): - metadata_file = wf_store_path / wf_module_name / "workflow.yaml" - metadata_file_migrated = wf_store_path / wf_module_name / "workflow.yaml" - logging.info(f"Loading workflow {wf_module_name} metadata from {metadata_file}") - if metadata_file.exists(): - with metadata_file.open() as f: - manifest_yaml = yaml.safe_load(f) - logging.info(f"Loaded workflow {wf_module_name} metadata from {metadata_file}") - if "groups" in manifest_yaml: - logging.info(f"Found groups in {metadata_file}, migrating to system_components") - manifest_yaml["system_components"] = manifest_yaml["groups"] - del manifest_yaml["groups"] - with metadata_file_migrated.open("w") as f: - yaml.dump(manifest_yaml, f, indent=4) - migrated_files.append(metadata_file_migrated) - else: - logging.info(f"Metadata file {metadata_file} not found, skipping") - continue - return migrated_files diff --git a/cognite/neat/workflows/steps/lib/legacy/__init__.py b/cognite/neat/workflows/steps/lib/legacy/__init__.py deleted file mode 100644 index 3c53aa44f..000000000 --- a/cognite/neat/workflows/steps/lib/legacy/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -from .graph_extractor import * # noqa -from .graph_transformer import * # noqa -from .graph_loader import * # noqa -from .graph_store import * # noqa -from .rules_importer import * # noqa -from .rules_exporter import * # noqa -from .graph_contextualization import * # noqa diff --git a/cognite/neat/workflows/steps/lib/legacy/graph_contextualization.py b/cognite/neat/workflows/steps/lib/legacy/graph_contextualization.py deleted file mode 100644 index 359ff68fd..000000000 --- a/cognite/neat/workflows/steps/lib/legacy/graph_contextualization.py +++ /dev/null @@ -1,82 +0,0 @@ -from typing import ClassVar, cast - -from cognite.neat.legacy.graph.transformations.entity_matcher import simple_entity_matcher -from cognite.neat.workflows._exceptions import StepNotInitialized -from cognite.neat.workflows.model import FlowMessage -from cognite.neat.workflows.steps.data_contracts import SolutionGraph, SourceGraph -from cognite.neat.workflows.steps.step_model import Configurable, Step - -__all__ = ["SimpleGraphEntityMatcher"] -CATEGORY = __name__.split(".")[-1].replace("_", " ").title() + " [LEGACY]" - - -class SimpleGraphEntityMatcher(Step): - version = "legacy" - description = "The step matches entities in the graph and creates links based on provided configurations" - category = CATEGORY - configurables: ClassVar[list[Configurable]] = [ - Configurable(name="source_class", value="", label="Name of the source class"), - Configurable(name="source_property", value="", label="Name of the source property"), - Configurable( - name="source_value_type", - value="single_value_str", - label="Type of the value in the source property.Propery can have single value \ - or multiple values separated by comma.", - options=["single_value_str", "multi_value_str"], - ), - Configurable(name="target_class", value="", label="Name of the target class"), - Configurable(name="target_property", value="", label="Name of the target property"), - Configurable(name="relationship_name", value="link", label="Label of the relationship to be created"), - Configurable( - name="link_direction", - value="target_to_source", - label="Direction of the relationship.", - options=["target_to_source", "source_to_target"], - ), - Configurable( - name="matching_method", - value="regexp", - label="Method to be used for matching. Supported options .", - options=["exact_match", "regexp"], - ), - Configurable( - name="graph_name", - value="source", - label="The name of the graph to be used for matching.", - options=["source", "solution"], - ), - Configurable( - name="link_namespace", - value="http://purl.org/cognite/neat#", - label="The namespace of the link to be created", - ), - ] - - def run(self, graph_store: SolutionGraph | SourceGraph) -> FlowMessage: # type: ignore[override, syntax] - # We can't use the graph_store to get the graph as input parameter directly, - # resolver might resolve the wrong graph - # if both are present in the flow context - if self.configs is None: - raise StepNotInitialized(type(self).__name__) - configs = self.configs - if self.configs["graph_name"] == "solution": - # Todo Anders: Why is the graph fetched from context when it is passed as an argument? - graph_store = cast(SourceGraph | SolutionGraph, self.flow_context["SolutionGraph"]) - else: - graph_store = cast(SourceGraph | SolutionGraph, self.flow_context["SourceGraph"]) - - self.graph_store = graph_store.graph - new_links_counter = simple_entity_matcher( - graph_store=self.graph_store, - source_class=configs["source_class"], - source_property=configs["source_property"], - source_value_type=configs["source_value_type"], - target_class=configs["target_class"], - target_property=configs["target_property"], - relationship_name=configs["relationship_name"], - link_direction=configs["link_direction"], - matching_method=configs["matching_method"], - link_namespace=configs["link_namespace"], - ) - output_text = f"Matcher has created {new_links_counter} links" - return FlowMessage(output_text=output_text) diff --git a/cognite/neat/workflows/steps/lib/legacy/graph_extractor.py b/cognite/neat/workflows/steps/lib/legacy/graph_extractor.py deleted file mode 100644 index 8f5e74ad7..000000000 --- a/cognite/neat/workflows/steps/lib/legacy/graph_extractor.py +++ /dev/null @@ -1,746 +0,0 @@ -import hashlib -import json -import logging -import uuid -import xml.etree.ElementTree as ET -from pathlib import Path -from typing import ClassVar, cast - -from rdflib import RDF, XSD, Literal, Namespace, URIRef - -from cognite.neat.constants import DEFAULT_NAMESPACE -from cognite.neat.legacy.graph import extractors -from cognite.neat.legacy.graph.extractors._mock_graph_generator import ( - generate_triples as generate_mock_triples, -) -from cognite.neat.legacy.rules.exporters._rules2triples import get_instances_as_triples -from cognite.neat.utils.auxiliary import create_sha256_hash -from cognite.neat.workflows._exceptions import StepNotInitialized -from cognite.neat.workflows.model import FlowMessage, StepExecutionStatus -from cognite.neat.workflows.steps.data_contracts import ( - RulesData, - SolutionGraph, - SourceGraph, -) -from cognite.neat.workflows.steps.step_model import Configurable, Step - -__all__ = [ - "ExtractGraphFromRdfFile", - "ExtractGraphFromRulesInstanceSheet", - "ExtractGraphFromGraphCapturingSheet", - "ExtractGraphFromMockGraph", - "ExtractGraphFromRulesDataModel", - "ExtractGraphFromJsonFile", - "ExtractGraphFromAvevaPiAssetFramework", - "ExtractGraphFromDexpiFile", -] - -CATEGORY = __name__.split(".")[-1].replace("_", " ").title() + " [LEGACY]" - - -class ExtractGraphFromRdfFile(Step): - """ - This step extract instances from a file into the source graph. The file must be in RDF format. - """ - - description = "This step extract instances from a file into the source graph. The file must be in RDF format." - version = "legacy" - category = CATEGORY - configurables: ClassVar[list[Configurable]] = [ - Configurable( - name="file_path", - value="source-graphs/source-graph-dump.xml", - label="File name of source graph data dump in RDF format", - ), - Configurable( - name="mime_type", - value="application/rdf+xml", - label="MIME type of file containing RDF graph", - options=[ - "application/rdf+xml", - "text/turtle", - "application/n-triples", - "application/n-quads", - "application/trig", - ], - ), - Configurable( - name="add_base_iri", - value="True", - label="Whether to add base IRI to graph in case if entity ids are relative", - options=["True", "False"], - ), - ] - - def run(self, source_graph: SourceGraph) -> FlowMessage: # type: ignore[override, syntax] - if self.configs is None or self.data_store_path is None: - raise StepNotInitialized(type(self).__name__) - if source_graph.graph.rdf_store_type.lower() in ("memory", "oxigraph"): - if source_file := self.configs["file_path"]: - source_graph.graph.import_from_file( - self.data_store_path / Path(source_file), - mime_type=self.configs["mime_type"], # type: ignore[arg-type] - add_base_iri=self.configs["add_base_iri"] == "True", - ) - logging.info(f"Loaded {source_file} into source graph.") - else: - raise ValueError("You need a source_rdf_store.file specified for source_rdf_store.type=memory") - else: - raise NotImplementedError(f"Graph type {source_graph.graph.rdf_store_type} is not supported.") - - return FlowMessage(output_text="Instances loaded to source graph") - - -class ExtractGraphFromDexpiFile(Step): - """ - This step converts DEXPI P&ID (XML) into Knowledge Graph - """ - - description = "This step converts DEXPI P&ID (XML) into Knowledge Graph" - version = "legacy" - category = CATEGORY - configurables: ClassVar[list[Configurable]] = [ - Configurable( - name="file_path", - value="source-graphs/dexpi-pid.xml", - label="File path to DEXPI P&ID in XML format", - ), - Configurable( - name="base_namespace", - value="http://purl.org/cognite/neat#", - label="Base namespace to be added to ids for all nodes found in P&ID", - ), - ] - - def run(self, source_graph: SourceGraph) -> FlowMessage: # type: ignore[override, syntax] - if self.configs is None or self.data_store_path is None: - raise StepNotInitialized(type(self).__name__) - - file_path = self.configs.get("file_path") - base_namespace = self.configs.get("base_namespace", None) - - if file_path: - triples = extractors.DexpiXML(self.data_store_path / Path(file_path), base_namespace).extract() - source_graph.graph.add_triples(triples, verbose=True) - - logging.info(f"Loaded {file_path} into source graph.") - else: - raise ValueError("You need a source_rdf_store.file specified") - - return FlowMessage(output_text="Instances loaded to source graph") - - -class ExtractGraphFromGraphCapturingSheet(Step): - """ - This step extracts nodes and edges from graph capture spreadsheet and load them into graph - """ - - description = "This step extracts nodes and edges from graph capturing spreadsheet and load them into graph" - version = "legacy" - category = CATEGORY - configurables: ClassVar[list[Configurable]] = [ - Configurable( - name="file_path", - value="source-graphs/graph_capture_sheet.xlsx", - label="File path to Graph Capturing Sheet", - ), - Configurable( - name="base_namespace", - value="http://purl.org/cognite/neat#", - label="Base namespace to be added to ids for all nodes extracted from graph capturing spreadsheet", - ), - Configurable( - name="graph_name", - value="solution", - label="The name of target graph to load nodes and edge sto.", - options=["source", "solution"], - ), - ] - - def run( # type: ignore[override, syntax] - self, rules: RulesData, graph_store: SolutionGraph | SourceGraph - ) -> FlowMessage: - if self.configs is None or self.data_store_path is None: - raise StepNotInitialized(type(self).__name__) - - file_path = self.configs.get("file_path") - - if file_path: - logging.info(f"Processing graph capture sheet {self.data_store_path / Path(file_path)}") - - triples = extractors.GraphCapturingSheet( - rules=rules.rules, - filepath=self.data_store_path / Path(file_path), - namespace=self.configs.get("base_namespace", None), - use_source_ids=True, - ).extract() - - else: - raise ValueError("You need a source_rdf_store.file specified") - - if self.configs["graph_name"] == "solution": - graph_store = cast(SolutionGraph, self.flow_context["SolutionGraph"]) - else: - graph_store = cast(SourceGraph, self.flow_context["SourceGraph"]) - - graph_store.graph.add_triples(triples, verbose=True) # type: ignore[arg-type] - return FlowMessage(output_text="Graph capture sheet processed") - - -class ExtractGraphFromMockGraph(Step): - """ - This step generate mock graph based on the defined classes and target number of instances - """ - - description = "This step generate mock graph based on the defined classes and target number of instances" - version = "legacy" - category = CATEGORY - configurables: ClassVar[list[Configurable]] = [ - Configurable( - name="class_count", - value='{"GeographicalRegion":5, "SubGeographicalRegion":10}', - label="Target number of instances for each class", - ), - Configurable( - name="graph_name", - value="solution", - label="The name of target graph.", - options=["source", "solution"], - ), - ] - - def run( # type: ignore[override, syntax] - self, transformation_rules: RulesData, graph_store: SolutionGraph | SourceGraph - ) -> FlowMessage: - if self.configs is None: - raise StepNotInitialized(type(self).__name__) - logging.info("Initiated generation of mock triples") - try: - class_count = json.loads(self.configs["class_count"]) - except Exception: - return FlowMessage( - error_text="Defected JSON stored in class_count", - step_execution_status=StepExecutionStatus.ABORT_AND_FAIL, - ) - - if self.configs["graph_name"] == "solution": - # Todo Anders: Why is the graph fetched from context when it is passed as an argument? - graph_store = cast(SourceGraph | SolutionGraph, self.flow_context["SolutionGraph"]) - else: - graph_store = cast(SourceGraph | SolutionGraph, self.flow_context["SourceGraph"]) - - logging.info(class_count) - logging.info(transformation_rules.rules.metadata.model_dump()) - try: - triples = generate_mock_triples(transformation_rules=transformation_rules.rules, class_count=class_count) - except Exception as e: - return FlowMessage( - error_text=f"Error: {e}", - step_execution_status=StepExecutionStatus.ABORT_AND_FAIL, - ) - - logging.info("Adding mock triples to graph") - graph_store.graph.add_triples(triples, verbose=True) # type: ignore[arg-type] - return FlowMessage(output_text=f"Mock graph generated containing total of {len(triples)} triples") - - -class ExtractGraphFromRulesInstanceSheet(Step): - """ - This step extracts instances from Rules object and loads them into the graph - """ - - description = "This step extracts instances from Rules object and loads them into the graph." - category = CATEGORY - version = "legacy" - - configurables: ClassVar[list[Configurable]] = [ - Configurable( - name="graph_name", - value="solution", - label="The name of target graph.", - options=["source", "solution"], - ), - ] - - def run( # type: ignore[override, syntax] - self, transformation_rules: RulesData, graph_store: SolutionGraph | SourceGraph - ) -> FlowMessage: - triples = get_instances_as_triples(transformation_rules.rules) - instance_ids = {triple[0] for triple in triples} - output_text = f"Extracted {len(instance_ids)} instances out of" - output_text += f"Loaded {len(triples)} statements defining" - output_text += f" {len(instance_ids)} instances" - - if self.configs["graph_name"] == "solution": - graph_store = cast(SolutionGraph, self.flow_context["SolutionGraph"]) - else: - graph_store = cast(SourceGraph, self.flow_context["SourceGraph"]) - - try: - graph_store.graph.add_triples(triples, verbose=True) # type: ignore[arg-type] - except Exception as e: - return FlowMessage( - error_text=f"Error: {e}", - step_execution_status=StepExecutionStatus.ABORT_AND_FAIL, - ) - - return FlowMessage(output_text=output_text) - - -class ExtractGraphFromRulesDataModel(Step): - """ - This step extracts data model from rules file and loads it into source graph - """ - - description = "This step extracts data model from rules file and loads it into source graph." - category = CATEGORY - version = "legacy" - - def run( # type: ignore[override, syntax] - self, transformation_rules: RulesData, source_graph: SourceGraph - ) -> FlowMessage: - ns = DEFAULT_NAMESPACE - classes = transformation_rules.rules.classes - properties = transformation_rules.rules.properties - counter = 0 - for class_name, class_def in classes.items(): - rdf_instance_id = URIRef(ns + "_" + class_def.class_id) - source_graph.graph.graph.add((rdf_instance_id, URIRef(ns + "Name"), Literal(class_name))) - source_graph.graph.graph.add((rdf_instance_id, RDF.type, URIRef(ns + class_def.class_id))) - if class_def.parent_class: - source_graph.graph.graph.add( - ( - rdf_instance_id, - URIRef(ns + "hasParent"), - URIRef(ns + "_" + cast(str, class_def.parent_class)), - ) - ) - counter += 1 - - for _property_name, property_def in properties.items(): - rdf_instance_id = URIRef(ns + "_" + property_def.class_id) - source_graph.graph.graph.add( - ( - rdf_instance_id, - URIRef(ns + property_def.property_id), - Literal(property_def.expected_value_type), - ) - ) - if property_def.expected_value_type.suffix not in ( - "string", - "integer", - "float", - "boolean", - ): - source_graph.graph.graph.add( - ( - rdf_instance_id, - URIRef(ns + "connectedTo"), - URIRef(ns + "_" + property_def.expected_value_type.suffix), - ) - ) - counter += 1 - - output_text = f"Loaded {counter} classes into source graph" - return FlowMessage(output_text=output_text) - - -class ExtractGraphFromJsonFile(Step): - """ - This step extracts instances from json file and loads them into a graph store. Warning : the step is experimental - """ - - description = "This step extracts instances from json file and loads them into a graph store" - category = CATEGORY - version = "legacy" - configurables: ClassVar[list[Configurable]] = [ - Configurable( - name="file_name", - value="data_dump.json", - label="Full path to the file containing data dump in JSON format", - ), - Configurable( - name="graph_name", - value="solution", - label="The name of target graph.", - options=["source", "solution"], - ), - Configurable( - name="object_id_generation_method", - value="hash_of_json_element", - label="Method to be used for generating object ids. \ - source_object_properties - takes multiple properties from the source object and concatenates them. \ - source_object_id_mapping - takes a single property from the \ - source object and maps it to a instance id. \ - The option should be used when source object already contains stable ids \ - hash_of_json_element - takes a hash of the JSON element.Very generic method but \ - can be slow working with big objects. \ - uuid - generates a random UUID, the option produces unstables ids . ", - options=[ - "source_object_properties", - "source_object_id_mapping", - "hash_of_json_element", - "uuid", - ], - ), - Configurable( - name="json_object_id_mapping", - value="name", - label="Comma separated list of object properties to be used for generating object ids. \ - Each property must be prefixed with the name of the object. For example: device:name,pump:id", - ), - Configurable( - name="json_object_labels_mapping", - value="", - label="Comma separated list of object properties to be used for generating object labels. \ - Each property must be prefixed with the name of the object. For example: asset:name,asset:type", - ), - Configurable( - name="namespace", - value="http://purl.org/cognite/neat#", - label="Namespace to be used for the generated objects.", - ), - Configurable( - name="namespace_prefix", - value="neat", - label="The prefix to be used for the namespace.", - ), - ] - - def get_json_object_id( - self, - method, - object_name: str, - json_object: dict, - parent_object_id: str, - id_mapping: dict, - ): - if method == "source_object_properties": - object_id = "" - if object_name in id_mapping: - for property_name in id_mapping[object_name]: - object_id += property_name + json_object[property_name] - elif method == "hash_of_json_element": - flat_json_object = {} - for key, value in json_object.items(): - if not isinstance(value, dict) and not isinstance(value, list): - flat_json_object[key] = value - - object_id = json.dumps(flat_json_object, sort_keys=True) - elif method == "uuid": - return uuid.uuid4() - elif method == "source_object_id_mapping": - # don't hash existing valid ids - try: - return json_object[id_mapping[object_name][0]] - except KeyError as e: - # back to hashing - logging.debug(f"Object {object_name} doesn't have a valid id.Error : {e}") - object_id = self.get_json_object_id( - "hash_of_json_element", - object_name, - json_object, - parent_object_id, - id_mapping, - ) - else: - raise ValueError( - f"Unknown object_id_generation_method: {(self.configs or {}).get('object_id_generation_method')}" - ) - - return hashlib.sha256(object_id.encode()).hexdigest() - - def run(self, graph_store: SolutionGraph | SourceGraph) -> FlowMessage: # type: ignore[override, syntax] - if self.configs is None or self.data_store_path is None: - raise StepNotInitialized(type(self).__name__) - - # self.graph.bind - if self.configs["graph_name"] == "solution": - # Todo Anders: Why is the graph fetched from context when it is passed as an argument? - graph_store = cast(SolutionGraph, self.flow_context["SolutionGraph"]) - else: - graph_store = cast(SourceGraph, self.flow_context["SourceGraph"]) - - ns = Namespace(self.configs["namespace"]) - graph_store.graph.graph.bind(self.configs["namespace_prefix"], ns) - - full_path = self.data_store_path / Path(self.configs["file_name"]) - logging.info(f"Loading data dump from {full_path}") - with full_path.open() as f: - json_data = json.load(f) - - graph = graph_store.graph - nodes_counter = 0 - property_counter = 0 - labels_mapping: dict[str, str] = {} - object_id_mapping: dict[str, list[str]] = {} - if self.configs["json_object_labels_mapping"]: - for label_mapping in self.configs["json_object_labels_mapping"].split(","): - object_name, property_name = label_mapping.split(":") - labels_mapping[object_name] = property_name - - if self.configs["json_object_id_mapping"]: - for id_mapping in self.configs["json_object_id_mapping"].split(","): - if ":" not in id_mapping: - continue - object_name, property_name = id_mapping.split(":") - # if multiple ids are used for the same object ,the order of the properties is important - if object_name in object_id_mapping: - object_id_mapping[object_name].append(property_name) - else: - object_id_mapping[object_name] = [property_name] - - # Iterate through the JSON data and convert it to triples - def convert_json_to_triples( - data: dict, - parent_node: URIRef, - parent_object_id: str, - parent_node_path: str, - property_name=None, - ): - nonlocal nodes_counter, property_counter - if isinstance(data, dict): - if len(data) == 0: - return - if property_name is None: - for key, value in data.items(): - convert_json_to_triples(value, parent_node, parent_object_id, parent_node_path, key) - else: - object_id = self.get_json_object_id( - self.configs["object_id_generation_method"], - property_name, - data, - parent_object_id, - object_id_mapping, - ) - new_node = URIRef(ns + object_id) - graph.graph.add((new_node, RDF.type, URIRef(ns + property_name))) - if labels_mapping and property_name in labels_mapping: - graph.graph.add( - ( - new_node, - URIRef(ns + "label"), - Literal(data[labels_mapping[property_name]]), - ) - ) - else: - graph.graph.add((new_node, URIRef(ns + "label"), Literal(property_name))) - graph.graph.add((new_node, URIRef(ns + "parent"), parent_node)) - nodes_counter += 1 - for key, value in data.items(): - new_node_path = parent_node_path + "/" + key - convert_json_to_triples(value, new_node, object_id, new_node_path, key) - elif isinstance(data, list): - if property_name is None: - for key, value in data.items(): - convert_json_to_triples(value, parent_node, parent_object_id, parent_node_path, key) - else: - for item in data: - convert_json_to_triples( - item, - parent_node, - parent_object_id, - parent_node_path, - property_name, - ) - else: - # Convert scalar values to RDF literals - if isinstance(data, bool): - data = Literal(data, datatype=XSD.boolean) - elif isinstance(data, int): - data = Literal(data, datatype=XSD.integer) - elif isinstance(data, float): - data = Literal(data, datatype=XSD.float) - elif isinstance(data, str): - data = Literal(data, datatype=XSD.string) - else: - data = Literal(str(data)) - property_counter += 1 - graph.graph.add((parent_node, URIRef(ns + property_name), data)) - - # Start conversion with a root node - root_node = URIRef(ns + "root") - graph.graph.add((root_node, URIRef(ns + "label"), Literal("root node"))) - graph.graph.add((root_node, RDF.type, URIRef(ns + "root_node_id"))) - convert_json_to_triples(json_data, root_node, "root", "root", None) - return FlowMessage( - output_text=f"Data from source file imported successfully. Imported {nodes_counter} objects \ - and {property_counter} properties ." - ) - - -class ExtractGraphFromAvevaPiAssetFramework(Step): - """ - This step extracts instances from Aveva PI AF and loads them into a graph store. Warning : the step is experimental - """ - - description = "This step extracts instances from Aveva PI AF and loads them into a graph store" - category = CATEGORY - version = "legacy" - configurables: ClassVar[list[Configurable]] = [ - Configurable( - name="file_name", - value="staging/pi_af_dump.xml", - label="Full path to the file \ - containing data dump in XML format", - ), - Configurable( - name="graph_name", - value="solution", - label="The name of target graph.", - options=["source", "solution"], - ), - Configurable( - name="root_node_external_id", - value="root", - label="External id of the root node. The node will be created if it doesn't exist", - ), - Configurable( - name="root_node_name", - value="root", - label="Name of the root node. The node will be created if it doesn't exist", - ), - Configurable( - name="root_node_type", - value="Asset", - label="Type of the root node. The node will be created if it doesn't exist", - ), - Configurable( - name="namespace", - value="http://purl.org/cognite/neat#", - label="Namespace to be used for the generated objects.", - ), - Configurable( - name="namespace_prefix", - value="neat", - label="The prefix to be used for the namespace.", - ), - ] - - def add_root_asset_to_source_graph(self) -> str: - root_external_id = self.configs["root_node_external_id"] - root_name = self.configs["root_node_name"] - root_asset_type = self.configs["root_node_type"] - rdf_root_instance_id = URIRef(self.ns + root_external_id) - self.graph_store.graph.add((rdf_root_instance_id, URIRef(self.ns + "Name"), Literal(root_name))) - self.graph_store.graph.add((rdf_root_instance_id, RDF.type, URIRef(self.ns + root_asset_type))) - return root_external_id - - def run( # type: ignore[override, syntax] - self, flow_msg: FlowMessage, graph_store: SolutionGraph | SourceGraph - ) -> FlowMessage: - if self.configs is None or self.data_store_path is None: - raise StepNotInitialized(type(self).__name__) - - if source_file := self.configs["file_name"]: - source_pi_dump = Path(self.data_store_path) / source_file - else: - return FlowMessage( - output_text="No source file specified", - next_step_ids=["step_error_handler"], - ) - - # self.graph.bind - if self.configs["graph_name"] == "solution": - # Todo Anders: Why is the graph fetched from context when it is passed as an argument? - self.graph_store = cast(SourceGraph | SolutionGraph, self.flow_context["SolutionGraph"]).graph - else: - self.graph_store = cast(SourceGraph | SolutionGraph, self.flow_context["SourceGraph"]).graph - - self.ns = Namespace(self.configs["namespace"]) - self.graph_store.graph.bind(self.configs["namespace_prefix"], self.ns) - - cdf_root_instance_id = self.add_root_asset_to_source_graph() - # Parse the XML data into an ElementTree object - root = ET.parse(source_pi_dump).getroot() - counter = 0 - root_af_element = root.find("AFDatabase/AFElement") - logging.info(f"Found AFElement: {root_af_element}") - - def process_af_attribute(af_element, element_path=None, parent_element_id: str | None = None): - name = af_element.find("Name").text - name = name.replace(" ", "_") - new_element_path = element_path + "/" + name - element_id = "_" + create_sha256_hash(new_element_path) - rdf_instance_id = URIRef(self.ns + element_id) - self.graph_store.graph.add((rdf_instance_id, URIRef(self.ns + "Name"), Literal(name))) - self.graph_store.graph.add((rdf_instance_id, RDF.type, URIRef(self.ns + "Attribute" + name))) - self.graph_store.graph.add((rdf_instance_id, URIRef(self.ns + "Path"), Literal(new_element_path))) - if parent_element_id: - self.graph_store.graph.add( - ( - rdf_instance_id, - URIRef(self.ns + "hasParent"), - URIRef(self.ns + parent_element_id), - ) - ) - for child in af_element: - if child.tag == "AFAttribute": - process_af_attribute(child, new_element_path, element_id) - elif child.tag == "Name": - pass - else: - try: - self.graph_store.graph.add( - ( - rdf_instance_id, - URIRef(self.ns + child.tag), - Literal(child.text), - ) - ) - except Exception as e: - logging.error(f"Error parsing AFAttribute {name} : {e}") - - def process_af_element(af_element, element_path=None, parent_element_id: str | None = None) -> str: - nonlocal counter - name = af_element.find("Name").text - template = None - new_element_path = element_path + "/" + name - element_id = "_" + create_sha256_hash(new_element_path) - rdf_instance_id = URIRef(self.ns + element_id) - self.graph_store.graph.add((rdf_instance_id, URIRef(self.ns + "Name"), Literal(name))) - self.graph_store.graph.add((rdf_instance_id, URIRef(self.ns + "Path"), Literal(new_element_path))) - if parent_element_id: - self.graph_store.graph.add( - ( - rdf_instance_id, - URIRef(self.ns + "hasParent"), - URIRef(self.ns + parent_element_id), - ) - ) - - for child in af_element: - if child.tag == "Name": - pass - if child.tag == "Template": - template = child.text - if child.tag == "AFAttribute": - process_af_attribute(child, new_element_path, element_id) - if child.tag == "AFElement": - counter += 1 - process_af_element(child, new_element_path, element_id) - else: - self.graph_store.graph.add( - ( - rdf_instance_id, - URIRef(self.ns + child.tag), - Literal(child.text), - ) - ) - - if template: - self.graph_store.graph.add((rdf_instance_id, RDF.type, URIRef(self.ns + template))) - else: - self.graph_store.graph.add((rdf_instance_id, RDF.type, URIRef(self.ns + "AFElement"))) - - return element_id - - process_af_element(root_af_element, "root", cdf_root_instance_id) - self.graph_store.restart() # restarting the graph to release the memory - return FlowMessage(output_text=f" {counter} PI assets loaded into the graph") - - def convert_attribute(self, attribute): - if "{" not in attribute: - return attribute - attr_splitted = attribute.split("{")[-1].split("}") - return attr_splitted[0] + "/" + attr_splitted[1] diff --git a/cognite/neat/workflows/steps/lib/legacy/graph_loader.py b/cognite/neat/workflows/steps/lib/legacy/graph_loader.py deleted file mode 100644 index 8ab6eb8cf..000000000 --- a/cognite/neat/workflows/steps/lib/legacy/graph_loader.py +++ /dev/null @@ -1,606 +0,0 @@ -import logging -import time -from datetime import datetime -from pathlib import Path -from typing import Any, ClassVar, cast - -from cognite.client import CogniteClient -from cognite.client.data_classes import Asset, AssetFilter -from prometheus_client import Gauge - -from cognite.neat.legacy.graph import loaders as graph_loader -from cognite.neat.legacy.graph.loaders import upload_labels -from cognite.neat.legacy.graph.loaders.core.rdf_to_assets import ( - NeatMetadataKeys, - categorize_assets, - rdf2assets, - remove_non_existing_labels, - unique_asset_labels, - upload_assets, -) -from cognite.neat.legacy.graph.loaders.core.rdf_to_relationships import ( - categorize_relationships, - rdf2relationships, - upload_relationships, -) -from cognite.neat.legacy.graph.loaders.rdf_to_dms import upload_edges, upload_nodes -from cognite.neat.legacy.graph.loaders.validator import validate_asset_hierarchy -from cognite.neat.legacy.rules.models.rdfpath import TransformationRuleType -from cognite.neat.utils.auxiliary import generate_exception_report -from cognite.neat.workflows._exceptions import StepFlowContextNotInitialized, StepNotInitialized -from cognite.neat.workflows.model import FlowMessage, StepExecutionStatus -from cognite.neat.workflows.steps.data_contracts import ( - CategorizedAssets, - CategorizedRelationships, - Edges, - Nodes, - RulesData, - SolutionGraph, - SourceGraph, -) -from cognite.neat.workflows.steps.step_model import Configurable, Step - -__all__ = [ - "GenerateAssetsFromGraph", - "GenerateRelationshipsFromGraph", - "GenerateNodesAndEdgesFromGraph", - "LoadLabelsToCDF", - "LoadAssetsToCDF", - "LoadRelationshipsToCDF", - "LoadNodesToCDF", - "LoadEdgesToCDF", - "LoadGraphToRdfFile", -] - -CATEGORY = __name__.split(".")[-1].replace("_", " ").title() + " [LEGACY]" - - -class LoadLabelsToCDF(Step): - """ - This step creates and loads default NEAT labels in CDF - """ - - description = "This step creates default NEAT labels in CDF" - category = CATEGORY - version = "legacy" - configurables: ClassVar[list[Configurable]] = [ - Configurable(name="data_set_id", value="", label=("CDF dataset id to which the labels will be added.")) - ] - - def run(self, rules: RulesData, cdf_client: CogniteClient) -> None: # type: ignore[override, syntax] - upload_labels( - cdf_client, - rules.rules, - data_set_id=int(self.configs["data_set_id"]), - extra_labels=["non-historic", "historic"], - ) - - -class GenerateNodesAndEdgesFromGraph(Step): - """ - The step generates nodes and edges from the graph - """ - - description = "The step generates nodes and edges from the graph" - category = CATEGORY - - configurables: ClassVar[list[Configurable]] = [ - Configurable( - name="graph_name", - value="source", - options=["source", "solution"], - label=("The name of the graph to be used for matching." " Supported options : source, solution"), - ), - Configurable( - name="add_class_prefix", - value="False", - options=["True", "False"], - label=("Whether to add class name as a prefix to external ids of instances or not"), - ), - Configurable( - name="data_validation_error_handling_strategy", - value="skip_and_report", - options=["skip_and_report", "fail_and_report"], - label=( - "The strategy for handling data validation errors. Supported options: \ - skip_and_report - failed instance (node or edge) will be skipped and reported , \ - fail_and_report - failed instance (node or edge) will fail the workflow and report the error" - ), - ), - Configurable( - name="apply_basic_transformation", - value="True", - options=["True", "False"], - label=("Whether to apply basic transformations rules (rdfpath) or not. Default is True."), - ), - ] - - def run( # type: ignore[override, syntax] - self, rules: RulesData, graph: SourceGraph | SolutionGraph - ) -> (FlowMessage, Nodes, Edges): # type: ignore[syntax] - if self.configs is None or self.data_store_path is None: - raise StepNotInitialized(type(self).__name__) - if self.flow_context is None: - raise StepFlowContextNotInitialized(type(self).__name__) - - graph_name = self.configs["graph_name"] or "source" - data_validation_error_handling_strategy = self.configs.get( - "data_validation_error_handling_strategy", "skip_and_report" - ) - if graph_name == "solution": - # Todo Anders: Why is the graph fetched from context when it is passed as an argument? - graph = cast(SourceGraph | SolutionGraph, self.flow_context["SolutionGraph"]) - else: - graph = cast(SourceGraph | SolutionGraph, self.flow_context["SourceGraph"]) - - add_class_prefix = True if self.configs["add_class_prefix"] == "True" else False - apply_basic_transformation = True if self.configs.get("apply_basic_transformation", "True") == "True" else False - - if apply_basic_transformation: - final_rules = rules.rules - else: - logging.debug("Basic transformation rules are not applied to the graph") - final_rules = rules.rules.model_copy(deep=True) - prefix = final_rules.metadata.prefix - for rule in final_rules.properties.values(): - rule.rule_type = TransformationRuleType.rdfpath - rule.rule = f"{prefix}:{rule.class_id}({prefix}:{rule.property_id})" - - loader = graph_loader.DMSLoader(final_rules, graph.graph, add_class_prefix=add_class_prefix) - nodes, edges, exceptions = loader.as_nodes_and_edges(stop_on_exception=False) - - msg = f"Total count of:

" - - if exceptions: - file_name = f'nodes-and-edges-exceptions_{datetime.now().strftime("%Y%d%m%H%M")}.txt' - exceptions_report_dir = self.data_store_path / "reports" - exceptions_report_dir.mkdir(parents=True, exist_ok=True) - exceptions_report_path = exceptions_report_dir / file_name - - exceptions_report_path.write_text(generate_exception_report(exceptions, "Errors")) - msg += ( - f"

There is total of { len(exceptions) } exceptions

" - f'
Full error report ' - ) - if data_validation_error_handling_strategy == "fail_and_report": - return FlowMessage(error_text=msg, step_execution_status=StepExecutionStatus.ABORT_AND_FAIL) - - return FlowMessage(output_text=msg), Nodes(nodes=nodes), Edges(edges=edges) - - -class LoadGraphToRdfFile(Step): - """ - The step generates loads graph to RDF file - """ - - description = "The step generates nodes and edges from the graph" - category = CATEGORY - version = "legacy" - configurables: ClassVar[list[Configurable]] = [ - Configurable( - name="graph_name", - value="source", - options=["source", "solution"], - label=("The name of the graph to be used for loading RDF File." " Supported options : source, solution"), - ), - Configurable( - name="rdf_file_path", - value="staging/graph_export.ttl", - label=("Relative path for the RDF file storage, " "must end with .ttl !"), - ), - ] - - def run( # type: ignore[override, syntax] - self, graph: SourceGraph | SolutionGraph - ) -> FlowMessage: # type: ignore[syntax] - if self.configs is None or self.data_store_path is None: - raise StepNotInitialized(type(self).__name__) - - storage_path = self.data_store_path / Path(self.configs["rdf_file_path"]) - relative_graph_file_path = str(storage_path).split("/data/")[1] - - graph_name = self.configs["graph_name"] or "source" - - if graph_name == "solution": - # Todo Anders: Why is the graph fetched from context when it is passed as an argument? - graph = cast(SourceGraph | SolutionGraph, self.flow_context["SolutionGraph"]) - else: - graph = cast(SourceGraph | SolutionGraph, self.flow_context["SourceGraph"]) - - graph.graph.serialize(str(storage_path), format="turtle") - - output_text = ( - "

" - "Graph loaded to RDF file can be downloaded here : " - f'{storage_path.stem}.ttl' - ) - - return FlowMessage(output_text=output_text) - - -class LoadNodesToCDF(Step): - """ - This step uploads nodes to CDF - """ - - description = "This step uploads nodes to CDF" - category = CATEGORY - version = "legacy" - - def run(self, cdf_client: CogniteClient, nodes: Nodes) -> FlowMessage: # type: ignore[override, syntax] - if nodes.nodes: - upload_nodes(cdf_client, nodes.nodes, max_retries=2, retry_delay=4) - return FlowMessage(output_text="CDF nodes uploaded successfully") - else: - return FlowMessage(output_text="No nodes to upload!") - - -class LoadEdgesToCDF(Step): - """ - This step uploads edges to CDF - """ - - description = "This step uploads edges to CDF" - category = CATEGORY - version = "legacy" - - def run(self, cdf_client: CogniteClient, edges: Edges) -> FlowMessage: # type: ignore[override, syntax] - if edges.edges: - upload_edges(cdf_client, edges.edges, max_retries=2, retry_delay=4) - return FlowMessage(output_text="CDF edges uploaded successfully") - else: - return FlowMessage(output_text="No edges to upload!") - - -class GenerateAssetsFromGraph(Step): - """ - The step generates assets from the graph ,categorizes them and stores them in CategorizedAssets object - """ - - description = ( - "The step generates assets from the graph ,categorizes them and stores them in CategorizedAssets object" - ) - category = CATEGORY - version = "legacy" - configurables: ClassVar[list[Configurable]] = [ - Configurable(name="data_set_id", value="", label=("CDF dataset id to which the labels will be added.")), - Configurable( - name="asset_external_id_prefix", - value="", - label=("Prefix to be added to all asset external ids, default None."), - ), - Configurable( - name="assets_cleanup_type", - value="nothing", - options=["nothing", "orphans", "circular", "full"], - label=( - "Configures asset cleanup process. Supported options: nothing - no cleanup, \ - orphans - all orphan assets will be removed, circular - all circular assets will be removed , \ - full - full cleanup , both orphans and circular assets will be removed. " - ), - ), - ] - - def run( # type: ignore[override] - self, rules: RulesData, cdf_client: CogniteClient, solution_graph: SolutionGraph - ) -> (FlowMessage, CategorizedAssets): # type: ignore[override, syntax] - if self.configs is None: - raise StepNotInitialized(type(self).__name__) - asset_cleanup_type = self.configs.get("assets_cleanup_type", "nothing") - data_set_id = int(self.configs["data_set_id"]) - asset_external_id_prefix = self.configs.get("asset_external_id_prefix", None) - - meta_keys = NeatMetadataKeys.load(self.configs) - - if self.metrics is None: - raise ValueError(self._not_configured_message) - prom_cdf_resource_stats = cast( - Gauge, - self.metrics.register_metric( - "cdf_resources_stats", - "CDF resource stats before and after running the workflow", - m_type="gauge", - metric_labels=["resource_type", "state"], - ), - ) - prom_data_issues_stats = cast( - Gauge, - self.metrics.register_metric( - "data_issues_stats", "Data validation issues", m_type="gauge", metric_labels=["resource_type"] - ), - ) - - rdf_asset_dicts = rdf2assets( - solution_graph.graph, - rules.rules, - data_set_id=data_set_id, - asset_external_id_prefix=asset_external_id_prefix, - stop_on_exception=True, - meta_keys=meta_keys, - ) - - # UPDATE: 2023-04-05 - correct aggregation of assets in CDF for specific dataset - total_assets_before = cdf_client.assets.aggregate(filter=AssetFilter(data_set_ids=[{"id": data_set_id}]))[ - 0 - ].count - - # Label Validation - labels_before = unique_asset_labels(rdf_asset_dicts.values()) - logging.info(f"Assets have {len(labels_before)} unique labels: {', '.join(sorted(labels_before))}") - - rdf_asset_dicts = cast(dict[str, dict[str, Any]], remove_non_existing_labels(cdf_client, rdf_asset_dicts)) - - labels_after = unique_asset_labels(rdf_asset_dicts.values()) - removed_labels = labels_before - labels_after - logging.info( - f"Removed {len(removed_labels)} labels as these do not exists in CDF. " - f"Removed labels: {', '.join(sorted(removed_labels))}" - ) - ###################### - - prom_cdf_resource_stats.labels(resource_type="asset", state="count_before_neat_update").set(total_assets_before) - logging.info(f"Total count of assets in CDF before upload: { total_assets_before }") - - orphanage_asset_external_id = ( - f"{asset_external_id_prefix}orphanage-{data_set_id}" - if asset_external_id_prefix - else f"orphanage-{data_set_id}" - ) - orphan_assets, circular_assets, parent_children_map = validate_asset_hierarchy(rdf_asset_dicts) - - # There could be assets already under a created orphan assets. Include those in oprhan assets list - if orphanage_asset_external_id in parent_children_map: - orphan_assets.extend(parent_children_map[orphanage_asset_external_id]) - - orphan_assets_count = len(orphan_assets) - circular_assets_count = len(circular_assets) - prom_data_issues_stats.labels(resource_type="circular_assets").set(len(circular_assets)) - prom_data_issues_stats.labels(resource_type="orphan_assets").set(len(orphan_assets)) - - if orphan_assets: - logging.error(f"Found orphaned assets: {', '.join(orphan_assets)}") - - if asset_cleanup_type in ["orphans", "full"]: - logging.info("Removing orphaned assets and its children") - - def delete_asset_and_children_recursive(asset_id, rdf_asset_dicts, parent_children_map): - if asset_id in rdf_asset_dicts: - del rdf_asset_dicts[asset_id] - - if asset_id in parent_children_map: - for child_id in parent_children_map[asset_id]: - delete_asset_and_children_recursive(child_id, rdf_asset_dicts, parent_children_map) - - def delete_orphan_assets_recursive(orphan_assets, rdf_asset_dicts, parent_children_map): - for orphan_asset in orphan_assets: - delete_asset_and_children_recursive(orphan_asset, rdf_asset_dicts, parent_children_map) - - # Make sure children, grand-children, great-grandchildren .... are deleted - delete_orphan_assets_recursive(orphan_assets, rdf_asset_dicts, parent_children_map) - - # delete orphange asset - if orphanage_asset_external_id in rdf_asset_dicts: - del rdf_asset_dicts[orphanage_asset_external_id] - - else: - # Kill the process if you dont have orphanage asset in your asset hierarchy - # and inform the user that it is missing ! - if orphanage_asset_external_id not in rdf_asset_dicts: - msg = f"You dont have Orphanage asset {orphanage_asset_external_id} in asset hierarchy!" - logging.error(msg) - return FlowMessage( - error_text=msg, step_execution_status=StepExecutionStatus.ABORT_AND_FAIL - ), CategorizedAssets(assets={}) - - logging.error("Orphaned assets will be assigned to 'Orphanage' root asset") - - for external_id in orphan_assets: - rdf_asset_dicts[external_id]["parent_external_id"] = orphanage_asset_external_id - else: - logging.info("No orphaned assets found, your assets look healthy !") - - if circular_assets: - logging.error(f"Found circular dependencies: {circular_assets}") - if asset_cleanup_type in ["circular", "full"]: - logging.info("Removing circular assets") - for circular_path in circular_assets: - circular_external_id = circular_path[-1] - del rdf_asset_dicts[circular_external_id] - else: - logging.info("No circular dependency among assets found, your assets hierarchy look healthy !") - - if orphan_assets or circular_assets: - orphan_assets, circular_assets, _ = validate_asset_hierarchy(rdf_asset_dicts) - if circular_assets: - msg = f"Found circular dependencies: {circular_assets!s}" - logging.error(msg) - return FlowMessage( - error_text=msg, step_execution_status=StepExecutionStatus.ABORT_AND_FAIL - ), CategorizedAssets(assets={}) - elif orphan_assets: - msg = f"Not able to fix orphans: {', '.join(orphan_assets)}" - logging.error(msg) - return FlowMessage( - error_text=msg, step_execution_status=StepExecutionStatus.ABORT_AND_FAIL - ), CategorizedAssets(assets={}) - else: - logging.info("No circular dependency among assets found, your assets hierarchy look healthy !") - - categorized_assets, report = categorize_assets( - cdf_client, rdf_asset_dicts, data_set_id=data_set_id, return_report=True - ) - - count_create_assets = len(categorized_assets["create"]) - count_update_assets = len(categorized_assets["update"]) - count_decommission_assets = len(categorized_assets["decommission"]) - count_resurrect_assets = len(categorized_assets["resurrect"]) - - prom_cdf_resource_stats.labels(resource_type="asset", state="create").set(count_create_assets) - prom_cdf_resource_stats.labels(resource_type="asset", state="update").set(count_update_assets) - prom_cdf_resource_stats.labels(resource_type="asset", state="decommission").set(count_decommission_assets) - prom_cdf_resource_stats.labels(resource_type="asset", state="resurrect").set(count_resurrect_assets) - - logging.info(f"Total count of assets to be created: { count_create_assets }") - logging.info(f"Total count of assets to be updated: { count_update_assets }") - logging.info(f"Total count of assets to be decommission: { count_decommission_assets }") - logging.info(f"Total count of assets to be resurrect: { count_resurrect_assets }") - - msg = f"Total count of assets { len(rdf_asset_dicts) } of which:" - msg += f"

{ count_create_assets } to be created

" - msg += f"

{ count_update_assets } to be updated

" - msg += f"

{ count_decommission_assets } to be decommissioned

" - msg += f"

{ count_resurrect_assets } to be resurrected

" - msg += f"

Found { orphan_assets_count } orphan assets and" - msg += f" { circular_assets_count } circular assets

" - if asset_cleanup_type != "nothing": - msg += "

All circular and orphan assets were removed successfully

" - number_of_updates = len(report["decommission"]) - logging.info(f"Total number of updates: {number_of_updates}") - - return FlowMessage(output_text=msg), CategorizedAssets(assets=categorized_assets) - - -class LoadAssetsToCDF(Step): - """ - This step uploads categorized assets to CDF - """ - - description = "This step uploads categorized assets to CDF" - category = CATEGORY - version = "legacy" - - def run( # type: ignore[override] - self, cdf_client: CogniteClient, categorized_assets: CategorizedAssets, flow_msg: FlowMessage - ) -> FlowMessage: - if flow_msg and flow_msg.payload and "action" in flow_msg.payload: - if flow_msg.payload["action"] != "approve": - raise Exception("Update not approved") - if self.metrics is None: - raise ValueError(self._not_configured_message) - - prom_cdf_resource_stats = cast( - Gauge, - self.metrics.register_metric( - "cdf_resources_stats", - "CDF resource stats before and after running the workflow", - m_type="gauge", - metric_labels=["resource_type", "state"], - ), - ) - upload_assets(cdf_client, categorized_assets.assets, max_retries=2, retry_delay=4) - count_create_assets = len(categorized_assets.assets["create"]) - - # gets first asset available irrespective of its category - asset_example = next((assets[0] for assets in categorized_assets.assets.values() if assets), None) - - if asset_example: - data_set_id = cast(Asset, asset_example).data_set_id - for _ in range(1000): - total_assets_after = cdf_client.assets.aggregate( - filter=AssetFilter(data_set_ids=[{"id": data_set_id}]) - )[0].count - if total_assets_after >= count_create_assets: - break - logging.info(f"Waiting for assets to be created, current count {total_assets_after}") - time.sleep(2) - - # UPDATE: 2023-04-05 - correct aggregation of assets in CDF for specific dataset - total_assets_after = cdf_client.assets.aggregate(filter=AssetFilter(data_set_ids=[{"id": data_set_id}]))[ - 0 - ].count - - prom_cdf_resource_stats.labels(resource_type="asset", state="count_after_neat_update").set( - total_assets_after - ) - logging.info(f"Total count of assets in CDF after update: { total_assets_after }") - del categorized_assets.assets # free up memory after upload . - return FlowMessage(output_text=f"Total count of assets in CDF after update: { total_assets_after }") - else: - return FlowMessage(output_text="No assets to upload!") - - -class GenerateRelationshipsFromGraph(Step): - """ - This step generates relationships from the graph and saves them to CategorizedRelationships object - """ - - description = "This step generates relationships from the graph and saves them to CategorizedRelationships object" - category = CATEGORY - version = "legacy" - configurables: ClassVar[list[Configurable]] = [ - Configurable(name="data_set_id", value="", label=("CDF dataset id to which the labels will be added.")), - Configurable( - name="relationship_external_id_prefix", - value="", - label=("Prefix to be added to all asset external ids, default None."), - ), - ] - - def run( # type: ignore[override] - self, rules: RulesData, cdf_client: CogniteClient, solution_graph: SolutionGraph - ) -> (FlowMessage, CategorizedRelationships): # type: ignore[arg-type, syntax] - # create, categorize and upload relationships - data_set_id = int(self.configs["data_set_id"]) - relationship_external_id_prefix = self.configs.get("relationship_external_id_prefix", None) - - rdf_relationships = rdf2relationships( - solution_graph.graph, - rules.rules, - data_set_id=data_set_id, - relationship_external_id_prefix=relationship_external_id_prefix, - ) - - categorized_relationships = categorize_relationships(cdf_client, rdf_relationships, data_set_id) - count_defined_relationships = len(rdf_relationships) - count_create_relationships = len(categorized_relationships["create"]) - count_decommission_relationships = len(categorized_relationships["decommission"]) - count_resurrect_relationships = len(categorized_relationships["resurrect"]) - - if self.metrics is None: - raise ValueError(self._not_configured_message) - - prom_cdf_resource_stats = cast( - Gauge, - self.metrics.register_metric( - "cdf_resources_stats", - "CDF resource stats before and after running the workflow", - m_type="gauge", - metric_labels=["resource_type", "state"], - ), - ) - - prom_cdf_resource_stats.labels(resource_type="relationships", state="defined").set(count_defined_relationships) - prom_cdf_resource_stats.labels(resource_type="relationships", state="create").set(count_create_relationships) - prom_cdf_resource_stats.labels(resource_type="relationships", state="decommission").set( - count_decommission_relationships - ) - prom_cdf_resource_stats.labels(resource_type="relationships", state="resurrect").set( - count_resurrect_relationships - ) - - msg = ( - f"Total count of relationships { count_defined_relationships } of which:" - f" { count_create_relationships } to be created" - ) - msg += f", { count_decommission_relationships } to be decommissioned" - msg += f", { count_resurrect_relationships } to be resurrected" - - return FlowMessage(output_text=msg), CategorizedRelationships(relationships=categorized_relationships) - - -class LoadRelationshipsToCDF(Step): - """ - This step uploads relationships to CDF - """ - - description = "This step uploads relationships to CDF" - category = CATEGORY - version = "legacy" - - def run( # type: ignore[override, syntax] - self, client: CogniteClient, categorized_relationships: CategorizedRelationships - ) -> FlowMessage: - upload_relationships(client, categorized_relationships.relationships, max_retries=2, retry_delay=4) - return FlowMessage(output_text="CDF relationships uploaded successfully") diff --git a/cognite/neat/workflows/steps/lib/legacy/graph_store.py b/cognite/neat/workflows/steps/lib/legacy/graph_store.py deleted file mode 100644 index 76d7efebe..000000000 --- a/cognite/neat/workflows/steps/lib/legacy/graph_store.py +++ /dev/null @@ -1,307 +0,0 @@ -import logging -from pathlib import Path -from typing import ClassVar, cast - -from cognite.neat.constants import DEFAULT_NAMESPACE, get_default_prefixes -from cognite.neat.legacy.graph import stores -from cognite.neat.workflows._exceptions import StepNotInitialized -from cognite.neat.workflows.model import FlowMessage -from cognite.neat.workflows.steps.data_contracts import ( - RulesData, - SolutionGraph, - SourceGraph, -) -from cognite.neat.workflows.steps.step_model import Configurable, Step - -__all__ = ["ResetGraphStores", "ConfigureGraphStore"] - -CATEGORY = __name__.split(".")[-1].replace("_", " ").title() + " [LEGACY]" - - -class ConfigureDefaultGraphStores(Step): - """ - This step initializes the source and solution graph stores - """ - - description = "This step initializes the source and solution graph stores." - category = CATEGORY - version = "legacy" - configurables: ClassVar[list[Configurable]] = [ - Configurable( - name="source_rdf_store.type", - value=stores.OxiGraphStore.rdf_store_type, - label="Data store type for source graph. Supported: oxigraph, memory,file, graphdb, sparql. ", - options=["oxigraph", "memory", "file", "graphdb", "sparql"], - ), - Configurable( - name="solution_rdf_store.type", - value=stores.OxiGraphStore.rdf_store_type, - label="Data store type for solutioin graph. Supported: oxigraph, memory,file, graphdb, sparql", - options=["oxigraph", "memory", "file", "graphdb", "sparql"], - ), - Configurable( - name="source_rdf_store.disk_store_dir", - value="source-graph-store", - label="Local directory for source graph store", - ), - Configurable( - name="source_rdf_store.query_url", - value="", - label="Sparql query endpoint.Only for sparql and graphdb store type", - ), - Configurable( - name="source_rdf_store.update_url", - value="", - label="Sparql update endpoint.Only for sparql and graphdb store type", - ), - Configurable( - name="solution_rdf_store.query_url", - value="", - label="Sparql query endpoint.Only for sparql and graphdb store type", - ), - Configurable( - name="solution_rdf_store.update_url", - value="", - label="Sparql update endpoint.Only for sparql and graphdb store type", - ), - Configurable( - name="solution_rdf_store.disk_store_dir", - value="solution-graph-store", - label="Local directory for solution graph store", - ), - Configurable( - name="stores_to_configure", - value="all", - label="Defines which stores to configure", - options=["all", "source", "solution"], - ), - Configurable( - name="solution_rdf_store.api_root_url", - value="", - label="Root url for graphdb or sparql endpoint", - ), - ] - - def run(self, rules_data: RulesData | None = None) -> (FlowMessage, SourceGraph, SolutionGraph): # type: ignore[override, syntax] - prefixes = rules_data.rules.prefixes if rules_data else get_default_prefixes() - - if self.configs is None or self.data_store_path is None: - raise StepNotInitialized(type(self).__name__) - logging.info("Initializing source graph") - stores_to_configure = self.configs["stores_to_configure"] - source_store_dir = self.configs["source_rdf_store.disk_store_dir"] - source_store_dir = Path(self.data_store_path) / Path(source_store_dir) if source_store_dir else None - source_store_type = self.configs["source_rdf_store.type"] - if stores_to_configure in ["all", "source"]: - if source_store_type == stores.OxiGraphStore.rdf_store_type and "SourceGraph" in self.flow_context: - cast(SourceGraph, self.flow_context["SourceGraph"]).graph.upsert_prefixes(prefixes) - return FlowMessage(output_text="Stores already configured") - try: - store_cls = stores.STORE_BY_TYPE[source_store_type] - except KeyError: - return FlowMessage(output_text="Invalid store type") - source_graph = store_cls(prefixes=prefixes, base_prefix="neat", namespace=DEFAULT_NAMESPACE) - - source_graph.init_graph( - self.configs["source_rdf_store.query_url"], - self.configs["source_rdf_store.update_url"], - "neat-tnt", - internal_storage_dir=source_store_dir, - ) - if stores_to_configure == "source": - return FlowMessage(output_text="Source graph store configured successfully"), SourceGraph( - graph=source_graph - ) - else: - source_graph = None - - if stores_to_configure in ["all", "solution"]: - solution_store_dir = self.configs["solution_rdf_store.disk_store_dir"] - solution_store_dir = self.data_store_path / Path(solution_store_dir) if solution_store_dir else None - solution_store_type = self.configs["solution_rdf_store.type"] - - if solution_store_type == stores.OxiGraphStore.rdf_store_type and "SolutionGraph" in self.flow_context: - cast(SolutionGraph, self.flow_context["SolutionGraph"]).graph.upsert_prefixes(prefixes) - return FlowMessage(output_text="Stores already configured") - - try: - solution_graph = stores.STORE_BY_TYPE[solution_store_type]( - prefixes=prefixes, base_prefix="neat", namespace=DEFAULT_NAMESPACE - ) - except KeyError: - return FlowMessage(output_text="Invalid store type") - - solution_graph.init_graph( - self.configs["solution_rdf_store.query_url"], - self.configs["solution_rdf_store.update_url"], - "tnt-solution", - internal_storage_dir=solution_store_dir, - ) - - if isinstance(solution_graph, stores.GraphDBStore): - solution_graph.graph_db_rest_url = self.configs["solution_rdf_store.api_root_url"] - - if stores_to_configure == "solution": - return FlowMessage(output_text="Solution graph store configured successfully"), SolutionGraph( - graph=solution_graph - ) - else: - solution_graph = None - - return ( - FlowMessage(output_text="All graph stores configured successfully"), - SourceGraph(graph=source_graph) if source_graph else None, - SolutionGraph(graph=solution_graph) if solution_graph else None, - ) - - -class ResetGraphStores(Step): - """ - This step resets graph stores to their initial state (clears all data) - """ - - description = "This step resets graph stores to their initial state (clears all data)." - category = CATEGORY - version = "legacy" - configurables: ClassVar[list[Configurable]] = [ - Configurable( - name="graph_name", - value="source", - label="Name of the data store. Supported: solution, source ", - options=["source", "solution"], - ) - ] - - def run(self) -> FlowMessage: # type: ignore[override, syntax] - if self.configs is None or self.data_store_path is None: - raise StepNotInitialized(type(self).__name__) - graph_name_mapping = {"source": "SourceGraph", "solution": "SolutionGraph"} - - graph_name = graph_name_mapping[self.configs["graph_name"]] - graph_store = cast(SourceGraph | SolutionGraph | None, self.flow_context.get(graph_name, None)) - if graph_store is not None: - reset_store(graph_store.graph.internal_storage_dir, graph_store.graph) - if graph_name in self.flow_context: - del self.flow_context[graph_name] - return FlowMessage(output_text="Reset operation completed") - else: - return FlowMessage(output_text="Stores already reset") - - -class ConfigureGraphStore(Step): - """ - This step initializes source OR solution graph store - """ - - description = "This step initializes the source and solution graph stores." - version = "legacy" - category = CATEGORY - configurables: ClassVar[list[Configurable]] = [ - Configurable( - name="graph_name", - value="source", - label="Name of the data store. Supported: solution, source ", - options=["source", "solution"], - ), - Configurable( - name="store_type", - value=stores.OxiGraphStore.rdf_store_type, - label="Data store type for source graph. Supported: oxigraph, memory,file, graphdb, sparql. ", - options=["oxigraph", "memory", "file", "graphdb", "sparql"], - ), - Configurable( - name="disk_store_dir", - value="source-graph-store", - label="Local directory that is used as local graph store.Only for oxigraph, file store types", - ), - Configurable( - name="sparql_query_url", - value="", - label="Query url for sparql endpoint.Only for sparql store type", - ), - Configurable( - name="sparql_update_url", - value="", - label="Update url for sparql endpoint.Only for sparql store type", - ), - Configurable( - name="db_server_api_root_url", - value="", - label="Root url for graphdb or sparql endpoint.Only for graphdb", - ), - Configurable( - name="init_procedure", - value="reset", - label="Operations to be performed on the graph store as part of init and configuration process. \ - Supported options : reset, clear, none", - options=["reset", "none"], - ), - ] - - def run( # type: ignore[override] - self, rules_data: RulesData | None = None - ) -> (FlowMessage, SourceGraph | SolutionGraph): # type: ignore[syntax] - if self.configs is None or self.data_store_path is None: - raise StepNotInitialized(type(self).__name__) - logging.info("Initializing graph") - store_dir = self.data_store_path / Path(value) if (value := self.configs["disk_store_dir"]) else None - store_type = self.configs["store_type"] - graph_name_mapping = {"source": "SourceGraph", "solution": "SolutionGraph"} - - graph_name = graph_name_mapping[self.configs["graph_name"]] - graph_store = cast(SourceGraph | SolutionGraph | None, self.flow_context.get(graph_name, None)) - if self.configs["init_procedure"] == "reset": - logging.info("Resetting graph") - reset_store(store_dir, graph_store.graph if graph_store else None) - if graph_name in self.flow_context: - del self.flow_context[graph_name] - graph_store = None - logging.info("Graph reset complete") - - prefixes = rules_data.rules.prefixes if rules_data else get_default_prefixes() - - if store_type == stores.OxiGraphStore.rdf_store_type and graph_store is not None: - # OXIGRAPH doesn't like to be initialized twice without a good reason - graph_store.graph.upsert_prefixes(prefixes) - return FlowMessage(output_text="Stores already configured") - try: - store_cls = stores.STORE_BY_TYPE[store_type] - except KeyError: - return FlowMessage(output_text="Invalid store type") - - new_graph_store = store_cls(prefixes=prefixes, base_prefix="neat", namespace=DEFAULT_NAMESPACE) - new_graph_store.init_graph( - self.configs["sparql_query_url"], - self.configs["sparql_update_url"], - "neat-tnt", - internal_storage_dir=store_dir, - ) - logging.info(50 * "*") - logging.info(50 * "*") - logging.info(50 * "*") - logging.info(type(new_graph_store)) - logging.info(50 * "*") - logging.info(50 * "*") - - return ( - FlowMessage(output_text="Graph store configured successfully"), - ( - SourceGraph(graph=new_graph_store) - if graph_name == "SourceGraph" - else SolutionGraph(graph=new_graph_store) - ), - ) - - -def reset_store(data_store_dir: Path | None, graph_store: stores.NeatGraphStoreBase | None = None): - if isinstance(graph_store, stores.OxiGraphStore): - if graph_store: - graph_store.close() - graph_store.drop_graph_store_storage(data_store_dir) - elif data_store_dir: - graph_store.drop_graph_store_storage(data_store_dir) - elif isinstance(graph_store, stores.GraphDBStore): - if graph_store: - graph_store.drop() - graph_store.reinitialize_graph() - return None diff --git a/cognite/neat/workflows/steps/lib/legacy/graph_transformer.py b/cognite/neat/workflows/steps/lib/legacy/graph_transformer.py deleted file mode 100644 index 007e7f860..000000000 --- a/cognite/neat/workflows/steps/lib/legacy/graph_transformer.py +++ /dev/null @@ -1,58 +0,0 @@ -from typing import ClassVar - -from cognite.client import CogniteClient - -from cognite.neat.legacy.graph.transformations.transformer import RuleProcessingReport, domain2app_knowledge_graph -from cognite.neat.legacy.rules.exporters._rules2triples import get_instances_as_triples -from cognite.neat.workflows.model import FlowMessage -from cognite.neat.workflows.steps.data_contracts import RulesData, SolutionGraph, SourceGraph -from cognite.neat.workflows.steps.step_model import Configurable, Step - -__all__ = ["TransformSourceToSolutionGraph"] - -CATEGORY = __name__.split(".")[-1].replace("_", " ").title() + " [LEGACY]" - - -class TransformSourceToSolutionGraph(Step): - """ - The step transforms source graph to solution graph - """ - - description = "The step transforms source graph to solution graph" - category = CATEGORY - version = "legacy" - configurables: ClassVar[list[Configurable]] = [ - Configurable( - name="cdf_lookup_database", - value="", - label="Name of the CDF raw database to use for data lookup (rawlookup rules).\ - Applicable only for transformations with rawlookup rules.", - ) - ] - - def run( # type: ignore[override, syntax] - self, - transformation_rules: RulesData, - cdf_client: CogniteClient, - source_graph: SourceGraph, - solution_graph: SolutionGraph, - ) -> FlowMessage: - report = RuleProcessingReport() - # run transformation and generate new graph - cdf_lookup_database = self.configs.get("cdf_lookup_database", "") - solution_graph.graph.set_graph( - domain2app_knowledge_graph( - source_graph.graph.get_graph(), - transformation_rules.rules, - app_instance_graph=solution_graph.graph.get_graph(), - extra_triples=get_instances_as_triples(transformation_rules.rules), - client=cdf_client, - cdf_lookup_database=cdf_lookup_database, # change this accordingly! - processing_report=report, - ) - ) - return FlowMessage( - output_text=f"Total processed rules: { report.total_rules } , success: { report.total_success } , \ - no results: { report.total_success_no_results } , failed: { report.total_failed }", - payload=report, - ) diff --git a/cognite/neat/workflows/steps/lib/legacy/rules_exporter.py b/cognite/neat/workflows/steps/lib/legacy/rules_exporter.py deleted file mode 100644 index e3678acb5..000000000 --- a/cognite/neat/workflows/steps/lib/legacy/rules_exporter.py +++ /dev/null @@ -1,511 +0,0 @@ -import logging -import time -import warnings -from pathlib import Path -from typing import ClassVar, Literal, cast - -from cognite.client import data_modeling as dm - -import cognite.neat.legacy.graph.extractors._graph_capturing_sheet -from cognite.neat.exceptions import wrangle_warnings -from cognite.neat.legacy.rules import exporters -from cognite.neat.legacy.rules.exporters._rules2dms import DMSSchemaComponents -from cognite.neat.legacy.rules.exporters._rules2graphql import GraphQLSchema -from cognite.neat.legacy.rules.exporters._rules2ontology import Ontology -from cognite.neat.utils.auxiliary import generate_exception_report -from cognite.neat.workflows._exceptions import StepNotInitialized -from cognite.neat.workflows.model import FlowMessage, StepExecutionStatus -from cognite.neat.workflows.steps.data_contracts import CogniteClient, DMSSchemaComponentsData, RulesData -from cognite.neat.workflows.steps.step_model import Configurable, Step - -__all__ = [ - "ExportDMSSchemaComponentsToYAML", - "ExportDMSSchemaComponentsToCDF", - "ExportRulesToGraphQLSchema", - "ExportRulesToOntology", - "ExportRulesToSHACL", - "ExportRulesToGraphCapturingSheet", - "ExportRulesToExcel", - "GenerateDMSSchemaComponentsFromRules", - "DeleteDMSSchemaComponents", -] - -CATEGORY = __name__.split(".")[-1].replace("_", " ").title() + " [LEGACY]" - - -class GenerateDMSSchemaComponentsFromRules(Step): - """ - This step generates DMS Schema components, such as data model, views, containers, etc. from Rules. - """ - - description = "This step generates DMS Schema components, such as data model, views, containers, etc. from Rules." - version = "legacy" - category = CATEGORY - - def run(self, rules: RulesData) -> (FlowMessage, DMSSchemaComponentsData): # type: ignore[override, syntax] - data_model = DMSSchemaComponents.from_rules(rules.rules) - - output_text = ( - "DMS Schema Components Generated: " - f"
  • - {len(data_model.spaces)} spaces
  • " - f"
  • - {len(data_model.containers)} containers
  • " - f"
  • - {len(data_model.views)} views
  • " - f" which are referred in data model {data_model.space}:{data_model.external_id}" - f"/v={data_model.version}" - ) - - # need to store the data model in the step so that it can be used by the next step - # see GraphQL step - - return FlowMessage(output_text=output_text), DMSSchemaComponentsData(components=data_model) - - -class ExportDMSSchemaComponentsToYAML(Step): - """ - This step exports DMS schema components as YAML files - """ - - description = "This step exports DMS schema components as YAML files" - version = "legacy" - category = CATEGORY - configurables: ClassVar[list[Configurable]] = [ - Configurable( - name="format", - value="yaml-dump", - label="Format of the output files", - options=["yaml-dump", "cognite-toolkit", "all"], - ), - ] - - def run(self, data_model_contract: DMSSchemaComponentsData) -> FlowMessage: # type: ignore[override, syntax] - if self.configs is None or self.data_store_path is None: - raise StepNotInitialized(type(self).__name__) - - format_ = self.configs["format"] - - staging_dir = self.config.staging_path - staging_dir.mkdir(parents=True, exist_ok=True) - - if format_ in ["yaml-dump", "all"]: - base_file_name = ( - f"{data_model_contract.components.space}-" - f"{data_model_contract.components.external_id}-" - f"v{data_model_contract.components.version.strip().replace('.', '_')}" - ) - - _container_file_name = f"{base_file_name}-containers.yaml" - _data_model_file_name = f"{base_file_name}-data-model.yaml" - - container_full_path = staging_dir / _container_file_name - data_model_full_path = staging_dir / _data_model_file_name - - data_model = dm.DataModelApply( - space=data_model_contract.components.space, - external_id=data_model_contract.components.external_id, - version=data_model_contract.components.version, - description=data_model_contract.components.description, - name=data_model_contract.components.name, - views=list(data_model_contract.components.views.values()), - ) - - containers = dm.ContainerApplyList(data_model_contract.components.containers.values()) - - container_full_path.write_text(containers.dump_yaml()) - data_model_full_path.write_text(data_model.dump_yaml()) - - output_text = ( - "

    " - "DMS Schema exported and can be downloaded here : " - "

    " - f'- {_data_model_file_name}' - "

    " - f'- {_container_file_name}' - ) - - return FlowMessage(output_text=output_text) - else: - return FlowMessage( - error_text=f"Export format {format_} not implemented!", - step_execution_status=StepExecutionStatus.ABORT_AND_FAIL, - ) - - -class ExportDMSSchemaComponentsToCDF(Step): - """ - This step exports generated DMS Schema components to CDF - """ - - description = "This step exports generated DMS Schema components to CDF." - version = "legacy" - category = CATEGORY - - configurables: ClassVar[list[Configurable]] = [ - Configurable( - name="components", - type="multi_select", - value="", - label="Select which DMS schema component(s) to export to CDF", - options=["space", "container", "view", "data model"], - ), - Configurable( - name="existing_component_handling", - value="fail", - label=( - "How to handle situation when components being exported in CDF already exist." - "Fail the step if any component already exists, " - "Skip the component if it already exists, " - " or Update the component try to update the component." - ), - options=["fail", "skip", "update"], - ), - Configurable( - name="multi_space_components_create", - value="False", - label=( - "Whether to create only components belonging to the data model space" - " (i.e. space define under Metadata sheet of Rules), " - "or also additionally components outside of the data model space." - ), - options=["True", "False"], - ), - ] - - def run(self, data_model: DMSSchemaComponentsData, cdf_client: CogniteClient) -> FlowMessage: # type: ignore[override, syntax] - existing_component_handling: str = self.configs["existing_component_handling"] - multi_space_components_create: bool = self.configs["multi_space_components_create"] == "True" - components_to_create = {key for key, value in self.complex_configs["components"].items() if value} - - if not components_to_create: - return FlowMessage( - error_text="No DMS Schema components selected for upload! Please select minimum one!", - step_execution_status=StepExecutionStatus.ABORT_AND_FAIL, - ) - - logs, errors = data_model.components.to_cdf( - cdf_client, - components_to_create=components_to_create, - existing_component_handling=cast(Literal["skip"], existing_component_handling), - multi_space_components_create=multi_space_components_create, - return_report=True, - ) - - report = "# DMS Schema Components Export to CDF\n\n" - for component, log in logs.items(): - if log: - report += f"## {component.upper()}\n" + "\n".join(log) + "\n\n" - - report += "\n\n# ERRORS\n\n" - for component, log in errors.items(): - if log: - report += f"## {component.upper()}\n" + "\n".join(log) + "\n\n" - - # report - report_file = "dms_component_creation_report.txt" - report_dir = self.config.staging_path - report_dir.mkdir(parents=True, exist_ok=True) - report_full_path = report_dir / report_file - report_full_path.write_text(report) - - output_text = ( - "

    " - "Download DMS Schema Components export " - f'report' - ) - - if any(value for value in errors.values()): - return FlowMessage(error_text=output_text, step_execution_status=StepExecutionStatus.ABORT_AND_FAIL) - else: - return FlowMessage(output_text=output_text) - - -class DeleteDMSSchemaComponents(Step): - """ - This step deletes DMS Schema components - """ - - description = "This step deletes DMS Data model and all underlying containers and views." - version = "legacy" - category = CATEGORY - - configurables: ClassVar[list[Configurable]] = [ - Configurable( - name="components", - type="multi_select", - value="", - label="Select which DMS schema component(s) to delete", - options=["space", "container", "view", "data model"], - ), - Configurable( - name="multi_space_components_removal", - value="False", - label=( - "False (default) = Only delete components inside the space referred" - " in Metadata Sheet of Rules" - r", True = Delete all components referred to in Rules<\p>" - ), - options=["True", "False"], - ), - ] - - def run(self, data_model: DMSSchemaComponentsData, cdf_client: CogniteClient) -> FlowMessage: # type: ignore[override, syntax] - components_to_remove = {key for key, value in self.complex_configs["components"].items() if value} - multi_space_components_removal: bool = self.configs["multi_space_components_removal"] == "True" - if not components_to_remove: - return FlowMessage( - error_text="No DMS Schema components selected for deletion! Please select minimum one!", - step_execution_status=StepExecutionStatus.ABORT_AND_FAIL, - ) - - logs, errors = data_model.components.remove( - cdf_client, - components_to_remove=components_to_remove, - multi_space_components_removal=multi_space_components_removal, - return_report=True, - ) - - report = "# DMS Schema Components Removal from CDF\n\n" - for component, log in logs.items(): - if log: - report += f"## {component.upper()}\n" + "\n".join(log) + "\n\n" - - report += "\n\n# ERRORS\n\n" - for component, log in errors.items(): - if log: - report += f"## {component.upper()}\n" + "\n".join(log) + "\n\n" - - # report - report_file = "dms_component_removal_report.txt" - report_dir = self.config.staging_path - report_dir.mkdir(parents=True, exist_ok=True) - report_full_path = report_dir / report_file - report_full_path.write_text(report) - - output_text = ( - "

    " - "Download DMS Schema Components removal " - f'report' - ) - - if any(value for value in errors.values()): - return FlowMessage(error_text=output_text, step_execution_status=StepExecutionStatus.ABORT_AND_FAIL) - else: - return FlowMessage(output_text=output_text) - - -class ExportRulesToGraphQLSchema(Step): - """ - This step generates GraphQL schema from data model defined in transformation rules - """ - - description = "This step generates GraphQL schema from data model defined in transformation rules." - version = "legacy" - category = CATEGORY - configurables: ClassVar[list[Configurable]] = [ - Configurable( - name="file_name", - value="", - label=( - "Name of the GraphQL schema file it must have .graphql extension," - " if empty defaults to form `prefix-version.graphql`" - ), - ), - Configurable(name="storage_dir", value="staging", label="Directory to store GraphQL schema file"), - ] - - def run(self, transformation_rules: RulesData) -> FlowMessage: # type: ignore[override, syntax] - if self.configs is None or self.data_store_path is None: - raise StepNotInitialized(type(self).__name__) - data_model_gql = GraphQLSchema.from_rules(transformation_rules.rules, verbose=True).schema - - default_name = ( - f"{transformation_rules.rules.metadata.prefix}-" - f"v{transformation_rules.rules.metadata.version.strip().replace('.', '_')}" - ".graphql" - ) - - schema_name = self.configs["file_name"] or default_name - - staging_dir_str = self.configs["storage_dir"] - staging_dir = self.data_store_path / Path(staging_dir_str) - staging_dir.mkdir(parents=True, exist_ok=True) - fdm_model_full_path = staging_dir / schema_name - - fdm_model_full_path.write_text(data_model_gql) - - output_text = ( - "

    " - "GraphQL Schema generated and can be downloaded here : " - f'{schema_name}' - ) - - return FlowMessage(output_text=output_text) - - -class ExportRulesToOntology(Step): - """ - This step exports Rules to OWL ontology - """ - - description = "This step exports Rules to OWL ontology" - version = "legacy" - category = CATEGORY - configurables: ClassVar[list[Configurable]] = [ - Configurable( - name="ontology_file_path", - value="staging/ontology.ttl", - label=( - "Relative path for the ontology file storage, " - "must end with .ttl ! Will be auto-created if not provided !" - ), - ) - ] - - def run(self, rules: RulesData) -> FlowMessage: # type: ignore[override, syntax] - if self.configs is None or self.data_store_path is None: - raise StepNotInitialized(type(self).__name__) - - # ontology file - default_path = self.data_store_path / Path( - f"{rules.rules.metadata.prefix}-" - f"v{rules.rules.metadata.version.strip().replace('.', '_')}" - "-ontology.ttl" - ) - - if not self.configs["ontology_file_path"]: - storage_path = default_path - else: - storage_path = self.data_store_path / Path(self.configs["ontology_file_path"]) - - storage_path.parent.mkdir(parents=True, exist_ok=True) - report_file_path = storage_path.parent / f"report_{storage_path.stem}.txt" - - with warnings.catch_warnings(record=True) as validation_warnings: - ontology = Ontology.from_rules(rules=rules.rules) - - storage_path.write_text(ontology.ontology) - report_file_path.write_text(generate_exception_report(wrangle_warnings(validation_warnings), "Warnings")) - - relative_ontology_file_path = str(storage_path).split("/data/")[1] - - output_text = ( - "

    " - "Rules exported to ontology can be downloaded here : " - f'{storage_path.stem}.ttl' - ) - - return FlowMessage(output_text=output_text) - - -class ExportRulesToSHACL(Step): - """ - This step exports Rules to SHACL - """ - - description = "This step exports Rules to SHACL" - version = "legacy" - category = CATEGORY - configurables: ClassVar[list[Configurable]] = [ - Configurable( - name="shacl_file_path", - value="staging/shacl.ttl", - label=( - "Relative path for the SHACL file storage, " - "must end with .ttl ! Will be auto-created if not provided !" - ), - ) - ] - - def run(self, rules: RulesData) -> FlowMessage: # type: ignore[override, syntax] - if self.configs is None or self.data_store_path is None: - raise StepNotInitialized(type(self).__name__) - - # ontology file - default_path = self.data_store_path / Path( - f"{rules.rules.metadata.prefix}-" f"v{rules.rules.metadata.version.strip().replace('.', '_')}" "-shacl.ttl" - ) - - if not self.configs["shacl_file_path"]: - storage_path = default_path - else: - storage_path = self.data_store_path / Path(self.configs["shacl_file_path"]) - report_file_path = storage_path.parent / f"report_{storage_path.stem}.txt" - - with warnings.catch_warnings(record=True) as validation_warnings: - ontology = Ontology.from_rules(rules=rules.rules) - - storage_path.parent.mkdir(parents=True, exist_ok=True) - storage_path.write_text(ontology.constraints) - report_file_path.write_text(generate_exception_report(wrangle_warnings(validation_warnings), "Warnings")) - - relative_shacl_file_path = str(storage_path).split("/data/")[1] - - output_text = ( - "

    " - "Rules exported to ontology can be downloaded here : " - f'{storage_path.stem}.ttl' - ) - - return FlowMessage(output_text=output_text) - - -class ExportRulesToGraphCapturingSheet(Step): - """ - This step generates graph capturing sheet - """ - - description = "This step generates graph capturing sheet" - version = "legacy" - category = CATEGORY - configurables: ClassVar[list[Configurable]] = [ - Configurable(name="file_name", value="graph_capture_sheet.xlsx", label="File name of the data capture sheet"), - Configurable(name="auto_identifier_type", value="index-based", label="Type of automatic identifier"), - Configurable(name="storage_dir", value="staging", label="Directory to store data capture sheets"), - ] - - def run(self, rules: RulesData) -> FlowMessage: # type: ignore[override, syntax] - if self.configs is None or self.data_store_path is None: - raise StepNotInitialized(type(self).__name__) - logging.info("Generate graph capture sheet") - sheet_name = self.configs["file_name"] - auto_identifier_type = self.configs["auto_identifier_type"] - staging_dir_str = self.configs["storage_dir"] - logging.info(f"Auto identifier type {auto_identifier_type}") - staging_dir = self.data_store_path / Path(staging_dir_str) - - staging_dir.mkdir(parents=True, exist_ok=True) - - data_capture_sheet_path = staging_dir / sheet_name - - cognite.neat.legacy.graph.extractors._graph_capturing_sheet.rules2graph_capturing_sheet( - rules.rules, data_capture_sheet_path, auto_identifier_type=auto_identifier_type - ) - - output_text = ( - "Data capture sheet generated and can be downloaded here : " - f'' - f"{sheet_name}" - ) - return FlowMessage(output_text=output_text) - - -class ExportRulesToExcel(Step): - description = "This step export Rules to Excel representation" - version = "legacy" - category = CATEGORY - configurables: ClassVar[list[Configurable]] = [ - Configurable( - name="output_file_path", value="rules/custom-rules.xlsx", label="File path to the generated Excel file" - ) - ] - - def run(self, rules_data: RulesData) -> FlowMessage: # type: ignore[override, syntax] - full_path = Path(self.data_store_path) / Path(self.configs["output_file_path"]) - exporters.ExcelExporter.from_rules(rules=rules_data.rules).export_to_file(filepath=full_path) - return FlowMessage(output_text="Generated Excel file from rules") diff --git a/cognite/neat/workflows/steps/lib/legacy/rules_importer.py b/cognite/neat/workflows/steps/lib/legacy/rules_importer.py deleted file mode 100644 index 29ecc690d..000000000 --- a/cognite/neat/workflows/steps/lib/legacy/rules_importer.py +++ /dev/null @@ -1,612 +0,0 @@ -import json -import logging -import time -from datetime import datetime -from pathlib import Path -from typing import ClassVar, cast - -import yaml -from prometheus_client import Gauge -from rdflib import Namespace - -from cognite.neat.legacy.rules import exporters, importers -from cognite.neat.legacy.rules.models.rdfpath import TransformationRuleType -from cognite.neat.legacy.rules.models.rules import Class, Classes, Metadata, Properties, Property, Rules -from cognite.neat.legacy.rules.models.value_types import ValueType -from cognite.neat.utils.auxiliary import generate_exception_report -from cognite.neat.workflows import utils -from cognite.neat.workflows._exceptions import StepNotInitialized -from cognite.neat.workflows.cdf_store import CdfStore -from cognite.neat.workflows.model import FlowMessage, StepExecutionStatus -from cognite.neat.workflows.steps.data_contracts import RulesData, SolutionGraph, SourceGraph -from cognite.neat.workflows.steps.step_model import Configurable, Step - -CATEGORY = __name__.split(".")[-1].replace("_", " ").title() + " [LEGACY]" - -__all__ = [ - "ImportExcelToRules", - "ImportOpenApiToRules", - "ImportGraphToRules", - "ImportOntologyToRules", -] - - -class ImportExcelToRules(Step): - """ - This step import rules from the Excel file - """ - - description = "This step import rules from the Excel file" - version = "legacy" - category = CATEGORY - configurables: ClassVar[list[Configurable]] = [ - Configurable( - name="validation_report_storage_dir", - value="rules_validation_report", - label="Directory to store validation report", - ), - Configurable( - name="validation_report_file", - value="rules_validation_report.txt", - label="File name to store validation report", - ), - Configurable( - name="file_name", - value="rules.xlsx", - label="Full name of the rules file in rules folder. If includes path, \ - it will be relative to the neat data folder", - ), - Configurable(name="version", value="", label="Optional version of the rules file"), - ] - - def run(self, cdf_store: CdfStore) -> (FlowMessage, RulesData): # type: ignore[syntax, override] - if self.configs is None or self.data_store_path is None: - raise StepNotInitialized(type(self).__name__) - store = cdf_store - # rules file - if self.configs is None: - raise ValueError(f"Step {type(self).__name__} has not been configured.") - rules_file = Path(self.configs["file_name"]) - if str(rules_file.parent) == ".": - rules_file_path = self.config.rules_store_path / rules_file - else: - rules_file_path = Path(self.data_store_path) / rules_file - - version = self.configs["version"] - - # rules validation - report_file = self.configs["validation_report_file"] - report_dir_str = self.configs["validation_report_storage_dir"] - report_dir = self.data_store_path / Path(report_dir_str) - report_dir.mkdir(parents=True, exist_ok=True) - report_full_path = report_dir / report_file - - if not rules_file_path.exists(): - logging.info(f"Rules files doesn't exist in local fs {rules_file_path}") - - if rules_file_path.exists() and not version: - logging.info(f"Loading rules from {rules_file_path}") - elif rules_file_path.exists() and version: - hash = utils.get_file_hash(rules_file_path) - if hash != version: - store.load_rules_file_from_cdf(str(rules_file), version) - else: - store.load_rules_file_from_cdf(str(rules_file), version) - - raw_rules = importers.ExcelImporter(rules_file_path).to_raw_rules() - rules, errors, _ = raw_rules.to_rules(return_report=True, skip_validation=False) - report = "# RULES VALIDATION REPORT\n\n" + generate_exception_report(errors, "Errors") - - report_full_path.write_text(report) - - text_for_report = ( - "

    " - "Download rules validation report " - f'here' - ) - - if rules is None: - return FlowMessage( - error_text=f"Failed to load transformation rules! {text_for_report}", - step_execution_status=StepExecutionStatus.ABORT_AND_FAIL, - ) - - if self.metrics is None: - raise ValueError(f"Step {type(self).__name__} has not been configured.") - rules_metrics = cast( - Gauge, - self.metrics.register_metric( - "data_model_rules", "Transformation rules stats", m_type="gauge", metric_labels=["component"] - ), - ) - rules_metrics.labels({"component": "classes"}).set(len(rules.classes)) - rules_metrics.labels({"component": "properties"}).set(len(rules.properties)) - logging.info(f"Loaded prefixes {rules.prefixes!s} rules from {rules_file_path.name!r}.") - output_text = f"

    Loaded {len(rules.properties)} rules! {text_for_report}" - - return FlowMessage(output_text=output_text), RulesData(rules=rules) - - -class ImportOntologyToRules(Step): - """The step extracts schema from OpenApi/Swagger specification and generates NEAT transformation rules object.""" - - description = "The step extracts NEAT rules object from OWL Ontology and \ - exports them as an Excel rules files for further editing." - category = CATEGORY - version = "legacy" - configurables: ClassVar[list[Configurable]] = [ - Configurable( - name="ontology_file_path", value="staging/ontology.ttl", label="Relative path to the OWL ontology file." - ), - Configurable( - name="excel_file_path", value="staging/rules.xlsx", label="Relative path for the Excel rules storage." - ), - Configurable( - name="make_compliant", - value="True", - label="Relative path for the Excel rules storage.", - options=["True", "False"], - ), - ] - - def run(self) -> FlowMessage: # type: ignore[override, syntax] - ontology_file_path = self.data_store_path / Path(self.configs["ontology_file_path"]) - excel_file_path = self.data_store_path / Path(self.configs["excel_file_path"]) - report_file_path = excel_file_path.parent / f"report_{excel_file_path.stem}.txt" - - make_compliant = self.configs["make_compliant"] == "True" - try: - rules = importers.OWLImporter(ontology_file_path).to_rules(make_compliant=make_compliant) - except Exception: - rules = importers.OWLImporter(ontology_file_path).to_rules( - skip_validation=True, make_compliant=make_compliant - ) - assert isinstance(rules, Rules) - exporters.ExcelExporter.from_rules(rules).export_to_file(excel_file_path) - - if report := importers.ExcelImporter(filepath=excel_file_path).to_raw_rules().validate_rules(): - report_file_path.write_text(report) - - relative_excel_file_path = str(excel_file_path).split("/data/")[1] - relative_report_file_path = str(report_file_path).split("/data/")[1] - - output_text = ( - "

    " - "Rules imported from OWL Ontology can be downloaded here : " - f'{excel_file_path.stem}.xlsx' - "

    " - "Report can be downloaded here : " - f'{report_file_path.stem}.txt' - ) - - return FlowMessage(output_text=output_text) - - -class ImportOpenApiToRules(Step): - """The step extracts schema from OpenApi/Swagger specification and generates NEAT transformation rules object.""" - - description = "The step extracts schema from OpenAPI specification and generates NEAT transformation rules object. \ - The rules object can be serialized to excel file or used directly in other steps." - category = CATEGORY - version = "legacy" - configurables: ClassVar[list[Configurable]] = [ - Configurable( - name="openapi_spec_file_path", - value="workflows/openapi_to_rules/source_data/openapi.json", - label="Relative path to the OpenAPI spec file.The file can be in either json or in yaml format.", - ), - Configurable( - name="fdm_compatibility_mode", - value="True", - label="If set to True, the step will try to convert property names to FDM compatible names.", - options=["True", "False"], - ), - ] - - def run(self) -> (FlowMessage, RulesData): # type: ignore[override, syntax] - openapi_file_path = self.data_store_path / Path(self.configs["openapi_spec_file_path"]) - self.processed_classes_counter = 0 - self.processed_properties_counter = 0 - self.failed_classes_counter = 0 - self.failed_properties_counter = 0 - self.failed_classes: dict[str, str] = {} - self.failed_properties: dict[str, str] = {} - self.is_fdm_compatibility_mode = self.configs["fdm_compatibility_mode"] == "True" - rules = self.open_api_to_rules(openapi_file_path) - report = f" Generated Rules and source data model from OpenApi specs \ -

    Processed {self.processed_classes_counter} classes \ - and {self.processed_properties_counter} properties.

    \ -

    Failed to process {self.failed_classes_counter} classes and \ - {self.failed_properties_counter} properties.

    " - report_obj = { - "processed_classes_counter": self.processed_classes_counter, - "processed_properies_counter": self.processed_properties_counter, - "failed_classes": self.failed_classes, - "failed_properties": self.failed_properties, - } - return (FlowMessage(output_text=report, payload=report_obj), RulesData(rules=rules)) - - def open_api_to_rules(self, open_api_spec_file_path: Path) -> Rules: - """Converts OpenAPI spec to NEAT transformation rules object.""" - with open_api_spec_file_path.open("r") as openapi_file: - if open_api_spec_file_path.suffix == ".json": - openapi_spec = json.load(openapi_file) - elif open_api_spec_file_path.suffix == ".yaml": - openapi_spec = yaml.safe_load(openapi_file) - - metadata = Metadata( - title="OpenAPI to DM transformation rules", - description="OpenAPI to DM transformation rules", - version="0.1", - creator="Cognite", - created=datetime.utcnow(), - namespace=Namespace("http://purl.org/cognite/neat#"), - prefix="neat", - suffix="OpenAPI", - ) - - classes = Classes() - properties = Properties() - - # Loop through OpenAPI components - for component_name, component_info in openapi_spec.get("components", {}).get("schemas", {}).items(): - if self.is_fdm_compatibility_mode: - class_name = get_dms_compatible_name(create_fdm_compatibility_class_name(component_name)) - else: - class_name = component_name - class_id = class_name - logging.info(f" OpenAPi parser : Processing class {class_id} ") - try: - class_ = Class( - class_id=class_id, - class_name=class_name, - description=component_info.get("description", component_info.get("title", "empty")), - ) - classes[class_id] = class_ - self.processed_classes_counter += 1 - # Loop through properties of OpenApi spec] - self.process_properies(properties, class_id, class_name, component_info) - - except Exception as e: - logging.error(f" OpenAPi parser : Error creating class {class_id}: {e}") - self.failed_classes_counter += 1 - self.failed_classes[class_id] = str(e) - - rules = Rules(metadata=metadata, classes=classes, properties=properties, prefixes={}, instances=[]) - - return rules - - def process_properies( - self, - rules_properties: Properties, - class_id: str, - class_name: str, - component: dict, - parent_property_name: str | None = None, - ): - # component can have keys : type, description, properties, required, title, allOf, anyOf, oneOf§ - logging.info(f" OpenAPi parser : Processing properties for class {class_id} , component {component}") - for component_name, component_info in component.items(): - if component_name == "allOf" or component_name == "anyOf" or component_name == "oneOf": - for sub_component in component_info: - self.process_properies(rules_properties, class_id, class_name, sub_component, component_name) - elif component_name == "properties": - for prop_name, prop_info in component_info.items(): - prop_id = prop_name - if prop_name == "allOf" or prop_name == "anyOf" or prop_name == "oneOf": - if isinstance(prop_info, list): - prop_type = prop_info[0].get("type", "string") - else: - logging.error(f" !!!!! prop info is not a list . its: {prop_info} ") - - else: - prop_type = prop_info.get("type", "string") - if prop_type == "array": - self.process_properies( - rules_properties, class_id, class_name, prop_info.get("items", {}), prop_name - ) - continue - expected_value_type = self.map_open_api_type(prop_type) - if prop_name == "$ref": - ref_class = self.get_ref_class_name(prop_info.get("$ref", "")) - expected_value_type = ref_class - try: - prop = Property( - class_id=class_id, - property_id=get_dms_compatible_name(prop_id) if self.is_fdm_compatibility_mode else prop_id, - property_name=( - get_dms_compatible_name(prop_name) if self.is_fdm_compatibility_mode else prop_name - ), - property_type="ObjectProperty", - description=prop_info.get("description", prop_info.get("title", "empty")), - expected_value_type=ValueType(prefix="neat", suffix=expected_value_type), - cdf_resource_type=["Asset"], - resource_type_property="Asset", # type: ignore - rule_type=TransformationRuleType("rdfpath"), - rule=f"neat:{class_name}(neat:{prop_name})", - label="linked to", - ) - self.processed_properties_counter += 1 - rules_properties[class_id + prop_id] = prop - except Exception as e: - logging.error(f" OpenAPi parser : Error creating property {prop_id}: {e}") - self.failed_properties_counter += 1 - self.failed_properties[prop_id] = str(e) - elif component_name == "$ref": - ref_class = self.get_ref_class_name(component_info) - if parent_property_name is not None: - prop = Property( - class_id=class_id, - property_id=parent_property_name, - property_name=parent_property_name, - property_type="ObjectProperty", - description="no", - expected_value_type=ValueType(prefix="neat", suffix=ref_class), - cdf_resource_type=["Asset"], - resource_type_property="Asset", # type: ignore - rule_type=TransformationRuleType("rdfpath"), - rule=f"neat:{class_name}(neat:{parent_property_name})", - label="linked to", - ) - rules_properties[class_id + parent_property_name] = prop - - def get_ref_class_name(self, ref: str) -> str: - ref_payload = ref.split("/")[-1] - if self.is_fdm_compatibility_mode: - return get_dms_compatible_name(create_fdm_compatibility_class_name(ref_payload)) - return ref_payload - - def map_open_api_type(self, openapi_type: str) -> str: - """Map OpenAPI type to NEAT compatible types""" - if openapi_type == "object": - datatype = "json" - elif openapi_type == "array": - datatype = "sequence" - elif openapi_type == "number": - datatype = "float" - else: - return openapi_type # Default to string - return datatype - - -class ImportArbitraryJsonYamlToRules(Step): - """The step extracts schema from arbitrary json or yaml file and generates NEAT transformation rules object.""" - - description = "The step extracts schema from arbitrary json file and generates NEAT transformation rules object." - category = CATEGORY - version = "legacy" - configurables: ClassVar[list[Configurable]] = [ - Configurable( - name="file_path", - value="workflows/openapi_to_rules/data/data.json", - label="Relative path to the json file.The file can be in either json or in yaml format.", - ), - Configurable( - name="fdm_compatibility_mode", - value="True", - label="If set to True, the step will try to convert property names to FDM compatible names.", - options=["True", "False"], - ), - ] - - def run(self) -> (FlowMessage, RulesData): # type: ignore[override, syntax] - openapi_file_path = Path(self.data_store_path) / Path(self.configs["file_path"]) - self.processed_classes_counter = 0 - self.processed_properies_counter = 0 - self.failed_classes_counter = 0 - self.failed_properties_counter = 0 - self.failed_classes: dict[str, str] = {} - self.failed_properties: dict[str, str] = {} - self.is_fdm_compatibility_mode = self.configs["fdm_compatibility_mode"] == "True" - - rules = self.dict_to_rules(openapi_file_path) - report = f" Generated Rules and source data model from json/yaml \ -

    Processed {self.processed_classes_counter} classes and {self.processed_properies_counter} properties.

    \ -

    Failed to process {self.failed_classes_counter} classes \ - and {self.failed_properties_counter} properties.

    " - report_obj = { - "processed_classes_counter": self.processed_classes_counter, - "processed_properies_counter": self.processed_properies_counter, - "failed_classes": self.failed_classes, - "failed_properties": self.failed_properties, - } - return FlowMessage(output_text=report, payload=report_obj), RulesData(rules=rules) - - def dict_to_rules(self, open_api_spec_file_path: Path) -> Rules: - """Converts OpenAPI spec to NEAT transformation rules object.""" - with open_api_spec_file_path.open("r") as openapi_file: - if open_api_spec_file_path.suffix == ".json": - src_data_obj = json.load(openapi_file) - elif open_api_spec_file_path.suffix == ".yaml": - src_data_obj = yaml.safe_load(openapi_file) - - metadata = Metadata( - title="OpenAPI to DM transformation rules", - description="OpenAPI to DM transformation rules", - version="0.1", - creator="Cognite", - created=datetime.utcnow(), - namespace=Namespace("http://purl.org/cognite/neat#"), - prefix="neat", - suffix="OpenAPI", - ) - - self.classes = Classes() - self.properties = Properties() - - self.convert_dict_to_classes_and_props(src_data_obj, None) - rules = Rules(metadata=metadata, classes=self.classes, properties=self.properties, prefixes={}, instances=[]) - - return rules - - def add_class(self, class_name: str, description: str | None = None, parent_class_name: str | None = None): - if class_name in self.classes: - return - if self.is_fdm_compatibility_mode: - class_name = get_dms_compatible_name(create_fdm_compatibility_class_name(class_name)) - try: - class_ = Class(class_id=class_name, class_name=class_name, description=description) - if parent_class_name: - self.add_property(class_name, "parent", parent_class_name, None) - self.classes[class_name] = class_ - self.processed_classes_counter += 1 - except Exception as e: - logging.error(f" OpenAPi parser : Error creating class {class_name}: {e}") - self.failed_classes_counter += 1 - self.failed_classes[class_name] = str(e) - return - - def add_property(self, class_name: str, property_name: str, property_type: str, description: str | None = None): - if class_name + property_name in self.properties: - return - if self.is_fdm_compatibility_mode: - property_name = get_dms_compatible_name(property_name) - class_name = get_dms_compatible_name(create_fdm_compatibility_class_name(class_name)) - try: - prop = Property( - class_id=class_name, - property_id=property_name, - property_name=property_name, - property_type="ObjectProperty", - description=description, - expected_value_type=ValueType(prefix="neat", suffix=property_type), - cdf_resource_type=["Asset"], - resource_type_property="Asset", # type: ignore - rule_type=TransformationRuleType("rdfpath"), - rule=f"neat:{class_name}(neat:{property_name})", - label="linked to", - ) - self.properties[class_name + property_name] = prop - self.processed_properies_counter += 1 - except Exception as e: - logging.error(f" OpenAPi parser : Error creating property {property_name}: {e}") - self.failed_properties_counter += 1 - self.failed_properties[class_name + property_name] = str(e) - return - - # Iterate through the JSON data and convert it to triples - def convert_dict_to_classes_and_props(self, data: dict, parent_property_name=None, grand_parent_property_name=None): - if isinstance(data, dict): - if len(data) == 0: - return - if parent_property_name is None: - for key, value in data.items(): - self.convert_dict_to_classes_and_props(value, key) - else: - description = None - self.add_class(parent_property_name, description, grand_parent_property_name) - for key, value in data.items(): - self.convert_dict_to_classes_and_props(value, key, parent_property_name) - elif isinstance(data, list): - for item in data: - self.convert_dict_to_classes_and_props(item, parent_property_name, grand_parent_property_name) - else: - # Convert scalar values to RDF literals - data_type = "" - if isinstance(data, bool): - data_type = "boolean" - elif isinstance(data, int): - data_type = "integer" - elif isinstance(data, float): - data_type = "float" - elif isinstance(data, str): - data_type = "string" - else: - data_type = "string" - if grand_parent_property_name is None: - logging.error(" grand_parent_property_name is None") - return - - self.add_property(grand_parent_property_name, parent_property_name, data_type, None) - - -def get_dms_compatible_name(name: str) -> str: - """Converts name to DMS compatible name.It applies both to class and property names""" - # reserverd words in DMS - reserved_words_mapping = { - "space": "src_space", - "externalId": "external_id", - "createdTime": "created_time", - "lastUpdatedTime": "last_updated_time", - "deletedTime": "deleted_time", - "edge_id": "src_edge_id", - "node_id": "src_node_id", - "project_id": "src_project_id", - "property_group": "src_property_group", - "seq": "src_seq", - "tg_table_name": "src__table_name", - "extensions": "src_extensions", - } - if name in reserved_words_mapping: - return reserved_words_mapping[name] - else: - return name.replace(".", "_") - - -def create_fdm_compatibility_class_name(input_string: str): - """Remove underscores and capitalize each word in the string , - the conversion is done to improve compliance with DMS naming conventions""" - - if "_" in input_string: - words = input_string.split("_") # Split the string by underscores - result = "".join([word.capitalize() for word in words]) # Capitalize each word - return result - else: - return input_string - - -class ImportGraphToRules(Step): - """The step extracts data model from RDF graph and generates NEAT transformation rules object.""" - - description = "The step extracts data model from RDF graph and generates NEAT transformation rules object. \ - The rules object can be serialized to excel file or used directly in other steps." - category = CATEGORY - version = "legacy" - configurables: ClassVar[list[Configurable]] = [ - Configurable( - name="excel_file_path", value="staging/rules.xlsx", label="Relative path for the Excel rules storage." - ), - Configurable( - name="max_number_of_instances", - value="-1", - label="Maximum number of instances per class to process, -1 means all instances", - ), - ] - - def run(self, graph_store: SourceGraph | SolutionGraph) -> FlowMessage: # type: ignore[override, syntax] - excel_file_path = self.data_store_path / Path(self.configs["excel_file_path"]) - report_file_path = excel_file_path.parent / f"report_{excel_file_path.stem}.txt" - - try: - rules = importers.GraphImporter( - graph_store.graph.graph, int(self.configs["max_number_of_instances"]) - ).to_rules() - except Exception: - rules = importers.GraphImporter( - graph_store.graph.graph, int(self.configs["max_number_of_instances"]) - ).to_rules(skip_validation=True) - - assert isinstance(rules, Rules) - exporters.ExcelExporter.from_rules(rules).export_to_file(excel_file_path) - - if report := importers.ExcelImporter(filepath=excel_file_path).to_raw_rules().validate_rules(): - report_file_path.write_text(report) - - relative_excel_file_path = str(excel_file_path).split("/data/")[1] - relative_report_file_path = str(report_file_path).split("/data/")[1] - - output_text = ( - "

    " - "Rules imported from Graph can be downloaded here : " - f'{excel_file_path.stem}.xlsx' - "

    " - "Report can be downloaded here : " - f'{report_file_path.stem}.txt' - ) - - return FlowMessage(output_text=output_text) diff --git a/tests/tests_legacy/__init__.py b/tests/tests_legacy/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/tests_legacy/conftest.py b/tests/tests_legacy/conftest.py deleted file mode 100644 index 8d705381b..000000000 --- a/tests/tests_legacy/conftest.py +++ /dev/null @@ -1,197 +0,0 @@ -import os -import shutil -import tempfile -from pathlib import Path - -import pytest -from rdflib import RDF, Literal, Namespace - -from cognite.neat.legacy.graph import extractors, loaders -from cognite.neat.legacy.graph.stores import MemoryStore -from cognite.neat.legacy.graph.transformations.transformer import ( - domain2app_knowledge_graph, -) -from cognite.neat.legacy.rules import importers -from cognite.neat.legacy.rules.exporters._rules2triples import get_instances_as_triples -from cognite.neat.legacy.rules.models.rules import Rules -from tests import config as config - -# Setup config for Neat App -_TMP_DIR = Path(tempfile.gettempdir()) / "neat" / "data" -# Cleanup tmp dir -shutil.rmtree(_TMP_DIR, ignore_errors=True) -_TMP_DIR.mkdir(parents=True, exist_ok=True) -os.environ["NEAT_DATA_PATH"] = str(_TMP_DIR) -os.environ["NEAT_CDF_PROJECT"] = "get-power-grid" -os.environ["NEAT_CDF_CLIENT_ID"] = "uuid" -os.environ["NEAT_CDF_CLIENT_SECRET"] = "secret" -os.environ["NEAT_CDF_CLIENT_NAME"] = "neat-test-service" -os.environ["NEAT_CDF_BASE_URL"] = "https://bluefield.cognitedata.com" -os.environ["NEAT_CDF_TOKEN_URL"] = " https://login.microsoftonline.com/uuid4/oauth2/v2.0/token" -os.environ["NEAT_CDF_SCOPES"] = "https://bluefield.cognitedata.com/.default" -os.environ["NEAT_CDF_DEFAULT_DATASET_ID"] = "3931920688237191" -os.environ["NEAT_LOAD_EXAMPLES"] = "1" - - -@pytest.fixture(scope="session") -def nordic44_inferred_rules() -> Rules: - return importers.ExcelImporter(config.NORDIC44_INFERRED_RULES).to_rules() - - -@pytest.fixture(scope="session") -def transformation_rules() -> Rules: - return importers.ExcelImporter(config.SIMPLECIM_TRANSFORMATION_RULES).to_rules() - - -@pytest.fixture(scope="session") -def dms_compliant_rules() -> Rules: - return importers.ExcelImporter(config.SIMPLECIM_TRANSFORMATION_RULES_DMS_COMPLIANT).to_rules() - - -@pytest.fixture(scope="session") -def simple_rules() -> Rules: - return importers.ExcelImporter(config.SIMPLE_TRANSFORMATION_RULES).to_rules() - - -@pytest.fixture(scope="session") -def transformation_rules_date() -> Rules: - return importers.ExcelImporter(config.SIMPLE_TRANSFORMATION_RULES_DATES).to_rules() - - -@pytest.fixture(scope="function") -def small_graph(simple_rules) -> MemoryStore: - graph_store = MemoryStore( - base_prefix=simple_rules.metadata.prefix, - namespace=simple_rules.metadata.namespace, - prefixes=simple_rules.prefixes, - ) - graph_store.init_graph() - - for triple in get_instances_as_triples(simple_rules): - graph_store.graph.add(triple) - return graph_store - - -@pytest.fixture(scope="function") -def graph_with_numeric_ids(simple_rules) -> MemoryStore: - graph_store = MemoryStore( - base_prefix=simple_rules.metadata.prefix, - namespace=simple_rules.metadata.namespace, - prefixes=simple_rules.prefixes, - ) - graph_store.init_graph() - - namespace = simple_rules.metadata.namespace - graph_store.graph.add((namespace["1"], RDF.type, namespace["PriceAreaConnection"])) - graph_store.graph.add((namespace["1"], namespace["name"], Literal("Price Area Connection 1"))) - graph_store.graph.add((namespace["1"], namespace["priceArea"], namespace["2"])) - graph_store.graph.add((namespace["1"], namespace["priceArea"], namespace["3"])) - return graph_store - - -@pytest.fixture(scope="function") -def graph_with_date(transformation_rules_date) -> MemoryStore: - graph_store = MemoryStore( - base_prefix=transformation_rules_date.metadata.prefix, - namespace=transformation_rules_date.metadata.namespace, - prefixes=transformation_rules_date.prefixes, - ) - graph_store.init_graph() - - namespace = transformation_rules_date.metadata.namespace - graph_store.graph.add((namespace["1"], RDF.type, namespace["PriceAreaConnection"])) - graph_store.graph.add((namespace["1"], namespace["name"], Literal("Price Area Connection 1"))) - graph_store.graph.add((namespace["1"], namespace["priceArea"], namespace["2"])) - graph_store.graph.add((namespace["1"], namespace["priceArea"], namespace["3"])) - graph_store.graph.add((namespace["1"], namespace["endDate"], Literal("2020-01-01"))) - return graph_store - - -@pytest.fixture(scope="session") -def source_knowledge_graph(dms_compliant_rules) -> MemoryStore: - graph = MemoryStore(namespace=Namespace("http://purl.org/nordic44#")) - graph.init_graph() - graph.import_from_file(config.NORDIC44_KNOWLEDGE_GRAPH) - - for prefix, namespace in dms_compliant_rules.prefixes.items(): - graph.graph.bind(prefix, namespace) - return graph - - -@pytest.fixture(scope="session") -def source_knowledge_graph_dirty(transformation_rules) -> MemoryStore: - graph = MemoryStore( - namespace=Namespace("http://purl.org/nordic44#"), - prefixes=transformation_rules.prefixes, - ) - graph.init_graph() - graph.import_from_file(config.NORDIC44_KNOWLEDGE_GRAPH_DIRTY) - for triple in get_instances_as_triples(transformation_rules): - graph.graph.add(triple) - return graph - - -@pytest.fixture(scope="session") -def solution_knowledge_graph_dirty(source_knowledge_graph_dirty, transformation_rules): - return domain2app_knowledge_graph(source_knowledge_graph_dirty, transformation_rules) - - -@pytest.fixture(scope="session") -def solution_knowledge_graph(source_knowledge_graph, transformation_rules): - return domain2app_knowledge_graph(source_knowledge_graph, transformation_rules) - - -@pytest.fixture(scope="function") -def mock_knowledge_graph(transformation_rules) -> MemoryStore: - mock_graph = MemoryStore( - prefixes=transformation_rules.prefixes, - namespace=transformation_rules.metadata.namespace, - ) - mock_graph.init_graph(base_prefix=transformation_rules.metadata.prefix) - - class_count = { - "RootCIMNode": 1, - "GeographicalRegion": 1, - "SubGeographicalRegion": 1, - "Substation": 1, - "Terminal": 2, - } - - mock_triples = extractors.MockGraphGenerator(transformation_rules, class_count).extract() - mock_graph.add_triples(mock_triples, batch_size=20000) - - return mock_graph - - -@pytest.fixture(scope="function") -def mock_rdf_assets(mock_knowledge_graph, transformation_rules): - return loaders.rdf2assets(mock_knowledge_graph, transformation_rules, data_set_id=123456) - - -@pytest.fixture(scope="function") -def mock_cdf_assets(mock_knowledge_graph, transformation_rules): - return loaders.rdf2assets(mock_knowledge_graph, transformation_rules, data_set_id=123456) - - -@pytest.fixture(scope="function") -def grid_graphql_schema(): - return """type CountryGroup { - name: String! -} - -type Country { - name: String! - countryGroup: CountryGroup - TSO: [String!]! -} - -type PriceArea { - name: String! - country: Country - priceAreaConnection: [PriceAreaConnection] -} - -type PriceAreaConnection { - name: String! - priceArea: [PriceArea] -}""" diff --git a/tests/tests_legacy/graph/__init__.py b/tests/tests_legacy/graph/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/tests_legacy/graph/extractors/__init__.py b/tests/tests_legacy/graph/extractors/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/tests_legacy/graph/extractors/test_dexpi.py b/tests/tests_legacy/graph/extractors/test_dexpi.py deleted file mode 100644 index f3ffb6f3d..000000000 --- a/tests/tests_legacy/graph/extractors/test_dexpi.py +++ /dev/null @@ -1,9 +0,0 @@ -from cognite.neat.legacy.graph import extractors as graph_loader -from tests import config - - -def test_dexpi_extractor(): - """Test that the dexpi extractor works.""" - triples = graph_loader.DexpiXML(config.DEXPI_EXAMPLE).extract() - - assert len(triples) == 1922 diff --git a/tests/tests_legacy/graph/extractors/test_mock_graph.py b/tests/tests_legacy/graph/extractors/test_mock_graph.py deleted file mode 100644 index 773977efd..000000000 --- a/tests/tests_legacy/graph/extractors/test_mock_graph.py +++ /dev/null @@ -1,42 +0,0 @@ -from cognite.neat.legacy.graph import extractors, loaders -from cognite.neat.legacy.graph.loaders.core import rdf_to_relationships -from cognite.neat.legacy.graph.stores import MemoryStore -from cognite.neat.legacy.rules.models.rules import Rules -from cognite.neat.utils.rdf_ import remove_namespace_from_uri - - -def test_mock_graph(transformation_rules: Rules): - rules = transformation_rules - - class_count = { - "RootCIMNode": 1, - "GeographicalRegion": 5, - "SubGeographicalRegion": 10, - "Substation": 20, - "Terminal": 60, - } - - graph_store = MemoryStore(prefixes=rules.prefixes, namespace=rules.metadata.namespace) - graph_store.init_graph(base_prefix=rules.metadata.prefix) - - mock_triples = extractors.MockGraphGenerator(rules, class_count).extract() - graph_store.add_triples(mock_triples) - - graph_class_count = { - remove_namespace_from_uri(res[0]): int(res[1]) - for res in list( - graph_store.graph.query( - "SELECT ?class (count(?s) as ?instances ) " - "WHERE { ?s a ?class . } group by ?class " - "order by DESC(?instances)" - ) - ) - } - - assets = loaders.rdf2assets(graph_store, rules, data_set_id=123456) - relationships = rdf_to_relationships.rdf2relationships(graph_store, rules, data_set_id=123456) - - assert len(mock_triples) == 503 - assert len(assets) == 97 - assert graph_class_count == class_count - assert len(relationships) == 135 diff --git a/tests/tests_legacy/graph/extractors/test_rules2graph_sheet.py b/tests/tests_legacy/graph/extractors/test_rules2graph_sheet.py deleted file mode 100644 index 21d502644..000000000 --- a/tests/tests_legacy/graph/extractors/test_rules2graph_sheet.py +++ /dev/null @@ -1,19 +0,0 @@ -from cognite.neat.legacy.graph import extractors as graph_extractor -from cognite.neat.legacy.graph.extractors._graph_capturing_sheet import rules2graph_capturing_sheet -from cognite.neat.legacy.rules.importers import ExcelImporter -from tests import config - - -def test_graph_capturing_sheet(tmp_path, simple_rules): - extractor = graph_extractor.GraphCapturingSheet( - simple_rules, config.GRAPH_CAPTURING_SHEET, store_graph_capturing_sheet=True - ) - _ = extractor.extract() - graph_capturing_sheet = extractor.sheet - tmp_sheet = tmp_path / "temp_graph_capturing.xlsx" - - rules2graph_capturing_sheet(simple_rules, tmp_sheet, add_drop_down_list=True, auto_identifier_type="uuid") - resulting_sheet = ExcelImporter(tmp_sheet).to_tables() - assert resulting_sheet.keys() == graph_capturing_sheet.keys() - for sheet_name in resulting_sheet.keys(): - assert list(resulting_sheet[sheet_name].columns) == list(graph_capturing_sheet[sheet_name].columns) diff --git a/tests/tests_legacy/graph/extractors/test_sheet2graph.py b/tests/tests_legacy/graph/extractors/test_sheet2graph.py deleted file mode 100644 index 1a1f23388..000000000 --- a/tests/tests_legacy/graph/extractors/test_sheet2graph.py +++ /dev/null @@ -1,30 +0,0 @@ -from rdflib import XSD, Literal - -from cognite.neat.legacy.graph import extractors -from cognite.neat.legacy.graph.stores import MemoryStore -from cognite.neat.utils.rdf_ import remove_namespace_from_uri -from tests import config - - -def test_sheet2graph(simple_rules): - graph_store = MemoryStore(prefixes=simple_rules.prefixes, namespace=simple_rules.metadata.namespace) - graph_store.init_graph(base_prefix=simple_rules.metadata.prefix) - - triples = extractors.GraphCapturingSheet(simple_rules, config.GRAPH_CAPTURING_SHEET, use_source_ids=True).extract() - - graph_store.add_triples(triples) - - count_dict = { - remove_namespace_from_uri(res[0]): int(res[1]) - for res in list( - graph_store.graph.query( - "SELECT ?class (count(?s) as ?instances ) WHERE { ?s a ?class . } " - "group by ?class order by DESC(?instances)" - ) - ) - } - - assert next(iter(graph_store.graph.query("Select ?o WHERE { neat:Country-1 neat:TSO ?o }")))[0] == Literal( - "Statnett", datatype=XSD.string - ) - assert count_dict == {"PriceArea": 2, "CountryGroup": 1, "Country": 1, "PriceAreaConnection": 1} diff --git a/tests/tests_legacy/graph/loaders/__init__.py b/tests/tests_legacy/graph/loaders/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/tests_legacy/graph/loaders/core/__init__.py b/tests/tests_legacy/graph/loaders/core/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/tests_legacy/graph/loaders/core/test_rdf_to_assets.py b/tests/tests_legacy/graph/loaders/core/test_rdf_to_assets.py deleted file mode 100644 index 58cded643..000000000 --- a/tests/tests_legacy/graph/loaders/core/test_rdf_to_assets.py +++ /dev/null @@ -1,193 +0,0 @@ -from copy import deepcopy - -import pandas as pd -import pytest -from cognite.client.data_classes import Asset, AssetList, Label, LabelDefinition, LabelDefinitionList, LabelFilter -from cognite.client.testing import monkeypatch_cognite_client - -from cognite.neat.legacy.graph.loaders.core.rdf_to_assets import ( - AssetLike, - NeatMetadataKeys, - _assets_to_update, - categorize_assets, - order_assets, - remove_non_existing_labels, -) -from cognite.neat.legacy.rules.exporters._core.rules2labels import get_labels - - -def test_asset_hierarchy_ordering(mock_rdf_assets): - ordered_assets = [asset.external_id for asset in order_assets(mock_rdf_assets)] - assert ordered_assets == [ - "RootCIMNode-0", - "GeographicalRegion-0", - "SubGeographicalRegion-0", - "Substation-0", - "Terminal-0", - "Terminal-1", - "orphanage-123456", - ] - - -def test_asset_hierarchy_ordering_orphan(mock_rdf_assets): - # Make orphan Terminal asset by popping Substation from asset dict - # This simulates case when new asset is created which already has parent in CDF - # so we do not need to create parent asset - mock_rdf_assets.pop("Substation-0") - ordered_assets = [asset.external_id for asset in order_assets(mock_rdf_assets)] - assert ordered_assets == [ - "RootCIMNode-0", - "GeographicalRegion-0", - "SubGeographicalRegion-0", - "Terminal-0", - "Terminal-1", - "orphanage-123456", - ] - - -def test_asset_diffing(mock_rdf_assets, mock_cdf_assets, transformation_rules): - rdf_assets = mock_rdf_assets - cdf_assets = mock_cdf_assets - - # Create non-active asset (aka decommissioned, historic) - resurrect_id = "GeographicalRegion-0" - non_active_asset = deepcopy(rdf_assets[resurrect_id]) - non_active_asset["metadata"]["active"] = "false" - non_active_asset["labels"] = ["GeographicalRegion", "historic"] - cdf_assets[resurrect_id] = non_active_asset - - # 1 asset to create - create_id = "Terminal-0" - cdf_assets.pop(create_id) - - # 1 assets to decommissioned - decommission_id = "SubGeographicalRegion-0" - rdf_assets.pop(decommission_id) - - # 1 assets to update - update_id = "RootCIMNode-0" - rdf_assets[update_id]["name"] = "Deus Ex Machina" - - with monkeypatch_cognite_client() as client_mock: - - def list_assets(data_set_ids: int = 123456, limit: int = -1, labels=None, **_): - labels = labels or LabelFilter(contains_any=["non-historic"]) - if labels == LabelFilter(contains_any=["non-historic"]): - return AssetList([Asset(**asset) for asset in cdf_assets.values()]) - else: - return AssetList([Asset(**non_active_asset)]) - - def list_labels(**_): - label_names = [*list(get_labels(transformation_rules)), "non-historic", "historic"] - return [Label(external_id=label_name, name=label_names) for label_name in label_names] - - client_mock.assets.list = list_assets - client_mock.labels.list = list_labels - - categorized_assets, report = categorize_assets(client_mock, rdf_assets, data_set_id=123456, return_report=True) - assert len(categorized_assets["create"]) == 1 - assert len(report["create"]) == 1 - assert create_id == categorized_assets["create"][0].external_id - assert create_id in report["create"] - - assert len(categorized_assets["update"]) == 1 - assert len(report["update"]) == 1 - assert update_id == categorized_assets["update"][0].external_id - assert update_id in report["update"] - assert categorized_assets["update"][0].name == "Deus Ex Machina" - assert report["update"][update_id] == { - "values_changed": {"root['name']": {"new_value": "Deus Ex Machina", "old_value": "RootCIMNode 0"}} - } - - assert len(categorized_assets["decommission"]) == 1 - assert len(report["decommission"]) == 1 - assert decommission_id == categorized_assets["decommission"][0].external_id - assert decommission_id in report["decommission"] - assert {label.external_id for label in categorized_assets["decommission"][0].labels} == { - "SubGeographicalRegion", - "historic", - } - assert categorized_assets["decommission"][0].metadata["active"] == "false" - - assert len(categorized_assets["resurrect"]) == 1 - assert len(report["resurrect"]) == 1 - assert resurrect_id == categorized_assets["resurrect"][0].external_id - assert resurrect_id in report["resurrect"] - assert {label.external_id for label in categorized_assets["resurrect"][0].labels} == { - "GeographicalRegion", - "non-historic", - } - assert categorized_assets["resurrect"][0].metadata["active"] == "true" - - -def generate_remove_non_existing_labels_test_data(): - labels = LabelDefinitionList([LabelDefinition(external_id="historic", name="historic")]) - assets = Asset(external_id="office1", name="Office 1") - yield pytest.param(labels, [assets], [assets], id="Asset without label") - - -@pytest.mark.parametrize("cdf_labels, assets, expected_assets", list(generate_remove_non_existing_labels_test_data())) -def test_remove_non_existing_labels(cdf_labels: LabelDefinitionList, assets: AssetLike, expected_assets: AssetLike): - # Arrange - with monkeypatch_cognite_client() as client: - client.labels.list.return_value = cdf_labels - - # Act - actual_assets = remove_non_existing_labels(client, assets) - - # Assert - assert actual_assets == expected_assets - - -def test_neat_metadata_keys_load(): - # Arrange - input_data = {"start_time": "beginning_time", "invalid_keys": "not valid"} - expected = NeatMetadataKeys(start_time="beginning_time") - - # Act - actual = NeatMetadataKeys.load(input_data) - - # Arrange - assert actual == expected - - -def test_neat_metadata_keys_alias(): - # Arrange - keys = NeatMetadataKeys(type="category") - expected = dict( - start_time="start_time", - end_time="end_time", - update_time="update_time", - resurrection_time="resurrection_time", - identifier="identifier", - active="active", - type="category", - ) - # Act - aliases = keys.as_aliases() - - # Assert - assert aliases == expected - - -def test_assets_to_update(mock_rdf_assets, mock_cdf_assets): - rdf_assets = mock_rdf_assets - rdf_asset_ids = set(rdf_assets.keys()) - cdf_assets = mock_cdf_assets - - rdf_assets["Terminal-0"]["parent_external_id"] = "Substation-0" - cdf_assets["Terminal-0"]["parent_external_id"] = "Substation-1" - cdf_assets_df = pd.DataFrame.from_records([asset for asset in cdf_assets.values()]) - - assets, report = _assets_to_update( - rdf_assets=rdf_assets, cdf_assets=cdf_assets_df, asset_ids=rdf_asset_ids, meta_keys=NeatMetadataKeys() - ) - - expected_report = { - "Terminal-0": { - "values_changed": {"root['parent_external_id']": {"new_value": "Substation-0", "old_value": "Substation-1"}} - } - } - - assert report == expected_report - assert len(assets) == 1 diff --git a/tests/tests_legacy/graph/loaders/core/test_rdf_to_relationships.py b/tests/tests_legacy/graph/loaders/core/test_rdf_to_relationships.py deleted file mode 100644 index 8975a5eb9..000000000 --- a/tests/tests_legacy/graph/loaders/core/test_rdf_to_relationships.py +++ /dev/null @@ -1,97 +0,0 @@ -from copy import deepcopy - -from cognite.client.data_classes import Asset, AssetList, Label, LabelFilter, Relationship, RelationshipList -from cognite.client.testing import monkeypatch_cognite_client - -from cognite.neat.legacy.graph.loaders.core.rdf_to_assets import rdf2assets -from cognite.neat.legacy.graph.loaders.core.rdf_to_relationships import categorize_relationships, rdf2relationships -from cognite.neat.legacy.rules.exporters._core.rules2labels import get_labels - - -def test_relationship_diffing(mock_knowledge_graph, transformation_rules): - data_set_id = 123456 - cdf_relationship = rdf2relationships(mock_knowledge_graph, transformation_rules, data_set_id=data_set_id) - - # Categorize ids - create_id = "SubGeographicalRegion-0:GeographicalRegion-0" - resurrect_id = "GeographicalRegion-0:RootCIMNode-0" - decommission_id = { - "Substation-0:Terminal-0", - "Terminal-0:Substation-0", - "Substation-0:Terminal-1", - "Terminal-1:Substation-0", - } - - # Resurrect - cdf_relationship.loc[cdf_relationship.external_id == resurrect_id, ["labels"]] = ["historic"] - - # Create - ind = cdf_relationship[cdf_relationship.external_id == create_id].index - cdf_relationship.drop(ind, inplace=True) - - # Here we are setting an asset to be historic, and accordingly we expect to see - # two relationships being decommissioned, one for each direction. - non_historic_assets = rdf2assets(mock_knowledge_graph, transformation_rules, data_set_id=data_set_id) - historic_assets = {"Terminal-0": deepcopy(non_historic_assets["Terminal-0"])} - historic_assets["Terminal-0"]["labels"] = ["historic"] - non_historic_assets.pop("Terminal-0") - - with monkeypatch_cognite_client() as client_mock: - - def list_relationships(data_set_ids: int = data_set_id, limit: int = -1, labels=None, **_): - labels = labels or LabelFilter(contains_any=["non-historic"]) - if labels == LabelFilter(contains_any=["non-historic"]): - return RelationshipList( - [Relationship(**row) for _, row in cdf_relationship.iterrows() if "non-historic" in row.labels] - ) - elif labels == LabelFilter(contains_any=["historic"]): - return RelationshipList( - [Relationship(**row) for _, row in cdf_relationship.iterrows() if "historic" in row.labels] - ) - else: - return None - - def list_assets(data_set_ids: int = data_set_id, limit: int = -1, labels=None, **_): - historic_asset_list = AssetList([Asset(**asset) for asset in historic_assets.values()]) - non_historic_asset_list = AssetList([Asset(**asset) for asset in non_historic_assets.values()]) - if labels == LabelFilter(contains_any=["non-historic"]): - return non_historic_asset_list - elif labels == LabelFilter(contains_any=["historic"]): - return historic_asset_list - else: - return historic_asset_list + non_historic_asset_list - - def list_labels(**_): - label_names = [*list(get_labels(transformation_rules)), "non-historic", "historic"] - return [Label(external_id=label_name, name=label_names) for label_name in label_names] - - client_mock.labels.list = list_labels - - client_mock.relationships.list = list_relationships - client_mock.assets.list = list_assets - - # Removing object from graph which should trigger decommissioning of relationships - mock_knowledge_graph.graph.remove((transformation_rules.metadata.namespace["Terminal-1"], None, None)) - rdf_relationships = rdf2relationships(mock_knowledge_graph, transformation_rules, data_set_id=data_set_id) - - categorized_relationships, report = categorize_relationships( - client=client_mock, - rdf_relationships=rdf_relationships, - data_set_id=data_set_id, - return_report=True, - ) - - assert len(categorized_relationships["create"]) == 1 - assert len(report["create"]) == 1 - assert create_id == categorized_relationships["create"][0].external_id - assert create_id in report["create"] - - assert len(categorized_relationships["resurrect"]) == 1 - assert len(report["resurrect"]) == 1 - assert resurrect_id == categorized_relationships["resurrect"][0]._external_id - assert resurrect_id in report["resurrect"] - - assert len(categorized_relationships["decommission"]) == 4 - assert len(report["decommission"]) == 4 - assert decommission_id == {relation._external_id for relation in categorized_relationships["decommission"]} - assert decommission_id == report["decommission"] diff --git a/tests/tests_legacy/graph/loaders/test_asset_loader.py b/tests/tests_legacy/graph/loaders/test_asset_loader.py deleted file mode 100644 index 204a6624b..000000000 --- a/tests/tests_legacy/graph/loaders/test_asset_loader.py +++ /dev/null @@ -1,68 +0,0 @@ -from typing import Any - -from freezegun import freeze_time -from rdflib import Graph - -from cognite.neat.legacy.graph.loaders import AssetLoader -from cognite.neat.legacy.graph.loaders.core.rdf_to_assets import rdf2assets -from cognite.neat.legacy.graph.loaders.core.rdf_to_relationships import rdf2relationships -from cognite.neat.legacy.graph.stores import MemoryStore -from cognite.neat.legacy.rules.models import Rules - - -class TestAssetLoader: - @freeze_time("2024-01-01") - def test_vs_existing_rdf2assets(self, transformation_rules: Rules, solution_knowledge_graph: Graph): - store = MemoryStore(solution_knowledge_graph) - loader = AssetLoader(transformation_rules, store, data_set_id=123456, always_store_in_metadata=True) - - loaded = list(loader.load_assets(stop_on_exception=False)) - - expected_assets = rdf2assets(store, transformation_rules, data_set_id=123456, stop_on_exception=False) - - # Need some extra processing to get the same format as expected_assets - actual_dumped: dict[str, dict[str, Any]] = {} - for asset in loaded: - dumped = asset.dump(camel_case=False) - if asset.labels: - dumped["labels"] = [label.external_id for label in asset.labels] - if "description" not in dumped: - dumped["description"] = None - if "parent_external_id" not in dumped: - dumped["parent_external_id"] = None - actual_dumped[asset.external_id] = dumped - - missing = set(expected_assets.keys()) - set(actual_dumped.keys()) - assert not missing, f"Missing {missing}" - extra = set(actual_dumped.keys()) - set(expected_assets.keys()) - assert not extra, f"Extra {extra}" - - for external_id, expected_asset in expected_assets.items(): - # Splitting this up to get diff output - actual_asset = actual_dumped[external_id] - assert actual_asset == expected_asset - - @freeze_time("2024-01-01") - def test_vs_existing_rdf2relationships(self, transformation_rules: Rules, solution_knowledge_graph: Graph): - store = MemoryStore(solution_knowledge_graph) - loader = AssetLoader(transformation_rules, store, data_set_id=123456) - - loaded = list(loader.load_relationships(stop_on_exception=False)) - - relationship_df = rdf2relationships(store, transformation_rules, data_set_id=123456, stop_on_exception=False) - - # Need some extra processing to get the same format as expected_relationships - expected_relationships = { - relationship["external_id"]: relationship for relationship in relationship_df.to_dict(orient="records") - } - actual_dumped: dict[str, dict[str, Any]] = {} - for relationship in loaded: - dumped = relationship.dump(camel_case=False) - if relationship.labels: - dumped["labels"] = [label.external_id for label in relationship.labels] - actual_dumped[relationship.external_id] = dumped - - missing = set(expected_relationships.keys()) - set(actual_dumped.keys()) - assert not missing, f"Missing {missing}" - extra = set(actual_dumped.keys()) - set(expected_relationships.keys()) - assert not extra, f"Extra {extra}" diff --git a/tests/tests_legacy/graph/loaders/test_rdf_to_dms.py b/tests/tests_legacy/graph/loaders/test_rdf_to_dms.py deleted file mode 100644 index 23f862985..000000000 --- a/tests/tests_legacy/graph/loaders/test_rdf_to_dms.py +++ /dev/null @@ -1,76 +0,0 @@ -import pytest -from rdflib import URIRef - -from cognite.neat.legacy.graph import loaders - - -@pytest.mark.skip("Are not updated as they will be deleted soon") -def test_rdf2nodes_and_edges(small_graph, simple_rules): - loader = loaders.DMSLoader(simple_rules, small_graph) - nodes, edges, exceptions = loader.as_nodes_and_edges() - - assert exceptions == [] - assert len(nodes) == 13 - assert len(edges) == 24 - - -@pytest.mark.skip("Are not updated as they will be deleted soon") -def test_rdf2nodes_and_edges_raise_exception(small_graph, simple_rules): - small_graph.graph.remove( - (URIRef("http://purl.org/cognite/neat#Nordics"), URIRef("http://purl.org/cognite/neat#name"), None) - ) - - # this will basically remove instance all together since there are no triples - # left to define this instance - small_graph.graph.remove( - (URIRef("http://purl.org/cognite/neat#Nordics.Norway.NO1"), URIRef("http://purl.org/cognite/neat#name"), None) - ) - - loader = loaders.DMSLoader(simple_rules, small_graph) - nodes, edges, exceptions = loader.as_nodes_and_edges() - - assert len(nodes) == 11 - assert len(edges) == 21 - assert len(exceptions) == 1 - assert [e["type"] for e in exceptions] == ["PropertyRequiredButNotProvided"] - - -@pytest.mark.skip("Are not updated as they will be deleted soon") -def test_add_class_prefix_to_external_ids(simple_rules, graph_with_numeric_ids): - loader = loaders.DMSLoader(simple_rules, graph_with_numeric_ids, add_class_prefix=True) - nodes, edges, exceptions = loader.as_nodes_and_edges() - - # Needs this as order of end nodes is not guaranteed - start_node_xid = set() - end_node_xid = set() - for edge in edges: - start_node_xid.add(edge.start_node.external_id) - end_node_xid.add(edge.end_node.external_id) - - assert exceptions == [] - assert len(nodes) == 1 - assert len(edges) == 2 - assert nodes[0].external_id == "PriceAreaConnection_1" - assert start_node_xid == {"PriceAreaConnection_1"} - assert end_node_xid == {"PriceArea_2", "PriceArea_3"} - - -@pytest.mark.skip("Are not updated as they will be deleted soon") -def test_rdf2nodes_property_date(graph_with_date, transformation_rules_date): - loader = loaders.DMSLoader(transformation_rules_date, graph_with_date) - nodes, edges, exceptions = loader.as_nodes_and_edges() - - assert exceptions == [] - assert len(nodes) == 1 - assert len(edges) == 2 - assert nodes[0].sources[0].properties["endDate"] == "2020-01-01" - - -def test_multi_namespace_rules(nordic44_inferred_rules, source_knowledge_graph): - source_knowledge_graph.graph.bind( - nordic44_inferred_rules.metadata.prefix, nordic44_inferred_rules.metadata.namespace - ) - loader = loaders.DMSLoader(nordic44_inferred_rules, source_knowledge_graph, add_class_prefix=True) - nodes, _, _ = loader.as_nodes_and_edges() - - assert len(nodes) == 493 diff --git a/tests/tests_legacy/graph/loaders/test_validator.py b/tests/tests_legacy/graph/loaders/test_validator.py deleted file mode 100644 index ef7cbc060..000000000 --- a/tests/tests_legacy/graph/loaders/test_validator.py +++ /dev/null @@ -1,29 +0,0 @@ -from cognite.neat.legacy.graph.loaders.core.rdf_to_assets import rdf2assets -from cognite.neat.legacy.graph.loaders.validator import validate_asset_hierarchy -from cognite.neat.legacy.graph.stores import MemoryStore - - -def test_orphan_assets(transformation_rules, solution_knowledge_graph): - assets = rdf2assets(MemoryStore(solution_knowledge_graph), transformation_rules, data_set_id=123456) - - asset_external_ids = [asset.get("external_id") for asset in assets.values()] - - # Create an orphan asset - assets[asset_external_ids[0]]["parent_external_id"] = "DisappearingParent" - - orphan_assets, _, _ = validate_asset_hierarchy(assets) - - assert len(orphan_assets) == 1 - assert orphan_assets[0] == asset_external_ids[0] - - -def test_circular_assets(transformation_rules, solution_knowledge_graph): - assets = rdf2assets(MemoryStore(solution_knowledge_graph), transformation_rules, data_set_id=123456) - - # Create a circular asset hierarchy - assets["f176960e-9aeb-11e5-91da-b8763fd99c5f"]["parent_external_id"] = "2dd9017c-bdfb-11e5-94fa-c8f73332c8f4" - assets["2dd9017c-bdfb-11e5-94fa-c8f73332c8f4"]["parent_external_id"] = "f176960e-9aeb-11e5-91da-b8763fd99c5f" - - _, circular_assets, _ = validate_asset_hierarchy(assets) - - assert len(circular_assets) == 8 diff --git a/tests/tests_legacy/graph/transformations/__init__.py b/tests/tests_legacy/graph/transformations/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/tests_legacy/graph/transformations/query_generator/__init__.py b/tests/tests_legacy/graph/transformations/query_generator/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/tests_legacy/graph/transformations/query_generator/test_sparql.py b/tests/tests_legacy/graph/transformations/query_generator/test_sparql.py deleted file mode 100644 index 290263852..000000000 --- a/tests/tests_legacy/graph/transformations/query_generator/test_sparql.py +++ /dev/null @@ -1,24 +0,0 @@ -import pandas as pd -from rdflib import Namespace - -from cognite.neat.constants import get_default_prefixes -from cognite.neat.legacy.graph.stores import NeatGraphStoreBase -from cognite.neat.legacy.graph.transformations.query_generator.sparql import ( - build_sparql_query, -) - - -def test_graph_traversal(source_knowledge_graph: NeatGraphStoreBase): - # Arrange - graph = source_knowledge_graph.get_graph() - rule = "cim:ACLineSegment->cim:BaseVoltage(cim:BaseVoltage.nominalVoltage)" - prefixes = get_default_prefixes() - prefixes["cim"] = Namespace("http://iec.ch/TC57/2013/CIM-schema-cim16#") - - # Act - query = build_sparql_query(graph, rule, prefixes) - - # Assert - df = pd.DataFrame(list(graph.query(query))) - df.rename(columns={0: "subject", 1: "predicate", 2: "object"}, inplace=True) - assert str(df.subject[0]) == "http://purl.org/nordic44#_f1769b90-9aeb-11e5-91da-b8763fd99c5f" diff --git a/tests/tests_legacy/graph/transformations/test_alternative_property.py b/tests/tests_legacy/graph/transformations/test_alternative_property.py deleted file mode 100644 index 708bcf0af..000000000 --- a/tests/tests_legacy/graph/transformations/test_alternative_property.py +++ /dev/null @@ -1,72 +0,0 @@ -from rdflib import Literal - -from cognite.neat.legacy.graph.loaders.core.rdf_to_assets import rdf2assets -from cognite.neat.legacy.graph.stores import MemoryStore -from cognite.neat.legacy.graph.transformations.transformer import domain2app_knowledge_graph - - -def test_missing_name(source_knowledge_graph, transformation_rules): - no_name_id = "2dd9017c-bdfb-11e5-94fa-c8f73332c8f4" - - # removing the name from the terminal with id 2dd9017c-bdfb-11e5-94fa-c8f73332c8f4 - # this will cause the name to be set to the terminal id - source_knowledge_graph.graph.remove( - ( - source_knowledge_graph.namespace[f"_{no_name_id}"], - transformation_rules.prefixes["cim"]["IdentifiedObject.name"], - None, - ) - ) - - tnt_knowledge_graph = domain2app_knowledge_graph(source_knowledge_graph, transformation_rules) - - assets = rdf2assets(MemoryStore(tnt_knowledge_graph), transformation_rules, data_set_id=123456) - - assert assets[no_name_id]["name"] == no_name_id - - -def test_alias_name(source_knowledge_graph, transformation_rules): - only_alias_name_id = "2dd903a3-bdfb-11e5-94fa-c8f73332c8f4" - - # removing the name from the terminal with id 2dd903a3-bdfb-11e5-94fa-c8f73332c8f4 - # and adding alias name which is used as alternative property for name - source_knowledge_graph.graph.add( - ( - source_knowledge_graph.namespace[f"_{only_alias_name_id}"], - transformation_rules.prefixes["cim"]["IdentifiedObject.aliasName"], - Literal("Terminal Alias Name"), - ) - ) - source_knowledge_graph.graph.remove( - ( - source_knowledge_graph.namespace[f"_{only_alias_name_id}"], - transformation_rules.prefixes["cim"]["IdentifiedObject.name"], - None, - ) - ) - - tnt_knowledge_graph = domain2app_knowledge_graph(source_knowledge_graph, transformation_rules) - - assets = rdf2assets(MemoryStore(tnt_knowledge_graph), transformation_rules, data_set_id=123456) - - assert assets[only_alias_name_id]["name"] == "Terminal Alias Name" - - -def test_all_name_property(source_knowledge_graph, transformation_rules): - all_names_id = "2dd903a0-bdfb-11e5-94fa-c8f73332c8f4" - - # adding alias name property for terminal - source_knowledge_graph.graph.add( - ( - source_knowledge_graph.namespace[f"_{all_names_id}"], - transformation_rules.prefixes["cim"]["IdentifiedObject.aliasName"], - Literal("Terminal Alias Name"), - ) - ) - - tnt_knowledge_graph = domain2app_knowledge_graph(source_knowledge_graph, transformation_rules) - - assets = rdf2assets(MemoryStore(tnt_knowledge_graph), transformation_rules, data_set_id=123456) - - assert assets[all_names_id]["name"] == "T2" - assert assets[all_names_id]["metadata"]["IdentifiedObject.aliasName"] == "Terminal Alias Name" diff --git a/tests/tests_legacy/graph/transformations/test_transformer.py b/tests/tests_legacy/graph/transformations/test_transformer.py deleted file mode 100644 index 67e07b813..000000000 --- a/tests/tests_legacy/graph/transformations/test_transformer.py +++ /dev/null @@ -1,57 +0,0 @@ -import pandas as pd -from cognite.client.testing import monkeypatch_cognite_client -from rdflib import Graph, Namespace - -from cognite.neat.legacy.graph.transformations.transformer import ( - domain2app_knowledge_graph, -) -from cognite.neat.legacy.rules.models.rdfpath import TransformationRuleType -from cognite.neat.legacy.rules.models.rules import Rules - - -def test_domain2app_knowledge_graph(transformation_rules: Rules, source_knowledge_graph: Graph): - # Arrange - rules = transformation_rules - domain_graph = source_knowledge_graph - - # Act - app_graph = domain2app_knowledge_graph(domain_graph, rules) - - # Assert - res = app_graph.query( - """SELECT ?o WHERE { rdf:type ?o}""" - ) - assert next(iter(res))[0] == Namespace("http://purl.org/cognite/simplecim#").Substation - - -def test_domain2app_knowledge_graph_raw_lookup(transformation_rules: Rules, source_knowledge_graph: Graph): - # Arrange - rules = transformation_rules.model_copy(deep=True) - rules.properties["row 18"].rule += " | TerminalName(NordicName, SimpleCIMName)" - rules.properties["row 18"].rule_type = TransformationRuleType.rawlookup - domain_graph = source_knowledge_graph - database_name = "look_up" - - # Act - with monkeypatch_cognite_client() as client_mock: - - def mock_retrieve(db_name, table_name, **_): - if db_name == database_name and table_name == "TerminalName": - return pd.DataFrame([{"NordicName": "ARENDAL 300 A T1", "SimpleCIMName": "Gjerstad"}]) - - client_mock.raw.rows.retrieve_dataframe = mock_retrieve - app_graph = domain2app_knowledge_graph( - domain_graph, rules, client=client_mock, cdf_lookup_database=database_name - ) - - Gjerstad_node = app_graph.query( - "SELECT ?o WHERE { " - "simplecim:IdentifiedObject.name ?o}" - ) - - NaN_node = app_graph.query( - "SELECT ?o WHERE { " - "simplecim:IdentifiedObject.name ?o}" - ) - assert next(iter(Gjerstad_node))[0].value == "Gjerstad" - assert next(iter(NaN_node))[0].value == "NaN" diff --git a/tests/tests_legacy/test_loader.py b/tests/tests_legacy/test_loader.py deleted file mode 100644 index cb6832fd3..000000000 --- a/tests/tests_legacy/test_loader.py +++ /dev/null @@ -1,27 +0,0 @@ -import filecmp -from pathlib import Path - -from cognite.neat.config import Config, copy_examples_to_directory -from cognite.neat.constants import EXAMPLE_GRAPHS, EXAMPLE_RULES, EXAMPLE_WORKFLOWS -from cognite.neat.legacy.rules.models.rules import Rules - - -def test_load_excel(transformation_rules: Rules): - assert transformation_rules - - -def test_copy_examples_to_directory(tmp_path: Path): - target_path = tmp_path / "data" - config = Config( - data_store_path=target_path, - ) - copy_examples_to_directory(config) - - rapport = filecmp.dircmp(config.rules_store_path, EXAMPLE_RULES) - assert not rapport.diff_files - - rapport = filecmp.dircmp(config.source_graph_path, EXAMPLE_GRAPHS) - assert not rapport.diff_files - - rapport = filecmp.dircmp(config.workflows_store_path, EXAMPLE_WORKFLOWS) - assert not rapport.diff_files diff --git a/tests/tests_legacy/tests_rules/__init__.py b/tests/tests_legacy/tests_rules/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/tests_legacy/tests_rules/exporter/__init__.py b/tests/tests_legacy/tests_rules/exporter/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/tests_legacy/tests_rules/exporter/test_rules2dms.py b/tests/tests_legacy/tests_rules/exporter/test_rules2dms.py deleted file mode 100644 index 2d509e648..000000000 --- a/tests/tests_legacy/tests_rules/exporter/test_rules2dms.py +++ /dev/null @@ -1,223 +0,0 @@ -import sys -from datetime import datetime - -import pytest -from rdflib import Namespace - -from cognite.neat.legacy.rules.exceptions import EntitiesContainNonDMSCompliantCharacters -from cognite.neat.legacy.rules.exporters._rules2dms import DMSSchemaComponents -from cognite.neat.legacy.rules.models._base import ParentClass -from cognite.neat.legacy.rules.models.rules import Class, Metadata, Property, Rules - -if sys.version_info < (3, 11): - from exceptiongroup import ExceptionGroup - - -def test_rules2dms_single_space(simple_rules): - data_model = DMSSchemaComponents.from_rules(rules=simple_rules) - - assert len(data_model.containers) == 4 - assert len(data_model.views) == 4 - assert list(data_model.views.keys()) == [ - "neat:CountryGroup", - "neat:Country", - "neat:PriceArea", - "neat:PriceAreaConnection", - ] - assert list(data_model.containers.keys()) == [ - "neat:CountryGroup", - "neat:Country", - "neat:PriceArea", - "neat:PriceAreaConnection", - ] - assert data_model.version == "0.1" - assert data_model.space == "neat" - assert data_model.external_id == "playground_model" - - -def test_rules2dms_multi_space(): - metadata = Metadata( - name="Dummy Data Model", - description="A description", - version="0.1", - creator="Cognite", - created=datetime.utcnow(), - namespace=Namespace("http://purl.org/cognite/neat#"), - prefix="neat", - ) - classes = { - "DummyClass": Class(class_id="DummyClass", description="A description", parent_class="mega:DummyClass"), - "EmptyClass": Class(class_id="EmptyClass"), - } - properties = { - "dummyProperty": Property( - class_id="DummyClass2", - property_id="dummyProperty", - expected_value_type="string", - max_count=1, - container="outerSpace:DummyClass", - ) - } - - # Act - rules = Rules(metadata=metadata, classes=classes, properties=properties, prefixes={}, instances=[]) - - data_model = DMSSchemaComponents.from_rules(rules=rules) - assert len(data_model.containers) == 1 - assert len(data_model.views) == 2 - assert list(data_model.views.keys()) == [ - "neat:DummyClass", - "neat:DummyClass2", - ] - assert list(data_model.containers.keys()) == [ - "outerSpace:DummyClass", - ] - - assert data_model.views["neat:DummyClass"].implements == [ - ParentClass.from_string(entity_string="mega:DummyClass(version=0.1)").view_id - ] - - -def test_raise_error10(transformation_rules): - with pytest.raises(EntitiesContainNonDMSCompliantCharacters): - _ = DMSSchemaComponents.from_rules(rules=transformation_rules) - - -def test_expected_value_type_cdf_resources(): - metadata = Metadata( - name="Dummy Data Model", - description="A description", - version="0.1", - creator="Cognite", - created=datetime.utcnow(), - namespace=Namespace("http://purl.org/cognite/neat#"), - prefix="neat", - ) - classes = { - "DummyClass": Class(class_id="DummyClass", description="A description", parent_class="mega:DummyClass"), - "EmptyClass": Class(class_id="EmptyClass"), - } - properties = { - "dummyTimeseries": Property( - class_id="DummyClass", - property_id="dummyTimeseries", - expected_value_type="timeseries", - max_count=1, - ), - "dummyFile": Property( - class_id="DummyClass", - property_id="dummyFile", - expected_value_type="file", - max_count=1, - ), - "dummySequence": Property( - class_id="DummyClass", - property_id="dummySequence", - expected_value_type="sequence", - max_count=1, - ), - "dummyJson": Property( - class_id="DummyClass", - property_id="dummyJson", - expected_value_type="json", - max_count=1, - ), - } - - # Act - rules = Rules(metadata=metadata, classes=classes, properties=properties, prefixes={}, instances=[]) - dms_schema = DMSSchemaComponents.from_rules(rules=rules) - - assert ( - type(dms_schema.containers["neat:DummyClass"].properties["dummyTimeseries"].type).__name__ - == "TimeSeriesReference" - ) - assert type(dms_schema.containers["neat:DummyClass"].properties["dummyFile"].type).__name__ == "FileReference" - assert ( - type(dms_schema.containers["neat:DummyClass"].properties["dummySequence"].type).__name__ == "SequenceReference" - ) - assert type(dms_schema.containers["neat:DummyClass"].properties["dummyJson"].type).__name__ == "Json" - - -def test_raise_container_error(): - metadata = Metadata( - name="Dummy Data Model", - description="A description", - version="0.1", - creator="Cognite", - created=datetime.utcnow(), - namespace=Namespace("http://purl.org/cognite/neat#"), - prefix="neat", - ) - classes = { - "DummyClass": Class(class_id="DummyClass", description="A description", parent_class="mega:DummyClass"), - "EmptyClass": Class(class_id="EmptyClass"), - } - properties = { - "dummyProperty": Property( - class_id="DummyClass2", - property_id="dummyProperty", - expected_value_type="string", - max_count=1, - container="outerSpace:DummyClass", - ), - "dummyProperty2": Property( - class_id="DummyClass2", - property_id="dummyProperty", - expected_value_type="float", - max_count=1, - container="outerSpace:DummyClass", - ), - } - - # Act - rules = Rules(metadata=metadata, classes=classes, properties=properties, prefixes={}, instances=[]) - - with pytest.raises(ExceptionGroup) as exc_info: - _ = DMSSchemaComponents.from_rules(rules=rules) - assert exc_info.value.message == "Properties value types have been redefined! This is prohibited! Aborting!" - assert "Container outerSpace:DummyClass property dummyProperty value type" in exc_info.value.exceptions[0].message - - -def test_raise_view_error(): - metadata = Metadata( - name="Dummy Data Model", - description="A description", - version="0.1", - creator="Cognite", - created=datetime.utcnow(), - namespace=Namespace("http://purl.org/cognite/neat#"), - prefix="neat", - ) - classes = { - "DummyClass": Class(class_id="DummyClass", description="A description", parent_class="mega:DummyClass"), - "EmptyClass": Class(class_id="EmptyClass"), - } - properties = { - "dummyProperty": Property( - class_id="DummyClass2", - property_id="dummyProperty", - expected_value_type="string", - max_count=1, - container="outerSpace:DummyClass", - ), - "dummyProperty2": Property( - class_id="DummyClass2", - property_id="dummyProperty", - expected_value_type="float", - max_count=1, - container="outerSpace:DummyClass", - container_property="dummyProperty2", - ), - } - - # Act - rules = Rules(metadata=metadata, classes=classes, properties=properties, prefixes={}, instances=[]) - - with pytest.raises(ExceptionGroup) as exc_info: - _ = DMSSchemaComponents.from_rules(rules=rules) - assert exc_info.value.message == "View properties have been redefined! This is prohibited! Aborting!" - assert ( - "View neat:DummyClass2 property dummyProperty has been redefined in the same view" - in exc_info.value.exceptions[0].message - ) diff --git a/tests/tests_legacy/tests_rules/exporter/test_rules2excel.py b/tests/tests_legacy/tests_rules/exporter/test_rules2excel.py deleted file mode 100644 index 0469181d0..000000000 --- a/tests/tests_legacy/tests_rules/exporter/test_rules2excel.py +++ /dev/null @@ -1,16 +0,0 @@ -from pathlib import Path - -from cognite.neat.legacy.rules.exporters import ExcelExporter -from cognite.neat.legacy.rules.importers import ExcelImporter - - -def test_rules2excel(simple_rules, tmp_path: Path) -> None: - file = tmp_path / "rules.xlsx" - - ExcelExporter(rules=simple_rules).export_to_file(filepath=file) - - importer = ExcelImporter(filepath=file).to_rules() - - assert len(importer.classes) == len(simple_rules.classes) - assert len(importer.properties) == len(simple_rules.properties) - assert importer.properties.model_dump() == simple_rules.properties.model_dump() diff --git a/tests/tests_legacy/tests_rules/exporter/test_rules2graphql.py b/tests/tests_legacy/tests_rules/exporter/test_rules2graphql.py deleted file mode 100644 index 7d4b3c71f..000000000 --- a/tests/tests_legacy/tests_rules/exporter/test_rules2graphql.py +++ /dev/null @@ -1,18 +0,0 @@ -from pathlib import Path - -import pytest - -from cognite.neat.legacy.rules.exceptions import EntitiesContainNonDMSCompliantCharacters -from cognite.neat.legacy.rules.exporters._rules2graphql import GraphQLSchemaExporter - - -def test_rules2graphql(simple_rules, grid_graphql_schema, tmp_path: Path): - filepath = tmp_path / "test.graphql" - GraphQLSchemaExporter(rules=simple_rules).export_to_file(filepath) - assert filepath.read_text() == grid_graphql_schema - - -def test_raise_error10(transformation_rules, tmp_path: Path): - filepath = tmp_path / "test.graphql" - with pytest.raises(EntitiesContainNonDMSCompliantCharacters): - GraphQLSchemaExporter(rules=transformation_rules).export_to_file(filepath=filepath) diff --git a/tests/tests_legacy/tests_rules/exporter/test_rules2ontology.py b/tests/tests_legacy/tests_rules/exporter/test_rules2ontology.py deleted file mode 100644 index bb58ce944..000000000 --- a/tests/tests_legacy/tests_rules/exporter/test_rules2ontology.py +++ /dev/null @@ -1,51 +0,0 @@ -from copy import deepcopy -from pathlib import Path - -import pytest -from rdflib import Graph - -from cognite.neat.legacy.rules.exceptions import PropertiesDefinedMultipleTimes -from cognite.neat.legacy.rules.exporters import OWLExporter, SemanticDataModelExporter, SHACLExporter - - -def test_rules2owl(transformation_rules, tmp_path): - filepath = tmp_path / "test.ttl" - OWLExporter(rules=transformation_rules).export_to_file(filepath) - - graph = Graph().parse(filepath) - graph.bind("owl", "http://www.w3.org/2002/07/owl#") - graph.bind("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#") - - # we have 7 classes in the ontology - assert len(graph.query("SELECT ?s WHERE {?s rdf:type owl:Class Filter (!isBlank(?s))}")) == 7 - - -def test_rules2shacl(transformation_rules, tmp_path: Path): - filepath = tmp_path / "test.ttl" - SHACLExporter(rules=transformation_rules).export_to_file(filepath=filepath) - graph = Graph().parse(filepath) - graph.bind("owl", "http://www.w3.org/2002/07/owl#") - graph.bind("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#") - # however we have 6 node shapes since for one class there are no properties defined - assert len(graph.query("SELECT ?s WHERE {?s rdf:type sh:NodeShape Filter (!isBlank(?s))}")) == 6 - - -def test_rules2semantic_model(transformation_rules, tmp_path: Path): - filepath = tmp_path / "test.ttl" - SemanticDataModelExporter(rules=transformation_rules).export_to_file(filepath=filepath) - - graph = Graph().parse(filepath) - graph.bind("owl", "http://www.w3.org/2002/07/owl#") - graph.bind("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#") - - assert len(graph.query("SELECT ?s WHERE {?s rdf:type sh:NodeShape Filter (!isBlank(?s))}")) == 6 - assert len(graph.query("SELECT ?s WHERE {?s rdf:type owl:Class Filter (!isBlank(?s))}")) == 7 - - -def test_rules2ontology_raise(transformation_rules, tmp_path: Path): - filepath = tmp_path / "test.ttl" - rules = deepcopy(transformation_rules) - rules.properties["row 150"] = rules.properties["row 15"] - - with pytest.raises(PropertiesDefinedMultipleTimes): - OWLExporter(rules=rules).export_to_file(filepath=filepath) diff --git a/tests/tests_legacy/tests_rules/exporter/test_rules2pydantic_models.py b/tests/tests_legacy/tests_rules/exporter/test_rules2pydantic_models.py deleted file mode 100644 index 014ab77e2..000000000 --- a/tests/tests_legacy/tests_rules/exporter/test_rules2pydantic_models.py +++ /dev/null @@ -1,68 +0,0 @@ -import pytest -from cognite.client.data_classes.data_modeling import DataModel as CogniteDataModel -from rdflib import URIRef -from yaml import safe_load - -from cognite.neat.legacy.rules import examples, exceptions -from cognite.neat.legacy.rules.exporters._rules2pydantic_models import ( - rules_to_pydantic_models, -) -from cognite.neat.legacy.rules.importers._dms2rules import DMSImporter - - -def test_rules2pydantic_models(dms_compliant_rules, source_knowledge_graph): - models = rules_to_pydantic_models(dms_compliant_rules, add_extra_fields=True) - - assert len(models) == 6 - assert set(models.keys()).issubset(set(dms_compliant_rules.classes.keys())) - - instance = models["Terminal"].from_graph( - source_knowledge_graph, - dms_compliant_rules, - URIRef("http://purl.org/nordic44#_2dd9019e-bdfb-11e5-94fa-c8f73332c8f4"), - ) - - assert instance.external_id == "2dd9019e-bdfb-11e5-94fa-c8f73332c8f4" - assert instance.name == "ARENDAL 300 A T1" - assert instance.class_to_asset_mapping == { - "metadata": ["mRID"], - "name": ["name", "aliasName"], - } - - asset = instance.to_asset(data_set_id=123456) - - assert asset.name == "ARENDAL 300 A T1" - assert asset.metadata["type"] == "Terminal" - - -def test_views2pydantic_models(dms_compliant_rules, source_knowledge_graph): - view = CogniteDataModel.load(safe_load(examples.power_grid_data_model.read_text())).views[3] - - rules = DMSImporter(views=[view]).to_rules( - validators_to_skip=[ - "properties_refer_existing_classes", - "is_type_defined_as_object", - ] - ) - - models = rules_to_pydantic_models(rules, add_extra_fields=True) - - assert len(models) == 1 - assert set(models.keys()).issubset({"Terminal"}) - - instance = models["Terminal"].from_graph( - source_knowledge_graph, - dms_compliant_rules, - URIRef("http://purl.org/nordic44#_2dd9019e-bdfb-11e5-94fa-c8f73332c8f4"), - ) - - assert instance.external_id == "2dd9019e-bdfb-11e5-94fa-c8f73332c8f4" - assert instance.name == "ARENDAL 300 A T1" - - node = instance.to_node() - assert node.external_id == "2dd9019e-bdfb-11e5-94fa-c8f73332c8f4" - assert node.space == "workshop" - - # there is no class_to_asset_mapping defined for this view so it should fail - with pytest.raises(exceptions.ClassToAssetMappingNotDefined): - _ = instance.to_asset(data_set_id=123456) diff --git a/tests/tests_legacy/tests_rules/exporter/test_rules2triples.py b/tests/tests_legacy/tests_rules/exporter/test_rules2triples.py deleted file mode 100644 index f806470fa..000000000 --- a/tests/tests_legacy/tests_rules/exporter/test_rules2triples.py +++ /dev/null @@ -1,5 +0,0 @@ -from cognite.neat.legacy.rules.models.rules import Rules - - -def test_extra_triples(transformation_rules: Rules): - assert len(transformation_rules.instances) == 11 diff --git a/tests/tests_legacy/tests_rules/importer/__init__.py b/tests/tests_legacy/tests_rules/importer/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/tests_legacy/tests_rules/importer/test_dms2tables.py b/tests/tests_legacy/tests_rules/importer/test_dms2tables.py deleted file mode 100644 index 4a051c185..000000000 --- a/tests/tests_legacy/tests_rules/importer/test_dms2tables.py +++ /dev/null @@ -1,39 +0,0 @@ -import pytest -from cognite.client.data_classes.data_modeling import DataModel, DataModelApply, View, ViewList -from yaml import safe_load - -from cognite.neat.legacy.rules import examples, importers -from cognite.neat.legacy.rules.models import rules - - -@pytest.fixture(scope="session") -def power_grid_rules() -> rules.Rules: - return importers.ExcelImporter(examples.power_grid_model).to_rules() - - -@pytest.fixture(scope="session") -def power_grid_data_model() -> DataModel[View]: - return DataModel.load(safe_load(examples.power_grid_data_model.read_text())) - - -@pytest.fixture(scope="session") -def power_grid_views(power_grid_data_model: DataModelApply) -> ViewList: - return ViewList(power_grid_data_model.views) - - -def test_import_information_model(power_grid_rules: rules.Rules, power_grid_views: ViewList) -> None: - # Arrange - # add views - dms_importer = importers.DMSImporter(power_grid_views) - - # Act - rules = dms_importer.to_rules() - - # Assert - assert sorted(rules.classes.keys()) == sorted(power_grid_rules.classes.keys()) - assert sorted((prop.class_id, prop.property_name) for prop in rules.properties.values()) == sorted( - [(prop.class_id, prop.property_name) for prop in power_grid_rules.properties.values()] - # The powerrules example lacks a circular dependency which was - # added to the data model to demonstrate the FDM capability - + [("GeographicalRegion", "subRegions")] - ) diff --git a/tests/tests_legacy/tests_rules/importer/test_excel2rules.py b/tests/tests_legacy/tests_rules/importer/test_excel2rules.py deleted file mode 100644 index 8da7ec80a..000000000 --- a/tests/tests_legacy/tests_rules/importer/test_excel2rules.py +++ /dev/null @@ -1,37 +0,0 @@ -import sys - -import pandas as pd -import pytest -from pydantic import ValidationError - -from cognite.neat.legacy.rules import importers -from cognite.neat.legacy.rules.models import Tables -from cognite.neat.legacy.rules.models.raw_rules import RawRules -from tests import config - -if sys.version_info < (3, 11): - pass - - -@pytest.fixture(scope="session") -def raw_rules() -> dict[str, pd.DataFrame]: - return importers.ExcelImporter(config.SIMPLECIM_TRANSFORMATION_RULES).to_raw_rules() - - -def test_raw_rules_validation(raw_rules): - assert raw_rules.to_rules() - - -def generate_invalid_raw_rules_test_data(): - raw_tables = importers.ExcelImporter(config.SIMPLECIM_TRANSFORMATION_RULES).to_tables() - - invalid_class_label = raw_tables - invalid_class_label[Tables.properties] = invalid_class_label[Tables.properties].copy() - invalid_class_label[Tables.properties].loc[0, "Class"] = "non existing class" - yield pytest.param(invalid_class_label, id="Invalid mapping rule") - - -@pytest.mark.parametrize("raw_tables", generate_invalid_raw_rules_test_data()) -def test_parse_transformation_invalid_rules(raw_tables: dict[str, pd.DataFrame]): - with pytest.raises(ValidationError): - RawRules.from_tables(raw_tables).to_rules() diff --git a/tests/tests_legacy/tests_rules/importer/test_graph2tables.py b/tests/tests_legacy/tests_rules/importer/test_graph2tables.py deleted file mode 100644 index 491b02eb1..000000000 --- a/tests/tests_legacy/tests_rules/importer/test_graph2tables.py +++ /dev/null @@ -1,10 +0,0 @@ -from cognite.neat.legacy.rules.importers._graph2rules import GraphImporter - - -def test_graph2tables_nordic44(source_knowledge_graph): - graph_importer = GraphImporter(graph=source_knowledge_graph.graph, max_number_of_instance=1) - rules, _, warnings = graph_importer.to_rules(return_report=True) - - assert len(rules.classes) == 59 - assert len(rules.properties) == 296 - assert "Substation" in rules.classes diff --git a/tests/tests_legacy/tests_rules/importer/test_json2tables.py b/tests/tests_legacy/tests_rules/importer/test_json2tables.py deleted file mode 100644 index 394115889..000000000 --- a/tests/tests_legacy/tests_rules/importer/test_json2tables.py +++ /dev/null @@ -1,24 +0,0 @@ -from cognite.neat.legacy.rules import importers -from tests import data - - -def test_json2rules_power_grid() -> None: - # Arrange - json_importer = importers.ArbitraryJSONImporter(data.POWER_GRID_JSON, "child-to-parent") - expected_properties = { - "GeographicalRegion": {"name"}, - "SubGraphicalRegion": {"name", "parent"}, - "Substation": {"name", "parent"}, - "Terminal": {"name", "aliasName", "parent"}, - } - - # Act - rules = json_importer.to_rules() - - # Assert - missing = set(rules.classes) - set(expected_properties) - assert not missing, f"JSONImporter did not find classes {missing}" - properties_by_class = rules.properties.groupby("class_id") - for class_name, expected in expected_properties.items(): - missing = expected - {prop.property_name for prop in properties_by_class.get(class_name, []).values()} - assert not missing, f"JSONImporter did not find properties {missing} for class {class_name}" diff --git a/tests/tests_legacy/tests_rules/importer/test_ontology2rules.py b/tests/tests_legacy/tests_rules/importer/test_ontology2rules.py deleted file mode 100644 index 07e18cc9a..000000000 --- a/tests/tests_legacy/tests_rules/importer/test_ontology2rules.py +++ /dev/null @@ -1,12 +0,0 @@ -from cognite.neat.legacy.rules.importers._owl2rules import OWLImporter -from cognite.neat.rules.examples import wind_energy_ontology - - -def test_owl2transformation_rules() -> None: - # Arrange - owl_importer = OWLImporter(wind_energy_ontology) - rules = owl_importer.to_rules(make_compliant=True) - - # Assert - assert str(rules.metadata.namespace) == "https://kg.cognite.ai/wind/" - assert len(rules.classes) == 71 diff --git a/tests/tests_legacy/tests_rules/test_analysis_rules_v1.py b/tests/tests_legacy/tests_rules/test_analysis_rules_v1.py deleted file mode 100644 index b4448d543..000000000 --- a/tests/tests_legacy/tests_rules/test_analysis_rules_v1.py +++ /dev/null @@ -1,29 +0,0 @@ -from cognite.neat.legacy.rules.analysis import ( - get_class_linkage, - get_connected_classes, - get_defined_classes, - get_disconnected_classes, - get_symmetric_pairs, -) - - -def test_rules_analysis(transformation_rules): - rules = transformation_rules - - defined_classes = { - "GeographicalRegion", - "Orphanage", - "RootCIMNode", - "SubGeographicalRegion", - "Substation", - "Terminal", - } - - assert get_defined_classes(rules) == defined_classes - assert get_disconnected_classes(rules) == {"Orphanage"} - - defined_classes.remove("Orphanage") - - assert get_connected_classes(rules) == defined_classes - assert get_symmetric_pairs(rules) == {("Substation", "Terminal"), ("Terminal", "Substation")} - assert len(get_class_linkage(rules)) == 5 diff --git a/tests/tests_legacy/tests_rules/test_models_old.py b/tests/tests_legacy/tests_rules/test_models_old.py deleted file mode 100644 index eacfeb73a..000000000 --- a/tests/tests_legacy/tests_rules/test_models_old.py +++ /dev/null @@ -1,43 +0,0 @@ -from datetime import datetime - -from rdflib import Namespace - -from cognite.neat.legacy.rules.models._base import ContainerEntity, ParentClass -from cognite.neat.legacy.rules.models.rules import Class, Metadata, Property, Rules -from cognite.neat.legacy.rules.models.value_types import XSD_VALUE_TYPE_MAPPINGS - - -def test_dummy_rules(): - metadata = Metadata( - name="Dummy Data Model", - description="A description", - version="0_1", - creator="Cognite", - created=datetime.utcnow(), - namespace=Namespace("http://purl.org/cognite/neat#"), - prefix="neat", - ) - classes = {"DummyClass": Class(class_id="DummyClass", description="A description", parent_class="mega:DummyClass")} - properties = { - "dummyProperty": Property( - class_id="DummyClass2", - property_id="dummyProperty", - expected_value_type="string", - max_count=1, - container="outerSpace:DummyClass", - ) - } - - # Act - rules = Rules(metadata=metadata, classes=classes, properties=properties, prefixes={}, instances=[]) - assert rules.metadata.name == "Dummy Data Model" - assert len(rules.classes) == 2 - assert rules.classes["DummyClass"].parent_class == [ - ParentClass.from_string(entity_string="mega:DummyClass(version=0_1)") - ] - assert rules.classes["DummyClass2"].class_id == "DummyClass2" - assert rules.properties["dummyProperty"].expected_value_type == XSD_VALUE_TYPE_MAPPINGS["string"] - assert rules.properties["dummyProperty"].container == ContainerEntity.from_string( - entity_string="outerSpace:DummyClass" - ) - assert rules.properties["dummyProperty"].container_property == "dummyProperty" diff --git a/tests/tests_legacy/tests_rules/test_rdfpath.py b/tests/tests_legacy/tests_rules/test_rdfpath.py deleted file mode 100644 index 59a8ecdfd..000000000 --- a/tests/tests_legacy/tests_rules/test_rdfpath.py +++ /dev/null @@ -1,348 +0,0 @@ -import pprint - -import pytest -from IPython.display import Markdown, display -from pydantic import ValidationError -from rdflib import Namespace - -from cognite.neat.legacy.graph.stores import MemoryStore -from cognite.neat.legacy.graph.transformations.query_generator import build_sparql_query -from cognite.neat.legacy.rules.models.rdfpath import ( - AllProperties, - AllReferences, - Entity, - Hop, - SingleProperty, - Step, - TransformationRuleType, - is_rawlookup, - is_rdfpath, - parse_rule, - parse_traversal, -) -from cognite.neat.legacy.rules.models.rules import Property -from tests import config - -nan = float("nan") - - -def generate_valid_property_test_data(): - yield pytest.param( - { - "Class": "GeographicalRegion", - "Description": "nan", - "Resource Type": "nan", - "Property": "*", - "Type": "nan", - "Rule Type": "rdfpath", - "Rule": "cim:GeographicalRegion(*)", - }, - id="Valid rdfpath rule", - ) - yield pytest.param( - { - "Class": "Terminal", - "Description": "nan", - "Resource Type": "nan", - "Property": "Terminal.Substation", - "Type": "nan", - "Rule Type": "rawlookup", - "Rule": "cim:Terminal->cim:ConnectivityNode->cim:VoltageLevel->cim:Substation | " - "TableName(Lookup, ValueColumn)", - }, - id="Valid rawlookup rule", - ) - - -@pytest.mark.parametrize("raw_input", generate_valid_property_test_data()) -def test_creating_valid_property(raw_input: dict): - assert Property(**raw_input) - - -def generate_invalid_property_test_data(): - yield pytest.param( - { - "Class": "GeographicalRegion", - "Description": "nan", - "Resource Type": "nan", - "Property": "*", - "Type": "nan", - "Rule Type": "rdfpath", - "Rule": "GeographicalRegion(*)", - }, - id="Invalid rdfpath rule", - ) - yield pytest.param( - { - "Class": "Terminal", - "Description": "nan", - "Resource Type": "nan", - "Property": "Terminal.Substation", - "Type": "nan", - "Rule Type": "rawlookup", - "Rule": "cim:Terminal->cim:ConnectivityNode->cim:VoltageLevel->cim:Substation | TableName(Lookup, )", - }, - id="Invalid rawlookup rule", - ) - - -@pytest.mark.parametrize("raw_input", generate_invalid_property_test_data()) -def test_creating_invalid_property(raw_input: dict): - with pytest.raises(ValidationError): - Property(**raw_input) - - -def generate_is_rdfpath_test_data(): - yield pytest.param("GeographicalRegion(*)", False, id="One to many missing namespace") - yield pytest.param("cim:GeographicalRegion(*)", True, id="Valid One to many") - yield pytest.param( - "cim:Terminal->cim:ConnectivityNode->cim:VoltageLevel->cim:Substation", - True, - id="Valid parent path", - ) - - -@pytest.mark.parametrize("raw_input, is_rdfpath_expected", generate_is_rdfpath_test_data()) -def test_is_rdfpath(raw_input: str, is_rdfpath_expected: bool): - assert is_rdfpath(raw_input) is is_rdfpath_expected - - -def generate_is_rawlookup_test_data(): - yield pytest.param( - "cim:Terminal->cim:ConnectivityNode->cim:VoltageLevel->cim:Substation | TableName(Lookup, ValueColumn)", - True, - id="Valid parent path with table lookup", - ) - yield pytest.param( - "cim:Terminal->cim:ConnectivityNode->cim:VoltageLevel->cim:Substation TableName(Lookup, ValueColumn)", - False, - id="Missing piping operator", - ) - yield pytest.param( - "cim:GeographicalRegion(*) | TableName(Lookup,)", - False, - id="Missing value column", - ) - - -@pytest.mark.parametrize("raw_input, is_rawlookup_expected", generate_is_rawlookup_test_data()) -def test_is_rawlookup(raw_input: str, is_rawlookup_expected: bool): - assert is_rawlookup(raw_input) is is_rawlookup_expected - - -def generate_parse_traversal(): - yield pytest.param( - "cim:GeographicalRegion(*)", - AllProperties( - class_=Entity( - prefix="cim", - suffix="GeographicalRegion", - name="GeographicalRegion", - type_="class", - ) - ), - id="All properties", - ) - yield pytest.param( - "cim:GeographicalRegion(cim:RootCIMNode.node)", - SingleProperty( - class_=Entity( - prefix="cim", - suffix="GeographicalRegion", - name="GeographicalRegion", - type_="class", - ), - property=Entity( - prefix="cim", - suffix="RootCIMNode.node", - name="RootCIMNode.node", - type_="property", - ), - ), - id="Single property", - ) - yield pytest.param( - "cim:GeographicalRegion", - AllReferences( - class_=Entity( - prefix="cim", - suffix="GeographicalRegion", - name="GeographicalRegion", - type_="class", - ) - ), - id="All references", - ) - yield pytest.param( - "cim:G", - AllReferences(class_=Entity(prefix="cim", suffix="G", name="G", type_="class")), - id="All references single character name", - ) - yield pytest.param( - "cim:Terminal->cim:ConnectivityNode->cim:VoltageLevel->cim:Substation", - Hop.from_string( - class_="cim:Terminal", - traversal=[ - Step.from_string(raw=step) - for step in [ - "->cim:ConnectivityNode", - "->cim:VoltageLevel", - "->cim:Substation", - ] - ], - ), - id="Child traversal without property", - ) - yield pytest.param( - "cim:T->cim:C->cim:V->cim:S", - Hop.from_string( - class_="cim:T", - traversal=[Step.from_string(raw=step) for step in ["->cim:C", "->cim:V", "->cim:S"]], - ), - id="Child traversal without property single character name", - ) - yield pytest.param( - "cim:Substation<-cim:VoltageLevel<-cim:ConnectivityNode<-cim:Terminal", - Hop.from_string( - class_="cim:Substation", - traversal=[ - Step.from_string(raw=step) - for step in [ - "<-cim:VoltageLevel", - "<-cim:ConnectivityNode", - "<-cim:Terminal", - ] - ], - ), - id="Parent traversal without property", - ) - yield pytest.param( - "cim:HydroPump<-cim:SynchronousMachine->cim:VoltageLevel->cim:Substation", - Hop.from_string( - class_="cim:HydroPump", - traversal=[ - Step.from_string(raw=step) - for step in [ - "<-cim:SynchronousMachine", - "->cim:VoltageLevel", - "->cim:Substation", - ] - ], - ), - id="Bidirectional traversal without property", - ) - - yield pytest.param( - "cim:Terminal->cim:ConnectivityNode->cim:VoltageLevel->cim:Substation(cim:IdentifiedObject.name)", - Hop.from_string( - class_="cim:Terminal", - traversal=[ - Step.from_string(raw=step) - for step in [ - "->cim:ConnectivityNode", - "->cim:VoltageLevel", - "->cim:Substation(cim:IdentifiedObject.name)", - ] - ], - ), - id="Child traversal with property", - ) - yield pytest.param( - "cim:Terminal->cim:ConnectivityNode->cim:VoltageLevel->cim:S(cim:n)", - Hop.from_string( - class_="cim:Terminal", - traversal=[ - Step.from_string(raw=step) - for step in [ - "->cim:ConnectivityNode", - "->cim:VoltageLevel", - "->cim:S(cim:n)", - ] - ], - ), - id="Child traversal with single character property", - ) - yield pytest.param( - "cim:Substation<-cim:VoltageLevel<-cim:ConnectivityNode<-cim:Terminal(cim:IdentifiedObject.name)", - Hop.from_string( - class_="cim:Substation", - traversal=[ - Step.from_string(raw=step) - for step in [ - "<-cim:VoltageLevel", - "<-cim:ConnectivityNode", - "<-cim:Terminal(cim:IdentifiedObject.name)", - ] - ], - ), - id="Parent traversal with property", - ) - yield pytest.param( - "cim:HydroPump<-cim:SynchronousMachine->cim:VoltageLevel->cim:Substation(cim:IdentifiedObject.name)", - Hop.from_string( - class_="cim:HydroPump", - traversal=[ - Step.from_string(raw=step) - for step in [ - "<-cim:SynchronousMachine", - "->cim:VoltageLevel", - "->cim:Substation(cim:IdentifiedObject.name)", - ] - ], - ), - id="Bidirectional traversal with property", - ) - - -@pytest.mark.parametrize("raw, expected_traversal", generate_parse_traversal()) -def test_parse_traversal(raw: str, expected_traversal: AllProperties): - # Act - actual_traversal = parse_traversal(raw) - - # Assert - assert type(actual_traversal) is type(expected_traversal) - assert actual_traversal.model_dump_json(indent=4) == expected_traversal.model_dump_json(indent=4) - - -def _load_nordic_knowledge_graph(): - graph = MemoryStore(namespace=Namespace("http://purl.org/nordic44#")) - graph.init_graph() - graph.import_from_file(config.NORDIC44_KNOWLEDGE_GRAPH) - return graph - - -GRAPH = None - - -def display_test_parse_traversal( - raw: str, - expected_traversal: AllProperties | AllReferences | Entity | Hop | SingleProperty, - name: str | None = None, -): - global GRAPH - if name: - display(Markdown(f"# {name}")) - display(Markdown("## Raw String")) - display(Markdown(raw)) - display(Markdown("## Parsed Traversal")) - pp = pprint.PrettyPrinter(indent=4) - pp.pprint(expected_traversal.dict()) - - rule = parse_rule(raw, TransformationRuleType.rdfpath) - if isinstance(rule, TransformationRuleType): - # Picking up rdf:type is done by default - query = "" - else: - if GRAPH is None: - GRAPH = _load_nordic_knowledge_graph() - try: - query = build_sparql_query(GRAPH, rule.traversal) - except Exception as e: - query = f"Failed to generate query: {e}" - display(Markdown("## Resulting SparkQL")) - display(Markdown(query)) - - -if __name__ == "__main__": - for parameters in generate_parse_traversal(): - display_test_parse_traversal(*parameters.values, suffix=parameters.id) diff --git a/tests/tests_legacy/tests_rules/tests_import_export/__init__.py b/tests/tests_legacy/tests_rules/tests_import_export/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/tests_legacy/tests_rules/tests_import_export/test_export_to_dms.py b/tests/tests_legacy/tests_rules/tests_import_export/test_export_to_dms.py deleted file mode 100644 index 458bbf730..000000000 --- a/tests/tests_legacy/tests_rules/tests_import_export/test_export_to_dms.py +++ /dev/null @@ -1,48 +0,0 @@ -import pytest -from cognite.client import data_modeling as dm - -from cognite.neat.legacy.rules import exporters, importers -from tests.data import ( - CAPACITY_BID_CONTAINERS, - CAPACITY_BID_JSON, - CAPACITY_BID_MODEL, - OSDUWELLS_MODEL, - SCENARIO_INSTANCE_MODEL, -) - - -@pytest.mark.parametrize( - "data_model", [pytest.param(m, id=m.external_id) for m in [OSDUWELLS_MODEL, SCENARIO_INSTANCE_MODEL]] -) -def test_import_export_data_model(data_model: dm.DataModel[dm.View]): - rules = importers.DMSImporter(data_model).to_rules() - - exported = exporters.DMSExporter(rules, data_model_id=data_model.as_id()).export() - - assert exported.data_model.dump() == data_model.as_apply().dump() - - -def test_import_arbitrary_json_export_data_model(): - original_model = CAPACITY_BID_MODEL - - rules = importers.ArbitraryJSONImporter(CAPACITY_BID_JSON).to_rules() - imported_model = exporters.DMSExporter(rules, data_model_id=original_model.as_id()).export() - - imported_model.data_model.name = original_model.name - imported_model.data_model.description = original_model.description - - assert imported_model.data_model.dump() == original_model.dump() - - -def test_import_arbitrary_json_export_containers(): - original_model = CAPACITY_BID_MODEL - - rules = importers.ArbitraryJSONImporter(CAPACITY_BID_JSON).to_rules() - imported_model = exporters.DMSExporter(rules, data_model_id=original_model.as_id()).export() - - imported_model.data_model.name = original_model.name - imported_model.data_model.description = original_model.description - - original_containers = CAPACITY_BID_CONTAINERS - - assert imported_model.containers.dump() == original_containers.dump() diff --git a/tests/tests_legacy/workflows/__init__.py b/tests/tests_legacy/workflows/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/tests_legacy/workflows/steps/__init__.py b/tests/tests_legacy/workflows/steps/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/tests_legacy/workflows/steps/test_graph_loader.py b/tests/tests_legacy/workflows/steps/test_graph_loader.py deleted file mode 100644 index f6f63c802..000000000 --- a/tests/tests_legacy/workflows/steps/test_graph_loader.py +++ /dev/null @@ -1,115 +0,0 @@ -from pathlib import Path -from unittest.mock import MagicMock - -import pytest -from cognite.client.data_classes import Asset, AssetList, Label -from cognite.client.testing import monkeypatch_cognite_client - -from cognite.neat.app.monitoring.metrics import NeatMetricsCollector -from cognite.neat.config import Config -from cognite.neat.legacy.graph.stores import MemoryStore -from cognite.neat.legacy.rules.exporters._core.rules2labels import get_labels -from cognite.neat.workflows.steps.data_contracts import RulesData, SolutionGraph -from cognite.neat.workflows.steps.lib.legacy.graph_loader import GenerateAssetsFromGraph - - -@pytest.fixture -def config_mock() -> Config: - mock = MagicMock(spec=Config) - mock.data_store_path = Path("/app/data") - return mock - - -def test_graph_loader_clean_orphans( - solution_knowledge_graph_dirty, - transformation_rules, - mock_cdf_assets, - config_mock: Config, -): - with monkeypatch_cognite_client() as client_mock: - - def list_assets(data_set_ids: int = 123456, limit: int = -1, labels=None, **_): - return AssetList([Asset(**asset) for asset in mock_cdf_assets.values()]) - - def list_labels(**_): - label_names = [ - *list(get_labels(transformation_rules)), - "non-historic", - "historic", - ] - return [Label(external_id=label_name, name=label_names) for label_name in label_names] - - client_mock.assets.list = list_assets - client_mock.labels.list = list_labels - - rules = RulesData(rules=transformation_rules) - solution_graph = SolutionGraph( - graph=MemoryStore( - graph=solution_knowledge_graph_dirty, - namespace="http://purl.org/cognite/simplecim#", - prefixes=solution_knowledge_graph_dirty.namespaces, - ) - ) - - test_assets_from_graph = GenerateAssetsFromGraph(config_mock) - test_assets_from_graph.configs = { - "assets_cleanup_type": "orphans", - "data_set_id": 123456, - } - test_assets_from_graph.metrics = NeatMetricsCollector("TestMetrics") - - _, assets = test_assets_from_graph.run(rules=rules, cdf_client=client_mock, solution_graph=solution_graph) - - assets_external_ids = [asset.external_id for asset in assets.assets["create"]] - assert "2dd90176-bdfb-11e5-94fa-c8f73332c8f4-terminal-orphan-test" not in assets_external_ids - assert "f17695fe-9aeb-11e5-91da-b8763fd99c5f-orphan-test" not in assets_external_ids - assert "f17695fe-9aeb-11e5-91da-b8763fd99c5f" not in assets_external_ids - assert "2dd90176-bdfb-11e5-94fa-c8f73332c8f4" not in assets_external_ids - assert "root-node" in assets_external_ids - - -def test_graph_loader_no_orphans_cleanup( - solution_knowledge_graph_dirty, - transformation_rules, - mock_cdf_assets, - config_mock: Config, -): - with monkeypatch_cognite_client() as client_mock: - - def list_assets(data_set_ids: int = 123456, limit: int = -1, labels=None, **_): - return AssetList([Asset(**asset) for asset in mock_cdf_assets.values()]) - - def list_labels(**_): - label_names = [ - *list(get_labels(transformation_rules)), - "non-historic", - "historic", - ] - return [Label(external_id=label_name, name=label_names) for label_name in label_names] - - client_mock.assets.list = list_assets - client_mock.labels.list = list_labels - - rules = RulesData(rules=transformation_rules) - solution_graph = SolutionGraph( - graph=MemoryStore( - graph=solution_knowledge_graph_dirty, - namespace="http://purl.org/cognite/simplecim#", - prefixes=solution_knowledge_graph_dirty.namespaces, - ) - ) - test_assets_from_graph = GenerateAssetsFromGraph(config_mock) - test_assets_from_graph.configs = { - "assets_cleanup_type": "nothing", - "data_set_id": 123456, - } - test_assets_from_graph.metrics = NeatMetricsCollector("TestMetrics") - - _, assets = test_assets_from_graph.run(rules=rules, cdf_client=client_mock, solution_graph=solution_graph) - - assets_external_ids = [asset.external_id for asset in assets.assets["create"]] - assert "2dd90176-bdfb-11e5-94fa-c8f73332c8f4-terminal-orphan-test" in assets_external_ids - assert "f17695fe-9aeb-11e5-91da-b8763fd99c5f-orphan-test" in assets_external_ids - assert "f17695fe-9aeb-11e5-91da-b8763fd99c5f" in assets_external_ids - assert "2dd90176-bdfb-11e5-94fa-c8f73332c8f4" in assets_external_ids - assert "orphanage" in assets_external_ids From 435eaa7f9ca47c8c5f2319b0e466c6e5c3bcad48 Mon Sep 17 00:00:00 2001 From: nikokaoja Date: Tue, 23 Jul 2024 11:29:58 +0200 Subject: [PATCH 2/3] remove code --- cognite/neat/app/api/data_classes/rest.py | 19 - cognite/neat/app/api/explorer.py | 10 +- cognite/neat/app/api/routers/core.py | 91 - cognite/neat/app/api/routers/crud.py | 32 +- .../neat/app/api/routers/data_exploration.py | 336 - cognite/neat/app/api/routers/rules.py | 203 - cognite/neat/app/api/routers/workflows.py | 118 +- cognite/neat/graph/stores/_base.py | 5 + .../workflow.yaml | 270 - .../neat/workflows/steps/data_contracts.py | 60 +- .../steps/lib/current/graph_extractor.py | 52 +- .../steps/lib/current/graph_loader.py | 25 +- .../steps/lib/current/graph_store.py | 152 +- cognite/neat/workflows/steps_registry.py | 12 +- tests/tests_unit/app/__init__.py | 0 tests/tests_unit/app/api/__init__.py | 0 tests/tests_unit/app/api/conftest.py | 17 - .../app/api/memory_cognite_client.py | 407 - .../tests_unit/app/api/test_configuration.py | 14 - tests/tests_unit/app/api/test_workflows.py | 305 - .../Extract_RDF_Graph_and_Generate_Assets.yml | 15771 ---------------- ...RDF_Graph_and_Generate_Assets_workflow.yml | 3 - .../api/test_workflows/sheet2cdf_workflow.yml | 468 - tests/tests_unit/app/conftest.py | 10 - 24 files changed, 118 insertions(+), 18262 deletions(-) delete mode 100644 cognite/neat/app/api/routers/core.py delete mode 100644 cognite/neat/app/api/routers/data_exploration.py delete mode 100644 cognite/neat/app/api/routers/rules.py delete mode 100644 cognite/neat/workflows/examples/Extract_RDF_Graph_and_Generate_Assets/workflow.yaml delete mode 100644 tests/tests_unit/app/__init__.py delete mode 100644 tests/tests_unit/app/api/__init__.py delete mode 100644 tests/tests_unit/app/api/conftest.py delete mode 100644 tests/tests_unit/app/api/memory_cognite_client.py delete mode 100644 tests/tests_unit/app/api/test_configuration.py delete mode 100644 tests/tests_unit/app/api/test_workflows.py delete mode 100644 tests/tests_unit/app/api/test_workflows/Extract_RDF_Graph_and_Generate_Assets.yml delete mode 100644 tests/tests_unit/app/api/test_workflows/Extract_RDF_Graph_and_Generate_Assets_workflow.yml delete mode 100644 tests/tests_unit/app/api/test_workflows/sheet2cdf_workflow.yml delete mode 100644 tests/tests_unit/app/conftest.py diff --git a/cognite/neat/app/api/data_classes/rest.py b/cognite/neat/app/api/data_classes/rest.py index 02068c0e6..e26ceed72 100644 --- a/cognite/neat/app/api/data_classes/rest.py +++ b/cognite/neat/app/api/data_classes/rest.py @@ -16,25 +16,6 @@ class RuleRequest(BaseModel): rule: str -class UploadToCdfRequest(BaseModel): - file_name: str = "" - file_type: str = "workflow" - comments: str = "" - author: str = "" - tag: str = "" - - -class DownloadFromCdfRequest(BaseModel): - file_name: str = "" - file_type: str = "workflow" - version: str = "" - - -class LoadGraphRequest(BaseModel): - graph_source_template_name: str - source_location: str - - class RunWorkflowRequest(BaseModel): name: str config: dict diff --git a/cognite/neat/app/api/explorer.py b/cognite/neat/app/api/explorer.py index c4cac89f8..76ed45b9c 100644 --- a/cognite/neat/app/api/explorer.py +++ b/cognite/neat/app/api/explorer.py @@ -10,7 +10,12 @@ from cognite.neat.app.api.asgi.metrics import prometheus_app from cognite.neat.app.api.configuration import NEAT_APP, UI_PATH from cognite.neat.app.api.context_manager import lifespan -from cognite.neat.app.api.routers import configuration, core, crud, data_exploration, metrics, rules, workflows +from cognite.neat.app.api.routers import ( + configuration, + crud, + metrics, + workflows, +) from cognite.neat.app.api.utils.logging import EndpointFilter app = FastAPI(title="Neat", lifespan=lifespan) @@ -45,10 +50,7 @@ app.include_router(configuration.router) app.include_router(metrics.router) app.include_router(workflows.router) -app.include_router(rules.router) app.include_router(crud.router) -app.include_router(data_exploration.router) -app.include_router(core.router) # General routes diff --git a/cognite/neat/app/api/routers/core.py b/cognite/neat/app/api/routers/core.py deleted file mode 100644 index 4dd19f3de..000000000 --- a/cognite/neat/app/api/routers/core.py +++ /dev/null @@ -1,91 +0,0 @@ -import json -import shutil -import tempfile -from copy import deepcopy -from pathlib import Path -from typing import cast - -from fastapi import APIRouter, UploadFile - -from cognite.neat.app.api.configuration import NEAT_APP -from cognite.neat.rules import exporters, importers -from cognite.neat.rules.models import DMSRules, RoleTypes - -router = APIRouter() - - -@router.post("/api/core/convert") -async def convert_data_model_to_rules(file: UploadFile): - suffix = Path(cast(str, file.filename)).suffix - - if suffix not in [".xlsx", ".ttl", ".owl", ".json", ".yaml"]: - return { - "filename": None, - "content": None, - "issues": [f"File type {suffix} not supported. Supported types are ['.xlsx', '.ttl', '.json', '.yaml']"], - } - - with tempfile.NamedTemporaryFile(suffix=suffix, delete=False) as temp: - shutil.copyfileobj(file.file, temp) # type: ignore # known issue with mypy - temp_filepath = Path(temp.name) - - # read as Excel rules - if suffix == ".xlsx": - rules, issues = importers.ExcelImporter(filepath=temp_filepath).to_rules(role=RoleTypes.dms) - - # load as OWL - elif suffix in [".ttl", ".owl"]: - rules, issues = importers.OWLImporter(filepath=temp_filepath).to_rules(role=RoleTypes.dms) - - # load as YAML - elif suffix in [".yml", ".yaml"]: - rules, issues = importers.YAMLImporter.from_file(temp_filepath).to_rules(role=RoleTypes.dms) - - # load as JSON - elif suffix == ".json": - with temp_filepath.open() as temp: - json_data = json.load(temp) - rules, issues = importers.YAMLImporter(raw_data=json.loads(json_data)).to_rules(role=RoleTypes.dms) - - # Remove the temporary file - temp_filepath.unlink() - - return {"filename": file.filename, "content": rules.model_dump(by_alias=True) if rules else None, "issues": issues} - - -@router.post("/api/core/rules2dms") -async def convert_rules_to_dms(rules: DMSRules): - dms_schema = exporters.DMSExporter().export(rules) - containers = {f"{container.space}:{container.external_id}": container.dump() for container in dms_schema.containers} - views = {f"{view.space}:{view.external_id}": view.dump() for view in dms_schema.views} - - if views and containers: - _to_visualization_compliant_views(views, containers) - - return { - "views": list(views.values()) if views else None, - "containers": list(containers.values()) if containers else None, - } - - -def _to_visualization_compliant_views(views, containers): - for view in views.values(): - for property in view["properties"].values(): - # needs coping information from container: - if property.get("container", None) and property["container"]["type"] == "container": - container_id = f"{property['container']['space']}:{property['container']['externalId']}" - container_property_def = deepcopy( - containers[container_id]["properties"][property["containerPropertyIdentifier"]] - ) - property["type"] = container_property_def["type"] - container_property_def.pop("type") - property.update(container_property_def) - - -@router.post("/api/core/publish-rules") -async def publish_rules_as_data_model(rules: DMSRules): - if NEAT_APP.cdf_client: - uploaded = exporters.DMSExporter().export_to_cdf(rules, NEAT_APP.cdf_client) - return {"uploaded": uploaded} - else: - return {"uploaded": []} diff --git a/cognite/neat/app/api/routers/crud.py b/cognite/neat/app/api/routers/crud.py index d53e6da6f..83fc8b87a 100644 --- a/cognite/neat/app/api/routers/crud.py +++ b/cognite/neat/app/api/routers/crud.py @@ -14,28 +14,13 @@ router = APIRouter() -@router.get("/api/cdf/neat-resources") -def get_neat_resources(resource_type: str | None = None): - if NEAT_APP.cdf_store is None: - return {"error": "NeatApp is not initialized"} - if resource_type is None: - return {"error": "Resource type is not specified"} - result = NEAT_APP.cdf_store.get_list_of_resources_from_cdf(resource_type=resource_type) - logging.debug(f"Got {len(result)} resources") - return {"result": result} - - -@router.post("/api/cdf/init-neat-resources") -def init_neat_cdf_resources(resource_type: str | None = None): - if NEAT_APP.cdf_store is None: - return {"error": "NeatApp is not initialized"} - NEAT_APP.cdf_store.init_cdf_resources(resource_type=resource_type) - return {"result": "ok"} - - @router.post("/api/file/upload/{workflow_name}/{file_type}/{step_id}/{action}") async def file_upload_handler( - files: list[UploadFile], workflow_name: str, file_type: str, step_id: str, action: str + files: list[UploadFile], + workflow_name: str, + file_type: str, + step_id: str, + action: str, ) -> dict[str, str]: if NEAT_APP.cdf_store is None or NEAT_APP.workflow_manager is None: return {"error": "NeatApp is not initialized"} @@ -91,7 +76,12 @@ async def file_upload_handler( if workflow is None: return {"error": f"Workflow {workflow_name} not found"} flow_msg = FlowMessage( - payload={"file_name": file_name, "hash": file_version, "full_path": full_path, "file_type": file_type} + payload={ + "file_name": file_name, + "hash": file_version, + "full_path": full_path, + "file_type": file_type, + } ) start_step_id = None if step_id == "none" else step_id diff --git a/cognite/neat/app/api/routers/data_exploration.py b/cognite/neat/app/api/routers/data_exploration.py deleted file mode 100644 index 1256a347e..000000000 --- a/cognite/neat/app/api/routers/data_exploration.py +++ /dev/null @@ -1,336 +0,0 @@ -import logging -import time -import traceback -from typing import Any, cast - -import rdflib -from fastapi import APIRouter - -from cognite.neat.app.api.asgi.metrics import counter -from cognite.neat.app.api.configuration import CACHE_STORE, NEAT_APP -from cognite.neat.app.api.data_classes.rest import ( - DatatypePropertyRequest, - NodesAndEdgesRequest, - QueryRequest, - RuleRequest, -) -from cognite.neat.app.api.utils.data_mapping import rdf_result_to_api_response -from cognite.neat.app.api.utils.query_templates import query_templates -from cognite.neat.legacy.graph.transformations import query_generator -from cognite.neat.utils.rdf_ import remove_namespace_from_uri -from cognite.neat.workflows.steps.data_contracts import RulesData, SolutionGraph, SourceGraph - -router = APIRouter() - - -@router.get("/api/list-queries") -def list_queries(): - counter.inc() - return query_templates - - -@router.post("/api/get-datatype-properties") -def get_datatype_properties(request: DatatypePropertyRequest): - logging.info("Querying datatype properties ordered by usage:") - if "get_datatype_properties" in CACHE_STORE and request.cache: - return CACHE_STORE["get_datatype_properties"] - sparql_query: str = ( - "SELECT DISTINCT ?property (count(?o) as ?occurrence ) " - "WHERE { ?s ?property ?o . FILTER(isLiteral(?o))} " - "group by ?property order by DESC(?occurrence)" - ) - if request.limit != -1: - query = f"{sparql_query} LIMIT {request.limit}" - else: - query = sparql_query - - results = get_data_from_graph(query, request.graph_name, request.workflow_name) - - try: - datatype_properties = [ - { - "id": row[rdflib.Variable("property")], - "count": int(row[rdflib.Variable("occurrence")]), - "name": remove_namespace_from_uri(row[rdflib.Variable("property")]), - } - for row in results["rows"] - ] - except Exception as e: - logging.error(f"Error while parsing datatype properties : {e}") - - merged_result = { - "datatype_properties": datatype_properties, - "error": "", - "elapsed_time_sec": results["elapsed_time_sec"], - "query": query, - } - - if request.cache: - CACHE_STORE["get_datatype_properties"] = merged_result - return merged_result - - -@router.post("/api/get-nodes-and-edges") -def get_nodes_and_edges(request: NodesAndEdgesRequest): - logging.info("Querying graph nodes and edges :") - if "get_nodes_and_edges" in CACHE_STORE and request.cache: - return CACHE_STORE["get_nodes_and_edges"] - elapsed_time_sec: float - edges_result: dict | list - nodes_result: dict | list - """ - "nodes": [ - { - "node_id": "http://purl.org/nordic44#_a63ce14e-fba0-4f9e-8b59-7ef2fe887ff8", - "node_class": "http://entsoe.eu/CIM/SchemaExtension/3/2#RateTemperature", - "node_name": "-5degreeCelsius" - }], - "edges": [ - { - "src_object_ref": "http://purl.org/nordic44#_f1769eaa-9aeb-11e5-91da-b8763fd99c5f", - "conn": "http://iec.ch/TC57/2013/CIM-schema-cim16#OperationalLimitSet.Terminal", - "dst_object_ref": "http://purl.org/nordic44#_2dd90186-bdfb-11e5-94fa-c8f73332c8f4" - }, - """ - if request.sparql_query: - mixed_result = get_data_from_graph(request.sparql_query, request.graph_name, request.workflow_name) - - edges_result = [] - nodes_result = [] - try: - nodes_result = [ - { - "node_id": v[rdflib.Variable("node_id")], - "node_class": v[rdflib.Variable("node_class")], - "node_name": v[rdflib.Variable("node_name")], - } - for v in mixed_result["rows"] - ] - except Exception as e: - logging.error(f"Error while parsing nodes : {e}") - - try: - edges_result = [ - { - "src_object_ref": v[rdflib.Variable("src_object_ref")], - "dst_object_ref": v[rdflib.Variable("dst_object_ref")], - } - for v in mixed_result["rows"] - if rdflib.Variable("src_object_ref") in v and rdflib.Variable("dst_object_ref") in v - ] - except Exception as e: - logging.error(f"Error while parsing edges : {e}") - - query = request.sparql_query - elapsed_time_sec = mixed_result["elapsed_time_sec"] - else: - nodes_filter = "" - edges_dst_filter = "" - edges_src_filter = "" - - if len(request.node_class_filter) > 0: - nodes_filter = "VALUES ?node_class { " + " ".join([f"<{x}>" for x in request.node_class_filter]) + " }" - - if request.node_name_property: - nodes_query = f"SELECT DISTINCT ?node_id ?node_class ?node_name WHERE \ - {{ {nodes_filter} \ - ?node_id {request.node_name_property} ?node_name . ?node_id a ?node_class }} " - else: - nodes_query = f"SELECT DISTINCT ?node_id ?node_class (?node_id AS ?node_name) WHERE \ - {{ {nodes_filter} ?node_id a ?node_class }} " - - if len(request.src_edge_filter) > 0: - edges_src_filter = ( - "VALUES ?src_object_class { " + " ".join([f"<{x}>" for x in request.src_edge_filter]) + " }" - ) - if len(request.dst_edge_filter) > 0: - edges_dst_filter = ( - "VALUES ?dst_object_class { " + " ".join([f"<{x}>" for x in request.dst_edge_filter]) + " }" - ) - - edges_query = f"SELECT ?src_object_ref ?conn ?dst_object_ref \ - WHERE {{ \ - {edges_src_filter} \ - {edges_dst_filter} \ - ?src_object_ref ?conn ?dst_object_ref . \ - ?src_object_ref a ?src_object_class . \ - ?dst_object_ref a ?dst_object_class .\ - }} " - nodes_query = f"{nodes_query} LIMIT {request.limit}" - edges_query = f"{edges_query} LIMIT {request.limit}" - logging.info(f"Nodes query : {nodes_query}") - logging.info(f"Edges query : {edges_query}") - nodes_result = get_data_from_graph(nodes_query, request.graph_name, request.workflow_name) - edges_result = get_data_from_graph(edges_query, request.graph_name, request.workflow_name) - elapsed_time_sec = nodes_result["elapsed_time_sec"] + edges_result["elapsed_time_sec"] - query = nodes_query + edges_query - nodes_result = nodes_result["rows"] - edges_result = edges_result["rows"] - - merged_result = { - "nodes": nodes_result, - "edges": edges_result, - "error": "", - "elapsed_time_sec": elapsed_time_sec, - "query": query, - } - - if request.cache: - CACHE_STORE["get_nodes_and_edges"] = merged_result - return merged_result - - -@router.post("/api/query") -def query_graph(request: QueryRequest): - logging.info(f"Querying graph {request.graph_name} with query {request.query}") - sparq_query = request.query - return get_data_from_graph(sparq_query, request.graph_name, workflow_name=request.workflow_name) - - -@router.post("/api/execute-rule") -def execute_rule(request: RuleRequest): - if NEAT_APP.workflow_manager is None: - return {"error": "NeatApp is not initialized"} - logging.debug( - f"Executing rule type: { request.rule_type } rule : {request.rule} , workflow : {request.workflow_name} , " - f"graph : {request.graph_name}" - ) - workflow = NEAT_APP.workflow_manager.get_workflow(request.workflow_name) - if workflow is None: - return {"error": f"Workflow {request.workflow_name} not found"} - - api_result = {"error": ""} - workflow_context = workflow.get_context() - if workflow_context is None: - return {"error": "Workflow context is not initialized"} - if request.graph_name == "source": - if "SourceGraph" in workflow_context: - graph = cast(SourceGraph, workflow_context["SourceGraph"]).graph - else: - logging.info("Source graph is empty , please load the graph first") - api_result["error"] = "source graph is empty , please load the graph first" - elif request.graph_name == "solution": - if "SolutionGraph" in workflow_context: - graph = cast(SolutionGraph, workflow_context["SolutionGraph"]).graph - else: - logging.info("Solution graph is empty , please load the graph first") - api_result["error"] = "solution graph is empty , please load the graph first" - else: - raise Exception("Unknown graph name") - - if request.rule_type == "rdfpath": - start_time = time.perf_counter() - rules = cast(RulesData, workflow_context["RulesData"]).rules - sparql_query = query_generator.build_sparql_query( - graph, # type: ignore[arg-type] - request.rule, - prefixes=rules.prefixes, - ) - else: - logging.error("Unknown rule type") - return {"error": "Unknown rule type"} - stop_time = time.perf_counter() - elapsed_time_sec_1 = stop_time - start_time - logging.info(f"Computed query : {sparql_query} in {elapsed_time_sec_1 * 1000} ms") - - api_result = get_data_from_graph(sparql_query, request.graph_name, workflow_name=request.workflow_name) - api_result["elapsed_time_sec"] += elapsed_time_sec_1 - return api_result - - -@router.get("/api/object-properties") -def get_object_properties(reference: str, graph_name: str = "source", workflow_name: str = "default"): - logging.info(f"Querying object-properties from {graph_name} :") - query = f"SELECT ?property ?value WHERE {{ <{reference}> ?property ?value }} ORDER BY ?property" - return get_data_from_graph(query, graph_name, workflow_name=workflow_name) - - -@router.get("/api/search") -def search( - search_str: str, graph_name: str = "source", search_type: str = "value_exact_match", workflow_name: str = "default" -): - logging.info(f"Search from {graph_name} :") - if search_type == "reference": - query = ( - f"SELECT ?class ?reference WHERE {{ {{?reference ?class <{search_str}> . }} " - f"UNION {{ <{search_str}> ?class ?reference }} }} limit 10000" - ) - - elif search_type == "value_exact_match": - query = ( - f"select ?object_ref ?type ?property ?value where " - f"{{ ?object_ref ?property ?value . ?object_ref a ?type . " - f'FILTER(?value="{search_str}") }} limit 10000' - ) - elif search_type == "value_regexp": - query = ( - f"select ?object_ref ?type ?property ?value where " - f"{{ ?object_ref ?property ?value . ?object_ref a ?type . " - f'FILTER regex(?value, "{search_str}","i") }} limit 10000' - ) - return get_data_from_graph(query, graph_name, workflow_name=workflow_name) - - -@router.get("/api/get-classes") -def get_classes(graph_name: str = "source", workflow_name: str = "default", cache: bool = True): - logging.info(f"Querying graph classes from graph name : {graph_name}, cache : {cache}") - cache_key = f"classes_{graph_name}" - if cache_key in CACHE_STORE and cache: - return CACHE_STORE[cache_key] - query = ( - "SELECT ?class (count(?s) as ?instances ) WHERE { ?s a ?class . } " "group by ?class order by DESC(?instances)" - ) - api_result = get_data_from_graph(query, graph_name, workflow_name) - CACHE_STORE["get_classes"] = api_result - return api_result - - -def get_data_from_graph(sparq_query: str, graph_name: str = "source", workflow_name: str = "default") -> dict[str, Any]: - if NEAT_APP.workflow_manager is None: - return {"error": "NeatApp is not initialized"} - total_elapsed_time = 0.0 - api_result: dict[str, Any] = {"error": ""} - result = None - - try: - logging.info(f"Preparing query :{sparq_query} ") - start_time = time.perf_counter() - workflow = NEAT_APP.workflow_manager.get_workflow(workflow_name) - if workflow is None: - return {"error": f"Workflow {workflow_name} not found"} - workflow_context = workflow.get_context() - - if graph_name == "source": - if "SourceGraph" in workflow_context: - result = cast(SourceGraph, workflow_context["SourceGraph"]).graph.query(sparq_query) - else: - logging.info("Source graph is empty , please load the graph first") - api_result["error"] = "source graph is empty , please load the graph first" - elif graph_name == "solution": - if "SolutionGraph" in workflow_context: - result = cast(SolutionGraph, workflow_context["SolutionGraph"]).graph.query(sparq_query) - else: - logging.info("Solution graph is empty , please load the graph first") - api_result["error"] = "solution graph is empty , please load the graph first" - else: - raise Exception("Unknown graph name") - - stop_time = time.perf_counter() - elapsed_time_sec_1 = stop_time - start_time - logging.info(f"Query prepared in {elapsed_time_sec_1 * 1000} ms") - - start_time = time.perf_counter() - if result: - api_result = rdf_result_to_api_response(result) - stop_time = time.perf_counter() - elapsed_time_sec_2 = stop_time - start_time - total_elapsed_time = elapsed_time_sec_1 + elapsed_time_sec_2 - except Exception as e: - logging.error(f"Error while executing query :{e}") - traceback.print_exc() - api_result["error"] = str(e) - - api_result["query"] = sparq_query - api_result["elapsed_time_sec"] = total_elapsed_time - logging.info(f"Data fetched in {total_elapsed_time * 1000} ms") - return api_result diff --git a/cognite/neat/app/api/routers/rules.py b/cognite/neat/app/api/routers/rules.py deleted file mode 100644 index 21443b890..000000000 --- a/cognite/neat/app/api/routers/rules.py +++ /dev/null @@ -1,203 +0,0 @@ -import logging -from pathlib import Path -from typing import Any, cast - -from fastapi import APIRouter, Response -from rdflib import Namespace - -from cognite.neat.app.api.configuration import NEAT_APP -from cognite.neat.app.api.data_classes.rest import TransformationRulesUpdateRequest -from cognite.neat.legacy.rules import exporters as legacy_exporters -from cognite.neat.legacy.rules import importers as legacy_importers -from cognite.neat.legacy.rules.models._base import EntityTypes -from cognite.neat.legacy.rules.models.rules import Class, Classes, Metadata, Properties, Property, Rules -from cognite.neat.rules import importers -from cognite.neat.rules.models import RoleTypes -from cognite.neat.workflows.steps.data_contracts import RulesData -from cognite.neat.workflows.steps.lib.current.rules_exporter import RulesToExcel -from cognite.neat.workflows.steps.lib.current.rules_importer import ExcelToRules -from cognite.neat.workflows.steps.lib.legacy.rules_importer import ImportExcelToRules -from cognite.neat.workflows.utils import get_file_hash - -router = APIRouter() - - -@router.get("/api/rules/list") -def get_rules_list(): - rules_dir = Path(NEAT_APP.config.rules_store_path) - return {"result": [str(file.name) for file in rules_dir.glob("*.xlsx")]} - - -@router.get("/api/rules") -def get_rules( - sheetname: str = "Properties", - url: str | None = None, - source_type: str | None = None, - orient: str = "columns", - workflow_name: str = "default", - file_name: str | None = None, - version: str | None = None, - as_role: str | None = None, -) -> dict[str, Any]: - rules_schema_version = "" - if NEAT_APP.cdf_store is None or NEAT_APP.workflow_manager is None: - return {"error": "NeatApp is not initialized"} - if workflow_name != "undefined": - workflow = NEAT_APP.workflow_manager.get_workflow(workflow_name) - if workflow is None: - return {"error": f"Workflow {workflow_name} is not found"} - workflow_definition = workflow.get_workflow_definition() - if not file_name: - for step in workflow_definition.steps: - if step.method == ImportExcelToRules.__name__: - file_name = step.configs["file_name"] - version = step.configs["version"] - break - if step.method == RulesToExcel.__name__ or step.method == ExcelToRules.__name__: - rules_schema_version = "v2" - as_role = step.configs.get("as_role", "") - file_name = step.configs.get("File name", "") - if file_name: - break - file_name = step.configs.get("File path", "") - break - - if not file_name: - return {"error": "File name is not provided"} - rules_file = Path(file_name) - if str(rules_file.parent) == ".": - path = Path(NEAT_APP.config.rules_store_path) / rules_file - else: - path = Path(NEAT_APP.config.data_store_path) / rules_file - - src = "local" - if url: - path = Path(url) - - if path.exists() and not version: - logging.info(f"Loading rules from {path}") - elif path.exists() and version: - hash_ = get_file_hash(path) - if hash_ != version: - NEAT_APP.cdf_store.load_rules_file_from_cdf(file_name, version) - src = "cdf" - else: - NEAT_APP.cdf_store.load_rules_file_from_cdf(file_name, version) - src = "cdf" - - error_text = "" - properties = [] - classes = [] - remaped_rules = {} - try: - # Trying to load rules V1 - if rules_schema_version == "" or rules_schema_version == "v1": - rules = cast( - Rules, legacy_importers.ExcelImporter(path).to_rules(return_report=False, skip_validation=False) - ) - properties = [ - { - "class": value.class_id, - "property": value.property_id, - "property_description": value.description, - "property_type": ( - value.expected_value_type.versioned_id - if value.property_type == EntityTypes.object_property - else value.expected_value_type.suffix - ), - "cdf_resource_type": value.cdf_resource_type, - "cdf_metadata_type": value.resource_type_property, - "rule_type": value.rule_type, - "rule": value.rule, - } - for value in rules.properties.values() - ] - - classes = [ - { - "class": value.class_id, - "class_description": value.description, - "cdf_resource_type": value.cdf_resource_type, - } - for value in rules.classes.values() - ] - rules_schema_version = "v1" - remaped_rules = {"properties": properties, "metadata": rules.metadata.model_dump(), "classes": classes} - except Exception as e: - error_text = str(e) - - if rules_schema_version == "" or rules_schema_version == "v2": - try: - role = RoleTypes(as_role) if as_role else None - rules_v2, issues = importers.ExcelImporter(path).to_rules(role=role) - error_text = "" - rules_schema_version = "v2" - if rules_v2: - remaped_rules = rules_v2.model_dump() - except Exception as e: - error_text = str(e) - rules_schema_version = "unknown" - - return { - "rules": remaped_rules, - "file_name": path.name, - "hash": get_file_hash(path), - "error_text": error_text, - "src": src, - "rules_schema_version": rules_schema_version, - } - - -@router.get("/api/rules/from_file") -def get_original_rules_from_file(file_name: str): - # """Endpoint for retrieving raw transformation from file""" - path = Path(NEAT_APP.config.rules_store_path) / file_name - rules = cast( - Rules, legacy_importers.ExcelImporter(filepath=path).to_rules(return_report=False, skip_validation=False) - ) - return Response(content=rules.model_dump_json(), media_type="application/json") - - -@router.get("/api/rules/from_workflow") -def get_original_rules_from_workflow(workflow_name: str): - """Endpoing for retrieving transformation from memmory""" - workflow = NEAT_APP.workflow_manager.get_workflow(workflow_name) - if workflow is None: - return {"error": f"Workflow {workflow_name} is not found"} - context = workflow.get_context() - rules_data = context["RulesData"] - if type(rules_data) is not RulesData: - return {"error": "RulesData is not found in workflow context"} - - return Response(content=rules_data.rules.model_dump_json(), media_type="application/json") - - -@router.post("/api/rules/model_and_transformations") -def upsert_rules(request: TransformationRulesUpdateRequest): - """Endpoing for updating transformation rules via API . This endpoint is still experimental""" - rules = request.rules_object - rules["metadata"]["namespace"] = Namespace(rules["metadata"]["namespace"]) - metadata = Metadata(**rules["metadata"]) - classes = Classes() - for class_, val in rules["classes"].items(): - classes[class_] = Class(**val) - properties = Properties() - - for prop, val in rules["properties"].items(): - val["resource_type_property"] = [] - properties[prop] = Property(**val) - - prefixes: dict[str, Namespace] = {} - for prefix, val in rules["prefixes"].items(): - prefixes[prefix] = Namespace(val) - - rules = Rules(metadata=metadata, classes=classes, properties=properties, prefixes=prefixes, instances=[]) - if request.output_format == "excel": - rules_file = Path(request.file_name) - if str(rules_file.parent) == ".": - path = Path(NEAT_APP.config.rules_store_path) / rules_file - else: - path = Path(NEAT_APP.config.data_store_path) / rules_file - - legacy_exporters.ExcelExporter(rules=rules).export_to_file(path) - return {"status": "ok"} diff --git a/cognite/neat/app/api/routers/workflows.py b/cognite/neat/app/api/routers/workflows.py index fb5935053..939a00f7b 100644 --- a/cognite/neat/app/api/routers/workflows.py +++ b/cognite/neat/app/api/routers/workflows.py @@ -1,20 +1,19 @@ import logging import shutil -from pathlib import Path from typing import cast from fastapi import APIRouter, Depends, HTTPException, Request, UploadFile from fastapi.responses import FileResponse, JSONResponse from cognite.neat.app.api.configuration import NEAT_APP -from cognite.neat.app.api.data_classes.rest import DownloadFromCdfRequest, RunWorkflowRequest, UploadToCdfRequest +from cognite.neat.app.api.data_classes.rest import ( + RunWorkflowRequest, +) from cognite.neat.workflows import WorkflowFullStateReport from cognite.neat.workflows.base import WorkflowDefinition -from cognite.neat.workflows.migration.wf_manifests import migrate_wf_manifest from cognite.neat.workflows.model import FlowMessage -from cognite.neat.workflows.steps.data_contracts import SolutionGraph, SourceGraph +from cognite.neat.workflows.steps.data_contracts import NeatGraph from cognite.neat.workflows.steps.step_model import DataContract -from cognite.neat.workflows.utils import get_file_hash router = APIRouter() @@ -27,7 +26,11 @@ def start_workflow(request: RunWorkflowRequest): start_status = NEAT_APP.workflow_manager.start_workflow_instance( request.name, sync=request.sync, flow_msg=FlowMessage() ) - result = {"workflow_instance": None, "is_success": start_status.is_success, "status_text": start_status.status_text} + result = { + "workflow_instance": None, + "is_success": start_status.is_success, + "status_text": start_status.status_text, + } return {"result": result} @@ -58,25 +61,6 @@ def get_workflow_files(workflow_name: str): return {"files": workflow.get_list_of_workflow_artifacts()} -@router.post("/api/workflow/package/{workflow_name}") -def package_workflow(workflow_name: str): - if NEAT_APP.cdf_store is None: - return {"error": "NeatApp is not initialized"} - package_file = NEAT_APP.cdf_store.package_workflow(workflow_name) - hash_ = get_file_hash(NEAT_APP.config.workflows_store_path / package_file) - return {"package": package_file, "hash": hash_} - - -@router.post("/api/workflow/context-cleanup/{workflow_name}") -def cleanup_workflow_data(workflow_name: str): - if NEAT_APP.cdf_store is None: - return {"error": "NeatApp is not initialized"} - workflow = NEAT_APP.workflow_manager.get_workflow(workflow_name) - if workflow is not None: - workflow.cleanup_workflow_context() - return {"result": "ok"} - - @router.post("/api/workflow/create") def create_new_workflow(request: WorkflowDefinition): if NEAT_APP.workflow_manager is None: @@ -95,30 +79,24 @@ def delete_workflow(workflow_name: str): return {"result": "ok"} -@router.get("/api/workflow/executions") -def get_list_of_workflow_executions(): - return {"executions": NEAT_APP.cdf_store.get_list_of_workflow_executions_from_cdf()} - - -@router.get("/api/workflow/detailed-execution-report/{execution_id}") -def get_detailed_execution(execution_id: str): - if NEAT_APP.cdf_store is None: - return {"error": "NeatApp is not initialized"} - return {"report": NEAT_APP.cdf_store.get_detailed_workflow_execution_report_from_cdf(execution_id)} - - @router.post("/api/workflow/reload-workflows") def reload_workflows(): NEAT_APP.workflow_manager.load_workflows_from_storage() NEAT_APP.triggers_manager.reload_all_triggers() - return {"result": "ok", "workflows": NEAT_APP.workflow_manager.get_list_of_workflows()} + return { + "result": "ok", + "workflows": NEAT_APP.workflow_manager.get_list_of_workflows(), + } @router.post("/api/workflow/reload-single-workflow/{workflow_name}") def reload_single_workflows(workflow_name: str): NEAT_APP.workflow_manager.load_single_workflow_from_storage(workflow_name) NEAT_APP.triggers_manager.reload_all_triggers() - return {"result": "ok", "workflows": NEAT_APP.workflow_manager.get_list_of_workflows()} + return { + "result": "ok", + "workflows": NEAT_APP.workflow_manager.get_list_of_workflows(), + } @router.get("/api/workflow/workflow-definition/{workflow_name}") @@ -152,58 +130,6 @@ def update_workflow_definition(workflow_name: str, request: WorkflowDefinition): return {"result": "ok"} -@router.post("/api/workflow/upload-wf-to-cdf/{workflow_name}") -def upload_workflow_to_cdf(workflow_name: str, request: UploadToCdfRequest): - if NEAT_APP.cdf_store is None: - return {"error": "NeatApp is not initialized"} - NEAT_APP.cdf_store.save_workflow_to_cdf( - workflow_name, changed_by=request.author, comments=request.comments, tag=request.tag - ) - return {"result": "ok"} - - -@router.post("/api/workflow/upload-rules-cdf/{workflow_name}") -def upload_rules_to_cdf(workflow_name: str, request: UploadToCdfRequest): - if NEAT_APP.cdf_store is None: - return {"error": "NeatApp is not initialized"} - file_path = Path(NEAT_APP.config.rules_store_path, request.file_name) - NEAT_APP.cdf_store.save_resource_to_cdf( - workflow_name, "neat-wf-rules", file_path, changed_by=request.author, comments=request.comments - ) - return {"result": "ok"} - - -@router.post("/api/workflow/download-wf-from-cdf") -def download_wf_from_cdf(request: DownloadFromCdfRequest): - if NEAT_APP.cdf_store is None: - return {"error": "NeatApp is not initialized"} - NEAT_APP.cdf_store.load_workflows_from_cdf(request.file_name, request.version) - return {"result": "ok"} - - -@router.post("/api/workflow/download-rules-from-cdf") -def download_rules_to_cdf(request: DownloadFromCdfRequest): - if NEAT_APP.cdf_store is None: - return {"error": "NeatApp is not initialized"} - NEAT_APP.cdf_store.load_rules_file_from_cdf(request.file_name, request.version) - return {"file_name": request.file_name, "hash": request.version} - - -@router.post("/api/workflow/migrate-workflow") -def migrate_workflow(): - return migrate_wf_manifest(NEAT_APP.config.data_store_path) - - -@router.get("/api/workflow/pre-cdf-assets/{workflow_name}") -def get_pre_cdf_assets(workflow_name: str): - if NEAT_APP.workflow_manager is None: - return {"error": "Workflow Manager is not initialized"} - workflow = NEAT_APP.workflow_manager.get_workflow(workflow_name) - if workflow is None: - return {"assets": []} - return {"assets": workflow.data["CategorizedAssets"]} - - @router.get("/api/workflow/context/{workflow_name}") def get_context(workflow_name: str): if NEAT_APP.workflow_manager is None: @@ -228,8 +154,8 @@ def get_context_object(workflow_name: str, object_name: str): if object_name not in context: return {"error": f"Item {object_name} is not found in workflow context"} - if object_name == "SourceGraph" or object_name == "SolutionGraph": - return {"object": cast(SourceGraph | SolutionGraph, context[object_name]).graph.diagnostic_report()} + if object_name == "NeatGraph": + return {"object": cast(NeatGraph, context[object_name]).graph.type_} cobject = context[object_name] if isinstance(cobject, DataContract): @@ -285,7 +211,11 @@ def http_trigger_start_workflow(workflow_name: str, step_id: str, request: Reque @router.post("/api/workflow/{workflow_name}/resume/{step_id}/{instance_id}") def http_trigger_resume_workflow( - workflow_name: str, step_id: str, instance_id: str, request: Request, body: bytes = fast_api_depends + workflow_name: str, + step_id: str, + instance_id: str, + request: Request, + body: bytes = fast_api_depends, ): if NEAT_APP.triggers_manager is None: return JSONResponse(content={"error": "Triggers Manager is not initialized"}, status_code=400) diff --git a/cognite/neat/graph/stores/_base.py b/cognite/neat/graph/stores/_base.py index ee818d4eb..1287a21f8 100644 --- a/cognite/neat/graph/stores/_base.py +++ b/cognite/neat/graph/stores/_base.py @@ -66,6 +66,11 @@ def __init__( self.queries = Queries(self.graph, self.rules) + @property + def type_(self) -> str: + "Return type of the graph store" + return type(self.graph.store).__name__ + def add_rules(self, rules: InformationRules) -> None: """This method is used to add rules to the graph store and it is the only correct way to add rules to the graph store, after the graph store has been initialized. diff --git a/cognite/neat/workflows/examples/Extract_RDF_Graph_and_Generate_Assets/workflow.yaml b/cognite/neat/workflows/examples/Extract_RDF_Graph_and_Generate_Assets/workflow.yaml deleted file mode 100644 index 944f121b5..000000000 --- a/cognite/neat/workflows/examples/Extract_RDF_Graph_and_Generate_Assets/workflow.yaml +++ /dev/null @@ -1,270 +0,0 @@ -configs: [] -description: null -implementation_module: null -name: Extract RDF Graph and Generate Assets -steps: - - complex_configs: {} - configs: {} - description: null - enabled: true - id: step_http_trigger - label: Process trigger - max_retries: 0 - method: null - params: - workflow_start_method: persistent_blocking - retry_delay: 3 - stype: http_trigger - system_component_id: null - transition_to: - - step_rules_loader - trigger: true - ui_config: - pos_x: 454 - pos_y: 77 - - complex_configs: {} - configs: - file_name: Rules-Nordic44.xlsx - validation_report_file: rules_validation_report.txt - validation_report_storage_dir: rules_validation_report - version: "" - description: null - enabled: true - id: step_rules_loader - label: Rules loader - max_retries: 0 - method: ImportExcelToRules - params: {} - retry_delay: 3 - stype: stdstep - system_component_id: null - transition_to: - - step_234135 - - step_configure_source_graph_store - trigger: false - ui_config: - pos_x: 452 - pos_y: 167 - - complex_configs: {} - configs: - add_base_iri: "True" - file_path: source-graphs/Knowledge-Graph-Nordic44-dirty.xml - mime_type: application/rdf+xml - description: null - enabled: true - id: step_graph_loader - label: Extract Graph from RDF File - max_retries: 0 - method: ExtractGraphFromRdfFile - params: {} - retry_delay: 3 - stype: stdstep - system_component_id: null - transition_to: - - step_transform - trigger: false - ui_config: - pos_x: 453 - pos_y: 418 - - complex_configs: {} - configs: - cdf_lookup_database: "" - description: null - enabled: true - id: step_transform - label: Transform graph - max_retries: 0 - method: TransformSourceToSolutionGraph - params: {} - retry_delay: 3 - stype: stdstep - system_component_id: null - transition_to: [] - trigger: false - ui_config: - pos_x: 453 - pos_y: 500 - - complex_configs: {} - configs: - asset_external_id_prefix: "" - assets_cleanup_type: full - data_set_id: "2626756768281823" - description: null - enabled: true - id: step_generate_assets - label: Generate cdf assets - max_retries: 0 - method: GenerateAssetsFromGraph - params: {} - retry_delay: 3 - stype: stdstep - system_component_id: null - transition_to: - - step_generate_relationships - trigger: false - ui_config: - pos_x: 455 - pos_y: 571 - - complex_configs: {} - configs: - data_set_id: "2626756768281823" - relationship_external_id_prefix: "" - description: null - enabled: true - id: step_generate_relationships - label: Generate relationships - max_retries: 0 - method: GenerateRelationshipsFromGraph - params: {} - retry_delay: 3 - stype: stdstep - system_component_id: null - transition_to: [] - trigger: false - ui_config: - pos_x: 456 - pos_y: 633 - - complex_configs: {} - configs: - db_server_api_root_url: "" - disk_store_dir: nordic44-graph-store - graph_name: source - init_procedure: reset - sparql_query_url: "" - sparql_update_url: "" - store_type: oxigraph - description: null - enabled: true - id: step_configure_source_graph_store - label: Configure source graph store - max_retries: 0 - method: ConfigureGraphStore - params: {} - retry_delay: 3 - stype: stdstep - system_component_id: null - transition_to: - - step_configure_solution_graph_store - trigger: false - ui_config: - pos_x: 451 - pos_y: 245 - - complex_configs: {} - configs: - db_server_api_root_url: "" - disk_store_dir: solution-graph-store-4 - graph_name: solution - init_procedure: reset - sparql_query_url: "" - sparql_update_url: "" - store_type: oxigraph - description: null - enabled: true - id: step_configure_solution_graph_store - label: Configure solution graph store - max_retries: 0 - method: ConfigureGraphStore - params: {} - retry_delay: 3 - stype: stdstep - system_component_id: null - transition_to: - - step_graph_loader - trigger: false - ui_config: - pos_x: 454 - pos_y: 334 -system_components: - - description: null - id: grid_management_system - label: Grid management system - transition_to: - - rdf_xml_file - ui_config: - pos_x: 171 - pos_y: 6 - - description: null - id: rdf_xml_file - label: RDF XML File - transition_to: - - graph_db_store - ui_config: - pos_x: 170 - pos_y: 103 - - description: null - id: graph_db_store - label: OxiGraph source graph_store - transition_to: - - transformer - ui_config: - pos_x: 240 - pos_y: 177 - - description: null - id: in_memmory_store - label: In-memory source graph_store - transition_to: [] - ui_config: - pos_x: 73 - pos_y: 175 - - description: null - id: transformation_rules - label: Transformation rules - transition_to: - - transformer - ui_config: - pos_x: 413 - pos_y: 195 - - description: null - id: cdf_raw_table - label: Cdf raw lookup table - transition_to: - - transformer - ui_config: - pos_x: 586 - pos_y: 195 - - description: null - id: transformer - label: Transformer - transition_to: - - solution_graph - ui_config: - pos_x: 237 - pos_y: 329 - - description: null - id: solution_graph - label: Solution graph - transition_to: - - cdf_classic_exporter - ui_config: - pos_x: 237 - pos_y: 405 - - description: null - id: cdf_classic_exporter - label: CDF Classic exporter - transition_to: - - cdf_classic - ui_config: - pos_x: 181 - pos_y: 498 - - description: null - id: cdf_fdm_exporter - label: CDF FDM exporter - transition_to: - - cdf_fdm - ui_config: - pos_x: 345 - pos_y: 498 - - description: null - id: cdf_classic - label: CDF Classic (Asset,Relationships) - transition_to: [] - ui_config: - pos_x: 182 - pos_y: 586 - - description: null - id: cdf_fdm - label: CDF FDM - transition_to: [] - ui_config: - pos_x: 347 - pos_y: 586 diff --git a/cognite/neat/workflows/steps/data_contracts.py b/cognite/neat/workflows/steps/data_contracts.py index 76790261a..a03045619 100644 --- a/cognite/neat/workflows/steps/data_contracts.py +++ b/cognite/neat/workflows/steps/data_contracts.py @@ -1,27 +1,24 @@ from pathlib import Path from cognite.client import CogniteClient -from cognite.client.data_classes import Asset, AssetUpdate, Relationship, RelationshipUpdate +from cognite.client.data_classes import ( + Asset, + AssetUpdate, + Relationship, + RelationshipUpdate, +) from cognite.client.data_classes.data_modeling import EdgeApply, NodeApply -from cognite.neat.legacy.graph.stores import NeatGraphStoreBase -from cognite.neat.legacy.rules.exporters._rules2dms import DMSSchemaComponents -from cognite.neat.legacy.rules.models.rules import Rules -from cognite.neat.rules.models import AssetRules, DMSRules, DomainRules, InformationRules +from cognite.neat.graph.stores import NeatGraphStore +from cognite.neat.rules.models import ( + AssetRules, + DMSRules, + DomainRules, + InformationRules, +) from cognite.neat.workflows.steps.step_model import DataContract -class RulesData(DataContract): - """ - This represents the TransformationRules object. - - Args: - rules: The TransformationRules object. - """ - - rules: Rules - - class MultiRuleData(DataContract): domain: DomainRules | None = None information: InformationRules | None = None @@ -53,27 +50,15 @@ class PathData(DataContract): excel_file_path: Path -class SourceGraph(DataContract): - """ - This represents the source graph. - - Args: - graph: The source graph. - """ - - graph: NeatGraphStoreBase - - -class SolutionGraph(DataContract): +class NeatGraph(DataContract): """ - This represents the solution graph. + This represents the neat graph. Args: - graph (NeatGraphStoreBase): The solution graph. - + graph: The neat graph store. """ - graph: NeatGraphStoreBase + graph: NeatGraphStore class ClientData(DataContract): @@ -130,14 +115,3 @@ class Edges(DataContract): """ edges: list[EdgeApply] - - -class DMSSchemaComponentsData(DataContract): - """ - This represents DMS Schema Model. - - Args: - components: DMS Schema Components model. - """ - - components: DMSSchemaComponents diff --git a/cognite/neat/workflows/steps/lib/current/graph_extractor.py b/cognite/neat/workflows/steps/lib/current/graph_extractor.py index e25fe9b2b..1d3b38d7e 100644 --- a/cognite/neat/workflows/steps/lib/current/graph_extractor.py +++ b/cognite/neat/workflows/steps/lib/current/graph_extractor.py @@ -3,11 +3,15 @@ from pathlib import Path from typing import ClassVar, cast +from rdflib import URIRef + +from cognite.neat.constants import DEFAULT_NAMESPACE +from cognite.neat.graph.extractors import RdfFileExtractor from cognite.neat.graph.extractors._mock_graph_generator import MockGraphGenerator from cognite.neat.rules._shared import DMSRules, InformationRules from cognite.neat.workflows._exceptions import StepNotInitialized from cognite.neat.workflows.model import FlowMessage, StepExecutionStatus -from cognite.neat.workflows.steps.data_contracts import MultiRuleData, SolutionGraph, SourceGraph +from cognite.neat.workflows.steps.data_contracts import MultiRuleData, NeatGraph from cognite.neat.workflows.steps.step_model import Configurable, Step __all__ = ["GraphFromRdfFile", "GraphFromMockData"] @@ -29,11 +33,16 @@ class GraphFromMockData(Step): value="", label="Target number of instances for each class", ), - Configurable(name="Graph", value="solution", label="The name of target graph.", options=["source", "solution"]), + Configurable( + name="Graph", + value="solution", + label="The name of target graph.", + options=["source", "solution"], + ), ] def run( # type: ignore[override, syntax] - self, rules: MultiRuleData, graph_store: SolutionGraph | SourceGraph + self, rules: MultiRuleData, graph_store: NeatGraph ) -> FlowMessage: if self.configs is None: raise StepNotInitialized(type(self).__name__) @@ -55,17 +64,12 @@ def run( # type: ignore[override, syntax] step_execution_status=StepExecutionStatus.ABORT_AND_FAIL, ) - if self.configs["Graph"] == "solution": - # Todo Anders: Why is the graph fetched from context when it is passed as an argument? - graph_store = cast(SourceGraph | SolutionGraph, self.flow_context["SolutionGraph"]) - else: - graph_store = cast(SourceGraph | SolutionGraph, self.flow_context["SourceGraph"]) - - logging.info("Initiated generation of mock triples") + extractor = MockGraphGenerator( + cast(InformationRules | DMSRules, rules.information or rules.dms), + class_count, + ) - extractor = MockGraphGenerator(cast(InformationRules | DMSRules, rules.information or rules.dms), class_count) - - graph_store.graph.add_triples(extractor.extract()) + NeatGraph.graph.write(extractor) return FlowMessage(output_text=f"Instances loaded to the {graph_store.__class__.__name__}") @@ -104,20 +108,20 @@ class GraphFromRdfFile(Step): ), ] - def run(self, source_graph: SourceGraph) -> FlowMessage: # type: ignore[override, syntax] + def run(self, graph_store: NeatGraph) -> FlowMessage: # type: ignore[override, syntax] if self.configs is None or self.data_store_path is None: raise StepNotInitialized(type(self).__name__) - if source_graph.graph.rdf_store_type.lower() in ("memory", "oxigraph"): - if source_file := self.configs["File path"]: - source_graph.graph.import_from_file( - self.data_store_path / Path(source_file), + + if source_file := self.configs["File path"]: + NeatGraph.graph.write( + RdfFileExtractor( # type: ignore[abstract] + filepath=self.data_store_path / Path(source_file), mime_type=self.configs["MIME type"], # type: ignore[arg-type] - add_base_iri=self.configs["Add base URI"] == "True", + base_uri=(URIRef(DEFAULT_NAMESPACE) if self.configs["Add base URI"] == "True" else None), ) - logging.info(f"Loaded {source_file} into source graph.") - else: - raise ValueError("You need a source_rdf_store.file specified for source_rdf_store.type=memory") + ) + else: - raise NotImplementedError(f"Graph type {source_graph.graph.rdf_store_type} is not supported.") + raise ValueError("You need a valid file path to be specified") - return FlowMessage(output_text="Instances loaded to source graph") + return FlowMessage(output_text="Instances loaded to NeatGraph!") diff --git a/cognite/neat/workflows/steps/lib/current/graph_loader.py b/cognite/neat/workflows/steps/lib/current/graph_loader.py index 3d64cc95b..0ffc85580 100644 --- a/cognite/neat/workflows/steps/lib/current/graph_loader.py +++ b/cognite/neat/workflows/steps/lib/current/graph_loader.py @@ -1,13 +1,10 @@ import time from pathlib import Path -from typing import ClassVar, cast +from typing import ClassVar from cognite.neat.workflows._exceptions import StepNotInitialized from cognite.neat.workflows.model import FlowMessage -from cognite.neat.workflows.steps.data_contracts import ( - SolutionGraph, - SourceGraph, -) +from cognite.neat.workflows.steps.data_contracts import NeatGraph from cognite.neat.workflows.steps.step_model import Configurable, Step __all__ = [ @@ -26,12 +23,6 @@ class GraphToRdfFile(Step): category = CATEGORY version = "private-beta" configurables: ClassVar[list[Configurable]] = [ - Configurable( - name="Graph", - value="source", - options=["source", "solution"], - label=("The graph to be used for loading RDF File." " Supported options : source, solution"), - ), Configurable( name="File path", value="staging/graph_export.ttl", @@ -40,7 +31,7 @@ class GraphToRdfFile(Step): ] def run( # type: ignore[override, syntax] - self, graph: SourceGraph | SolutionGraph + self, graph: NeatGraph ) -> FlowMessage: # type: ignore[syntax] if self.configs is None or self.data_store_path is None: raise StepNotInitialized(type(self).__name__) @@ -48,15 +39,7 @@ def run( # type: ignore[override, syntax] storage_path = self.data_store_path / Path(self.configs["File path"]) relative_graph_file_path = str(storage_path).split("/data/")[1] - graph_name = self.configs["Graph"] or "source" - - if graph_name == "solution": - # Todo Anders: Why is the graph fetched from context when it is passed as an argument? - graph = cast(SourceGraph | SolutionGraph, self.flow_context["SolutionGraph"]) - else: - graph = cast(SourceGraph | SolutionGraph, self.flow_context["SourceGraph"]) - - graph.graph.serialize(str(storage_path), format="turtle") + graph.graph.graph.serialize(str(storage_path), format="turtle") output_text = ( "

    " diff --git a/cognite/neat/workflows/steps/lib/current/graph_store.py b/cognite/neat/workflows/steps/lib/current/graph_store.py index 3714234dc..7c2e4f2c8 100644 --- a/cognite/neat/workflows/steps/lib/current/graph_store.py +++ b/cognite/neat/workflows/steps/lib/current/graph_store.py @@ -1,19 +1,14 @@ -import logging -from pathlib import Path -from typing import ClassVar, cast +from typing import ClassVar -from cognite.neat.constants import DEFAULT_NAMESPACE, get_default_prefixes -from cognite.neat.legacy.graph import stores -from cognite.neat.workflows._exceptions import StepNotInitialized +from cognite.neat.graph.stores import NeatGraphStore from cognite.neat.workflows.model import FlowMessage from cognite.neat.workflows.steps.data_contracts import ( - RulesData, - SolutionGraph, - SourceGraph, + MultiRuleData, + NeatGraph, ) from cognite.neat.workflows.steps.step_model import Configurable, Step -__all__ = ["GraphStoreConfiguration", "GraphStoreReset"] +__all__ = ["GraphStoreConfiguration"] CATEGORY = __name__.split(".")[-1].replace("_", " ").title() @@ -27,138 +22,27 @@ class GraphStoreConfiguration(Step): version = "private-beta" category = CATEGORY configurables: ClassVar[list[Configurable]] = [ - Configurable( - name="Graph", - value="source", - label="Graph categorization, supported: source, solution ", - options=["source", "solution"], - ), Configurable( name="Graph store type", - value=stores.OxiGraphStore.rdf_store_type, - label="Graph store type, supported: oxigraph, memory,file, graphdb, sparql. ", - options=["oxigraph", "memory", "file", "graphdb", "sparql"], - ), - Configurable( - name="Disk storage directory", - value="source-graph-store", - label="Local directory that is used as local graph store.Only for oxigraph, file store types", - ), - Configurable( - name="Query URL", - value="", - label="Query URL for SPARQL endpoint. Only for SPARQL store type", - ), - Configurable( - name="Update URL", - value="", - label="Update URL for SPARQL endpoint. Only for SPARQL store type", - ), - Configurable( - name="GraphDB API root URL", - value="", - label="Root url for GraphDB. Only for graphdb", - ), - Configurable( - name="Init procedure", - value="reset", - label="Operations to be performed on the graph store as part of init and configuration process. \ - Supported options : reset, clear, none", - options=["reset", "none"], + value="oxigraph", + label="Graph store type, supported: oxigraph, rdflib", + options=["oxigraph", "rdflib"], ), ] def run( # type: ignore[override] - self, rules_data: RulesData | None = None - ) -> (FlowMessage, SourceGraph | SolutionGraph): # type: ignore[syntax] - if self.configs is None or self.data_store_path is None: - raise StepNotInitialized(type(self).__name__) - logging.info("Initializing graph") - store_dir = self.data_store_path / Path(value) if (value := self.configs["Disk storage directory"]) else None - store_type = self.configs["Graph store type"] - graph_name_mapping = {"source": "SourceGraph", "solution": "SolutionGraph"} + self, rules_data: MultiRuleData | None = None + ) -> (FlowMessage, NeatGraph): # type: ignore[syntax] + store_type = self.configs.get("Graph store type", "oxigraph") - graph_name = graph_name_mapping[self.configs["Graph"]] - graph_store = cast(SourceGraph | SolutionGraph | None, self.flow_context.get(graph_name, None)) - if self.configs["Init procedure"] == "reset": - logging.info("Resetting graph") - reset_store(store_dir, graph_store.graph if graph_store else None) - if graph_name in self.flow_context: - del self.flow_context[graph_name] - graph_store = None - logging.info("Graph reset complete") - - prefixes = rules_data.rules.prefixes if rules_data else get_default_prefixes() - - if store_type == stores.OxiGraphStore.rdf_store_type and graph_store is not None: - # OXIGRAPH doesn't like to be initialized twice without a good reason - graph_store.graph.upsert_prefixes(prefixes) - return FlowMessage(output_text="Stores already configured") - try: - store_cls = stores.STORE_BY_TYPE[store_type] - except KeyError: - return FlowMessage(output_text="Invalid store type") - - new_graph_store = store_cls(prefixes=prefixes, base_prefix="neat", namespace=DEFAULT_NAMESPACE) - new_graph_store.init_graph( - self.configs["Query URL"], - self.configs["Update URL"], - "neat-tnt", - internal_storage_dir=store_dir, - ) + if store_type == "oxigraph": + store = NeatGraph(graph=NeatGraphStore.from_oxi_store(rules=rules_data.information if rules_data else None)) + else: + store = NeatGraph( + graph=NeatGraphStore.from_memory_store(rules=rules_data.information if rules_data else None) + ) return ( FlowMessage(output_text="Graph store configured successfully"), - ( - SourceGraph(graph=new_graph_store) - if graph_name == "SourceGraph" - else SolutionGraph(graph=new_graph_store) - ), - ) - - -def reset_store(data_store_dir: Path | None, graph_store: stores.NeatGraphStoreBase | None = None): - if isinstance(graph_store, stores.OxiGraphStore): - if graph_store: - graph_store.close() - graph_store.drop_graph_store_storage(data_store_dir) - elif data_store_dir: - graph_store.drop_graph_store_storage(data_store_dir) - elif isinstance(graph_store, stores.GraphDBStore): - if graph_store: - graph_store.drop() - graph_store.reinitialize_graph() - return None - - -class GraphStoreReset(Step): - """ - This step resets graph stores to their initial state (clears all data) - """ - - description = "This step resets graph stores to their initial state (clears all data)." - category = CATEGORY - version = "private-alpha" - configurables: ClassVar[list[Configurable]] = [ - Configurable( - name="Graph", - value="source", - label="Graph store to be reset. Supported: solution, source ", - options=["source", "solution"], + store, ) - ] - - def run(self) -> FlowMessage: # type: ignore[override, syntax] - if self.configs is None or self.data_store_path is None: - raise StepNotInitialized(type(self).__name__) - graph_name_mapping = {"source": "SourceGraph", "solution": "SolutionGraph"} - - graph_name = graph_name_mapping[self.configs["Graph"]] - graph_store = cast(SourceGraph | SolutionGraph | None, self.flow_context.get(graph_name, None)) - if graph_store is not None: - reset_store(graph_store.graph.internal_storage_dir, graph_store.graph) - if graph_name in self.flow_context: - del self.flow_context[graph_name] - return FlowMessage(output_text="Reset operation completed") - else: - return FlowMessage(output_text="Stores already reset") diff --git a/cognite/neat/workflows/steps_registry.py b/cognite/neat/workflows/steps_registry.py index 6b1e2e11e..43c88a0d8 100644 --- a/cognite/neat/workflows/steps_registry.py +++ b/cognite/neat/workflows/steps_registry.py @@ -12,7 +12,6 @@ # steps import cognite.neat.workflows.steps.lib.current import cognite.neat.workflows.steps.lib.io -import cognite.neat.workflows.steps.lib.legacy from cognite.neat.app.monitoring.metrics import NeatMetricsCollector from cognite.neat.config import Config from cognite.neat.exceptions import InvalidWorkFlowError @@ -42,7 +41,11 @@ def __init__(self, config: Config): self._step_classes: list[type[Step]] = [] self.user_steps_path: Path = config.data_store_path / "steps" self.data_store_path: str = str(config.data_store_path) - self.categorized_steps: dict[str, set] = {"legacy": set(), "current": set(), "io": set()} + self.categorized_steps: dict[str, set] = { + "legacy": set(), + "current": set(), + "io": set(), + } def load_step_classes(self): if self._step_classes: @@ -58,11 +61,6 @@ def load_step_classes(self): logging.info(f"Loading NEAT step {name}") self._step_classes.append(step_cls) self.categorized_steps["io"].add(name) - for name, step_cls in inspect.getmembers(cognite.neat.workflows.steps.lib.legacy): - if inspect.isclass(step_cls): - logging.info(f"Loading NEAT step {name}") - self._step_classes.append(step_cls) - self.categorized_steps["legacy"].add(name) sys.path.append(str(Path(self.data_store_path) / "workflows")) try: if self.user_steps_path: diff --git a/tests/tests_unit/app/__init__.py b/tests/tests_unit/app/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/tests_unit/app/api/__init__.py b/tests/tests_unit/app/api/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/tests_unit/app/api/conftest.py b/tests/tests_unit/app/api/conftest.py deleted file mode 100644 index 977bf7160..000000000 --- a/tests/tests_unit/app/api/conftest.py +++ /dev/null @@ -1,17 +0,0 @@ -import pytest -from fastapi.testclient import TestClient - -from cognite.neat.app.api.explorer import app -from tests.tests_unit.app.api.memory_cognite_client import memory_cognite_client - - -@pytest.fixture(scope="session") -def cognite_client(): - with memory_cognite_client() as client: - yield client - - -@pytest.fixture(scope="session") -def fastapi_client(cognite_client): - with TestClient(app) as test_client: - yield test_client diff --git a/tests/tests_unit/app/api/memory_cognite_client.py b/tests/tests_unit/app/api/memory_cognite_client.py deleted file mode 100644 index ac1a95722..000000000 --- a/tests/tests_unit/app/api/memory_cognite_client.py +++ /dev/null @@ -1,407 +0,0 @@ -import os -from collections.abc import Iterator, Sequence -from contextlib import contextmanager, suppress -from typing import Any, Literal, TypeVar - -from cognite.client._constants import DEFAULT_LIMIT_READ -from cognite.client.data_classes import ( - Asset, - AssetFilter, - AssetHierarchy, - AssetList, - AssetUpdate, - CountAggregate, - GeoLocationFilter, - LabelDefinition, - LabelDefinitionList, - LabelFilter, - Relationship, - RelationshipFilter, - RelationshipList, - RelationshipUpdate, - TimestampRange, -) -from cognite.client.data_classes._base import CogniteFilter, T_CogniteResource, T_CogniteResourceList -from cognite.client.testing import monkeypatch_cognite_client -from cognite.client.utils._identifier import Identifier, IdentifierSequence, SingletonIdentifierSequence - -ExternalId = str -ID = int - -T = TypeVar("T") - - -class MemoryClient: - _RESOURCE_PATH: str - - def __init__(self, list_cls: type[T_CogniteResourceList], cls: type[T_CogniteResource]) -> None: - self.store: dict[int, dict[ExternalId | ID, T_CogniteResource]] = {os.getpid(): {}} - self._next_id = 1 - self._CREATE_LIMIT = 10_000 - self._RESOURCE_PATH = "assets" - self.cls = cls - self.list_cls = list_cls - - class Config: - max_workers = 10 - - self._config = Config() - - def _retrieve( - self, - identifier: Identifier, - cls: type[T_CogniteResource], - resource_path: str | None = None, - params: dict | None = None, - headers: dict | None = None, - ) -> T_CogniteResource | None: - return self.store[os.getpid()].get(identifier.as_primitive()) - - def _retrieve_multiple( - self, - identifiers: SingletonIdentifierSequence | IdentifierSequence, - resource_path: str | None = None, - ignore_unknown_ids: bool | None = None, - headers: dict[str, Any] | None = None, - other_params: dict[str, Any] | None = None, - ) -> T_CogniteResourceList | T_CogniteResource | None: - if identifiers.is_singleton(): - return self._retrieve(identifier=identifiers[0], cls=self.cls) - return self.list_cls([self._retrieve(identifier, cls=self.cls) for identifier in identifiers]) - - def _list( - self, - method: Literal["POST", "GET"], - resource_path: str | None = None, - url_path: str | None = None, - limit: int | None = None, - filter: dict | None = None, - other_params: dict | None = None, - partitions: int | None = None, - sort: Sequence[str] | None = None, - headers: dict | None = None, - initial_cursor: str | None = None, - ) -> T_CogniteResourceList: - return self.list_cls(self._list_unique_in_store()) - - def _list_unique_in_store(self) -> T_CogniteResourceList: - unique_ids = {id(item): item for item in self.store[os.getpid()].values()} - return self.list_cls(unique_ids.values()) - - def dump(self, ordered: bool = False, exclude: set[str] | None = None) -> list[dict]: - exclude = exclude or set() - iterable = (self._dump_item(item, ordered, exclude) for item in self._list_unique_in_store()) - if ordered: - return sorted(iterable, key=lambda x: x.get("external_id", x["externalId"]).casefold()) - return list(iterable) - - @classmethod - def _dump_item(cls, item: T_CogniteResource, ordered: bool, exclude: set[str]) -> dict[str, Any]: - dump = item.dump() - if ordered and "metadata" in dump: - for key, value in list(dump["metadata"].items()): - if isinstance(value, list): - dump["metadata"][key] = sorted(value, key=lambda x: x.casefold()) - - if "labels" in dump: - # Labels are not properly dumped to dict. - iterable = (label.dump() if hasattr(label, "dump") else label for label in dump["labels"]) - dump["labels"] = sorted(iterable, key=lambda x: x["externalId"].casefold()) if ordered else list(iterable) - if exclude: - for to_exclude in exclude: - keys = to_exclude.split(".") - dump_from = dump - with suppress(KeyError): - for key in keys[:-1]: - dump_from = dump_from[key] - dump_from.pop(keys[-1]) - return dump - - def _aggregate( - self, - cls: type[T], - resource_path: str | None = None, - filter: CogniteFilter | dict | None = None, - aggregate: str | None = None, - fields: Sequence[str] | None = None, - keys: Sequence[str] | None = None, - headers: dict | None = None, - ) -> list[T]: - raise NotImplementedError() - - def _create_multiple( - self, - items: Sequence[T_CogniteResource] | Sequence[dict[str, Any]] | T_CogniteResource | dict[str, Any], - resource_path: str | None = None, - params: dict | None = None, - headers: dict | None = None, - extra_body_fields: dict | None = None, - limit: int | None = None, - ) -> T_CogniteResourceList | T_CogniteResource: - is_single = not isinstance(items, Sequence) - create_items = [items] if is_single else items - for item in create_items: - self.store[os.getpid()][item.external_id] = item - return create_items - - -class AssetsMemory(MemoryClient): - def __init__(self): - super().__init__(AssetList, Asset) - - def __call__( - self, - chunk_size: int | None = None, - name: str | None = None, - parent_ids: Sequence[int] | None = None, - parent_external_ids: Sequence[str] | None = None, - asset_subtree_ids: int | Sequence[int] | None = None, - asset_subtree_external_ids: str | Sequence[str] | None = None, - metadata: dict[str, str] | None = None, - data_set_ids: int | Sequence[int] | None = None, - data_set_external_ids: str | Sequence[str] | None = None, - labels: LabelFilter = None, - geo_location: GeoLocationFilter = None, - source: str | None = None, - created_time: dict[str, Any] | TimestampRange = None, - last_updated_time: dict[str, Any] | TimestampRange = None, - root: bool | None = None, - external_id_prefix: str | None = None, - aggregated_properties: Sequence[str] | None = None, - limit: int | None = None, - partitions: int | None = None, - ) -> Iterator[Asset] | Iterator[AssetList]: - return iter(self._list(method="POST")) - - def retrieve(self, id: int | None = None, external_id: str | None = None) -> Asset | None: - identifier = IdentifierSequence.load(ids=id, external_ids=external_id).as_singleton() - return self._retrieve_multiple(identifiers=identifier) - - def retrieve_multiple( - self, - ids: Sequence[int] | None = None, - external_ids: Sequence[str] | None = None, - ignore_unknown_ids: bool = False, - ) -> AssetList: - identifiers = IdentifierSequence.load(ids=ids, external_ids=external_ids) - return self._retrieve_multiple(identifiers=identifiers, ignore_unknown_ids=ignore_unknown_ids) - - def aggregate(self, filter: AssetFilter | dict = None) -> list[CountAggregate]: - return [CountAggregate(count=len(self._list_unique_in_store()))] - - def create(self, asset: Asset | Sequence[Asset]) -> Asset | AssetList: - return self._create_multiple(items=asset) - - def create_hierarchy( - self, - assets: Sequence[Asset] | AssetHierarchy, - *, - upsert: bool = False, - upsert_mode: Literal["patch", "replace"] = "patch", - ) -> AssetList: - if isinstance(assets, AssetHierarchy): - raise NotImplementedError() - - return self.create(assets) - - def delete( - self, - id: int | Sequence[int] | None = None, - external_id: str | Sequence[str] | None = None, - recursive: bool = False, - ignore_unknown_ids: bool = False, - ) -> None: - raise NotImplementedError() - - def update(self, item: Asset | AssetUpdate | Sequence[Asset | AssetUpdate]) -> Asset | AssetList: - raise NotImplementedError() - - def search( - self, - name: str | None = None, - description: str | None = None, - query: str | None = None, - filter: AssetFilter | dict = None, - limit: int = 100, - ) -> AssetList: - raise NotImplementedError() - - def retrieve_subtree( - self, id: int | None = None, external_id: str | None = None, depth: int | None = None - ) -> AssetList: - raise NotImplementedError() - - def list( - self, - name: str | None = None, - parent_ids: Sequence[int] | None = None, - parent_external_ids: Sequence[str] | None = None, - asset_subtree_ids: int | Sequence[int] | None = None, - asset_subtree_external_ids: str | Sequence[str] | None = None, - data_set_ids: int | Sequence[int] | None = None, - data_set_external_ids: str | Sequence[str] | None = None, - labels: LabelFilter = None, - geo_location: GeoLocationFilter = None, - metadata: dict[str, str] | None = None, - source: str | None = None, - created_time: dict[str, Any] | TimestampRange = None, - last_updated_time: dict[str, Any] | TimestampRange = None, - root: bool | None = None, - external_id_prefix: str | None = None, - aggregated_properties: Sequence[str] | None = None, - partitions: int | None = None, - limit: int = DEFAULT_LIMIT_READ, - ) -> AssetList: - return self._list(method="POST") - - -class RelationshipsMemory(MemoryClient): - def __init__(self): - super().__init__(RelationshipList, Relationship) - - def _create_filter( - self, - source_external_ids: Sequence[str] | None = None, - source_types: Sequence[str] | None = None, - target_external_ids: Sequence[str] | None = None, - target_types: Sequence[str] | None = None, - data_set_ids: Sequence[dict[str, Any]] | None = None, - start_time: dict[str, int] | None = None, - end_time: dict[str, int] | None = None, - confidence: dict[str, int] | None = None, - last_updated_time: dict[str, int] | None = None, - created_time: dict[str, int] | None = None, - active_at_time: dict[str, int] | None = None, - labels: LabelFilter = None, - ) -> dict[str, Any]: - return RelationshipFilter( - source_external_ids=source_external_ids, - source_types=source_types, - target_external_ids=target_external_ids, - target_types=target_types, - data_set_ids=data_set_ids, - start_time=start_time, - end_time=end_time, - confidence=confidence, - last_updated_time=last_updated_time, - created_time=created_time, - active_at_time=active_at_time, - labels=labels, - ).dump(camel_case=True) - - def __call__( - self, - source_external_ids: Sequence[str] | None = None, - source_types: Sequence[str] | None = None, - target_external_ids: Sequence[str] | None = None, - target_types: Sequence[str] | None = None, - data_set_ids: int | Sequence[int] | None = None, - data_set_external_ids: str | Sequence[str] | None = None, - start_time: dict[str, int] | None = None, - end_time: dict[str, int] | None = None, - confidence: dict[str, int] | None = None, - last_updated_time: dict[str, int] | None = None, - created_time: dict[str, int] | None = None, - active_at_time: dict[str, int] | None = None, - labels: LabelFilter = None, - limit: int | None = None, - fetch_resources: bool = False, - chunk_size: int | None = None, - partitions: int | None = None, - ) -> Iterator[Relationship] | Iterator[RelationshipList]: - return iter(self._list(method="POST")) - - def retrieve(self, external_id: str, fetch_resources: bool = False) -> Relationship | None: - identifiers = IdentifierSequence.load(ids=None, external_ids=external_id).as_singleton() - return self._retrieve_multiple(identifiers=identifiers) - - def retrieve_multiple(self, external_ids: Sequence[str], fetch_resources: bool = False) -> RelationshipList: - identifiers = IdentifierSequence.load(ids=None, external_ids=external_ids) - return self._retrieve_multiple(identifiers=identifiers) - - def create(self, relationship: Relationship | Sequence[Relationship]) -> Relationship | RelationshipList: - if isinstance(relationship, Sequence): - relationship = [r._validate_resource_types() for r in relationship] - else: - relationship = relationship._validate_resource_types() - - return self._create_multiple(items=relationship) - - def update( - self, item: Relationship | RelationshipUpdate | Sequence[Relationship | RelationshipUpdate] - ) -> Relationship | RelationshipList: - raise NotImplementedError() - # return self._update_multiple( - - def delete(self, external_id: str | Sequence[str], ignore_unknown_ids: bool = False) -> None: - raise NotImplementedError() - # self._delete_multiple( - - def list( - self, - source_external_ids: Sequence[str] | None = None, - source_types: Sequence[str] | None = None, - target_external_ids: Sequence[str] | None = None, - target_types: Sequence[str] | None = None, - data_set_ids: int | Sequence[int] | None = None, - data_set_external_ids: str | Sequence[str] | None = None, - start_time: dict[str, int] | None = None, - end_time: dict[str, int] | None = None, - confidence: dict[str, int] | None = None, - last_updated_time: dict[str, int] | None = None, - created_time: dict[str, int] | None = None, - active_at_time: dict[str, int] | None = None, - labels: LabelFilter = None, - limit: int = 100, - partitions: int | None = None, - fetch_resources: bool = False, - ) -> RelationshipList: - return self._list(method="POST") - - -class LabelsMemory(MemoryClient): - _RESOURCE_PATH = "/labels" - - def __init__(self): - super().__init__(LabelDefinitionList, LabelDefinition) - - def __call__( - self, - name: str | None = None, - external_id_prefix: str | None = None, - limit: int | None = None, - chunk_size: int | None = None, - data_set_ids: int | Sequence[int] | None = None, - data_set_external_ids: str | Sequence[str] | None = None, - ) -> Iterator[LabelDefinition] | Iterator[LabelDefinitionList]: - return iter(self._list(method="POST")) - - def create(self, label: LabelDefinition | Sequence[LabelDefinition]) -> LabelDefinition | LabelDefinitionList: - if isinstance(label, Sequence): - if len(label) > 0 and not isinstance(label[0], LabelDefinition): - raise TypeError("'label' must be of type LabelDefinition or Sequence[LabelDefinition]") - elif not isinstance(label, LabelDefinition): - raise TypeError("'label' must be of type LabelDefinition or Sequence[LabelDefinition]") - return self._create_multiple(items=label) - - def delete(self, external_id: str | Sequence[str] | None = None) -> None: - raise NotImplementedError() - - def list( - self, - name: str | None = None, - external_id_prefix: str | None = None, - data_set_ids: int | Sequence[int] | None = None, - data_set_external_ids: str | Sequence[str] | None = None, - limit: int = DEFAULT_LIMIT_READ, - ) -> LabelDefinitionList: - return self._list(method="POST", limit=limit) - - -@contextmanager -def memory_cognite_client(): - with monkeypatch_cognite_client() as client: - client.assets = AssetsMemory() - client.relationships = RelationshipsMemory() - client.labels = LabelsMemory() - yield client diff --git a/tests/tests_unit/app/api/test_configuration.py b/tests/tests_unit/app/api/test_configuration.py deleted file mode 100644 index 73319c567..000000000 --- a/tests/tests_unit/app/api/test_configuration.py +++ /dev/null @@ -1,14 +0,0 @@ -from pathlib import Path - -from cognite.neat.config import Config - - -def test_dump_and_load_default_config(tmp_path: Path): - config = Config() - filepath = tmp_path / "tmp_config.yaml" - - config.to_yaml(filepath) - - loaded_config = config.from_yaml(filepath) - - assert config == loaded_config diff --git a/tests/tests_unit/app/api/test_workflows.py b/tests/tests_unit/app/api/test_workflows.py deleted file mode 100644 index d6b4263b9..000000000 --- a/tests/tests_unit/app/api/test_workflows.py +++ /dev/null @@ -1,305 +0,0 @@ -from pathlib import Path -from urllib.parse import quote - -import pytest -from cognite.client import CogniteClient -from starlette.testclient import TestClient - -from cognite import neat -from cognite.neat.app.api.configuration import NEAT_APP -from cognite.neat.app.api.data_classes.rest import ( - DatatypePropertyRequest, - QueryRequest, - RuleRequest, - RunWorkflowRequest, -) -from cognite.neat.app.api.utils.query_templates import query_templates -from cognite.neat.constants import EXAMPLE_WORKFLOWS -from cognite.neat.legacy.rules.models.rules import Rules -from cognite.neat.workflows.base import BaseWorkflow -from cognite.neat.workflows.model import WorkflowDefinition -from tests.tests_unit.app.api.memory_cognite_client import MemoryClient - - -@pytest.fixture(scope="session") -def workflow_directories() -> list[Path]: - return [ - example - for example in EXAMPLE_WORKFLOWS.iterdir() - if not example.name.startswith(".") and not example.name.startswith("_") - ] - - -@pytest.fixture(scope="session") -def workflow_definitions(workflow_directories: list[Path]) -> list[WorkflowDefinition]: - definitions = [] - for example in workflow_directories: - definition = (example / "workflow.yaml").read_text() - loaded = BaseWorkflow.deserialize_definition(definition, "yaml") - definitions.append(loaded) - return definitions - - -@pytest.fixture(scope="session") -def workflow_names(workflow_directories: list[Path]) -> list[str]: - return [example.name for example in workflow_directories] - - -def test_workflow_workflows(workflow_names: list[str], fastapi_client: TestClient): - # Act - response = fastapi_client.get("/api/workflow/workflows") - - # Assert - result = response.json() - assert sorted(result["workflows"]) == sorted(workflow_names) - - -def test_rules(transformation_rules: Rules, fastapi_client: TestClient): - # transformation_rules load Rules-Nordic44.xlsx - # /api/rules fetch rules related to default workflow which are Rules-Nordic44.xlsx - response = fastapi_client.get("/api/rules", params={"workflow_name": "Extract_RDF_Graph_and_Generate_Assets"}) - - # Assert - assert response.status_code == 200 - rules = response.json() - assert len(transformation_rules.classes) == len(rules["rules"]["classes"]) - assert len(transformation_rules.properties) == len(rules["rules"]["properties"]) - - -@pytest.mark.freeze_time("2024-01-21") -@pytest.mark.parametrize("workflow_name", ["Extract_RDF_Graph_and_Generate_Assets"]) -def test_workflow_start( - workflow_name: str, - cognite_client: CogniteClient, - fastapi_client: TestClient, - data_regression, - tmp_path, -): - # Arrange - if workflow_name == "Extract_RDF_Graph_and_Generate_Assets": - # When running this test in GitHub actions, you get permission issues with the default disk_store_dir. - response = fastapi_client.get("/api/workflow/workflow-definition/Extract_RDF_Graph_and_Generate_Assets") - definition = WorkflowDefinition(**response.json()["definition"]) - response = fastapi_client.post( - "/api/workflow/workflow-definition/Extract_RDF_Graph_and_Generate_Assets", - json=definition.model_dump(), - ) - assert response.status_code == 200 - - # Act - response = fastapi_client.post( - "/api/workflow/start", - json=RunWorkflowRequest(name=workflow_name, sync=True, config={}, start_step="").model_dump(), - ) - - assert response.status_code == 200 - result = response.json()["result"] - assert result["is_success"] - data = {} - for resource_name in ["assets", "relationships", "labels"]: - memory: MemoryClient = getattr(cognite_client, resource_name) - data[resource_name] = memory.dump( - ordered=True, - exclude={"metadata.start_time", "metadata.update_time", "start_time"}, - ) - data_regression.check(data, basename=f"{workflow_name}_workflow") - - -@pytest.mark.parametrize("workflow_name", ["Extract_RDF_Graph_and_Generate_Assets"]) -def test_workflow_stats(workflow_name: str, fastapi_client: TestClient): - # Act - response = fastapi_client.get(f"/api/workflow/stats/{workflow_name}") - - assert response.status_code == 200 - assert response.json()["workflow_name"] == workflow_name - assert response.json()["state"] == "COMPLETED" - - -def test_workflow_reload_workflows(workflow_names: list[str], fastapi_client: TestClient): - # Act - response = fastapi_client.post("/api/workflow/reload-workflows") - - assert response.status_code == 200 - assert response.json()["result"] == "ok" - assert sorted(response.json()["workflows"]) == sorted(workflow_names) - - -@pytest.mark.parametrize("workflow_name", ["Extract_RDF_Graph_and_Generate_Assets"]) -def test_workflow_workflow_definition_get(workflow_name: str, fastapi_client: TestClient): - # Act - response = fastapi_client.get(f"/api/workflow/workflow-definition/{workflow_name}") - - assert response.status_code == 200 - assert response.json()["definition"]["name"] == workflow_name - - -def test_about(fastapi_client: TestClient): - # Act - response = fastapi_client.get("/api/about") - - assert response.status_code == 200 - assert response.json()["version"] == neat.__version__ - - -def test_configs_global(fastapi_client: TestClient): - # Act - response = fastapi_client.get("/api/configs/global") - - assert response.status_code == 200 - assert response.json()["log_level"] == "INFO" - assert response.json()["workflows_store_type"] == "file" - - -def test_list_queries(fastapi_client: TestClient): - # Act - response = fastapi_client.get("/api/list-queries") - - assert response.status_code == 200 - assert response.json() == query_templates - - -@pytest.mark.parametrize("workflow_name", ["Extract_RDF_Graph_and_Generate_Assets"]) -def test_query(workflow_name: str, fastapi_client: TestClient): - # Act - workflow = NEAT_APP.workflow_manager.get_workflow(workflow_name) - workflow_defintion = workflow.get_workflow_definition() - for step in workflow_defintion.steps: - if step.method == "ConfigureGraphStore": - step.configs["store_type"] = "memory" - - workflow.enable_step("step_generate_assets", False) - NEAT_APP.workflow_manager.start_workflow_instance(workflow_name, sync=True) - - response = fastapi_client.post( - "/api/query", - json=QueryRequest( - graph_name="source", - workflow_name=workflow_name, - query="SELECT DISTINCT ?class WHERE { ?s a ?class }", - ).model_dump(), - ) - - content = response.json() - - assert response.status_code == 200 - assert content, "Missing content" - assert content["fields"] == ["class"], f"Missing fields, got {content}" - assert len(content["rows"]) == 59 - assert len(content["rows"]) == 59 - assert {"class": "http://iec.ch/TC57/2013/CIM-schema-cim16#Terminal"} in content["rows"] - - -@pytest.mark.parametrize("workflow_name", ["Extract_RDF_Graph_and_Generate_Assets"]) -def test_execute_rule(workflow_name: str, fastapi_client: TestClient): - # Act - workflow = NEAT_APP.workflow_manager.get_workflow(workflow_name) - if "SourceGraph" not in workflow.get_context(): - workflow.enable_step("step_generate_assets", False) - NEAT_APP.workflow_manager.start_workflow_instance(workflow_name, sync=True) - - response = fastapi_client.post( - "/api/execute-rule", - json=RuleRequest( - graph_name="source", - workflow_name=workflow_name, - rule_type="rdfpath", - rule="cim:Terminal->cim:ConnectivityNode->cim:VoltageLevel->cim:Substation", - ).model_dump(), - ) - - content = response.json() - - assert response.status_code == 200 - assert content["fields"] == ["subject", "predicate", "object"] - assert { - "subject": "http://purl.org/cognite/neat#_2dd901ff-bdfb-11e5-94fa-c8f73332c8f4", - "predicate": "http://purl.org/dc/terms/relation", - "object": "http://purl.org/cognite/neat#_f176965a-9aeb-11e5-91da-b8763fd99c5f", - } in content["rows"] - - -@pytest.mark.parametrize("workflow_name", ["Extract_RDF_Graph_and_Generate_Assets"]) -def test_get_datatype_properties(workflow_name: str, fastapi_client: TestClient): - # Act - workflow = NEAT_APP.workflow_manager.get_workflow(workflow_name) - if "SourceGraph" not in workflow.get_context(): - workflow.enable_step("step_generate_assets", False) - NEAT_APP.workflow_manager.start_workflow_instance(workflow_name, sync=True) - - response = fastapi_client.post( - "/api/get-datatype-properties", - json=DatatypePropertyRequest(graph_name="source", workflow_name=workflow_name, limit=1).model_dump(), - ) - - content = response.json() - - assert response.status_code == 200 - assert { - "id": "http://iec.ch/TC57/2013/CIM-schema-cim16#IdentifiedObject.name", - "count": 2506, - "name": "IdentifiedObject.name", - } in content["datatype_properties"] - - -@pytest.mark.skip( - "This test is dependent on the data in the graph, thus it is dependens " - "on test execution in a specific order. This needs to be fixed before it can be added back in." -) -@pytest.mark.parametrize("workflow_name", ["Extract_RDF_Graph_and_Generate_Assets"]) -def test_object_properties(workflow_name: str, fastapi_client: TestClient): - reference = quote("http://purl.org/cognite/neat#_lazarevac") - graph_name = "source" - - # Act - response = fastapi_client.get( - f"/api/object-properties?reference={reference}&graph_name={graph_name}&workflow_name={workflow_name}" - ) - - content = response.json() - - assert response.status_code == 200 - assert "fields" in content, f"Missing fields got {content}" - assert content["fields"] == ["property", "value"] - assert { - "property": "http://iec.ch/TC57/2013/CIM-schema-cim16#IdentifiedObject.description", - "value": "Serbia", - } in content["rows"] - - -@pytest.mark.parametrize("workflow_name", ["Extract_RDF_Graph_and_Generate_Assets"]) -def test_search(workflow_name: str, fastapi_client: TestClient): - search_str = "Serbia" - graph_name = "source" - search_type = "value_exact_match" - - # Act - response = fastapi_client.get( - f"/api/search?search_str={search_str}&graph_name={graph_name}&search_type={search_type}&workflow_name={workflow_name}" - ) - - content = response.json() - - assert response.status_code == 200 - assert content["fields"] == ["object_ref", "type", "property", "value"] - assert { - "object_ref": "http://purl.org/cognite/neat#_lazarevac", - "type": "http://iec.ch/TC57/2013/CIM-schema-cim16#GeographicalRegion", - "property": "http://iec.ch/TC57/2013/CIM-schema-cim16#IdentifiedObject.description", - "value": "Serbia", - } in content["rows"] - - -@pytest.mark.parametrize("workflow_name", ["Extract_RDF_Graph_and_Generate_Assets"]) -def test_get_classes(workflow_name: str, fastapi_client: TestClient): - # Act - response = fastapi_client.get(f"/api/get-classes?graph_name=source&workflow_name={workflow_name}&cache=true") - - content = response.json() - - assert response.status_code == 200 - assert content["fields"] == ["class", "instances"] - assert { - "class": "http://iec.ch/TC57/2013/CIM-schema-cim16#Substation", - "instances": "45", - } in content["rows"] - assert len(content["rows"]) == 59 diff --git a/tests/tests_unit/app/api/test_workflows/Extract_RDF_Graph_and_Generate_Assets.yml b/tests/tests_unit/app/api/test_workflows/Extract_RDF_Graph_and_Generate_Assets.yml deleted file mode 100644 index f89a9293e..000000000 --- a/tests/tests_unit/app/api/test_workflows/Extract_RDF_Graph_and_Generate_Assets.yml +++ /dev/null @@ -1,15771 +0,0 @@ -assets: -- dataSetId: 2626756768281823 - externalId: 02b41435-bd3e-bb4b-9b89-24e3a8983c0f - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 02b41435-bd3e-bb4b-9b89-24e3a8983c0f - IdentifiedObject.name: T2 - Terminal.Substation: f176966a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 02b41435-bd3e-bb4b-9b89-24e3a8983c0f - type: Terminal - name: T2 - parentExternalId: f176966a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 0345061e-37c9-9a49-b632-2af77bdca3a2 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 0345061e-37c9-9a49-b632-2af77bdca3a2 - IdentifiedObject.name: KRISTIAN300KV1 BD_S T2 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 0345061e-37c9-9a49-b632-2af77bdca3a2 - type: Terminal - name: KRISTIAN300KV1 BD_S T2 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 0619c12e-abb9-b141-b3a7-e2788d3d375c - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 0619c12e-abb9-b141-b3a7-e2788d3d375c - IdentifiedObject.name: KRISTIAN300G1 AD_S T2 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 0619c12e-abb9-b141-b3a7-e2788d3d375c - type: Terminal - name: KRISTIAN300G1 AD_S T2 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 076da31b-080d-c742-9cb1-134a6ba5b1ed - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 076da31b-080d-c742-9cb1-134a6ba5b1ed - IdentifiedObject.name: ARENDAL 300KR1 BD_S T2 - Terminal.Substation: f1769670-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 076da31b-080d-c742-9cb1-134a6ba5b1ed - type: Terminal - name: ARENDAL 300KR1 BD_S T2 - parentExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 093a3df5-0583-894f-99c9-016d752ff920 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 093a3df5-0583-894f-99c9-016d752ff920 - IdentifiedObject.name: KRISTIAN300G3 BD_S T1 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 093a3df5-0583-894f-99c9-016d752ff920 - type: Terminal - name: KRISTIAN300G3 BD_S T1 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 0a122f63-f2c6-ca43-be49-76571474d98c - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 0a122f63-f2c6-ca43-be49-76571474d98c - IdentifiedObject.name: KRISTIAN300G2 BB_S T1 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 0a122f63-f2c6-ca43-be49-76571474d98c - type: Terminal - name: KRISTIAN300G2 BB_S T1 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 0b50ae74-92eb-f244-872b-07e629f53494 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 0b50ae74-92eb-f244-872b-07e629f53494 - IdentifiedObject.name: KRISTIAN300G1 AB_S T2 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 0b50ae74-92eb-f244-872b-07e629f53494 - type: Terminal - name: KRISTIAN300G1 AB_S T2 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 0d7f39d0-5447-fb42-9d4f-3d0775bceef0 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 0d7f39d0-5447-fb42-9d4f-3d0775bceef0 - IdentifiedObject.name: KRISTIAN300G4 AD_S T1 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 0d7f39d0-5447-fb42-9d4f-3d0775bceef0 - type: Terminal - name: KRISTIAN300G4 AD_S T1 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 0dae5d37-4d01-3645-9e94-607410c5ac34 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 0dae5d37-4d01-3645-9e94-607410c5ac34 - IdentifiedObject.name: KRISTIAN300L1 BB_S T2 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 0dae5d37-4d01-3645-9e94-607410c5ac34 - type: Terminal - name: KRISTIAN300L1 BB_S T2 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 0ef62251-5e2e-d14a-aa44-396d4a885c4f - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 0ef62251-5e2e-d14a-aa44-396d4a885c4f - IdentifiedObject.name: ARENDAL 300SC1 AB_S T2 - Terminal.Substation: f1769670-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 0ef62251-5e2e-d14a-aa44-396d4a885c4f - type: Terminal - name: ARENDAL 300SC1 AB_S T2 - parentExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 13116d72-3969-1344-91a7-14fb404a00c0 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 13116d72-3969-1344-91a7-14fb404a00c0 - IdentifiedObject.name: KRISTIAN300FE1 BD_S T1 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 13116d72-3969-1344-91a7-14fb404a00c0 - type: Terminal - name: KRISTIAN300FE1 BD_S T1 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 16659ede-d69f-1d48-bcf2-ebb2ee6c259e - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 16659ede-d69f-1d48-bcf2-ebb2ee6c259e - IdentifiedObject.name: KRISTIAN300G4 AB_S T2 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 16659ede-d69f-1d48-bcf2-ebb2ee6c259e - type: Terminal - name: KRISTIAN300G4 AB_S T2 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 1a6d8d77-f2ca-f043-b475-f7db6304fe3e - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 1a6d8d77-f2ca-f043-b475-f7db6304fe3e - IdentifiedObject.name: ARENDAL 420T1 AB_S T2 - Terminal.Substation: f1769670-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 1a6d8d77-f2ca-f043-b475-f7db6304fe3e - type: Terminal - name: ARENDAL 420T1 AB_S T2 - parentExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 1ef7a41b-2cff-6046-b48f-c60a16fd20b0 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 1ef7a41b-2cff-6046-b48f-c60a16fd20b0 - IdentifiedObject.name: ARENDAL 300KR1 BB_S T1 - Terminal.Substation: f1769670-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 1ef7a41b-2cff-6046-b48f-c60a16fd20b0 - type: Terminal - name: ARENDAL 300KR1 BB_S T1 - parentExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 22c3512a-fdf2-464c-b1f4-85d173fa2d18 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 22c3512a-fdf2-464c-b1f4-85d173fa2d18 - IdentifiedObject.name: ARENDAL 300KR2 BD_S T1 - Terminal.Substation: f1769670-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 22c3512a-fdf2-464c-b1f4-85d173fa2d18 - type: Terminal - name: ARENDAL 300KR2 BD_S T1 - parentExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 292835be-c432-734c-8c28-3b8c61c0c077 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 292835be-c432-734c-8c28-3b8c61c0c077 - IdentifiedObject.name: KRISTIAN300G3 BB_S T1 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 292835be-c432-734c-8c28-3b8c61c0c077 - type: Terminal - name: KRISTIAN300G3 BB_S T1 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2a67a243-6e4a-a84d-8aea-f250399166c9 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2a67a243-6e4a-a84d-8aea-f250399166c9 - IdentifiedObject.name: KRISTIAN300G1 BB_S T2 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2a67a243-6e4a-a84d-8aea-f250399166c9 - type: Terminal - name: KRISTIAN300G1 BB_S T2 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90156-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90156-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695aa-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90156-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695aa-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9015c-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9015c-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695b8-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9015c-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695b8-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9015e-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9015e-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695be-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9015e-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695be-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90162-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90162-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695c8-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90162-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695c8-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90166-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90166-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695d2-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90166-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695d2-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90168-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90168-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695d8-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90168-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695d8-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9016b-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9016b-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695df-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9016b-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695df-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9016d-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9016d-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695e5-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9016d-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695e5-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9016f-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9016f-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695eb-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9016f-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695eb-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90171-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90171-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695f1-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90171-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90173-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90173-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695f7-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90173-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695f7-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90176-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90176-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695fe-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90176-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695fe-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9017e-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9017e-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f1769614-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9017e-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f1769614-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90184-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90184-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f1769624-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90184-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f1769624-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90186-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90186-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f176962a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90186-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f176962a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90188-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90188-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f1769630-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90188-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f1769630-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9018a-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9018a-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f1769636-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9018a-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f1769636-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90196-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90196-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: KRISTIAN300 BS1 T1 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90196-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: KRISTIAN300 BS1 T1 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9019a-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9019a-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f1769664-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9019a-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f1769664-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9019c-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9019c-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f176966a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9019c-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f176966a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9019e-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9019e-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: ARENDAL 300 A T1 - Terminal.Substation: f1769670-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9019e-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: ARENDAL 300 A T1 - parentExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd901a0-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd901a0-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f1769676-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd901a0-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f1769676-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd901a2-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: Some Alias Name Which is not used since name exist - IdentifiedObject.mRID: 2dd901a2-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f176967c-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd901a2-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f176967c-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd901a4-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: Alias Name - IdentifiedObject.mRID: 2dd901a4-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: '' - Terminal.Substation: f1769682-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd901a4-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: Alias Name - parentExternalId: f1769682-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd901a8-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd901a8-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f176968e-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd901a8-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f176968e-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd901aa-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd901aa-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f1769694-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd901aa-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f1769694-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd901ae-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd901ae-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f176969e-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd901ae-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f176969e-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd901b2-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd901b2-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17696a8-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd901b2-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17696a8-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd901be-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd901be-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17696ca-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd901be-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd901c2-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd901c2-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17696d4-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd901c2-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17696d4-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd901c4-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd901c4-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17696da-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd901c4-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17696da-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd901c6-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd901c6-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695aa-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd901c6-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695aa-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd901c9-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd901c9-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695aa-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd901c9-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695aa-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd901cc-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd901cc-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695aa-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd901cc-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695aa-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd901cf-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd901cf-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695b8-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd901cf-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695b8-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd901d2-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd901d2-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695be-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd901d2-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695be-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd901d5-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd901d5-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695c8-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd901d5-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695c8-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd901d8-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd901d8-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695e5-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd901d8-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695e5-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd901db-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd901db-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695eb-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd901db-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695eb-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd901de-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd901de-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695eb-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd901de-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695eb-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd901e1-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd901e1-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695f1-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd901e1-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd901e4-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd901e4-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695f1-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd901e4-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd901e7-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd901e7-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695f1-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd901e7-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd901ea-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd901ea-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695f1-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd901ea-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd901ed-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd901ed-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695f7-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd901ed-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695f7-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd901f3-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd901f3-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f1769624-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd901f3-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f1769624-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd901ff-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd901ff-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: KRISTIAN300 L1 T1 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd901ff-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: KRISTIAN300 L1 T1 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90202-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90202-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: KRISTIAN300 L2 T1 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90202-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: KRISTIAN300 L2 T1 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90205-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90205-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f1769676-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90205-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f1769676-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90208-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90208-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f176967c-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90208-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f176967c-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9020b-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9020b-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f176968e-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9020b-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f176968e-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9020e-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9020e-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f176968e-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9020e-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f176968e-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90211-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90211-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f1769694-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90211-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f1769694-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90214-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90214-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f1769694-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90214-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f1769694-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90217-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90217-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f1769694-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90217-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f1769694-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9021a-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9021a-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f176969e-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9021a-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f176969e-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9021d-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9021d-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17696a8-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9021d-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17696a8-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90220-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90220-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17696a8-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90220-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17696a8-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90244-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90244-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17696ca-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90244-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90247-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90247-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17696ca-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90247-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9024a-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9024a-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17696ca-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9024a-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9024d-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9024d-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17696ca-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9024d-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90250-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90250-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17696d4-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90250-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17696d4-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90253-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90253-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17696da-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90253-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17696da-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90258-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90258-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695aa-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90258-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695aa-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9025b-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9025b-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695aa-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9025b-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695aa-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9025e-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9025e-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695aa-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9025e-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695aa-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90262-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90262-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695c8-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90262-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695c8-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90265-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90265-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695c8-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90265-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695c8-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90268-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90268-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695c8-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90268-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695c8-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9026b-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9026b-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695c8-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9026b-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695c8-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9026e-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9026e-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695c8-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9026e-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695c8-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90272-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90272-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695df-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90272-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695df-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90276-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90276-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695e5-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90276-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695e5-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90279-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90279-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695e5-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90279-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695e5-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9027c-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9027c-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695e5-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9027c-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695e5-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9027f-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9027f-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695e5-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9027f-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695e5-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90282-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90282-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695e5-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90282-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695e5-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90285-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90285-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695e5-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90285-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695e5-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90288-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90288-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695e5-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90288-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695e5-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9028b-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9028b-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695e5-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9028b-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695e5-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9028f-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9028f-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695eb-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9028f-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695eb-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90292-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90292-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695eb-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90292-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695eb-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90295-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90295-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695eb-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90295-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695eb-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90298-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90298-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695eb-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90298-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695eb-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9029b-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9029b-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695eb-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9029b-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695eb-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9029e-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9029e-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695eb-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9029e-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695eb-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd902a2-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd902a2-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695f1-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd902a2-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd902a5-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd902a5-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695f1-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd902a5-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd902a8-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd902a8-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695f1-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd902a8-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd902ab-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd902ab-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695f1-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd902ab-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd902ae-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd902ae-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695f1-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd902ae-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd902b1-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd902b1-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695f1-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd902b1-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd902bc-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd902bc-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f1769624-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd902bc-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f1769624-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd902bf-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd902bf-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f1769624-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd902bf-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f1769624-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd902c2-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd902c2-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f1769624-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd902c2-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f1769624-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd902c5-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd902c5-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f1769624-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd902c5-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f1769624-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd902c8-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd902c8-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f1769624-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd902c8-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f1769624-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd902cb-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd902cb-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f1769624-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd902cb-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f1769624-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd902dd-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd902dd-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: KRISTIAN300 M1 T1 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd902dd-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: KRISTIAN300 M1 T1 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd902e0-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd902e0-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: KRISTIAN300 M2 T1 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd902e0-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: KRISTIAN300 M2 T1 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd902e3-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd902e3-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: KRISTIAN300 M3 T1 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd902e3-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: KRISTIAN300 M3 T1 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd902e6-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd902e6-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: KRISTIAN300 M4 T1 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd902e6-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: KRISTIAN300 M4 T1 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd902ea-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd902ea-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f1769682-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd902ea-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f1769682-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd902ed-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd902ed-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f1769682-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd902ed-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f1769682-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd902f0-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd902f0-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f1769682-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd902f0-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f1769682-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd902f3-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd902f3-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f1769682-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd902f3-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f1769682-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd902f7-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd902f7-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f176968e-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd902f7-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f176968e-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd902fa-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd902fa-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f176968e-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd902fa-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f176968e-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd902fd-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd902fd-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f176968e-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd902fd-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f176968e-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90300-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90300-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f176968e-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90300-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f176968e-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90303-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90303-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f176968e-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90303-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f176968e-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90307-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90307-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f1769694-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90307-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f1769694-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9030a-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9030a-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f1769694-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9030a-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f1769694-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9030d-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9030d-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f1769694-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9030d-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f1769694-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90310-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90310-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f1769694-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90310-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f1769694-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90314-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90314-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f176969e-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90314-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f176969e-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90317-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90317-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f176969e-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90317-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f176969e-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9031a-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9031a-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f176969e-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9031a-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f176969e-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9031d-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9031d-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f176969e-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9031d-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f176969e-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90347-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90347-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17696ca-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90347-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9034a-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9034a-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17696ca-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9034a-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9034d-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9034d-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17696ca-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9034d-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90350-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90350-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17696ca-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90350-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90353-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90353-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17696ca-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90353-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90356-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90356-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17696ca-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90356-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90357-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90357-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695aa-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90357-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695aa-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90358-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90358-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T2 - Terminal.Substation: f17695b8-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90358-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T2 - parentExternalId: f17695b8-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9035a-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9035a-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695aa-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9035a-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695aa-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9035b-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9035b-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T2 - Terminal.Substation: f17695c8-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9035b-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T2 - parentExternalId: f17695c8-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9035d-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9035d-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695aa-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9035d-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695aa-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9035e-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9035e-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T2 - Terminal.Substation: f17695df-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9035e-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T2 - parentExternalId: f17695df-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90360-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90360-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695aa-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90360-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695aa-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90361-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90361-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T2 - Terminal.Substation: f17695df-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90361-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T2 - parentExternalId: f17695df-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90363-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90363-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695aa-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90363-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695aa-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90364-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90364-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T2 - Terminal.Substation: f17695eb-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90364-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T2 - parentExternalId: f17695eb-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90366-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90366-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695aa-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90366-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695aa-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90367-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90367-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T2 - Terminal.Substation: f17695eb-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90367-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T2 - parentExternalId: f17695eb-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90369-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90369-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695be-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90369-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695be-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9036a-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9036a-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T2 - Terminal.Substation: f17695c8-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9036a-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T2 - parentExternalId: f17695c8-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9036c-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9036c-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695be-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9036c-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695be-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9036d-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9036d-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T2 - Terminal.Substation: f17695d2-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9036d-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T2 - parentExternalId: f17695d2-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9036f-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9036f-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695be-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9036f-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695be-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90370-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90370-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T2 - Terminal.Substation: f17695d2-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90370-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T2 - parentExternalId: f17695d2-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90372-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90372-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695be-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90372-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695be-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90373-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90373-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T2 - Terminal.Substation: f17695d2-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90373-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T2 - parentExternalId: f17695d2-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90375-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90375-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695be-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90375-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695be-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90376-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90376-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T2 - Terminal.Substation: f17695e5-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90376-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T2 - parentExternalId: f17695e5-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90378-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90378-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695be-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90378-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695be-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90379-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90379-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T2 - Terminal.Substation: f17695f1-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90379-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T2 - parentExternalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9037b-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9037b-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695be-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9037b-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695be-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9037c-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9037c-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T2 - Terminal.Substation: f17695f1-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9037c-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T2 - parentExternalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9037e-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9037e-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695c8-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9037e-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695c8-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9037f-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9037f-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T2 - Terminal.Substation: f17695df-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9037f-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T2 - parentExternalId: f17695df-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90381-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90381-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695c8-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90381-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695c8-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90382-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90382-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T2 - Terminal.Substation: f17695e5-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90382-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T2 - parentExternalId: f17695e5-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90384-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90384-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695c8-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90384-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695c8-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90385-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90385-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T2 - Terminal.Substation: f17696a8-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90385-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T2 - parentExternalId: f17696a8-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90387-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90387-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695c8-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90387-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695c8-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9038a-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9038a-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695d2-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9038a-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695d2-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9038b-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9038b-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T2 - Terminal.Substation: f17695eb-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9038b-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T2 - parentExternalId: f17695eb-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9038d-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9038d-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695d2-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9038d-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695d2-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9038e-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9038e-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T2 - Terminal.Substation: f17695f1-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9038e-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T2 - parentExternalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90390-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90390-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695d2-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90390-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695d2-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90391-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90391-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T2 - Terminal.Substation: f17696ca-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90391-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T2 - parentExternalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90393-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90393-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695d8-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90393-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695d8-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90394-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90394-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T2 - Terminal.Substation: f1769694-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90394-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T2 - parentExternalId: f1769694-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90396-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90396-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695e5-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90396-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695e5-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90399-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90399-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695eb-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90399-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695eb-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9039a-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9039a-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T2 - Terminal.Substation: f17696ca-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9039a-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T2 - parentExternalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9039c-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9039c-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695eb-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9039c-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695eb-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9039d-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9039d-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T2 - Terminal.Substation: f17696ca-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9039d-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T2 - parentExternalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9039f-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9039f-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695f1-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9039f-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd903a2-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd903a2-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695f1-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd903a2-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd903a5-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd903a5-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695f1-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd903a5-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd903a6-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd903a6-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T2 - Terminal.Substation: f17696ca-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd903a6-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T2 - parentExternalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd903a8-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd903a8-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695f1-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd903a8-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd903a9-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd903a9-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T2 - Terminal.Substation: f17696ca-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd903a9-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T2 - parentExternalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd903ab-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd903ab-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695fe-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd903ab-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695fe-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd903ac-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd903ac-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T2 - Terminal.Substation: f176969e-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd903ac-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T2 - parentExternalId: f176969e-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd903b2-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd903b2-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T2 - Terminal.Substation: f1769694-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd903b2-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T2 - parentExternalId: f1769694-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd903b5-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd903b5-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T2 - Terminal.Substation: f1769614-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd903b5-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T2 - parentExternalId: f1769614-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd903c3-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd903c3-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f1769614-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd903c3-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f1769614-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd903c6-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd903c6-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f1769614-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd903c6-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f1769614-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd903c7-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd903c7-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T2 - Terminal.Substation: f1769630-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd903c7-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T2 - parentExternalId: f1769630-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd903c9-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd903c9-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f1769614-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd903c9-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f1769614-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd903ca-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd903ca-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T2 - Terminal.Substation: f1769688-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd903ca-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T2 - parentExternalId: f1769688-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd903cd-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd903cd-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f1769614-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd903cd-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f1769614-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd903d0-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd903d0-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f1769688-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd903d0-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f1769688-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd903d3-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd903d3-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T2 - Terminal.Substation: f1769630-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd903d3-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T2 - parentExternalId: f1769630-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd903d6-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd903d6-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T2 - Terminal.Substation: f1769630-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd903d6-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T2 - parentExternalId: f1769630-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd903d8-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd903d8-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f1769624-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd903d8-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f1769624-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd903d9-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd903d9-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T2 - Terminal.Substation: f176968e-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd903d9-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T2 - parentExternalId: f176968e-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd903db-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd903db-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f176962a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd903db-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f176962a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd903dc-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd903dc-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T2 - Terminal.Substation: f1769630-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd903dc-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T2 - parentExternalId: f1769630-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd903de-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd903de-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f176962a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd903de-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f176962a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd903df-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd903df-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T2 - Terminal.Substation: f1769636-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd903df-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T2 - parentExternalId: f1769636-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd903e1-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd903e1-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f176962a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd903e1-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f176962a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd903e2-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd903e2-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T2 - Terminal.Substation: f1769688-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd903e2-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T2 - parentExternalId: f1769688-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd903e4-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd903e4-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f1769630-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd903e4-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f1769630-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd903e5-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd903e5-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T2 - Terminal.Substation: f1769636-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd903e5-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T2 - parentExternalId: f1769636-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd903e7-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd903e7-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f1769630-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd903e7-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f1769630-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd903e8-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd903e8-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T2 - Terminal.Substation: f1769636-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd903e8-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T2 - parentExternalId: f1769636-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd903ee-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd903ee-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T2 - Terminal.Substation: f1769682-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd903ee-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T2 - parentExternalId: f1769682-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd903f4-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd903f4-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T2 - Terminal.Substation: f176966a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd903f4-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T2 - parentExternalId: f176966a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd903f7-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd903f7-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T2 - Terminal.Substation: f1769688-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd903f7-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T2 - parentExternalId: f1769688-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd903fd-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd903fd-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f1769688-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd903fd-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f1769688-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90400-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90400-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T2 - Terminal.Substation: f1769688-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90400-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T2 - parentExternalId: f1769688-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90403-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90403-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: 300ASKER-ARENDAL T1 - Terminal.Substation: f1769670-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90403-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: 300ASKER-ARENDAL T1 - parentExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90409-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90409-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: ARENDAL 300 LSC1 T1 - Terminal.Substation: f1769670-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90409-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: ARENDAL 300 LSC1 T1 - parentExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9040b-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9040b-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: 300KRISTIAN-STAVANGE_T1 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9040b-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: 300KRISTIAN-STAVANGE_T1 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9040c-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9040c-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T2 - Terminal.Substation: f1769664-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9040c-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T2 - parentExternalId: f1769664-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9040e-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9040e-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: 300KRISTIAN-ARENDAL_T1 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9040e-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: 300KRISTIAN-ARENDAL_T1 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9040f-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9040f-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: 300KRISTIAN-ARENDAL T2 - Terminal.Substation: f1769670-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9040f-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: 300KRISTIAN-ARENDAL T2 - parentExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90411-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90411-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: 300KRISTIAN-FEDA_T1 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90411-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: 300KRISTIAN-FEDA_T1 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90412-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90412-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T2 - Terminal.Substation: f176967c-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90412-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T2 - parentExternalId: f176967c-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90414-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90414-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: 300KRISTIAN-KVILLDAL_T1 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90414-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: 300KRISTIAN-KVILLDAL_T1 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90415-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90415-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T2 - Terminal.Substation: f1769682-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90415-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T2 - parentExternalId: f1769682-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90417-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90417-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: 300KRISTIAN-ARENDAL T1 - Terminal.Substation: f1769670-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90417-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: 300KRISTIAN-ARENDAL T1 - parentExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90418-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90418-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T2 - Terminal.Substation: f1769676-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90418-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T2 - parentExternalId: f1769676-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9041a-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9041a-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f1769682-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9041a-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f1769682-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9041b-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9041b-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T2 - Terminal.Substation: f176968e-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9041b-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T2 - parentExternalId: f176968e-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9041d-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9041d-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f1769694-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9041d-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f1769694-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9041e-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9041e-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T2 - Terminal.Substation: f176969e-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9041e-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T2 - parentExternalId: f176969e-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90420-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90420-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f1769694-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90420-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f1769694-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90421-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90421-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T2 - Terminal.Substation: f176969e-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90421-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T2 - parentExternalId: f176969e-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90432-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90432-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17696ca-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90432-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90433-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90433-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T2 - Terminal.Substation: f17696d4-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90433-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T2 - parentExternalId: f17696d4-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90435-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90435-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17696ca-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90435-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90436-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90436-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T2 - Terminal.Substation: f17696da-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90436-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T2 - parentExternalId: f17696da-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90439-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90439-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695d8-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90439-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695d8-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9043d-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9043d-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T2 - Terminal.Substation: f17695df-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9043d-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T2 - parentExternalId: f17695df-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90440-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90440-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695fe-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90440-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695fe-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90444-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90444-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T2 - Terminal.Substation: f17695e5-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90444-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T2 - parentExternalId: f17695e5-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90447-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90447-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f17695f1-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90447-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9044b-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9044b-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T2 - Terminal.Substation: f17695f7-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9044b-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T2 - parentExternalId: f17695f7-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90455-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90455-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f1769624-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90455-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f1769624-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90459-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90459-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T2 - Terminal.Substation: f176962a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90459-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T2 - parentExternalId: f176962a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90471-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90471-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f1769664-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90471-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f1769664-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90475-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90475-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T2 - Terminal.Substation: f1769688-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90475-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T2 - parentExternalId: f1769688-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90478-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90478-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: ARENDAL T1 T1 - Terminal.Substation: f1769670-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90478-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: ARENDAL T1 T1 - parentExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9047c-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9047c-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: ARENDAL T1 T2 - Terminal.Substation: f1769670-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9047c-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: ARENDAL T1 T2 - parentExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9047f-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9047f-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f1769682-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9047f-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f1769682-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90483-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90483-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T2 - Terminal.Substation: f1769688-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90483-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T2 - parentExternalId: f1769688-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd90486-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd90486-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T1 - Terminal.Substation: f176969e-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd90486-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T1 - parentExternalId: f176969e-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9048a-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2dd9048a-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: T2 - Terminal.Substation: f17696a8-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2dd9048a-bdfb-11e5-94fa-c8f73332c8f4 - type: Terminal - name: T2 - parentExternalId: f17696a8-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2dd9048c-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: GeographicalRegion - - externalId: non-historic - metadata: - IdentifiedObject.mRID: 2dd9048c-bdfb-11e5-94fa-c8f73332c8f4 - IdentifiedObject.name: 'NO' - RootCIMNode.node: root-node - active: 'true' - identifier: 2dd9048c-bdfb-11e5-94fa-c8f73332c8f4 - type: GeographicalRegion - name: 'NO' - parentExternalId: root-node -- dataSetId: 2626756768281823 - externalId: 2e64900b-94a8-6e40-bd63-3e69510c316f - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2e64900b-94a8-6e40-bd63-3e69510c316f - IdentifiedObject.name: ARENDAL 300AS1 AB_S T2 - Terminal.Substation: f1769670-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2e64900b-94a8-6e40-bd63-3e69510c316f - type: Terminal - name: ARENDAL 300AS1 AB_S T2 - parentExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2e98d68c-e0c5-6d47-b392-a1746743ef63 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2e98d68c-e0c5-6d47-b392-a1746743ef63 - IdentifiedObject.name: KRISTIAN300AR1 BB_S T1 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2e98d68c-e0c5-6d47-b392-a1746743ef63 - type: Terminal - name: KRISTIAN300AR1 BB_S T1 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 2f8c9437-e564-e948-8764-be3494565b1f - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 2f8c9437-e564-e948-8764-be3494565b1f - IdentifiedObject.name: KRISTIAN300G2 AD_S T2 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 2f8c9437-e564-e948-8764-be3494565b1f - type: Terminal - name: KRISTIAN300G2 AD_S T2 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 30ab0123-1669-1242-99ca-610e4bcdc6c5 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 30ab0123-1669-1242-99ca-610e4bcdc6c5 - IdentifiedObject.name: KRISTIAN300G2 BD_S T1 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 30ab0123-1669-1242-99ca-610e4bcdc6c5 - type: Terminal - name: KRISTIAN300G2 BD_S T1 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 329965e7-b5de-c54f-856a-358c9e1f9c50 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 329965e7-b5de-c54f-856a-358c9e1f9c50 - IdentifiedObject.name: KRISTIAN300ST1 AB_S T1 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 329965e7-b5de-c54f-856a-358c9e1f9c50 - type: Terminal - name: KRISTIAN300ST1 AB_S T1 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 356c6f11-da4d-3f4d-b3fd-49234106345c - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 356c6f11-da4d-3f4d-b3fd-49234106345c - IdentifiedObject.name: ARENDAL 300T1 AB_S T1 - Terminal.Substation: f1769670-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 356c6f11-da4d-3f4d-b3fd-49234106345c - type: Terminal - name: ARENDAL 300T1 AB_S T1 - parentExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 3718af45-a6ee-fb40-a4c6-f1118c114393 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 3718af45-a6ee-fb40-a4c6-f1118c114393 - IdentifiedObject.name: KRISTIAN300L1 AB_S T1 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 3718af45-a6ee-fb40-a4c6-f1118c114393 - type: Terminal - name: KRISTIAN300L1 AB_S T1 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 37c82b4c-7441-9946-98bf-7ade99732ecf - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 37c82b4c-7441-9946-98bf-7ade99732ecf - IdentifiedObject.name: KRISTIAN300KV1 BB_S T1 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 37c82b4c-7441-9946-98bf-7ade99732ecf - type: Terminal - name: KRISTIAN300KV1 BB_S T1 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 38b8e08a-112e-b046-8504-efef4beb0c17 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 38b8e08a-112e-b046-8504-efef4beb0c17 - IdentifiedObject.name: KRISTIAN300L1 AD_S T2 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 38b8e08a-112e-b046-8504-efef4beb0c17 - type: Terminal - name: KRISTIAN300L1 AD_S T2 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 3916ae31-9f3d-af4e-8d87-87d373d8200a - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 3916ae31-9f3d-af4e-8d87-87d373d8200a - IdentifiedObject.name: ARENDAL 300KR2 BB_S T1 - Terminal.Substation: f1769670-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 3916ae31-9f3d-af4e-8d87-87d373d8200a - type: Terminal - name: ARENDAL 300KR2 BB_S T1 - parentExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 40725e5a-33bf-6046-8d11-da19f9c726b4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 40725e5a-33bf-6046-8d11-da19f9c726b4 - IdentifiedObject.name: KRISTIAN300G2 BB_S T2 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 40725e5a-33bf-6046-8d11-da19f9c726b4 - type: Terminal - name: KRISTIAN300G2 BB_S T2 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 40c8340e-7ccf-d446-8a6b-bffa19ec88e6 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 40c8340e-7ccf-d446-8a6b-bffa19ec88e6 - IdentifiedObject.name: KRISTIAN300G3 BB_S T2 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 40c8340e-7ccf-d446-8a6b-bffa19ec88e6 - type: Terminal - name: KRISTIAN300G3 BB_S T2 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 4a5b7813-14d3-fa4d-8907-edcdce7dbfd9 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 4a5b7813-14d3-fa4d-8907-edcdce7dbfd9 - IdentifiedObject.name: KRISTIAN300FE1 AD_S T2 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 4a5b7813-14d3-fa4d-8907-edcdce7dbfd9 - type: Terminal - name: KRISTIAN300FE1 AD_S T2 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 51883d43-111e-f24b-89f7-0573426fa32f - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 51883d43-111e-f24b-89f7-0573426fa32f - IdentifiedObject.name: ARENDAL 300KR1 AD_S T1 - Terminal.Substation: f1769670-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 51883d43-111e-f24b-89f7-0573426fa32f - type: Terminal - name: ARENDAL 300KR1 AD_S T1 - parentExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 5831696b-e9e1-8e4a-a169-0e4e09f15f1f - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 5831696b-e9e1-8e4a-a169-0e4e09f15f1f - IdentifiedObject.name: KRISTIAN300FE1 BB_S T2 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 5831696b-e9e1-8e4a-a169-0e4e09f15f1f - type: Terminal - name: KRISTIAN300FE1 BB_S T2 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 5906b2a0-4ec9-1949-98b8-709380af0060 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 5906b2a0-4ec9-1949-98b8-709380af0060 - IdentifiedObject.name: ARENDAL 300KR1 AB_S T2 - Terminal.Substation: f1769670-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 5906b2a0-4ec9-1949-98b8-709380af0060 - type: Terminal - name: ARENDAL 300KR1 AB_S T2 - parentExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 5c3fa20d-4748-5e44-b915-481e6d2552cd - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 5c3fa20d-4748-5e44-b915-481e6d2552cd - IdentifiedObject.name: KRISTIAN300ST1 AD_S T2 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 5c3fa20d-4748-5e44-b915-481e6d2552cd - type: Terminal - name: KRISTIAN300ST1 AD_S T2 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 5ddb272d-00bd-d74e-8c96-e2049351c3e4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 5ddb272d-00bd-d74e-8c96-e2049351c3e4 - IdentifiedObject.name: KRISTIAN300L1 BD_S T2 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 5ddb272d-00bd-d74e-8c96-e2049351c3e4 - type: Terminal - name: KRISTIAN300L1 BD_S T2 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 5ffde614-bc34-384d-923e-e62ac9f3f584 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 5ffde614-bc34-384d-923e-e62ac9f3f584 - IdentifiedObject.name: KRISTIAN300AR1 AD_S T2 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 5ffde614-bc34-384d-923e-e62ac9f3f584 - type: Terminal - name: KRISTIAN300AR1 AD_S T2 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 669c1d0c-87dd-a943-9316-88308bd36f15 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 669c1d0c-87dd-a943-9316-88308bd36f15 - IdentifiedObject.name: 420ARENDAL-SANDEFJORD T1 - Terminal.Substation: f1769670-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 669c1d0c-87dd-a943-9316-88308bd36f15 - type: Terminal - name: 420ARENDAL-SANDEFJORD T1 - parentExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 67bc203e-13e4-534b-94b8-bf19f5b31080 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 67bc203e-13e4-534b-94b8-bf19f5b31080 - IdentifiedObject.name: KRISTIAN300AR1 BD_S T2 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 67bc203e-13e4-534b-94b8-bf19f5b31080 - type: Terminal - name: KRISTIAN300AR1 BD_S T2 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 67dfc70a-36cf-da4e-b97c-fd1eda9cd8ac - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 67dfc70a-36cf-da4e-b97c-fd1eda9cd8ac - IdentifiedObject.name: KRISTIAN300AR1 AB_S T1 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 67dfc70a-36cf-da4e-b97c-fd1eda9cd8ac - type: Terminal - name: KRISTIAN300AR1 AB_S T1 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 692809cf-1bb4-8642-9b70-ee677f063304 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 692809cf-1bb4-8642-9b70-ee677f063304 - IdentifiedObject.name: KRISTIAN300AR1 BB_S T2 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 692809cf-1bb4-8642-9b70-ee677f063304 - type: Terminal - name: KRISTIAN300AR1 BB_S T2 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 6abb96f5-a71f-8e41-b563-7c569789d6bd - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 6abb96f5-a71f-8e41-b563-7c569789d6bd - IdentifiedObject.name: KRISTIAN300G4 BD_S T1 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 6abb96f5-a71f-8e41-b563-7c569789d6bd - type: Terminal - name: KRISTIAN300G4 BD_S T1 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 6ac2f3cd-c906-0a4b-ba1b-5942e9c65307 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 6ac2f3cd-c906-0a4b-ba1b-5942e9c65307 - IdentifiedObject.name: KRISTIAN300L2 BD_S T2 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 6ac2f3cd-c906-0a4b-ba1b-5942e9c65307 - type: Terminal - name: KRISTIAN300L2 BD_S T2 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 6da89f37-cc6f-4e4e-8b20-975295017390 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 6da89f37-cc6f-4e4e-8b20-975295017390 - IdentifiedObject.name: ARENDAL 300KR2 AB_S T2 - Terminal.Substation: f1769670-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 6da89f37-cc6f-4e4e-8b20-975295017390 - type: Terminal - name: ARENDAL 300KR2 AB_S T2 - parentExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 70f1852f-186d-5e43-98bf-b69f028cfcc5 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 70f1852f-186d-5e43-98bf-b69f028cfcc5 - IdentifiedObject.name: KRISTIAN300L2 BD_S T1 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 70f1852f-186d-5e43-98bf-b69f028cfcc5 - type: Terminal - name: KRISTIAN300L2 BD_S T1 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 714c775a-246f-e34f-9a6b-667f4b66138c - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 714c775a-246f-e34f-9a6b-667f4b66138c - IdentifiedObject.name: KRISTIAN300L2 AB_S T2 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 714c775a-246f-e34f-9a6b-667f4b66138c - type: Terminal - name: KRISTIAN300L2 AB_S T2 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 71d09d39-a080-5d4f-86f3-7b3185e51b8e - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 71d09d39-a080-5d4f-86f3-7b3185e51b8e - IdentifiedObject.name: KRISTIAN300FE1 BB_S T1 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 71d09d39-a080-5d4f-86f3-7b3185e51b8e - type: Terminal - name: KRISTIAN300FE1 BB_S T1 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 761a49da-2b71-2d4e-877f-3cf7fa86ef63 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 761a49da-2b71-2d4e-877f-3cf7fa86ef63 - IdentifiedObject.name: ARENDAL 300T1 AB_S T2 - Terminal.Substation: f1769670-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 761a49da-2b71-2d4e-877f-3cf7fa86ef63 - type: Terminal - name: ARENDAL 300T1 AB_S T2 - parentExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 79887cf1-6c94-3f4f-a1f8-7ddef866d55f - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 79887cf1-6c94-3f4f-a1f8-7ddef866d55f - IdentifiedObject.name: KRISTIAN300KV1 BB_S T2 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 79887cf1-6c94-3f4f-a1f8-7ddef866d55f - type: Terminal - name: KRISTIAN300KV1 BB_S T2 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 7af49a35-a119-5443-84be-af343b7761c8 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 7af49a35-a119-5443-84be-af343b7761c8 - IdentifiedObject.name: KRISTIAN300G1 BD_S T2 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 7af49a35-a119-5443-84be-af343b7761c8 - type: Terminal - name: KRISTIAN300G1 BD_S T2 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 7d101d2b-ce05-9843-ab59-2c3e5e244cbb - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 7d101d2b-ce05-9843-ab59-2c3e5e244cbb - IdentifiedObject.name: KRISTIAN300AR1 AB_S T2 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 7d101d2b-ce05-9843-ab59-2c3e5e244cbb - type: Terminal - name: KRISTIAN300AR1 AB_S T2 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 7f811234-10fe-e34f-a9af-9af61a6dd4c7 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 7f811234-10fe-e34f-a9af-9af61a6dd4c7 - IdentifiedObject.name: KRISTIAN300FE1 AB_S T1 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 7f811234-10fe-e34f-a9af-9af61a6dd4c7 - type: Terminal - name: KRISTIAN300FE1 AB_S T1 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 84775b5d-95c1-bd46-bb0d-0f74e4c06757 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 84775b5d-95c1-bd46-bb0d-0f74e4c06757 - IdentifiedObject.name: KRISTIAN300KV1 BD_S T1 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 84775b5d-95c1-bd46-bb0d-0f74e4c06757 - type: Terminal - name: KRISTIAN300KV1 BD_S T1 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 854b40ee-7109-d04c-b4ef-f0665722e451 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 854b40ee-7109-d04c-b4ef-f0665722e451 - IdentifiedObject.name: KRISTIAN300G3 BD_S T2 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 854b40ee-7109-d04c-b4ef-f0665722e451 - type: Terminal - name: KRISTIAN300G3 BD_S T2 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 8574aebb-23c4-3e4a-bd04-b5651c97d27a - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 8574aebb-23c4-3e4a-bd04-b5651c97d27a - IdentifiedObject.name: KRISTIAN300L1 AD_S T1 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 8574aebb-23c4-3e4a-bd04-b5651c97d27a - type: Terminal - name: KRISTIAN300L1 AD_S T1 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 8a0babd6-6d6f-854b-b65f-4245e65c9e9c - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 8a0babd6-6d6f-854b-b65f-4245e65c9e9c - IdentifiedObject.name: ARENDAL 300AS1 BD_S T1 - Terminal.Substation: f1769670-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 8a0babd6-6d6f-854b-b65f-4245e65c9e9c - type: Terminal - name: ARENDAL 300AS1 BD_S T1 - parentExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 8d35b51b-06bd-7541-b3e6-2ddbbb1204fe - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 8d35b51b-06bd-7541-b3e6-2ddbbb1204fe - IdentifiedObject.name: KRISTIAN300L2 BB_S T2 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 8d35b51b-06bd-7541-b3e6-2ddbbb1204fe - type: Terminal - name: KRISTIAN300L2 BB_S T2 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 8e8e6b0c-ee96-7a4f-9790-c9ae62edafbe - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 8e8e6b0c-ee96-7a4f-9790-c9ae62edafbe - IdentifiedObject.name: KRISTIAN300AR1 BD_S T1 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 8e8e6b0c-ee96-7a4f-9790-c9ae62edafbe - type: Terminal - name: KRISTIAN300AR1 BD_S T1 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 8f8094c3-5c43-1241-9014-6c7a3ae10213 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 8f8094c3-5c43-1241-9014-6c7a3ae10213 - IdentifiedObject.name: ARENDAL 300 B T1 - Terminal.Substation: f1769670-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 8f8094c3-5c43-1241-9014-6c7a3ae10213 - type: Terminal - name: ARENDAL 300 B T1 - parentExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 925f3ab7-54ce-b748-887a-fc6ae23bbdc2 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 925f3ab7-54ce-b748-887a-fc6ae23bbdc2 - IdentifiedObject.name: KRISTIAN300KV1 AB_S T1 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 925f3ab7-54ce-b748-887a-fc6ae23bbdc2 - type: Terminal - name: KRISTIAN300KV1 AB_S T1 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 947636e9-e939-294e-a94b-2090060b74ca - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 947636e9-e939-294e-a94b-2090060b74ca - IdentifiedObject.name: KRISTIAN300G1 BD_S T1 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 947636e9-e939-294e-a94b-2090060b74ca - type: Terminal - name: KRISTIAN300G1 BD_S T1 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 9acd76b4-8d78-2b4b-81d5-e404e244997d - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 9acd76b4-8d78-2b4b-81d5-e404e244997d - IdentifiedObject.name: ARENDAL 300KR1 BD_S T1 - Terminal.Substation: f1769670-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 9acd76b4-8d78-2b4b-81d5-e404e244997d - type: Terminal - name: ARENDAL 300KR1 BD_S T1 - parentExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: 9b49634c-4910-9841-b595-0d0cb0d0d1de - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: 9b49634c-4910-9841-b595-0d0cb0d0d1de - IdentifiedObject.name: KRISTIAN300KV1 AD_S T1 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: 9b49634c-4910-9841-b595-0d0cb0d0d1de - type: Terminal - name: KRISTIAN300KV1 AD_S T1 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: a0ad0036-fbee-6646-b195-3a86614529f2 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: a0ad0036-fbee-6646-b195-3a86614529f2 - IdentifiedObject.name: KRISTIAN300KV1 AB_S T2 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: a0ad0036-fbee-6646-b195-3a86614529f2 - type: Terminal - name: KRISTIAN300KV1 AB_S T2 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: a2a690ef-b78a-3f46-90c5-5d434e26a64e - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: a2a690ef-b78a-3f46-90c5-5d434e26a64e - IdentifiedObject.name: KRISTIAN300G4 BD_S T2 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: a2a690ef-b78a-3f46-90c5-5d434e26a64e - type: Terminal - name: KRISTIAN300G4 BD_S T2 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: a4bc44aa-25e4-684a-ab2c-c7fc3378d9e5 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: a4bc44aa-25e4-684a-ab2c-c7fc3378d9e5 - IdentifiedObject.name: KRISTIAN300G4 AB_S T1 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: a4bc44aa-25e4-684a-ab2c-c7fc3378d9e5 - type: Terminal - name: KRISTIAN300G4 AB_S T1 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: a533d3fd-822f-3b40-9030-e18b571cd1ea - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: a533d3fd-822f-3b40-9030-e18b571cd1ea - IdentifiedObject.name: ARENDAL 300AS1 BD_S T2 - Terminal.Substation: f1769670-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: a533d3fd-822f-3b40-9030-e18b571cd1ea - type: Terminal - name: ARENDAL 300AS1 BD_S T2 - parentExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: a749d698-52a2-564e-8c6f-068fe62045fe - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: a749d698-52a2-564e-8c6f-068fe62045fe - IdentifiedObject.name: KRISTIAN300AR1 AD_S T1 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: a749d698-52a2-564e-8c6f-068fe62045fe - type: Terminal - name: KRISTIAN300AR1 AD_S T1 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: a949cafd-39f3-8e4c-bca5-9928b004b4da - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: a949cafd-39f3-8e4c-bca5-9928b004b4da - IdentifiedObject.name: ARENDAL 300KR2 AB_S T1 - Terminal.Substation: f1769670-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: a949cafd-39f3-8e4c-bca5-9928b004b4da - type: Terminal - name: ARENDAL 300KR2 AB_S T1 - parentExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: aadfa134-666a-ae4f-8c09-6f3ae114c3b5 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: aadfa134-666a-ae4f-8c09-6f3ae114c3b5 - IdentifiedObject.name: ARENDAL 300AS1 AD_S T1 - Terminal.Substation: f1769670-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: aadfa134-666a-ae4f-8c09-6f3ae114c3b5 - type: Terminal - name: ARENDAL 300AS1 AD_S T1 - parentExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: ab88a3a2-a572-3440-be06-757e0225ea42 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: ab88a3a2-a572-3440-be06-757e0225ea42 - IdentifiedObject.name: ARENDAL 300KR2 BD_S T2 - Terminal.Substation: f1769670-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: ab88a3a2-a572-3440-be06-757e0225ea42 - type: Terminal - name: ARENDAL 300KR2 BD_S T2 - parentExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: ad37e24a-9993-4f45-80ed-49a80b6a85d9 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: ad37e24a-9993-4f45-80ed-49a80b6a85d9 - IdentifiedObject.name: KRISTIAN300G2 BD_S T2 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: ad37e24a-9993-4f45-80ed-49a80b6a85d9 - type: Terminal - name: KRISTIAN300G2 BD_S T2 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: ad970e05-69d9-5340-9dbd-ce6a9a2e0996 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: ad970e05-69d9-5340-9dbd-ce6a9a2e0996 - IdentifiedObject.name: KRISTIAN300G1 AB_S T1 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: ad970e05-69d9-5340-9dbd-ce6a9a2e0996 - type: Terminal - name: KRISTIAN300G1 AB_S T1 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: ae68b597-69fb-e14b-a070-67c7dcc1d698 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: ae68b597-69fb-e14b-a070-67c7dcc1d698 - IdentifiedObject.name: KRISTIAN300L1 AB_S T2 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: ae68b597-69fb-e14b-a070-67c7dcc1d698 - type: Terminal - name: KRISTIAN300L1 AB_S T2 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: b016bc42-5a9f-ec42-b326-e9b9eed9e6d3 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: b016bc42-5a9f-ec42-b326-e9b9eed9e6d3 - IdentifiedObject.name: KRISTIAN300G3 AD_S T2 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: b016bc42-5a9f-ec42-b326-e9b9eed9e6d3 - type: Terminal - name: KRISTIAN300G3 AD_S T2 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: b3ff19da-83b8-5e40-b38c-38982b748732 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: b3ff19da-83b8-5e40-b38c-38982b748732 - IdentifiedObject.name: KRISTIAN300G2 AB_S T1 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: b3ff19da-83b8-5e40-b38c-38982b748732 - type: Terminal - name: KRISTIAN300G2 AB_S T1 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: b77c60e8-fe4f-9149-9bc6-c3eb8f899de2 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: b77c60e8-fe4f-9149-9bc6-c3eb8f899de2 - IdentifiedObject.name: ARENDAL 300KR2 BB_S T2 - Terminal.Substation: f1769670-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: b77c60e8-fe4f-9149-9bc6-c3eb8f899de2 - type: Terminal - name: ARENDAL 300KR2 BB_S T2 - parentExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: bc0f852a-3cc8-e143-8174-f0854a2d38aa - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: bc0f852a-3cc8-e143-8174-f0854a2d38aa - IdentifiedObject.name: KRISTIAN300KV1 AD_S T2 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: bc0f852a-3cc8-e143-8174-f0854a2d38aa - type: Terminal - name: KRISTIAN300KV1 AD_S T2 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: bff806b2-7825-af4c-a34b-be542dbe5ae6 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: bff806b2-7825-af4c-a34b-be542dbe5ae6 - IdentifiedObject.name: ARENDAL 300AS1 BB_S T1 - Terminal.Substation: f1769670-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: bff806b2-7825-af4c-a34b-be542dbe5ae6 - type: Terminal - name: ARENDAL 300AS1 BB_S T1 - parentExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: c1aa295d-e817-db46-b14e-bee2739fcd9d - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: c1aa295d-e817-db46-b14e-bee2739fcd9d - IdentifiedObject.name: KRISTIAN300L1 BB_S T1 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: c1aa295d-e817-db46-b14e-bee2739fcd9d - type: Terminal - name: KRISTIAN300L1 BB_S T1 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: c1ca1160-be67-9448-a16b-e92a6112e380 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: c1ca1160-be67-9448-a16b-e92a6112e380 - IdentifiedObject.name: KRISTIAN300G3 AB_S T1 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: c1ca1160-be67-9448-a16b-e92a6112e380 - type: Terminal - name: KRISTIAN300G3 AB_S T1 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: c3458e72-1c7a-a74b-b561-8afe0a3aa2be - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: c3458e72-1c7a-a74b-b561-8afe0a3aa2be - IdentifiedObject.name: ARENDAL 300KR2 AD_S T1 - Terminal.Substation: f1769670-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: c3458e72-1c7a-a74b-b561-8afe0a3aa2be - type: Terminal - name: ARENDAL 300KR2 AD_S T1 - parentExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: c8ac9d5f-d60e-a345-8790-12b86184c31b - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: c8ac9d5f-d60e-a345-8790-12b86184c31b - IdentifiedObject.name: KRISTIAN300ST1 BD_S T2 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: c8ac9d5f-d60e-a345-8790-12b86184c31b - type: Terminal - name: KRISTIAN300ST1 BD_S T2 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: c8f95fee-e9a6-a44b-97ac-23e272a05ecd - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: c8f95fee-e9a6-a44b-97ac-23e272a05ecd - IdentifiedObject.name: KRISTIAN300G3 AB_S T2 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: c8f95fee-e9a6-a44b-97ac-23e272a05ecd - type: Terminal - name: KRISTIAN300G3 AB_S T2 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: c95039b2-aea2-ad4d-b444-c6efc77461e5 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: c95039b2-aea2-ad4d-b444-c6efc77461e5 - IdentifiedObject.name: KRISTIAN300G4 BB_S T1 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: c95039b2-aea2-ad4d-b444-c6efc77461e5 - type: Terminal - name: KRISTIAN300G4 BB_S T1 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: caa10f15-4591-e241-ac0b-81675acf5687 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: caa10f15-4591-e241-ac0b-81675acf5687 - IdentifiedObject.name: KRISTIAN300KV1 BB_S T1 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: caa10f15-4591-e241-ac0b-81675acf5687 - type: Terminal - name: KRISTIAN300KV1 BB_S T1 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: cd25f47c-ad0c-f74d-8c0e-fd91a95505b6 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: cd25f47c-ad0c-f74d-8c0e-fd91a95505b6 - IdentifiedObject.name: KRISTIAN300ST1 BD_S T1 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: cd25f47c-ad0c-f74d-8c0e-fd91a95505b6 - type: Terminal - name: KRISTIAN300ST1 BD_S T1 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: cf654436-a05a-7948-8cc3-f3a288608964 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: cf654436-a05a-7948-8cc3-f3a288608964 - IdentifiedObject.name: KRISTIAN300L1 BD_S T1 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: cf654436-a05a-7948-8cc3-f3a288608964 - type: Terminal - name: KRISTIAN300L1 BD_S T1 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: d288bd96-ae6e-cf46-9775-f38e2699f1ce - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: d288bd96-ae6e-cf46-9775-f38e2699f1ce - IdentifiedObject.name: ARENDAL 300KR1 BB_S T2 - Terminal.Substation: f1769670-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: d288bd96-ae6e-cf46-9775-f38e2699f1ce - type: Terminal - name: ARENDAL 300KR1 BB_S T2 - parentExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: d28bc89e-0592-1440-9721-5cbe124c1f1b - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: d28bc89e-0592-1440-9721-5cbe124c1f1b - IdentifiedObject.name: ARENDAL 300SC1 AB_S T1 - Terminal.Substation: f1769670-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: d28bc89e-0592-1440-9721-5cbe124c1f1b - type: Terminal - name: ARENDAL 300SC1 AB_S T1 - parentExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: d7a472cc-6b91-564d-93b4-0f3c24f2342c - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: d7a472cc-6b91-564d-93b4-0f3c24f2342c - IdentifiedObject.name: KRISTIAN300G4 BB_S T2 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: d7a472cc-6b91-564d-93b4-0f3c24f2342c - type: Terminal - name: KRISTIAN300G4 BB_S T2 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: d92cb2eb-fd81-194c-9e43-618df025d6b4 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: d92cb2eb-fd81-194c-9e43-618df025d6b4 - IdentifiedObject.name: KRISTIAN300KV1 BB_S T2 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: d92cb2eb-fd81-194c-9e43-618df025d6b4 - type: Terminal - name: KRISTIAN300KV1 BB_S T2 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: da4b0f71-a251-f947-be12-2de53a2d272a - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: da4b0f71-a251-f947-be12-2de53a2d272a - IdentifiedObject.name: KRISTIAN300ST1 AD_S T1 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: da4b0f71-a251-f947-be12-2de53a2d272a - type: Terminal - name: KRISTIAN300ST1 AD_S T1 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: dae3bb4b-0a86-9243-99c1-a1d5b163774d - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: dae3bb4b-0a86-9243-99c1-a1d5b163774d - IdentifiedObject.name: KRISTIAN300G2 AB_S T2 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: dae3bb4b-0a86-9243-99c1-a1d5b163774d - type: Terminal - name: KRISTIAN300G2 AB_S T2 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: db724627-9dae-4748-b4e3-2daf627ffa17 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: db724627-9dae-4748-b4e3-2daf627ffa17 - IdentifiedObject.name: ARENDAL 420T1 AB_S T1 - Terminal.Substation: f1769670-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: db724627-9dae-4748-b4e3-2daf627ffa17 - type: Terminal - name: ARENDAL 420T1 AB_S T1 - parentExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: dc791b3b-fe63-a14b-b144-953748088613 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: dc791b3b-fe63-a14b-b144-953748088613 - IdentifiedObject.name: ARENDAL 300AS1 AB_S T1 - Terminal.Substation: f1769670-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: dc791b3b-fe63-a14b-b144-953748088613 - type: Terminal - name: ARENDAL 300AS1 AB_S T1 - parentExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: e035c87e-704d-404f-8ea8-91c62e0d7d8a - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: e035c87e-704d-404f-8ea8-91c62e0d7d8a - IdentifiedObject.name: KRISTIAN300L2 AD_S T2 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: e035c87e-704d-404f-8ea8-91c62e0d7d8a - type: Terminal - name: KRISTIAN300L2 AD_S T2 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: e25c03e3-96a7-a64f-b776-a155f009d5f8 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: e25c03e3-96a7-a64f-b776-a155f009d5f8 - IdentifiedObject.name: KRISTIAN300G2 AD_S T1 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: e25c03e3-96a7-a64f-b776-a155f009d5f8 - type: Terminal - name: KRISTIAN300G2 AD_S T1 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: e2f56599-a78e-494f-8db3-c0b0bdab1d70 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: e2f56599-a78e-494f-8db3-c0b0bdab1d70 - IdentifiedObject.name: ARENDAL 300KR1 AD_S T2 - Terminal.Substation: f1769670-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: e2f56599-a78e-494f-8db3-c0b0bdab1d70 - type: Terminal - name: ARENDAL 300KR1 AD_S T2 - parentExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: e3d56d8b-5f71-3042-9734-7118977e887c - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: e3d56d8b-5f71-3042-9734-7118977e887c - IdentifiedObject.name: KRISTIAN300FE1 AD_S T1 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: e3d56d8b-5f71-3042-9734-7118977e887c - type: Terminal - name: KRISTIAN300FE1 AD_S T1 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: e3ef93a1-723a-224e-8863-472e4f269633 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: e3ef93a1-723a-224e-8863-472e4f269633 - IdentifiedObject.name: KRISTIAN300L2 AB_S T1 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: e3ef93a1-723a-224e-8863-472e4f269633 - type: Terminal - name: KRISTIAN300L2 AB_S T1 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: e65c4130-3a4d-db42-9020-c9f7c447d473 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: e65c4130-3a4d-db42-9020-c9f7c447d473 - IdentifiedObject.name: KRISTIAN300G4 AD_S T2 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: e65c4130-3a4d-db42-9020-c9f7c447d473 - type: Terminal - name: KRISTIAN300G4 AD_S T2 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: ea014fdb-b96f-2a4b-b1df-d38e846d4941 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: ea014fdb-b96f-2a4b-b1df-d38e846d4941 - IdentifiedObject.name: KRISTIAN300 B BS T1 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: ea014fdb-b96f-2a4b-b1df-d38e846d4941 - type: Terminal - name: KRISTIAN300 B BS T1 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: eaf768b1-3d82-c64d-a1c6-5bd7f9a72751 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: eaf768b1-3d82-c64d-a1c6-5bd7f9a72751 - IdentifiedObject.name: KRISTIAN300L2 BB_S T1 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: eaf768b1-3d82-c64d-a1c6-5bd7f9a72751 - type: Terminal - name: KRISTIAN300L2 BB_S T1 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: eb8b2a90-9bc8-154f-92dd-3887e3676498 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: eb8b2a90-9bc8-154f-92dd-3887e3676498 - IdentifiedObject.name: ARENDAL 300AS1 AD_S T2 - Terminal.Substation: f1769670-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: eb8b2a90-9bc8-154f-92dd-3887e3676498 - type: Terminal - name: ARENDAL 300AS1 AD_S T2 - parentExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: eba035f1-7c96-0142-8561-37b28625abf7 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: eba035f1-7c96-0142-8561-37b28625abf7 - IdentifiedObject.name: ARENDAL 300KR2 AD_S T2 - Terminal.Substation: f1769670-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: eba035f1-7c96-0142-8561-37b28625abf7 - type: Terminal - name: ARENDAL 300KR2 AD_S T2 - parentExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: ecfaf384-4581-4249-8646-b5d31943ad61 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: ecfaf384-4581-4249-8646-b5d31943ad61 - IdentifiedObject.name: KRISTIAN300FE1 BD_S T2 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: ecfaf384-4581-4249-8646-b5d31943ad61 - type: Terminal - name: KRISTIAN300FE1 BD_S T2 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: ee4223ab-0538-dc4a-9b79-d463c048bb08 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: ee4223ab-0538-dc4a-9b79-d463c048bb08 - IdentifiedObject.name: ARENDAL 300AS1 BB_S T2 - Terminal.Substation: f1769670-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: ee4223ab-0538-dc4a-9b79-d463c048bb08 - type: Terminal - name: ARENDAL 300AS1 BB_S T2 - parentExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: efe663e2-7518-3044-88d4-209bf597536a - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: efe663e2-7518-3044-88d4-209bf597536a - IdentifiedObject.name: KRISTIAN300L2 AD_S T1 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: efe663e2-7518-3044-88d4-209bf597536a - type: Terminal - name: KRISTIAN300L2 AD_S T1 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: f08f910c-300e-8941-aa85-3106d93e3429 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: f08f910c-300e-8941-aa85-3106d93e3429 - IdentifiedObject.name: KRISTIAN300FE1 AB_S T2 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: f08f910c-300e-8941-aa85-3106d93e3429 - type: Terminal - name: KRISTIAN300FE1 AB_S T2 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: f17695aa-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: non-historic - - externalId: Substation - metadata: - IdentifiedObject.mRID: f17695aa-9aeb-11e5-91da-b8763fd99c5f - IdentifiedObject.name: FORSMARK - Substation.Region: f17695af-9aeb-11e5-91da-b8763fd99c5f - Substation.Terminal: 2dd90156-bdfb-11e5-94fa-c8f73332c8f4, 2dd901c6-bdfb-11e5-94fa-c8f73332c8f4, - 2dd901c9-bdfb-11e5-94fa-c8f73332c8f4, 2dd901cc-bdfb-11e5-94fa-c8f73332c8f4, - 2dd90258-bdfb-11e5-94fa-c8f73332c8f4, 2dd9025b-bdfb-11e5-94fa-c8f73332c8f4, - 2dd9025e-bdfb-11e5-94fa-c8f73332c8f4, 2dd90357-bdfb-11e5-94fa-c8f73332c8f4, - 2dd9035a-bdfb-11e5-94fa-c8f73332c8f4, 2dd9035d-bdfb-11e5-94fa-c8f73332c8f4, - 2dd90360-bdfb-11e5-94fa-c8f73332c8f4, 2dd90363-bdfb-11e5-94fa-c8f73332c8f4, - 2dd90366-bdfb-11e5-94fa-c8f73332c8f4 - active: 'true' - identifier: f17695aa-9aeb-11e5-91da-b8763fd99c5f - type: Substation - name: FORSMARK - parentExternalId: f17695af-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: f17695af-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: non-historic - - externalId: SubGeographicalRegion - metadata: - IdentifiedObject.mRID: f17695af-9aeb-11e5-91da-b8763fd99c5f - IdentifiedObject.name: SE3 SGR - SubGeographicalRegion.Region: 2dd9048c-bdfb-11e5-94fa-c8f73332c8f4 - active: 'true' - identifier: f17695af-9aeb-11e5-91da-b8763fd99c5f - type: SubGeographicalRegion - name: SE3 SGR - parentExternalId: 2dd9048c-bdfb-11e5-94fa-c8f73332c8f4 -- dataSetId: 2626756768281823 - externalId: f17695b8-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: non-historic - - externalId: Substation - metadata: - IdentifiedObject.mRID: f17695b8-9aeb-11e5-91da-b8763fd99c5f - IdentifiedObject.name: DANNEBO_HVDC - Substation.Region: f17695af-9aeb-11e5-91da-b8763fd99c5f - Substation.Terminal: 2dd9015c-bdfb-11e5-94fa-c8f73332c8f4, 2dd901cf-bdfb-11e5-94fa-c8f73332c8f4, - 2dd90358-bdfb-11e5-94fa-c8f73332c8f4 - active: 'true' - identifier: f17695b8-9aeb-11e5-91da-b8763fd99c5f - type: Substation - name: DANNEBO_HVDC - parentExternalId: f17695af-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: f17695be-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: non-historic - - externalId: Substation - metadata: - IdentifiedObject.mRID: f17695be-9aeb-11e5-91da-b8763fd99c5f - IdentifiedObject.name: HJALTA - Substation.Region: f17695c3-9aeb-11e5-91da-b8763fd99c5f - Substation.Terminal: 2dd9015e-bdfb-11e5-94fa-c8f73332c8f4, 2dd901d2-bdfb-11e5-94fa-c8f73332c8f4, - 2dd90369-bdfb-11e5-94fa-c8f73332c8f4, 2dd9036c-bdfb-11e5-94fa-c8f73332c8f4, - 2dd9036f-bdfb-11e5-94fa-c8f73332c8f4, 2dd90372-bdfb-11e5-94fa-c8f73332c8f4, - 2dd90375-bdfb-11e5-94fa-c8f73332c8f4, 2dd90378-bdfb-11e5-94fa-c8f73332c8f4, - 2dd9037b-bdfb-11e5-94fa-c8f73332c8f4 - active: 'true' - identifier: f17695be-9aeb-11e5-91da-b8763fd99c5f - type: Substation - name: HJALTA - parentExternalId: f17695c3-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: f17695c3-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: non-historic - - externalId: SubGeographicalRegion - metadata: - IdentifiedObject.mRID: f17695c3-9aeb-11e5-91da-b8763fd99c5f - IdentifiedObject.name: SE2 SGR - SubGeographicalRegion.Region: 2dd9048c-bdfb-11e5-94fa-c8f73332c8f4 - active: 'true' - identifier: f17695c3-9aeb-11e5-91da-b8763fd99c5f - type: SubGeographicalRegion - name: SE2 SGR - parentExternalId: 2dd9048c-bdfb-11e5-94fa-c8f73332c8f4 -- dataSetId: 2626756768281823 - externalId: f17695c8-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: non-historic - - externalId: Substation - metadata: - IdentifiedObject.mRID: f17695c8-9aeb-11e5-91da-b8763fd99c5f - IdentifiedObject.name: PORJUS - Substation.Region: f17695cd-9aeb-11e5-91da-b8763fd99c5f - Substation.Terminal: 2dd90162-bdfb-11e5-94fa-c8f73332c8f4, 2dd901d5-bdfb-11e5-94fa-c8f73332c8f4, - 2dd90262-bdfb-11e5-94fa-c8f73332c8f4, 2dd90265-bdfb-11e5-94fa-c8f73332c8f4, - 2dd90268-bdfb-11e5-94fa-c8f73332c8f4, 2dd9026b-bdfb-11e5-94fa-c8f73332c8f4, - 2dd9026e-bdfb-11e5-94fa-c8f73332c8f4, 2dd9035b-bdfb-11e5-94fa-c8f73332c8f4, - 2dd9036a-bdfb-11e5-94fa-c8f73332c8f4, 2dd9037e-bdfb-11e5-94fa-c8f73332c8f4, - 2dd90381-bdfb-11e5-94fa-c8f73332c8f4, 2dd90384-bdfb-11e5-94fa-c8f73332c8f4, - 2dd90387-bdfb-11e5-94fa-c8f73332c8f4 - active: 'true' - identifier: f17695c8-9aeb-11e5-91da-b8763fd99c5f - type: Substation - name: PORJUS - parentExternalId: f17695cd-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: f17695cd-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: non-historic - - externalId: SubGeographicalRegion - metadata: - IdentifiedObject.mRID: f17695cd-9aeb-11e5-91da-b8763fd99c5f - IdentifiedObject.name: SE1 SGR - SubGeographicalRegion.Region: 2dd9048c-bdfb-11e5-94fa-c8f73332c8f4 - active: 'true' - identifier: f17695cd-9aeb-11e5-91da-b8763fd99c5f - type: SubGeographicalRegion - name: SE1 SGR - parentExternalId: 2dd9048c-bdfb-11e5-94fa-c8f73332c8f4 -- dataSetId: 2626756768281823 - externalId: f17695d2-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: non-historic - - externalId: Substation - metadata: - IdentifiedObject.mRID: f17695d2-9aeb-11e5-91da-b8763fd99c5f - IdentifiedObject.name: TENHULT - Substation.Region: f17695af-9aeb-11e5-91da-b8763fd99c5f - Substation.Terminal: 2dd90166-bdfb-11e5-94fa-c8f73332c8f4, 2dd9036d-bdfb-11e5-94fa-c8f73332c8f4, - 2dd90370-bdfb-11e5-94fa-c8f73332c8f4, 2dd90373-bdfb-11e5-94fa-c8f73332c8f4, - 2dd9038a-bdfb-11e5-94fa-c8f73332c8f4, 2dd9038d-bdfb-11e5-94fa-c8f73332c8f4, - 2dd90390-bdfb-11e5-94fa-c8f73332c8f4 - active: 'true' - identifier: f17695d2-9aeb-11e5-91da-b8763fd99c5f - type: Substation - name: TENHULT - parentExternalId: f17695af-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: f17695d8-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: non-historic - - externalId: Substation - metadata: - IdentifiedObject.mRID: f17695d8-9aeb-11e5-91da-b8763fd99c5f - IdentifiedObject.name: HOGASEN - Substation.Region: f17695c3-9aeb-11e5-91da-b8763fd99c5f - Substation.Terminal: 2dd90168-bdfb-11e5-94fa-c8f73332c8f4, 2dd90393-bdfb-11e5-94fa-c8f73332c8f4, - 2dd90439-bdfb-11e5-94fa-c8f73332c8f4 - active: 'true' - identifier: f17695d8-9aeb-11e5-91da-b8763fd99c5f - type: Substation - name: HOGASEN - parentExternalId: f17695c3-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: f17695df-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: non-historic - - externalId: Substation - metadata: - IdentifiedObject.mRID: f17695df-9aeb-11e5-91da-b8763fd99c5f - IdentifiedObject.name: JARPSTROMMEN - Substation.Region: f17695c3-9aeb-11e5-91da-b8763fd99c5f - Substation.Terminal: 2dd9016b-bdfb-11e5-94fa-c8f73332c8f4, 2dd90272-bdfb-11e5-94fa-c8f73332c8f4, - 2dd9035e-bdfb-11e5-94fa-c8f73332c8f4, 2dd90361-bdfb-11e5-94fa-c8f73332c8f4, - 2dd9037f-bdfb-11e5-94fa-c8f73332c8f4, 2dd9043d-bdfb-11e5-94fa-c8f73332c8f4 - active: 'true' - identifier: f17695df-9aeb-11e5-91da-b8763fd99c5f - type: Substation - name: JARPSTROMMEN - parentExternalId: f17695c3-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: f17695e5-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: non-historic - - externalId: Substation - metadata: - IdentifiedObject.mRID: f17695e5-9aeb-11e5-91da-b8763fd99c5f - IdentifiedObject.name: GRUNDFORS - Substation.Region: f17695c3-9aeb-11e5-91da-b8763fd99c5f - Substation.Terminal: 2dd9016d-bdfb-11e5-94fa-c8f73332c8f4, 2dd901d8-bdfb-11e5-94fa-c8f73332c8f4, - 2dd90276-bdfb-11e5-94fa-c8f73332c8f4, 2dd90279-bdfb-11e5-94fa-c8f73332c8f4, - 2dd9027c-bdfb-11e5-94fa-c8f73332c8f4, 2dd9027f-bdfb-11e5-94fa-c8f73332c8f4, - 2dd90282-bdfb-11e5-94fa-c8f73332c8f4, 2dd90285-bdfb-11e5-94fa-c8f73332c8f4, - 2dd90288-bdfb-11e5-94fa-c8f73332c8f4, 2dd9028b-bdfb-11e5-94fa-c8f73332c8f4, - 2dd90376-bdfb-11e5-94fa-c8f73332c8f4, 2dd90382-bdfb-11e5-94fa-c8f73332c8f4, - 2dd90396-bdfb-11e5-94fa-c8f73332c8f4, 2dd90444-bdfb-11e5-94fa-c8f73332c8f4 - active: 'true' - identifier: f17695e5-9aeb-11e5-91da-b8763fd99c5f - type: Substation - name: GRUNDFORS - parentExternalId: f17695c3-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: f17695eb-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: non-historic - - externalId: Substation - metadata: - IdentifiedObject.mRID: f17695eb-9aeb-11e5-91da-b8763fd99c5f - IdentifiedObject.name: OSKARSHAMN - Substation.Region: f17695af-9aeb-11e5-91da-b8763fd99c5f - Substation.Terminal: 2dd9016f-bdfb-11e5-94fa-c8f73332c8f4, 2dd901db-bdfb-11e5-94fa-c8f73332c8f4, - 2dd901de-bdfb-11e5-94fa-c8f73332c8f4, 2dd9028f-bdfb-11e5-94fa-c8f73332c8f4, - 2dd90292-bdfb-11e5-94fa-c8f73332c8f4, 2dd90295-bdfb-11e5-94fa-c8f73332c8f4, - 2dd90298-bdfb-11e5-94fa-c8f73332c8f4, 2dd9029b-bdfb-11e5-94fa-c8f73332c8f4, - 2dd9029e-bdfb-11e5-94fa-c8f73332c8f4, 2dd90364-bdfb-11e5-94fa-c8f73332c8f4, - 2dd90367-bdfb-11e5-94fa-c8f73332c8f4, 2dd9038b-bdfb-11e5-94fa-c8f73332c8f4, - 2dd90399-bdfb-11e5-94fa-c8f73332c8f4, 2dd9039c-bdfb-11e5-94fa-c8f73332c8f4 - active: 'true' - identifier: f17695eb-9aeb-11e5-91da-b8763fd99c5f - type: Substation - name: OSKARSHAMN - parentExternalId: f17695af-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: non-historic - - externalId: Substation - metadata: - IdentifiedObject.mRID: f17695f1-9aeb-11e5-91da-b8763fd99c5f - IdentifiedObject.name: RINGHALS - Substation.Region: f17695af-9aeb-11e5-91da-b8763fd99c5f - Substation.Terminal: 2dd90171-bdfb-11e5-94fa-c8f73332c8f4, 2dd901e1-bdfb-11e5-94fa-c8f73332c8f4, - 2dd901e4-bdfb-11e5-94fa-c8f73332c8f4, 2dd901e7-bdfb-11e5-94fa-c8f73332c8f4, - 2dd901ea-bdfb-11e5-94fa-c8f73332c8f4, 2dd902a2-bdfb-11e5-94fa-c8f73332c8f4, - 2dd902a5-bdfb-11e5-94fa-c8f73332c8f4, 2dd902a8-bdfb-11e5-94fa-c8f73332c8f4, - 2dd902ab-bdfb-11e5-94fa-c8f73332c8f4, 2dd902ae-bdfb-11e5-94fa-c8f73332c8f4, - 2dd902b1-bdfb-11e5-94fa-c8f73332c8f4, 2dd90379-bdfb-11e5-94fa-c8f73332c8f4, - 2dd9037c-bdfb-11e5-94fa-c8f73332c8f4, 2dd9038e-bdfb-11e5-94fa-c8f73332c8f4, - 2dd9039f-bdfb-11e5-94fa-c8f73332c8f4, 2dd903a2-bdfb-11e5-94fa-c8f73332c8f4, - 2dd903a5-bdfb-11e5-94fa-c8f73332c8f4, 2dd903a8-bdfb-11e5-94fa-c8f73332c8f4, - 2dd90447-bdfb-11e5-94fa-c8f73332c8f4 - active: 'true' - identifier: f17695f1-9aeb-11e5-91da-b8763fd99c5f - type: Substation - name: RINGHALS - parentExternalId: f17695af-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: f17695f7-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: non-historic - - externalId: Substation - metadata: - IdentifiedObject.mRID: f17695f7-9aeb-11e5-91da-b8763fd99c5f - IdentifiedObject.name: STENKU_HVDC - Substation.Region: f17695af-9aeb-11e5-91da-b8763fd99c5f - Substation.Terminal: 2dd90173-bdfb-11e5-94fa-c8f73332c8f4, 2dd901ed-bdfb-11e5-94fa-c8f73332c8f4, - 2dd9044b-bdfb-11e5-94fa-c8f73332c8f4 - active: 'true' - identifier: f17695f7-9aeb-11e5-91da-b8763fd99c5f - type: Substation - name: STENKU_HVDC - parentExternalId: f17695af-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: f17695fe-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: non-historic - - externalId: Substation - metadata: - IdentifiedObject.mRID: f17695fe-9aeb-11e5-91da-b8763fd99c5f - IdentifiedObject.name: AJAURE - Substation.Region: f17695c3-9aeb-11e5-91da-b8763fd99c5f - Substation.Terminal: 2dd90176-bdfb-11e5-94fa-c8f73332c8f4, 2dd903ab-bdfb-11e5-94fa-c8f73332c8f4, - 2dd90440-bdfb-11e5-94fa-c8f73332c8f4 - active: 'true' - identifier: f17695fe-9aeb-11e5-91da-b8763fd99c5f - type: Substation - name: AJAURE - parentExternalId: f17695c3-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: f1769614-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: non-historic - - externalId: Substation - metadata: - IdentifiedObject.mRID: f1769614-9aeb-11e5-91da-b8763fd99c5f - IdentifiedObject.name: DAGALI - Substation.Region: f1769619-9aeb-11e5-91da-b8763fd99c5f - Substation.Terminal: 2dd9017e-bdfb-11e5-94fa-c8f73332c8f4, 2dd903b5-bdfb-11e5-94fa-c8f73332c8f4, - 2dd903c3-bdfb-11e5-94fa-c8f73332c8f4, 2dd903c6-bdfb-11e5-94fa-c8f73332c8f4, - 2dd903c9-bdfb-11e5-94fa-c8f73332c8f4, 2dd903cd-bdfb-11e5-94fa-c8f73332c8f4 - active: 'true' - identifier: f1769614-9aeb-11e5-91da-b8763fd99c5f - type: Substation - name: DAGALI - parentExternalId: f1769619-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: f1769619-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: non-historic - - externalId: SubGeographicalRegion - metadata: - IdentifiedObject.mRID: f1769619-9aeb-11e5-91da-b8763fd99c5f - IdentifiedObject.name: NO5 SGR - SubGeographicalRegion.Region: 2dd9048c-bdfb-11e5-94fa-c8f73332c8f4 - active: 'true' - identifier: f1769619-9aeb-11e5-91da-b8763fd99c5f - type: SubGeographicalRegion - name: NO5 SGR - parentExternalId: 2dd9048c-bdfb-11e5-94fa-c8f73332c8f4 -- dataSetId: 2626756768281823 - externalId: f1769624-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: non-historic - - externalId: Substation - metadata: - IdentifiedObject.mRID: f1769624-9aeb-11e5-91da-b8763fd99c5f - IdentifiedObject.name: SIMA - Substation.Region: f1769619-9aeb-11e5-91da-b8763fd99c5f - Substation.Terminal: 2dd90184-bdfb-11e5-94fa-c8f73332c8f4, 2dd901f3-bdfb-11e5-94fa-c8f73332c8f4, - 2dd902bc-bdfb-11e5-94fa-c8f73332c8f4, 2dd902bf-bdfb-11e5-94fa-c8f73332c8f4, - 2dd902c2-bdfb-11e5-94fa-c8f73332c8f4, 2dd902c5-bdfb-11e5-94fa-c8f73332c8f4, - 2dd902c8-bdfb-11e5-94fa-c8f73332c8f4, 2dd902cb-bdfb-11e5-94fa-c8f73332c8f4, - 2dd903d8-bdfb-11e5-94fa-c8f73332c8f4, 2dd90455-bdfb-11e5-94fa-c8f73332c8f4 - active: 'true' - identifier: f1769624-9aeb-11e5-91da-b8763fd99c5f - type: Substation - name: SIMA - parentExternalId: f1769619-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: f176962a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: non-historic - - externalId: Substation - metadata: - IdentifiedObject.mRID: f176962a-9aeb-11e5-91da-b8763fd99c5f - IdentifiedObject.name: AURLAND - Substation.Region: f1769619-9aeb-11e5-91da-b8763fd99c5f - Substation.Terminal: 2dd90186-bdfb-11e5-94fa-c8f73332c8f4, 2dd903db-bdfb-11e5-94fa-c8f73332c8f4, - 2dd903de-bdfb-11e5-94fa-c8f73332c8f4, 2dd903e1-bdfb-11e5-94fa-c8f73332c8f4, - 2dd90459-bdfb-11e5-94fa-c8f73332c8f4 - active: 'true' - identifier: f176962a-9aeb-11e5-91da-b8763fd99c5f - type: Substation - name: AURLAND - parentExternalId: f1769619-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: f1769630-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: non-historic - - externalId: Substation - metadata: - IdentifiedObject.mRID: f1769630-9aeb-11e5-91da-b8763fd99c5f - IdentifiedObject.name: GEILO - Substation.Region: f1769619-9aeb-11e5-91da-b8763fd99c5f - Substation.Terminal: 2dd90188-bdfb-11e5-94fa-c8f73332c8f4, 2dd903c7-bdfb-11e5-94fa-c8f73332c8f4, - 2dd903d3-bdfb-11e5-94fa-c8f73332c8f4, 2dd903d6-bdfb-11e5-94fa-c8f73332c8f4, - 2dd903dc-bdfb-11e5-94fa-c8f73332c8f4, 2dd903e4-bdfb-11e5-94fa-c8f73332c8f4, - 2dd903e7-bdfb-11e5-94fa-c8f73332c8f4 - active: 'true' - identifier: f1769630-9aeb-11e5-91da-b8763fd99c5f - type: Substation - name: GEILO - parentExternalId: f1769619-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: f1769636-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: non-historic - - externalId: Substation - metadata: - IdentifiedObject.mRID: f1769636-9aeb-11e5-91da-b8763fd99c5f - IdentifiedObject.name: EIDFJORD - Substation.Region: f1769619-9aeb-11e5-91da-b8763fd99c5f - Substation.Terminal: 2dd9018a-bdfb-11e5-94fa-c8f73332c8f4, 2dd903df-bdfb-11e5-94fa-c8f73332c8f4, - 2dd903e5-bdfb-11e5-94fa-c8f73332c8f4, 2dd903e8-bdfb-11e5-94fa-c8f73332c8f4 - active: 'true' - identifier: f1769636-9aeb-11e5-91da-b8763fd99c5f - type: Substation - name: EIDFJORD - parentExternalId: f1769619-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: non-historic - - externalId: Substation - metadata: - IdentifiedObject.mRID: f176965a-9aeb-11e5-91da-b8763fd99c5f - IdentifiedObject.name: KRISTIANSAND - Substation.Region: f176965f-9aeb-11e5-91da-b8763fd99c5f - Substation.Terminal: 0345061e-37c9-9a49-b632-2af77bdca3a2, 0619c12e-abb9-b141-b3a7-e2788d3d375c, - 093a3df5-0583-894f-99c9-016d752ff920, 0a122f63-f2c6-ca43-be49-76571474d98c, - 0b50ae74-92eb-f244-872b-07e629f53494, 0d7f39d0-5447-fb42-9d4f-3d0775bceef0, - 0dae5d37-4d01-3645-9e94-607410c5ac34, 13116d72-3969-1344-91a7-14fb404a00c0, - 16659ede-d69f-1d48-bcf2-ebb2ee6c259e, 292835be-c432-734c-8c28-3b8c61c0c077, - 2a67a243-6e4a-a84d-8aea-f250399166c9, 2dd90196-bdfb-11e5-94fa-c8f73332c8f4, - 2dd901ff-bdfb-11e5-94fa-c8f73332c8f4, 2dd90202-bdfb-11e5-94fa-c8f73332c8f4, - 2dd902dd-bdfb-11e5-94fa-c8f73332c8f4, 2dd902e0-bdfb-11e5-94fa-c8f73332c8f4, - 2dd902e3-bdfb-11e5-94fa-c8f73332c8f4, 2dd902e6-bdfb-11e5-94fa-c8f73332c8f4, - 2dd9040b-bdfb-11e5-94fa-c8f73332c8f4, 2dd9040e-bdfb-11e5-94fa-c8f73332c8f4, - 2dd90411-bdfb-11e5-94fa-c8f73332c8f4, 2dd90414-bdfb-11e5-94fa-c8f73332c8f4, - 2e98d68c-e0c5-6d47-b392-a1746743ef63, 2f8c9437-e564-e948-8764-be3494565b1f, - 30ab0123-1669-1242-99ca-610e4bcdc6c5, 329965e7-b5de-c54f-856a-358c9e1f9c50, - 3718af45-a6ee-fb40-a4c6-f1118c114393, 37c82b4c-7441-9946-98bf-7ade99732ecf, - 38b8e08a-112e-b046-8504-efef4beb0c17, 40725e5a-33bf-6046-8d11-da19f9c726b4, - 40c8340e-7ccf-d446-8a6b-bffa19ec88e6, 4a5b7813-14d3-fa4d-8907-edcdce7dbfd9, - 5831696b-e9e1-8e4a-a169-0e4e09f15f1f, 5c3fa20d-4748-5e44-b915-481e6d2552cd, - 5ddb272d-00bd-d74e-8c96-e2049351c3e4, 5ffde614-bc34-384d-923e-e62ac9f3f584, - 67bc203e-13e4-534b-94b8-bf19f5b31080, 67dfc70a-36cf-da4e-b97c-fd1eda9cd8ac, - 692809cf-1bb4-8642-9b70-ee677f063304, 6abb96f5-a71f-8e41-b563-7c569789d6bd, - 6ac2f3cd-c906-0a4b-ba1b-5942e9c65307, 70f1852f-186d-5e43-98bf-b69f028cfcc5, - 714c775a-246f-e34f-9a6b-667f4b66138c, 71d09d39-a080-5d4f-86f3-7b3185e51b8e, - 79887cf1-6c94-3f4f-a1f8-7ddef866d55f, 7af49a35-a119-5443-84be-af343b7761c8, - 7d101d2b-ce05-9843-ab59-2c3e5e244cbb, 7f811234-10fe-e34f-a9af-9af61a6dd4c7, - 84775b5d-95c1-bd46-bb0d-0f74e4c06757, 854b40ee-7109-d04c-b4ef-f0665722e451, - 8574aebb-23c4-3e4a-bd04-b5651c97d27a, 8d35b51b-06bd-7541-b3e6-2ddbbb1204fe, - 8e8e6b0c-ee96-7a4f-9790-c9ae62edafbe, 925f3ab7-54ce-b748-887a-fc6ae23bbdc2, - 947636e9-e939-294e-a94b-2090060b74ca, 9b49634c-4910-9841-b595-0d0cb0d0d1de, - a0ad0036-fbee-6646-b195-3a86614529f2, a2a690ef-b78a-3f46-90c5-5d434e26a64e, - a4bc44aa-25e4-684a-ab2c-c7fc3378d9e5, a749d698-52a2-564e-8c6f-068fe62045fe, - ad37e24a-9993-4f45-80ed-49a80b6a85d9, ad970e05-69d9-5340-9dbd-ce6a9a2e0996, - ae68b597-69fb-e14b-a070-67c7dcc1d698, b016bc42-5a9f-ec42-b326-e9b9eed9e6d3, - b3ff19da-83b8-5e40-b38c-38982b748732, bc0f852a-3cc8-e143-8174-f0854a2d38aa, - c1aa295d-e817-db46-b14e-bee2739fcd9d, c1ca1160-be67-9448-a16b-e92a6112e380, - c8ac9d5f-d60e-a345-8790-12b86184c31b, c8f95fee-e9a6-a44b-97ac-23e272a05ecd, - c95039b2-aea2-ad4d-b444-c6efc77461e5, caa10f15-4591-e241-ac0b-81675acf5687, - cd25f47c-ad0c-f74d-8c0e-fd91a95505b6, cf654436-a05a-7948-8cc3-f3a288608964, - d7a472cc-6b91-564d-93b4-0f3c24f2342c, d92cb2eb-fd81-194c-9e43-618df025d6b4, - da4b0f71-a251-f947-be12-2de53a2d272a, dae3bb4b-0a86-9243-99c1-a1d5b163774d, - e035c87e-704d-404f-8ea8-91c62e0d7d8a, e25c03e3-96a7-a64f-b776-a155f009d5f8, - e3d56d8b-5f71-3042-9734-7118977e887c, e3ef93a1-723a-224e-8863-472e4f269633, - e65c4130-3a4d-db42-9020-c9f7c447d473, ea014fdb-b96f-2a4b-b1df-d38e846d4941, - eaf768b1-3d82-c64d-a1c6-5bd7f9a72751, ecfaf384-4581-4249-8646-b5d31943ad61, - efe663e2-7518-3044-88d4-209bf597536a, f08f910c-300e-8941-aa85-3106d93e3429, - f4dba262-3ff3-9f41-961d-7fbe1f8a0cd7, f710cc4a-50dd-e443-8ac7-4c56b70041e8, - f723530d-ae1e-df4f-9c7f-21e42da147d6, f9e8d6d1-76e1-6c4d-a640-e128ecfa723e - active: 'true' - identifier: f176965a-9aeb-11e5-91da-b8763fd99c5f - type: Substation - name: KRISTIANSAND - parentExternalId: f176965f-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: f176965f-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: non-historic - - externalId: SubGeographicalRegion - metadata: - IdentifiedObject.mRID: f176965f-9aeb-11e5-91da-b8763fd99c5f - IdentifiedObject.name: NO2 SGR - SubGeographicalRegion.Region: 2dd9048c-bdfb-11e5-94fa-c8f73332c8f4 - active: 'true' - identifier: f176965f-9aeb-11e5-91da-b8763fd99c5f - type: SubGeographicalRegion - name: NO2 SGR - parentExternalId: 2dd9048c-bdfb-11e5-94fa-c8f73332c8f4 -- dataSetId: 2626756768281823 - externalId: f1769664-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: non-historic - - externalId: Substation - metadata: - IdentifiedObject.mRID: f1769664-9aeb-11e5-91da-b8763fd99c5f - IdentifiedObject.name: STAVANGER - Substation.Region: f176965f-9aeb-11e5-91da-b8763fd99c5f - Substation.Terminal: 2dd9019a-bdfb-11e5-94fa-c8f73332c8f4, 2dd9040c-bdfb-11e5-94fa-c8f73332c8f4, - 2dd90471-bdfb-11e5-94fa-c8f73332c8f4 - active: 'true' - identifier: f1769664-9aeb-11e5-91da-b8763fd99c5f - type: Substation - name: STAVANGER - parentExternalId: f176965f-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: f176966a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: non-historic - - externalId: Substation - metadata: - IdentifiedObject.mRID: f176966a-9aeb-11e5-91da-b8763fd99c5f - IdentifiedObject.name: SANDEFJORD - Substation.Region: f176965f-9aeb-11e5-91da-b8763fd99c5f - Substation.Terminal: 02b41435-bd3e-bb4b-9b89-24e3a8983c0f, 2dd9019c-bdfb-11e5-94fa-c8f73332c8f4, - 2dd903f4-bdfb-11e5-94fa-c8f73332c8f4 - active: 'true' - identifier: f176966a-9aeb-11e5-91da-b8763fd99c5f - type: Substation - name: SANDEFJORD - parentExternalId: f176965f-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: non-historic - - externalId: Substation - metadata: - IdentifiedObject.mRID: f1769670-9aeb-11e5-91da-b8763fd99c5f - IdentifiedObject.name: ARENDAL - Substation.Region: f176965f-9aeb-11e5-91da-b8763fd99c5f - Substation.Terminal: 076da31b-080d-c742-9cb1-134a6ba5b1ed, 0ef62251-5e2e-d14a-aa44-396d4a885c4f, - 1a6d8d77-f2ca-f043-b475-f7db6304fe3e, 1ef7a41b-2cff-6046-b48f-c60a16fd20b0, - 22c3512a-fdf2-464c-b1f4-85d173fa2d18, 2dd9019e-bdfb-11e5-94fa-c8f73332c8f4, - 2dd90403-bdfb-11e5-94fa-c8f73332c8f4, 2dd90409-bdfb-11e5-94fa-c8f73332c8f4, - 2dd9040f-bdfb-11e5-94fa-c8f73332c8f4, 2dd90417-bdfb-11e5-94fa-c8f73332c8f4, - 2dd90478-bdfb-11e5-94fa-c8f73332c8f4, 2dd9047c-bdfb-11e5-94fa-c8f73332c8f4, - 2e64900b-94a8-6e40-bd63-3e69510c316f, 356c6f11-da4d-3f4d-b3fd-49234106345c, - 3916ae31-9f3d-af4e-8d87-87d373d8200a, 51883d43-111e-f24b-89f7-0573426fa32f, - 5906b2a0-4ec9-1949-98b8-709380af0060, 669c1d0c-87dd-a943-9316-88308bd36f15, - 6da89f37-cc6f-4e4e-8b20-975295017390, 761a49da-2b71-2d4e-877f-3cf7fa86ef63, - 8a0babd6-6d6f-854b-b65f-4245e65c9e9c, 8f8094c3-5c43-1241-9014-6c7a3ae10213, - 9acd76b4-8d78-2b4b-81d5-e404e244997d, a533d3fd-822f-3b40-9030-e18b571cd1ea, - a949cafd-39f3-8e4c-bca5-9928b004b4da, aadfa134-666a-ae4f-8c09-6f3ae114c3b5, - ab88a3a2-a572-3440-be06-757e0225ea42, b77c60e8-fe4f-9149-9bc6-c3eb8f899de2, - bff806b2-7825-af4c-a34b-be542dbe5ae6, c3458e72-1c7a-a74b-b561-8afe0a3aa2be, - d288bd96-ae6e-cf46-9775-f38e2699f1ce, d28bc89e-0592-1440-9721-5cbe124c1f1b, - db724627-9dae-4748-b4e3-2daf627ffa17, dc791b3b-fe63-a14b-b144-953748088613, - e2f56599-a78e-494f-8db3-c0b0bdab1d70, eb8b2a90-9bc8-154f-92dd-3887e3676498, - eba035f1-7c96-0142-8561-37b28625abf7, ee4223ab-0538-dc4a-9b79-d463c048bb08, - fe798f30-0725-e94e-8142-c06ca7701b14 - active: 'true' - identifier: f1769670-9aeb-11e5-91da-b8763fd99c5f - type: Substation - name: ARENDAL - parentExternalId: f176965f-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: f1769676-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: non-historic - - externalId: Substation - metadata: - IdentifiedObject.mRID: f1769676-9aeb-11e5-91da-b8763fd99c5f - IdentifiedObject.name: KRISTIA_HVDC - Substation.Region: f176965f-9aeb-11e5-91da-b8763fd99c5f - Substation.Terminal: 2dd901a0-bdfb-11e5-94fa-c8f73332c8f4, 2dd90205-bdfb-11e5-94fa-c8f73332c8f4, - 2dd90418-bdfb-11e5-94fa-c8f73332c8f4 - active: 'true' - identifier: f1769676-9aeb-11e5-91da-b8763fd99c5f - type: Substation - name: KRISTIA_HVDC - parentExternalId: f176965f-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: f176967c-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: non-historic - - externalId: Substation - metadata: - IdentifiedObject.mRID: f176967c-9aeb-11e5-91da-b8763fd99c5f - IdentifiedObject.name: FEDA_HVDC - Substation.Region: f176965f-9aeb-11e5-91da-b8763fd99c5f - Substation.Terminal: 2dd901a2-bdfb-11e5-94fa-c8f73332c8f4, 2dd90208-bdfb-11e5-94fa-c8f73332c8f4, - 2dd90412-bdfb-11e5-94fa-c8f73332c8f4 - active: 'true' - identifier: f176967c-9aeb-11e5-91da-b8763fd99c5f - type: Substation - name: FEDA_HVDC - parentExternalId: f176965f-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: f1769682-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: non-historic - - externalId: Substation - metadata: - IdentifiedObject.mRID: f1769682-9aeb-11e5-91da-b8763fd99c5f - IdentifiedObject.name: KVILLDAL - Substation.Region: f176965f-9aeb-11e5-91da-b8763fd99c5f - Substation.Terminal: 2dd901a4-bdfb-11e5-94fa-c8f73332c8f4, 2dd902ea-bdfb-11e5-94fa-c8f73332c8f4, - 2dd902ed-bdfb-11e5-94fa-c8f73332c8f4, 2dd902f0-bdfb-11e5-94fa-c8f73332c8f4, - 2dd902f3-bdfb-11e5-94fa-c8f73332c8f4, 2dd903ee-bdfb-11e5-94fa-c8f73332c8f4, - 2dd90415-bdfb-11e5-94fa-c8f73332c8f4, 2dd9041a-bdfb-11e5-94fa-c8f73332c8f4, - 2dd9047f-bdfb-11e5-94fa-c8f73332c8f4 - active: 'true' - identifier: f1769682-9aeb-11e5-91da-b8763fd99c5f - type: Substation - name: KVILLDAL - parentExternalId: f176965f-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: f1769688-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: non-historic - - externalId: Substation - metadata: - IdentifiedObject.mRID: f1769688-9aeb-11e5-91da-b8763fd99c5f - IdentifiedObject.name: HAGAFOSS - Substation.Region: f1769619-9aeb-11e5-91da-b8763fd99c5f - Substation.Terminal: 2dd903ca-bdfb-11e5-94fa-c8f73332c8f4, 2dd903d0-bdfb-11e5-94fa-c8f73332c8f4, - 2dd903e2-bdfb-11e5-94fa-c8f73332c8f4, 2dd903f7-bdfb-11e5-94fa-c8f73332c8f4, - 2dd903fd-bdfb-11e5-94fa-c8f73332c8f4, 2dd90400-bdfb-11e5-94fa-c8f73332c8f4, - 2dd90475-bdfb-11e5-94fa-c8f73332c8f4, 2dd90483-bdfb-11e5-94fa-c8f73332c8f4, - terminal-without-name-property - active: 'true' - identifier: f1769688-9aeb-11e5-91da-b8763fd99c5f - type: Substation - name: HAGAFOSS - parentExternalId: f1769619-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: f176968e-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: non-historic - - externalId: Substation - metadata: - IdentifiedObject.mRID: f176968e-9aeb-11e5-91da-b8763fd99c5f - IdentifiedObject.name: BLAFALLI - Substation.Region: f176965f-9aeb-11e5-91da-b8763fd99c5f - Substation.Terminal: 2dd901a8-bdfb-11e5-94fa-c8f73332c8f4, 2dd9020b-bdfb-11e5-94fa-c8f73332c8f4, - 2dd9020e-bdfb-11e5-94fa-c8f73332c8f4, 2dd902f7-bdfb-11e5-94fa-c8f73332c8f4, - 2dd902fa-bdfb-11e5-94fa-c8f73332c8f4, 2dd902fd-bdfb-11e5-94fa-c8f73332c8f4, - 2dd90300-bdfb-11e5-94fa-c8f73332c8f4, 2dd90303-bdfb-11e5-94fa-c8f73332c8f4, - 2dd903d9-bdfb-11e5-94fa-c8f73332c8f4, 2dd9041b-bdfb-11e5-94fa-c8f73332c8f4 - active: 'true' - identifier: f176968e-9aeb-11e5-91da-b8763fd99c5f - type: Substation - name: BLAFALLI - parentExternalId: f176965f-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: f1769694-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: non-historic - - externalId: Substation - metadata: - IdentifiedObject.mRID: f1769694-9aeb-11e5-91da-b8763fd99c5f - IdentifiedObject.name: TRONDHEIM - Substation.Region: f1769699-9aeb-11e5-91da-b8763fd99c5f - Substation.Terminal: 2dd901aa-bdfb-11e5-94fa-c8f73332c8f4, 2dd90211-bdfb-11e5-94fa-c8f73332c8f4, - 2dd90214-bdfb-11e5-94fa-c8f73332c8f4, 2dd90217-bdfb-11e5-94fa-c8f73332c8f4, - 2dd90307-bdfb-11e5-94fa-c8f73332c8f4, 2dd9030a-bdfb-11e5-94fa-c8f73332c8f4, - 2dd9030d-bdfb-11e5-94fa-c8f73332c8f4, 2dd90310-bdfb-11e5-94fa-c8f73332c8f4, - 2dd90394-bdfb-11e5-94fa-c8f73332c8f4, 2dd903b2-bdfb-11e5-94fa-c8f73332c8f4, - 2dd9041d-bdfb-11e5-94fa-c8f73332c8f4, 2dd90420-bdfb-11e5-94fa-c8f73332c8f4 - active: 'true' - identifier: f1769694-9aeb-11e5-91da-b8763fd99c5f - type: Substation - name: TRONDHEIM - parentExternalId: f1769699-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: f1769699-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: non-historic - - externalId: SubGeographicalRegion - metadata: - IdentifiedObject.mRID: f1769699-9aeb-11e5-91da-b8763fd99c5f - IdentifiedObject.name: NO3 SGR - SubGeographicalRegion.Region: 2dd9048c-bdfb-11e5-94fa-c8f73332c8f4 - active: 'true' - identifier: f1769699-9aeb-11e5-91da-b8763fd99c5f - type: SubGeographicalRegion - name: NO3 SGR - parentExternalId: 2dd9048c-bdfb-11e5-94fa-c8f73332c8f4 -- dataSetId: 2626756768281823 - externalId: f176969e-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: non-historic - - externalId: Substation - metadata: - IdentifiedObject.mRID: f176969e-9aeb-11e5-91da-b8763fd99c5f - IdentifiedObject.name: MO - Substation.Region: f17696a3-9aeb-11e5-91da-b8763fd99c5f - Substation.Terminal: 2dd901ae-bdfb-11e5-94fa-c8f73332c8f4, 2dd9021a-bdfb-11e5-94fa-c8f73332c8f4, - 2dd90314-bdfb-11e5-94fa-c8f73332c8f4, 2dd90317-bdfb-11e5-94fa-c8f73332c8f4, - 2dd9031a-bdfb-11e5-94fa-c8f73332c8f4, 2dd9031d-bdfb-11e5-94fa-c8f73332c8f4, - 2dd903ac-bdfb-11e5-94fa-c8f73332c8f4, 2dd9041e-bdfb-11e5-94fa-c8f73332c8f4, - 2dd90421-bdfb-11e5-94fa-c8f73332c8f4, 2dd90486-bdfb-11e5-94fa-c8f73332c8f4 - active: 'true' - identifier: f176969e-9aeb-11e5-91da-b8763fd99c5f - type: Substation - name: MO - parentExternalId: f17696a3-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: f17696a3-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: non-historic - - externalId: SubGeographicalRegion - metadata: - IdentifiedObject.mRID: f17696a3-9aeb-11e5-91da-b8763fd99c5f - IdentifiedObject.name: NO4 SGR - SubGeographicalRegion.Region: 2dd9048c-bdfb-11e5-94fa-c8f73332c8f4 - active: 'true' - identifier: f17696a3-9aeb-11e5-91da-b8763fd99c5f - type: SubGeographicalRegion - name: NO4 SGR - parentExternalId: 2dd9048c-bdfb-11e5-94fa-c8f73332c8f4 -- dataSetId: 2626756768281823 - externalId: f17696a8-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: non-historic - - externalId: Substation - metadata: - IdentifiedObject.mRID: f17696a8-9aeb-11e5-91da-b8763fd99c5f - IdentifiedObject.name: NARVIK - Substation.Region: f17696a3-9aeb-11e5-91da-b8763fd99c5f - Substation.Terminal: 2dd901b2-bdfb-11e5-94fa-c8f73332c8f4, 2dd9021d-bdfb-11e5-94fa-c8f73332c8f4, - 2dd90220-bdfb-11e5-94fa-c8f73332c8f4, 2dd90385-bdfb-11e5-94fa-c8f73332c8f4, - 2dd9048a-bdfb-11e5-94fa-c8f73332c8f4 - active: 'true' - identifier: f17696a8-9aeb-11e5-91da-b8763fd99c5f - type: Substation - name: NARVIK - parentExternalId: f17696a3-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: non-historic - - externalId: Substation - metadata: - IdentifiedObject.mRID: f17696ca-9aeb-11e5-91da-b8763fd99c5f - IdentifiedObject.name: MALMO - Substation.Region: f17696cf-9aeb-11e5-91da-b8763fd99c5f - Substation.Terminal: 2dd901be-bdfb-11e5-94fa-c8f73332c8f4, 2dd90244-bdfb-11e5-94fa-c8f73332c8f4, - 2dd90247-bdfb-11e5-94fa-c8f73332c8f4, 2dd9024a-bdfb-11e5-94fa-c8f73332c8f4, - 2dd9024d-bdfb-11e5-94fa-c8f73332c8f4, 2dd90347-bdfb-11e5-94fa-c8f73332c8f4, - 2dd9034a-bdfb-11e5-94fa-c8f73332c8f4, 2dd9034d-bdfb-11e5-94fa-c8f73332c8f4, - 2dd90350-bdfb-11e5-94fa-c8f73332c8f4, 2dd90353-bdfb-11e5-94fa-c8f73332c8f4, - 2dd90356-bdfb-11e5-94fa-c8f73332c8f4, 2dd90391-bdfb-11e5-94fa-c8f73332c8f4, - 2dd9039a-bdfb-11e5-94fa-c8f73332c8f4, 2dd9039d-bdfb-11e5-94fa-c8f73332c8f4, - 2dd903a6-bdfb-11e5-94fa-c8f73332c8f4, 2dd903a9-bdfb-11e5-94fa-c8f73332c8f4, - 2dd90432-bdfb-11e5-94fa-c8f73332c8f4, 2dd90435-bdfb-11e5-94fa-c8f73332c8f4 - active: 'true' - identifier: f17696ca-9aeb-11e5-91da-b8763fd99c5f - type: Substation - name: MALMO - parentExternalId: f17696cf-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: f17696cf-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: non-historic - - externalId: SubGeographicalRegion - metadata: - IdentifiedObject.mRID: f17696cf-9aeb-11e5-91da-b8763fd99c5f - IdentifiedObject.name: SE4 SGR - SubGeographicalRegion.Region: 2dd9048c-bdfb-11e5-94fa-c8f73332c8f4 - active: 'true' - identifier: f17696cf-9aeb-11e5-91da-b8763fd99c5f - type: SubGeographicalRegion - name: SE4 SGR - parentExternalId: 2dd9048c-bdfb-11e5-94fa-c8f73332c8f4 -- dataSetId: 2626756768281823 - externalId: f17696d4-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: non-historic - - externalId: Substation - metadata: - IdentifiedObject.mRID: f17696d4-9aeb-11e5-91da-b8763fd99c5f - IdentifiedObject.name: ARRIE_HVDC - Substation.Region: f17696cf-9aeb-11e5-91da-b8763fd99c5f - Substation.Terminal: 2dd901c2-bdfb-11e5-94fa-c8f73332c8f4, 2dd90250-bdfb-11e5-94fa-c8f73332c8f4, - 2dd90433-bdfb-11e5-94fa-c8f73332c8f4 - active: 'true' - identifier: f17696d4-9aeb-11e5-91da-b8763fd99c5f - type: Substation - name: ARRIE_HVDC - parentExternalId: f17696cf-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: f17696da-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: non-historic - - externalId: Substation - metadata: - IdentifiedObject.mRID: f17696da-9aeb-11e5-91da-b8763fd99c5f - IdentifiedObject.name: KARLSH_HVDC - Substation.Region: f17696cf-9aeb-11e5-91da-b8763fd99c5f - Substation.Terminal: 2dd901c4-bdfb-11e5-94fa-c8f73332c8f4, 2dd90253-bdfb-11e5-94fa-c8f73332c8f4, - 2dd90436-bdfb-11e5-94fa-c8f73332c8f4 - active: 'true' - identifier: f17696da-9aeb-11e5-91da-b8763fd99c5f - type: Substation - name: KARLSH_HVDC - parentExternalId: f17696cf-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: f4dba262-3ff3-9f41-961d-7fbe1f8a0cd7 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: f4dba262-3ff3-9f41-961d-7fbe1f8a0cd7 - IdentifiedObject.name: KRISTIAN300G1 AD_S T1 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: f4dba262-3ff3-9f41-961d-7fbe1f8a0cd7 - type: Terminal - name: KRISTIAN300G1 AD_S T1 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: f710cc4a-50dd-e443-8ac7-4c56b70041e8 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: f710cc4a-50dd-e443-8ac7-4c56b70041e8 - IdentifiedObject.name: KRISTIAN300ST1 AB_S T2 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: f710cc4a-50dd-e443-8ac7-4c56b70041e8 - type: Terminal - name: KRISTIAN300ST1 AB_S T2 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: f723530d-ae1e-df4f-9c7f-21e42da147d6 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: f723530d-ae1e-df4f-9c7f-21e42da147d6 - IdentifiedObject.name: KRISTIAN300G1 BB_S T1 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: f723530d-ae1e-df4f-9c7f-21e42da147d6 - type: Terminal - name: KRISTIAN300G1 BB_S T1 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: f9e8d6d1-76e1-6c4d-a640-e128ecfa723e - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: f9e8d6d1-76e1-6c4d-a640-e128ecfa723e - IdentifiedObject.name: KRISTIAN300G3 AD_S T1 - Terminal.Substation: f176965a-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: f9e8d6d1-76e1-6c4d-a640-e128ecfa723e - type: Terminal - name: KRISTIAN300G3 AD_S T1 - parentExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: fe798f30-0725-e94e-8142-c06ca7701b14 - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: fe798f30-0725-e94e-8142-c06ca7701b14 - IdentifiedObject.name: ARENDAL 300KR1 AB_S T1 - Terminal.Substation: f1769670-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: fe798f30-0725-e94e-8142-c06ca7701b14 - type: Terminal - name: ARENDAL 300KR1 AB_S T1 - parentExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f -- dataSetId: 2626756768281823 - externalId: orphanage - labels: - - externalId: non-historic - - externalId: Orphanage - metadata: - IdentifiedObject.description: Used to store all assets which parent does not exist - IdentifiedObject.mRID: '' - IdentifiedObject.name: Orphanage - active: 'true' - cdfResourceType: Asset - identifier: orphanage - type: Orphanage - name: Orphanage -- dataSetId: 2626756768281823 - externalId: root-node - labels: - - externalId: non-historic - - externalId: RootCIMNode - metadata: - IdentifiedObject.description: Used as root asset for all CIM objects - IdentifiedObject.mRID: '' - IdentifiedObject.name: Root CIM Node - active: 'true' - cdfResourceType: Asset - identifier: root-node - type: RootCIMNode - name: Root CIM Node -- dataSetId: 2626756768281823 - externalId: terminal-without-name-property - labels: - - externalId: non-historic - - externalId: Terminal - metadata: - IdentifiedObject.aliasName: '' - IdentifiedObject.mRID: terminal-without-name-property - IdentifiedObject.name: '' - Terminal.Substation: f1769688-9aeb-11e5-91da-b8763fd99c5f - active: 'true' - identifier: terminal-without-name-property - type: Terminal - name: terminal-without-name-property - parentExternalId: f1769688-9aeb-11e5-91da-b8763fd99c5f -labels: -- dataSetId: 2626756768281823 - externalId: Analog - name: Analog -- dataSetId: 2626756768281823 - externalId: belongsTo - name: belongsTo -- dataSetId: 2626756768281823 - externalId: connectsTo - name: connectsTo -- dataSetId: 2626756768281823 - externalId: GeographicalRegion - name: GeographicalRegion -- dataSetId: 2626756768281823 - externalId: historic - name: historic -- dataSetId: 2626756768281823 - externalId: IdentifiedObject.aliasName - name: IdentifiedObject.aliasName -- dataSetId: 2626756768281823 - externalId: IdentifiedObject.mRID - name: IdentifiedObject.mRID -- dataSetId: 2626756768281823 - externalId: IdentifiedObject.name - name: IdentifiedObject.name -- dataSetId: 2626756768281823 - externalId: non-historic - name: non-historic -- dataSetId: 2626756768281823 - externalId: Orphanage - name: Orphanage -- dataSetId: 2626756768281823 - externalId: RootCIMNode - name: RootCIMNode -- dataSetId: 2626756768281823 - externalId: RootCIMNode.node - name: RootCIMNode.node -- dataSetId: 2626756768281823 - externalId: SubGeographicalRegion - name: SubGeographicalRegion -- dataSetId: 2626756768281823 - externalId: SubGeographicalRegion.Region - name: SubGeographicalRegion.Region -- dataSetId: 2626756768281823 - externalId: Substation - name: Substation -- dataSetId: 2626756768281823 - externalId: Substation.Region - name: Substation.Region -- dataSetId: 2626756768281823 - externalId: Substation.Terminal - name: Substation.Terminal -- dataSetId: 2626756768281823 - externalId: Terminal - name: Terminal -- dataSetId: 2626756768281823 - externalId: Terminal.Substation - name: Terminal.Substation -relationships: -- dataSetId: 2626756768281823 - externalId: 02b41435-bd3e-bb4b-9b89-24e3a8983c0f:f176966a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 02b41435-bd3e-bb4b-9b89-24e3a8983c0f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176966a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 0345061e-37c9-9a49-b632-2af77bdca3a2:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 0345061e-37c9-9a49-b632-2af77bdca3a2 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 0619c12e-abb9-b141-b3a7-e2788d3d375c:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 0619c12e-abb9-b141-b3a7-e2788d3d375c - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 076da31b-080d-c742-9cb1-134a6ba5b1ed:f1769670-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 076da31b-080d-c742-9cb1-134a6ba5b1ed - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 093a3df5-0583-894f-99c9-016d752ff920:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 093a3df5-0583-894f-99c9-016d752ff920 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 0a122f63-f2c6-ca43-be49-76571474d98c:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 0a122f63-f2c6-ca43-be49-76571474d98c - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 0b50ae74-92eb-f244-872b-07e629f53494:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 0b50ae74-92eb-f244-872b-07e629f53494 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 0d7f39d0-5447-fb42-9d4f-3d0775bceef0:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 0d7f39d0-5447-fb42-9d4f-3d0775bceef0 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 0dae5d37-4d01-3645-9e94-607410c5ac34:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 0dae5d37-4d01-3645-9e94-607410c5ac34 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 0ef62251-5e2e-d14a-aa44-396d4a885c4f:f1769670-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 0ef62251-5e2e-d14a-aa44-396d4a885c4f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 13116d72-3969-1344-91a7-14fb404a00c0:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 13116d72-3969-1344-91a7-14fb404a00c0 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 16659ede-d69f-1d48-bcf2-ebb2ee6c259e:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 16659ede-d69f-1d48-bcf2-ebb2ee6c259e - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 1a6d8d77-f2ca-f043-b475-f7db6304fe3e:f1769670-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 1a6d8d77-f2ca-f043-b475-f7db6304fe3e - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 1ef7a41b-2cff-6046-b48f-c60a16fd20b0:f1769670-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 1ef7a41b-2cff-6046-b48f-c60a16fd20b0 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 22c3512a-fdf2-464c-b1f4-85d173fa2d18:f1769670-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 22c3512a-fdf2-464c-b1f4-85d173fa2d18 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 292835be-c432-734c-8c28-3b8c61c0c077:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 292835be-c432-734c-8c28-3b8c61c0c077 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2a67a243-6e4a-a84d-8aea-f250399166c9:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2a67a243-6e4a-a84d-8aea-f250399166c9 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90156-bdfb-11e5-94fa-c8f73332c8f4:f17695aa-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90156-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695aa-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9015c-bdfb-11e5-94fa-c8f73332c8f4:f17695b8-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9015c-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695b8-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9015e-bdfb-11e5-94fa-c8f73332c8f4:f17695be-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9015e-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695be-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90162-bdfb-11e5-94fa-c8f73332c8f4:f17695c8-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90162-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695c8-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90166-bdfb-11e5-94fa-c8f73332c8f4:f17695d2-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90166-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695d2-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90168-bdfb-11e5-94fa-c8f73332c8f4:f17695d8-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90168-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695d8-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9016b-bdfb-11e5-94fa-c8f73332c8f4:f17695df-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9016b-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695df-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9016d-bdfb-11e5-94fa-c8f73332c8f4:f17695e5-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9016d-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695e5-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9016f-bdfb-11e5-94fa-c8f73332c8f4:f17695eb-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9016f-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695eb-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90171-bdfb-11e5-94fa-c8f73332c8f4:f17695f1-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90171-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90173-bdfb-11e5-94fa-c8f73332c8f4:f17695f7-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90173-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695f7-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90176-bdfb-11e5-94fa-c8f73332c8f4:f17695fe-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90176-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695fe-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9017e-bdfb-11e5-94fa-c8f73332c8f4:f1769614-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9017e-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769614-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90184-bdfb-11e5-94fa-c8f73332c8f4:f1769624-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90184-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769624-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90186-bdfb-11e5-94fa-c8f73332c8f4:f176962a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90186-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176962a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90188-bdfb-11e5-94fa-c8f73332c8f4:f1769630-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90188-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769630-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9018a-bdfb-11e5-94fa-c8f73332c8f4:f1769636-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9018a-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769636-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90196-bdfb-11e5-94fa-c8f73332c8f4:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90196-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9019a-bdfb-11e5-94fa-c8f73332c8f4:f1769664-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9019a-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769664-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9019c-bdfb-11e5-94fa-c8f73332c8f4:f176966a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9019c-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176966a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9019e-bdfb-11e5-94fa-c8f73332c8f4:f1769670-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9019e-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd901a0-bdfb-11e5-94fa-c8f73332c8f4:f1769676-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd901a0-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769676-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd901a2-bdfb-11e5-94fa-c8f73332c8f4:f176967c-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd901a2-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176967c-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd901a4-bdfb-11e5-94fa-c8f73332c8f4:f1769682-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd901a4-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769682-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd901a8-bdfb-11e5-94fa-c8f73332c8f4:f176968e-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd901a8-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176968e-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd901aa-bdfb-11e5-94fa-c8f73332c8f4:f1769694-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd901aa-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769694-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd901ae-bdfb-11e5-94fa-c8f73332c8f4:f176969e-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd901ae-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176969e-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd901b2-bdfb-11e5-94fa-c8f73332c8f4:f17696a8-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd901b2-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17696a8-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd901be-bdfb-11e5-94fa-c8f73332c8f4:f17696ca-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd901be-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd901c2-bdfb-11e5-94fa-c8f73332c8f4:f17696d4-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd901c2-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17696d4-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd901c4-bdfb-11e5-94fa-c8f73332c8f4:f17696da-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd901c4-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17696da-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd901c6-bdfb-11e5-94fa-c8f73332c8f4:f17695aa-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd901c6-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695aa-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd901c9-bdfb-11e5-94fa-c8f73332c8f4:f17695aa-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd901c9-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695aa-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd901cc-bdfb-11e5-94fa-c8f73332c8f4:f17695aa-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd901cc-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695aa-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd901cf-bdfb-11e5-94fa-c8f73332c8f4:f17695b8-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd901cf-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695b8-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd901d2-bdfb-11e5-94fa-c8f73332c8f4:f17695be-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd901d2-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695be-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd901d5-bdfb-11e5-94fa-c8f73332c8f4:f17695c8-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd901d5-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695c8-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd901d8-bdfb-11e5-94fa-c8f73332c8f4:f17695e5-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd901d8-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695e5-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd901db-bdfb-11e5-94fa-c8f73332c8f4:f17695eb-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd901db-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695eb-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd901de-bdfb-11e5-94fa-c8f73332c8f4:f17695eb-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd901de-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695eb-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd901e1-bdfb-11e5-94fa-c8f73332c8f4:f17695f1-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd901e1-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd901e4-bdfb-11e5-94fa-c8f73332c8f4:f17695f1-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd901e4-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd901e7-bdfb-11e5-94fa-c8f73332c8f4:f17695f1-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd901e7-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd901ea-bdfb-11e5-94fa-c8f73332c8f4:f17695f1-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd901ea-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd901ed-bdfb-11e5-94fa-c8f73332c8f4:f17695f7-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd901ed-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695f7-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd901f3-bdfb-11e5-94fa-c8f73332c8f4:f1769624-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd901f3-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769624-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd901ff-bdfb-11e5-94fa-c8f73332c8f4:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd901ff-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90202-bdfb-11e5-94fa-c8f73332c8f4:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90202-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90205-bdfb-11e5-94fa-c8f73332c8f4:f1769676-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90205-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769676-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90208-bdfb-11e5-94fa-c8f73332c8f4:f176967c-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90208-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176967c-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9020b-bdfb-11e5-94fa-c8f73332c8f4:f176968e-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9020b-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176968e-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9020e-bdfb-11e5-94fa-c8f73332c8f4:f176968e-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9020e-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176968e-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90211-bdfb-11e5-94fa-c8f73332c8f4:f1769694-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90211-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769694-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90214-bdfb-11e5-94fa-c8f73332c8f4:f1769694-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90214-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769694-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90217-bdfb-11e5-94fa-c8f73332c8f4:f1769694-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90217-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769694-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9021a-bdfb-11e5-94fa-c8f73332c8f4:f176969e-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9021a-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176969e-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9021d-bdfb-11e5-94fa-c8f73332c8f4:f17696a8-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9021d-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17696a8-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90220-bdfb-11e5-94fa-c8f73332c8f4:f17696a8-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90220-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17696a8-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90244-bdfb-11e5-94fa-c8f73332c8f4:f17696ca-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90244-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90247-bdfb-11e5-94fa-c8f73332c8f4:f17696ca-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90247-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9024a-bdfb-11e5-94fa-c8f73332c8f4:f17696ca-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9024a-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9024d-bdfb-11e5-94fa-c8f73332c8f4:f17696ca-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9024d-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90250-bdfb-11e5-94fa-c8f73332c8f4:f17696d4-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90250-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17696d4-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90253-bdfb-11e5-94fa-c8f73332c8f4:f17696da-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90253-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17696da-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90258-bdfb-11e5-94fa-c8f73332c8f4:f17695aa-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90258-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695aa-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9025b-bdfb-11e5-94fa-c8f73332c8f4:f17695aa-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9025b-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695aa-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9025e-bdfb-11e5-94fa-c8f73332c8f4:f17695aa-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9025e-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695aa-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90262-bdfb-11e5-94fa-c8f73332c8f4:f17695c8-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90262-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695c8-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90265-bdfb-11e5-94fa-c8f73332c8f4:f17695c8-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90265-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695c8-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90268-bdfb-11e5-94fa-c8f73332c8f4:f17695c8-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90268-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695c8-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9026b-bdfb-11e5-94fa-c8f73332c8f4:f17695c8-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9026b-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695c8-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9026e-bdfb-11e5-94fa-c8f73332c8f4:f17695c8-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9026e-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695c8-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90272-bdfb-11e5-94fa-c8f73332c8f4:f17695df-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90272-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695df-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90276-bdfb-11e5-94fa-c8f73332c8f4:f17695e5-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90276-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695e5-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90279-bdfb-11e5-94fa-c8f73332c8f4:f17695e5-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90279-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695e5-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9027c-bdfb-11e5-94fa-c8f73332c8f4:f17695e5-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9027c-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695e5-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9027f-bdfb-11e5-94fa-c8f73332c8f4:f17695e5-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9027f-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695e5-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90282-bdfb-11e5-94fa-c8f73332c8f4:f17695e5-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90282-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695e5-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90285-bdfb-11e5-94fa-c8f73332c8f4:f17695e5-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90285-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695e5-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90288-bdfb-11e5-94fa-c8f73332c8f4:f17695e5-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90288-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695e5-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9028b-bdfb-11e5-94fa-c8f73332c8f4:f17695e5-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9028b-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695e5-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9028f-bdfb-11e5-94fa-c8f73332c8f4:f17695eb-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9028f-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695eb-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90292-bdfb-11e5-94fa-c8f73332c8f4:f17695eb-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90292-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695eb-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90295-bdfb-11e5-94fa-c8f73332c8f4:f17695eb-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90295-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695eb-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90298-bdfb-11e5-94fa-c8f73332c8f4:f17695eb-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90298-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695eb-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9029b-bdfb-11e5-94fa-c8f73332c8f4:f17695eb-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9029b-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695eb-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9029e-bdfb-11e5-94fa-c8f73332c8f4:f17695eb-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9029e-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695eb-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd902a2-bdfb-11e5-94fa-c8f73332c8f4:f17695f1-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd902a2-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd902a5-bdfb-11e5-94fa-c8f73332c8f4:f17695f1-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd902a5-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd902a8-bdfb-11e5-94fa-c8f73332c8f4:f17695f1-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd902a8-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd902ab-bdfb-11e5-94fa-c8f73332c8f4:f17695f1-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd902ab-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd902ae-bdfb-11e5-94fa-c8f73332c8f4:f17695f1-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd902ae-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd902b1-bdfb-11e5-94fa-c8f73332c8f4:f17695f1-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd902b1-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd902bc-bdfb-11e5-94fa-c8f73332c8f4:f1769624-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd902bc-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769624-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd902bf-bdfb-11e5-94fa-c8f73332c8f4:f1769624-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd902bf-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769624-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd902c2-bdfb-11e5-94fa-c8f73332c8f4:f1769624-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd902c2-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769624-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd902c5-bdfb-11e5-94fa-c8f73332c8f4:f1769624-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd902c5-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769624-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd902c8-bdfb-11e5-94fa-c8f73332c8f4:f1769624-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd902c8-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769624-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd902cb-bdfb-11e5-94fa-c8f73332c8f4:f1769624-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd902cb-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769624-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd902dd-bdfb-11e5-94fa-c8f73332c8f4:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd902dd-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd902e0-bdfb-11e5-94fa-c8f73332c8f4:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd902e0-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd902e3-bdfb-11e5-94fa-c8f73332c8f4:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd902e3-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd902e6-bdfb-11e5-94fa-c8f73332c8f4:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd902e6-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd902ea-bdfb-11e5-94fa-c8f73332c8f4:f1769682-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd902ea-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769682-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd902ed-bdfb-11e5-94fa-c8f73332c8f4:f1769682-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd902ed-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769682-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd902f0-bdfb-11e5-94fa-c8f73332c8f4:f1769682-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd902f0-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769682-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd902f3-bdfb-11e5-94fa-c8f73332c8f4:f1769682-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd902f3-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769682-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd902f7-bdfb-11e5-94fa-c8f73332c8f4:f176968e-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd902f7-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176968e-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd902fa-bdfb-11e5-94fa-c8f73332c8f4:f176968e-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd902fa-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176968e-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd902fd-bdfb-11e5-94fa-c8f73332c8f4:f176968e-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd902fd-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176968e-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90300-bdfb-11e5-94fa-c8f73332c8f4:f176968e-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90300-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176968e-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90303-bdfb-11e5-94fa-c8f73332c8f4:f176968e-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90303-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176968e-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90307-bdfb-11e5-94fa-c8f73332c8f4:f1769694-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90307-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769694-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9030a-bdfb-11e5-94fa-c8f73332c8f4:f1769694-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9030a-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769694-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9030d-bdfb-11e5-94fa-c8f73332c8f4:f1769694-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9030d-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769694-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90310-bdfb-11e5-94fa-c8f73332c8f4:f1769694-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90310-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769694-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90314-bdfb-11e5-94fa-c8f73332c8f4:f176969e-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90314-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176969e-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90317-bdfb-11e5-94fa-c8f73332c8f4:f176969e-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90317-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176969e-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9031a-bdfb-11e5-94fa-c8f73332c8f4:f176969e-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9031a-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176969e-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9031d-bdfb-11e5-94fa-c8f73332c8f4:f176969e-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9031d-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176969e-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90347-bdfb-11e5-94fa-c8f73332c8f4:f17696ca-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90347-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9034a-bdfb-11e5-94fa-c8f73332c8f4:f17696ca-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9034a-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9034d-bdfb-11e5-94fa-c8f73332c8f4:f17696ca-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9034d-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90350-bdfb-11e5-94fa-c8f73332c8f4:f17696ca-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90350-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90353-bdfb-11e5-94fa-c8f73332c8f4:f17696ca-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90353-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90356-bdfb-11e5-94fa-c8f73332c8f4:f17696ca-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90356-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90357-bdfb-11e5-94fa-c8f73332c8f4:f17695aa-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90357-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695aa-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90358-bdfb-11e5-94fa-c8f73332c8f4:f17695b8-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90358-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695b8-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9035a-bdfb-11e5-94fa-c8f73332c8f4:f17695aa-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9035a-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695aa-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9035b-bdfb-11e5-94fa-c8f73332c8f4:f17695c8-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9035b-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695c8-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9035d-bdfb-11e5-94fa-c8f73332c8f4:f17695aa-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9035d-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695aa-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9035e-bdfb-11e5-94fa-c8f73332c8f4:f17695df-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9035e-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695df-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90360-bdfb-11e5-94fa-c8f73332c8f4:f17695aa-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90360-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695aa-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90361-bdfb-11e5-94fa-c8f73332c8f4:f17695df-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90361-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695df-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90363-bdfb-11e5-94fa-c8f73332c8f4:f17695aa-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90363-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695aa-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90364-bdfb-11e5-94fa-c8f73332c8f4:f17695eb-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90364-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695eb-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90366-bdfb-11e5-94fa-c8f73332c8f4:f17695aa-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90366-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695aa-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90367-bdfb-11e5-94fa-c8f73332c8f4:f17695eb-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90367-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695eb-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90369-bdfb-11e5-94fa-c8f73332c8f4:f17695be-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90369-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695be-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9036a-bdfb-11e5-94fa-c8f73332c8f4:f17695c8-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9036a-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695c8-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9036c-bdfb-11e5-94fa-c8f73332c8f4:f17695be-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9036c-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695be-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9036d-bdfb-11e5-94fa-c8f73332c8f4:f17695d2-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9036d-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695d2-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9036f-bdfb-11e5-94fa-c8f73332c8f4:f17695be-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9036f-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695be-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90370-bdfb-11e5-94fa-c8f73332c8f4:f17695d2-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90370-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695d2-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90372-bdfb-11e5-94fa-c8f73332c8f4:f17695be-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90372-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695be-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90373-bdfb-11e5-94fa-c8f73332c8f4:f17695d2-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90373-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695d2-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90375-bdfb-11e5-94fa-c8f73332c8f4:f17695be-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90375-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695be-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90376-bdfb-11e5-94fa-c8f73332c8f4:f17695e5-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90376-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695e5-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90378-bdfb-11e5-94fa-c8f73332c8f4:f17695be-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90378-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695be-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90379-bdfb-11e5-94fa-c8f73332c8f4:f17695f1-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90379-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9037b-bdfb-11e5-94fa-c8f73332c8f4:f17695be-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9037b-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695be-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9037c-bdfb-11e5-94fa-c8f73332c8f4:f17695f1-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9037c-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9037e-bdfb-11e5-94fa-c8f73332c8f4:f17695c8-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9037e-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695c8-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9037f-bdfb-11e5-94fa-c8f73332c8f4:f17695df-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9037f-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695df-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90381-bdfb-11e5-94fa-c8f73332c8f4:f17695c8-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90381-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695c8-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90382-bdfb-11e5-94fa-c8f73332c8f4:f17695e5-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90382-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695e5-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90384-bdfb-11e5-94fa-c8f73332c8f4:f17695c8-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90384-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695c8-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90385-bdfb-11e5-94fa-c8f73332c8f4:f17696a8-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90385-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17696a8-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90387-bdfb-11e5-94fa-c8f73332c8f4:f17695c8-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90387-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695c8-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9038a-bdfb-11e5-94fa-c8f73332c8f4:f17695d2-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9038a-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695d2-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9038b-bdfb-11e5-94fa-c8f73332c8f4:f17695eb-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9038b-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695eb-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9038d-bdfb-11e5-94fa-c8f73332c8f4:f17695d2-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9038d-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695d2-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9038e-bdfb-11e5-94fa-c8f73332c8f4:f17695f1-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9038e-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90390-bdfb-11e5-94fa-c8f73332c8f4:f17695d2-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90390-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695d2-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90391-bdfb-11e5-94fa-c8f73332c8f4:f17696ca-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90391-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90393-bdfb-11e5-94fa-c8f73332c8f4:f17695d8-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90393-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695d8-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90394-bdfb-11e5-94fa-c8f73332c8f4:f1769694-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90394-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769694-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90396-bdfb-11e5-94fa-c8f73332c8f4:f17695e5-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90396-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695e5-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90399-bdfb-11e5-94fa-c8f73332c8f4:f17695eb-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90399-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695eb-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9039a-bdfb-11e5-94fa-c8f73332c8f4:f17696ca-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9039a-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9039c-bdfb-11e5-94fa-c8f73332c8f4:f17695eb-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9039c-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695eb-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9039d-bdfb-11e5-94fa-c8f73332c8f4:f17696ca-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9039d-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9039f-bdfb-11e5-94fa-c8f73332c8f4:f17695f1-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9039f-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd903a2-bdfb-11e5-94fa-c8f73332c8f4:f17695f1-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd903a2-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd903a5-bdfb-11e5-94fa-c8f73332c8f4:f17695f1-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd903a5-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd903a6-bdfb-11e5-94fa-c8f73332c8f4:f17696ca-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd903a6-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd903a8-bdfb-11e5-94fa-c8f73332c8f4:f17695f1-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd903a8-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd903a9-bdfb-11e5-94fa-c8f73332c8f4:f17696ca-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd903a9-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd903ab-bdfb-11e5-94fa-c8f73332c8f4:f17695fe-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd903ab-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695fe-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd903ac-bdfb-11e5-94fa-c8f73332c8f4:f176969e-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd903ac-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176969e-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd903b2-bdfb-11e5-94fa-c8f73332c8f4:f1769694-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd903b2-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769694-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd903b5-bdfb-11e5-94fa-c8f73332c8f4:f1769614-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd903b5-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769614-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd903c3-bdfb-11e5-94fa-c8f73332c8f4:f1769614-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd903c3-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769614-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd903c6-bdfb-11e5-94fa-c8f73332c8f4:f1769614-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd903c6-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769614-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd903c7-bdfb-11e5-94fa-c8f73332c8f4:f1769630-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd903c7-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769630-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd903c9-bdfb-11e5-94fa-c8f73332c8f4:f1769614-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd903c9-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769614-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd903ca-bdfb-11e5-94fa-c8f73332c8f4:f1769688-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd903ca-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769688-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd903cd-bdfb-11e5-94fa-c8f73332c8f4:f1769614-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd903cd-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769614-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd903d0-bdfb-11e5-94fa-c8f73332c8f4:f1769688-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd903d0-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769688-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd903d3-bdfb-11e5-94fa-c8f73332c8f4:f1769630-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd903d3-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769630-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd903d6-bdfb-11e5-94fa-c8f73332c8f4:f1769630-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd903d6-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769630-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd903d8-bdfb-11e5-94fa-c8f73332c8f4:f1769624-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd903d8-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769624-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd903d9-bdfb-11e5-94fa-c8f73332c8f4:f176968e-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd903d9-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176968e-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd903db-bdfb-11e5-94fa-c8f73332c8f4:f176962a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd903db-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176962a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd903dc-bdfb-11e5-94fa-c8f73332c8f4:f1769630-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd903dc-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769630-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd903de-bdfb-11e5-94fa-c8f73332c8f4:f176962a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd903de-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176962a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd903df-bdfb-11e5-94fa-c8f73332c8f4:f1769636-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd903df-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769636-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd903e1-bdfb-11e5-94fa-c8f73332c8f4:f176962a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd903e1-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176962a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd903e2-bdfb-11e5-94fa-c8f73332c8f4:f1769688-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd903e2-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769688-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd903e4-bdfb-11e5-94fa-c8f73332c8f4:f1769630-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd903e4-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769630-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd903e5-bdfb-11e5-94fa-c8f73332c8f4:f1769636-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd903e5-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769636-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd903e7-bdfb-11e5-94fa-c8f73332c8f4:f1769630-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd903e7-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769630-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd903e8-bdfb-11e5-94fa-c8f73332c8f4:f1769636-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd903e8-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769636-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd903ee-bdfb-11e5-94fa-c8f73332c8f4:f1769682-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd903ee-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769682-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd903f4-bdfb-11e5-94fa-c8f73332c8f4:f176966a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd903f4-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176966a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd903f7-bdfb-11e5-94fa-c8f73332c8f4:f1769688-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd903f7-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769688-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd903fd-bdfb-11e5-94fa-c8f73332c8f4:f1769688-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd903fd-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769688-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90400-bdfb-11e5-94fa-c8f73332c8f4:f1769688-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90400-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769688-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90403-bdfb-11e5-94fa-c8f73332c8f4:f1769670-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90403-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90409-bdfb-11e5-94fa-c8f73332c8f4:f1769670-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90409-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9040b-bdfb-11e5-94fa-c8f73332c8f4:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9040b-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9040c-bdfb-11e5-94fa-c8f73332c8f4:f1769664-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9040c-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769664-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9040e-bdfb-11e5-94fa-c8f73332c8f4:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9040e-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9040f-bdfb-11e5-94fa-c8f73332c8f4:f1769670-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9040f-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90411-bdfb-11e5-94fa-c8f73332c8f4:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90411-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90412-bdfb-11e5-94fa-c8f73332c8f4:f176967c-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90412-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176967c-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90414-bdfb-11e5-94fa-c8f73332c8f4:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90414-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90415-bdfb-11e5-94fa-c8f73332c8f4:f1769682-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90415-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769682-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90417-bdfb-11e5-94fa-c8f73332c8f4:f1769670-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90417-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90418-bdfb-11e5-94fa-c8f73332c8f4:f1769676-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90418-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769676-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9041a-bdfb-11e5-94fa-c8f73332c8f4:f1769682-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9041a-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769682-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9041b-bdfb-11e5-94fa-c8f73332c8f4:f176968e-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9041b-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176968e-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9041d-bdfb-11e5-94fa-c8f73332c8f4:f1769694-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9041d-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769694-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9041e-bdfb-11e5-94fa-c8f73332c8f4:f176969e-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9041e-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176969e-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90420-bdfb-11e5-94fa-c8f73332c8f4:f1769694-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90420-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769694-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90421-bdfb-11e5-94fa-c8f73332c8f4:f176969e-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90421-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176969e-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90432-bdfb-11e5-94fa-c8f73332c8f4:f17696ca-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90432-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90433-bdfb-11e5-94fa-c8f73332c8f4:f17696d4-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90433-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17696d4-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90435-bdfb-11e5-94fa-c8f73332c8f4:f17696ca-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90435-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90436-bdfb-11e5-94fa-c8f73332c8f4:f17696da-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90436-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17696da-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90439-bdfb-11e5-94fa-c8f73332c8f4:f17695d8-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90439-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695d8-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9043d-bdfb-11e5-94fa-c8f73332c8f4:f17695df-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9043d-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695df-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90440-bdfb-11e5-94fa-c8f73332c8f4:f17695fe-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90440-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695fe-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90444-bdfb-11e5-94fa-c8f73332c8f4:f17695e5-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90444-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695e5-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90447-bdfb-11e5-94fa-c8f73332c8f4:f17695f1-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90447-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9044b-bdfb-11e5-94fa-c8f73332c8f4:f17695f7-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9044b-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17695f7-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90455-bdfb-11e5-94fa-c8f73332c8f4:f1769624-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90455-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769624-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90459-bdfb-11e5-94fa-c8f73332c8f4:f176962a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90459-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176962a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90471-bdfb-11e5-94fa-c8f73332c8f4:f1769664-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90471-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769664-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90475-bdfb-11e5-94fa-c8f73332c8f4:f1769688-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90475-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769688-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90478-bdfb-11e5-94fa-c8f73332c8f4:f1769670-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90478-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9047c-bdfb-11e5-94fa-c8f73332c8f4:f1769670-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9047c-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9047f-bdfb-11e5-94fa-c8f73332c8f4:f1769682-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9047f-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769682-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90483-bdfb-11e5-94fa-c8f73332c8f4:f1769688-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90483-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769688-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd90486-bdfb-11e5-94fa-c8f73332c8f4:f176969e-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd90486-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176969e-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9048a-bdfb-11e5-94fa-c8f73332c8f4:f17696a8-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2dd9048a-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f17696a8-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2dd9048c-bdfb-11e5-94fa-c8f73332c8f4:root-node - labels: - - externalId: belongsTo - - externalId: GeographicalRegion - - externalId: non-historic - - externalId: RootCIMNode - - externalId: RootCIMNode.node - sourceExternalId: 2dd9048c-bdfb-11e5-94fa-c8f73332c8f4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: root-node - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2e64900b-94a8-6e40-bd63-3e69510c316f:f1769670-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2e64900b-94a8-6e40-bd63-3e69510c316f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2e98d68c-e0c5-6d47-b392-a1746743ef63:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2e98d68c-e0c5-6d47-b392-a1746743ef63 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 2f8c9437-e564-e948-8764-be3494565b1f:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 2f8c9437-e564-e948-8764-be3494565b1f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 30ab0123-1669-1242-99ca-610e4bcdc6c5:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 30ab0123-1669-1242-99ca-610e4bcdc6c5 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 329965e7-b5de-c54f-856a-358c9e1f9c50:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 329965e7-b5de-c54f-856a-358c9e1f9c50 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 356c6f11-da4d-3f4d-b3fd-49234106345c:f1769670-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 356c6f11-da4d-3f4d-b3fd-49234106345c - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 3718af45-a6ee-fb40-a4c6-f1118c114393:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 3718af45-a6ee-fb40-a4c6-f1118c114393 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 37c82b4c-7441-9946-98bf-7ade99732ecf:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 37c82b4c-7441-9946-98bf-7ade99732ecf - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 38b8e08a-112e-b046-8504-efef4beb0c17:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 38b8e08a-112e-b046-8504-efef4beb0c17 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 3916ae31-9f3d-af4e-8d87-87d373d8200a:f1769670-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 3916ae31-9f3d-af4e-8d87-87d373d8200a - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 40725e5a-33bf-6046-8d11-da19f9c726b4:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 40725e5a-33bf-6046-8d11-da19f9c726b4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 40c8340e-7ccf-d446-8a6b-bffa19ec88e6:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 40c8340e-7ccf-d446-8a6b-bffa19ec88e6 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 4a5b7813-14d3-fa4d-8907-edcdce7dbfd9:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 4a5b7813-14d3-fa4d-8907-edcdce7dbfd9 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 51883d43-111e-f24b-89f7-0573426fa32f:f1769670-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 51883d43-111e-f24b-89f7-0573426fa32f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 5831696b-e9e1-8e4a-a169-0e4e09f15f1f:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 5831696b-e9e1-8e4a-a169-0e4e09f15f1f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 5906b2a0-4ec9-1949-98b8-709380af0060:f1769670-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 5906b2a0-4ec9-1949-98b8-709380af0060 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 5c3fa20d-4748-5e44-b915-481e6d2552cd:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 5c3fa20d-4748-5e44-b915-481e6d2552cd - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 5ddb272d-00bd-d74e-8c96-e2049351c3e4:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 5ddb272d-00bd-d74e-8c96-e2049351c3e4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 5ffde614-bc34-384d-923e-e62ac9f3f584:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 5ffde614-bc34-384d-923e-e62ac9f3f584 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 669c1d0c-87dd-a943-9316-88308bd36f15:f1769670-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 669c1d0c-87dd-a943-9316-88308bd36f15 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 67bc203e-13e4-534b-94b8-bf19f5b31080:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 67bc203e-13e4-534b-94b8-bf19f5b31080 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 67dfc70a-36cf-da4e-b97c-fd1eda9cd8ac:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 67dfc70a-36cf-da4e-b97c-fd1eda9cd8ac - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 692809cf-1bb4-8642-9b70-ee677f063304:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 692809cf-1bb4-8642-9b70-ee677f063304 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 6abb96f5-a71f-8e41-b563-7c569789d6bd:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 6abb96f5-a71f-8e41-b563-7c569789d6bd - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 6ac2f3cd-c906-0a4b-ba1b-5942e9c65307:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 6ac2f3cd-c906-0a4b-ba1b-5942e9c65307 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 6da89f37-cc6f-4e4e-8b20-975295017390:f1769670-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 6da89f37-cc6f-4e4e-8b20-975295017390 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 70f1852f-186d-5e43-98bf-b69f028cfcc5:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 70f1852f-186d-5e43-98bf-b69f028cfcc5 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 714c775a-246f-e34f-9a6b-667f4b66138c:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 714c775a-246f-e34f-9a6b-667f4b66138c - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 71d09d39-a080-5d4f-86f3-7b3185e51b8e:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 71d09d39-a080-5d4f-86f3-7b3185e51b8e - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 761a49da-2b71-2d4e-877f-3cf7fa86ef63:f1769670-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 761a49da-2b71-2d4e-877f-3cf7fa86ef63 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 79887cf1-6c94-3f4f-a1f8-7ddef866d55f:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 79887cf1-6c94-3f4f-a1f8-7ddef866d55f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 7af49a35-a119-5443-84be-af343b7761c8:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 7af49a35-a119-5443-84be-af343b7761c8 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 7d101d2b-ce05-9843-ab59-2c3e5e244cbb:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 7d101d2b-ce05-9843-ab59-2c3e5e244cbb - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 7f811234-10fe-e34f-a9af-9af61a6dd4c7:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 7f811234-10fe-e34f-a9af-9af61a6dd4c7 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 84775b5d-95c1-bd46-bb0d-0f74e4c06757:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 84775b5d-95c1-bd46-bb0d-0f74e4c06757 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 854b40ee-7109-d04c-b4ef-f0665722e451:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 854b40ee-7109-d04c-b4ef-f0665722e451 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 8574aebb-23c4-3e4a-bd04-b5651c97d27a:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 8574aebb-23c4-3e4a-bd04-b5651c97d27a - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 8a0babd6-6d6f-854b-b65f-4245e65c9e9c:f1769670-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 8a0babd6-6d6f-854b-b65f-4245e65c9e9c - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 8d35b51b-06bd-7541-b3e6-2ddbbb1204fe:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 8d35b51b-06bd-7541-b3e6-2ddbbb1204fe - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 8e8e6b0c-ee96-7a4f-9790-c9ae62edafbe:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 8e8e6b0c-ee96-7a4f-9790-c9ae62edafbe - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 8f8094c3-5c43-1241-9014-6c7a3ae10213:f1769670-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 8f8094c3-5c43-1241-9014-6c7a3ae10213 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 925f3ab7-54ce-b748-887a-fc6ae23bbdc2:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 925f3ab7-54ce-b748-887a-fc6ae23bbdc2 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 947636e9-e939-294e-a94b-2090060b74ca:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 947636e9-e939-294e-a94b-2090060b74ca - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 9acd76b4-8d78-2b4b-81d5-e404e244997d:f1769670-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 9acd76b4-8d78-2b4b-81d5-e404e244997d - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: 9b49634c-4910-9841-b595-0d0cb0d0d1de:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: 9b49634c-4910-9841-b595-0d0cb0d0d1de - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: a0ad0036-fbee-6646-b195-3a86614529f2:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: a0ad0036-fbee-6646-b195-3a86614529f2 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: a2a690ef-b78a-3f46-90c5-5d434e26a64e:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: a2a690ef-b78a-3f46-90c5-5d434e26a64e - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: a4bc44aa-25e4-684a-ab2c-c7fc3378d9e5:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: a4bc44aa-25e4-684a-ab2c-c7fc3378d9e5 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: a533d3fd-822f-3b40-9030-e18b571cd1ea:f1769670-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: a533d3fd-822f-3b40-9030-e18b571cd1ea - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: a749d698-52a2-564e-8c6f-068fe62045fe:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: a749d698-52a2-564e-8c6f-068fe62045fe - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: a949cafd-39f3-8e4c-bca5-9928b004b4da:f1769670-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: a949cafd-39f3-8e4c-bca5-9928b004b4da - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: aadfa134-666a-ae4f-8c09-6f3ae114c3b5:f1769670-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: aadfa134-666a-ae4f-8c09-6f3ae114c3b5 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: ab88a3a2-a572-3440-be06-757e0225ea42:f1769670-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: ab88a3a2-a572-3440-be06-757e0225ea42 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: ad37e24a-9993-4f45-80ed-49a80b6a85d9:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: ad37e24a-9993-4f45-80ed-49a80b6a85d9 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: ad970e05-69d9-5340-9dbd-ce6a9a2e0996:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: ad970e05-69d9-5340-9dbd-ce6a9a2e0996 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: ae68b597-69fb-e14b-a070-67c7dcc1d698:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: ae68b597-69fb-e14b-a070-67c7dcc1d698 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: b016bc42-5a9f-ec42-b326-e9b9eed9e6d3:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: b016bc42-5a9f-ec42-b326-e9b9eed9e6d3 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: b3ff19da-83b8-5e40-b38c-38982b748732:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: b3ff19da-83b8-5e40-b38c-38982b748732 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: b77c60e8-fe4f-9149-9bc6-c3eb8f899de2:f1769670-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: b77c60e8-fe4f-9149-9bc6-c3eb8f899de2 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: bc0f852a-3cc8-e143-8174-f0854a2d38aa:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: bc0f852a-3cc8-e143-8174-f0854a2d38aa - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: bff806b2-7825-af4c-a34b-be542dbe5ae6:f1769670-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: bff806b2-7825-af4c-a34b-be542dbe5ae6 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: c1aa295d-e817-db46-b14e-bee2739fcd9d:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: c1aa295d-e817-db46-b14e-bee2739fcd9d - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: c1ca1160-be67-9448-a16b-e92a6112e380:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: c1ca1160-be67-9448-a16b-e92a6112e380 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: c3458e72-1c7a-a74b-b561-8afe0a3aa2be:f1769670-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: c3458e72-1c7a-a74b-b561-8afe0a3aa2be - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: c8ac9d5f-d60e-a345-8790-12b86184c31b:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: c8ac9d5f-d60e-a345-8790-12b86184c31b - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: c8f95fee-e9a6-a44b-97ac-23e272a05ecd:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: c8f95fee-e9a6-a44b-97ac-23e272a05ecd - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: c95039b2-aea2-ad4d-b444-c6efc77461e5:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: c95039b2-aea2-ad4d-b444-c6efc77461e5 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: caa10f15-4591-e241-ac0b-81675acf5687:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: caa10f15-4591-e241-ac0b-81675acf5687 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: cd25f47c-ad0c-f74d-8c0e-fd91a95505b6:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: cd25f47c-ad0c-f74d-8c0e-fd91a95505b6 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: cf654436-a05a-7948-8cc3-f3a288608964:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: cf654436-a05a-7948-8cc3-f3a288608964 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: d288bd96-ae6e-cf46-9775-f38e2699f1ce:f1769670-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: d288bd96-ae6e-cf46-9775-f38e2699f1ce - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: d28bc89e-0592-1440-9721-5cbe124c1f1b:f1769670-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: d28bc89e-0592-1440-9721-5cbe124c1f1b - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: d7a472cc-6b91-564d-93b4-0f3c24f2342c:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: d7a472cc-6b91-564d-93b4-0f3c24f2342c - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: d92cb2eb-fd81-194c-9e43-618df025d6b4:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: d92cb2eb-fd81-194c-9e43-618df025d6b4 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: da4b0f71-a251-f947-be12-2de53a2d272a:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: da4b0f71-a251-f947-be12-2de53a2d272a - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: dae3bb4b-0a86-9243-99c1-a1d5b163774d:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: dae3bb4b-0a86-9243-99c1-a1d5b163774d - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: db724627-9dae-4748-b4e3-2daf627ffa17:f1769670-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: db724627-9dae-4748-b4e3-2daf627ffa17 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: dc791b3b-fe63-a14b-b144-953748088613:f1769670-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: dc791b3b-fe63-a14b-b144-953748088613 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: e035c87e-704d-404f-8ea8-91c62e0d7d8a:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: e035c87e-704d-404f-8ea8-91c62e0d7d8a - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: e25c03e3-96a7-a64f-b776-a155f009d5f8:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: e25c03e3-96a7-a64f-b776-a155f009d5f8 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: e2f56599-a78e-494f-8db3-c0b0bdab1d70:f1769670-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: e2f56599-a78e-494f-8db3-c0b0bdab1d70 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: e3d56d8b-5f71-3042-9734-7118977e887c:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: e3d56d8b-5f71-3042-9734-7118977e887c - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: e3ef93a1-723a-224e-8863-472e4f269633:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: e3ef93a1-723a-224e-8863-472e4f269633 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: e65c4130-3a4d-db42-9020-c9f7c447d473:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: e65c4130-3a4d-db42-9020-c9f7c447d473 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: ea014fdb-b96f-2a4b-b1df-d38e846d4941:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: ea014fdb-b96f-2a4b-b1df-d38e846d4941 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: eaf768b1-3d82-c64d-a1c6-5bd7f9a72751:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: eaf768b1-3d82-c64d-a1c6-5bd7f9a72751 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: eb8b2a90-9bc8-154f-92dd-3887e3676498:f1769670-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: eb8b2a90-9bc8-154f-92dd-3887e3676498 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: eba035f1-7c96-0142-8561-37b28625abf7:f1769670-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: eba035f1-7c96-0142-8561-37b28625abf7 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: ecfaf384-4581-4249-8646-b5d31943ad61:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: ecfaf384-4581-4249-8646-b5d31943ad61 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: ee4223ab-0538-dc4a-9b79-d463c048bb08:f1769670-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: ee4223ab-0538-dc4a-9b79-d463c048bb08 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: efe663e2-7518-3044-88d4-209bf597536a:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: efe663e2-7518-3044-88d4-209bf597536a - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f08f910c-300e-8941-aa85-3106d93e3429:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: f08f910c-300e-8941-aa85-3106d93e3429 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695aa-9aeb-11e5-91da-b8763fd99c5f:2dd90156-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695aa-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90156-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695aa-9aeb-11e5-91da-b8763fd99c5f:2dd901c6-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695aa-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd901c6-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695aa-9aeb-11e5-91da-b8763fd99c5f:2dd901c9-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695aa-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd901c9-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695aa-9aeb-11e5-91da-b8763fd99c5f:2dd901cc-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695aa-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd901cc-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695aa-9aeb-11e5-91da-b8763fd99c5f:2dd90258-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695aa-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90258-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695aa-9aeb-11e5-91da-b8763fd99c5f:2dd9025b-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695aa-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9025b-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695aa-9aeb-11e5-91da-b8763fd99c5f:2dd9025e-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695aa-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9025e-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695aa-9aeb-11e5-91da-b8763fd99c5f:2dd90357-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695aa-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90357-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695aa-9aeb-11e5-91da-b8763fd99c5f:2dd9035a-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695aa-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9035a-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695aa-9aeb-11e5-91da-b8763fd99c5f:2dd9035d-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695aa-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9035d-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695aa-9aeb-11e5-91da-b8763fd99c5f:2dd90360-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695aa-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90360-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695aa-9aeb-11e5-91da-b8763fd99c5f:2dd90363-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695aa-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90363-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695aa-9aeb-11e5-91da-b8763fd99c5f:2dd90366-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695aa-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90366-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695af-9aeb-11e5-91da-b8763fd99c5f:2dd9048c-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: belongsTo - - externalId: GeographicalRegion - - externalId: non-historic - - externalId: SubGeographicalRegion - - externalId: SubGeographicalRegion.Region - sourceExternalId: f17695af-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9048c-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695b8-9aeb-11e5-91da-b8763fd99c5f:2dd9015c-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695b8-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9015c-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695b8-9aeb-11e5-91da-b8763fd99c5f:2dd901cf-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695b8-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd901cf-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695b8-9aeb-11e5-91da-b8763fd99c5f:2dd90358-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695b8-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90358-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695be-9aeb-11e5-91da-b8763fd99c5f:2dd9015e-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695be-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9015e-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695be-9aeb-11e5-91da-b8763fd99c5f:2dd901d2-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695be-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd901d2-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695be-9aeb-11e5-91da-b8763fd99c5f:2dd90369-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695be-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90369-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695be-9aeb-11e5-91da-b8763fd99c5f:2dd9036c-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695be-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9036c-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695be-9aeb-11e5-91da-b8763fd99c5f:2dd9036f-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695be-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9036f-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695be-9aeb-11e5-91da-b8763fd99c5f:2dd90372-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695be-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90372-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695be-9aeb-11e5-91da-b8763fd99c5f:2dd90375-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695be-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90375-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695be-9aeb-11e5-91da-b8763fd99c5f:2dd90378-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695be-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90378-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695be-9aeb-11e5-91da-b8763fd99c5f:2dd9037b-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695be-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9037b-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695c3-9aeb-11e5-91da-b8763fd99c5f:2dd9048c-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: belongsTo - - externalId: GeographicalRegion - - externalId: non-historic - - externalId: SubGeographicalRegion - - externalId: SubGeographicalRegion.Region - sourceExternalId: f17695c3-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9048c-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695c8-9aeb-11e5-91da-b8763fd99c5f:2dd90162-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695c8-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90162-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695c8-9aeb-11e5-91da-b8763fd99c5f:2dd901d5-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695c8-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd901d5-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695c8-9aeb-11e5-91da-b8763fd99c5f:2dd90262-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695c8-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90262-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695c8-9aeb-11e5-91da-b8763fd99c5f:2dd90265-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695c8-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90265-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695c8-9aeb-11e5-91da-b8763fd99c5f:2dd90268-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695c8-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90268-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695c8-9aeb-11e5-91da-b8763fd99c5f:2dd9026b-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695c8-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9026b-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695c8-9aeb-11e5-91da-b8763fd99c5f:2dd9026e-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695c8-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9026e-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695c8-9aeb-11e5-91da-b8763fd99c5f:2dd9035b-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695c8-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9035b-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695c8-9aeb-11e5-91da-b8763fd99c5f:2dd9036a-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695c8-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9036a-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695c8-9aeb-11e5-91da-b8763fd99c5f:2dd9037e-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695c8-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9037e-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695c8-9aeb-11e5-91da-b8763fd99c5f:2dd90381-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695c8-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90381-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695c8-9aeb-11e5-91da-b8763fd99c5f:2dd90384-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695c8-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90384-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695c8-9aeb-11e5-91da-b8763fd99c5f:2dd90387-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695c8-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90387-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695cd-9aeb-11e5-91da-b8763fd99c5f:2dd9048c-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: belongsTo - - externalId: GeographicalRegion - - externalId: non-historic - - externalId: SubGeographicalRegion - - externalId: SubGeographicalRegion.Region - sourceExternalId: f17695cd-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9048c-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695d2-9aeb-11e5-91da-b8763fd99c5f:2dd90166-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695d2-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90166-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695d2-9aeb-11e5-91da-b8763fd99c5f:2dd9036d-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695d2-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9036d-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695d2-9aeb-11e5-91da-b8763fd99c5f:2dd90370-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695d2-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90370-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695d2-9aeb-11e5-91da-b8763fd99c5f:2dd90373-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695d2-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90373-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695d2-9aeb-11e5-91da-b8763fd99c5f:2dd9038a-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695d2-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9038a-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695d2-9aeb-11e5-91da-b8763fd99c5f:2dd9038d-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695d2-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9038d-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695d2-9aeb-11e5-91da-b8763fd99c5f:2dd90390-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695d2-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90390-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695d8-9aeb-11e5-91da-b8763fd99c5f:2dd90168-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695d8-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90168-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695d8-9aeb-11e5-91da-b8763fd99c5f:2dd90393-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695d8-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90393-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695d8-9aeb-11e5-91da-b8763fd99c5f:2dd90439-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695d8-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90439-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695df-9aeb-11e5-91da-b8763fd99c5f:2dd9016b-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695df-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9016b-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695df-9aeb-11e5-91da-b8763fd99c5f:2dd90272-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695df-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90272-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695df-9aeb-11e5-91da-b8763fd99c5f:2dd9035e-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695df-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9035e-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695df-9aeb-11e5-91da-b8763fd99c5f:2dd90361-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695df-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90361-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695df-9aeb-11e5-91da-b8763fd99c5f:2dd9037f-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695df-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9037f-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695df-9aeb-11e5-91da-b8763fd99c5f:2dd9043d-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695df-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9043d-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695e5-9aeb-11e5-91da-b8763fd99c5f:2dd9016d-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695e5-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9016d-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695e5-9aeb-11e5-91da-b8763fd99c5f:2dd901d8-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695e5-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd901d8-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695e5-9aeb-11e5-91da-b8763fd99c5f:2dd90276-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695e5-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90276-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695e5-9aeb-11e5-91da-b8763fd99c5f:2dd90279-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695e5-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90279-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695e5-9aeb-11e5-91da-b8763fd99c5f:2dd9027c-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695e5-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9027c-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695e5-9aeb-11e5-91da-b8763fd99c5f:2dd9027f-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695e5-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9027f-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695e5-9aeb-11e5-91da-b8763fd99c5f:2dd90282-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695e5-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90282-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695e5-9aeb-11e5-91da-b8763fd99c5f:2dd90285-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695e5-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90285-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695e5-9aeb-11e5-91da-b8763fd99c5f:2dd90288-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695e5-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90288-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695e5-9aeb-11e5-91da-b8763fd99c5f:2dd9028b-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695e5-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9028b-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695e5-9aeb-11e5-91da-b8763fd99c5f:2dd90376-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695e5-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90376-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695e5-9aeb-11e5-91da-b8763fd99c5f:2dd90382-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695e5-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90382-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695e5-9aeb-11e5-91da-b8763fd99c5f:2dd90396-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695e5-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90396-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695e5-9aeb-11e5-91da-b8763fd99c5f:2dd90444-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695e5-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90444-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695eb-9aeb-11e5-91da-b8763fd99c5f:2dd9016f-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695eb-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9016f-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695eb-9aeb-11e5-91da-b8763fd99c5f:2dd901db-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695eb-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd901db-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695eb-9aeb-11e5-91da-b8763fd99c5f:2dd901de-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695eb-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd901de-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695eb-9aeb-11e5-91da-b8763fd99c5f:2dd9028f-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695eb-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9028f-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695eb-9aeb-11e5-91da-b8763fd99c5f:2dd90292-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695eb-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90292-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695eb-9aeb-11e5-91da-b8763fd99c5f:2dd90295-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695eb-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90295-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695eb-9aeb-11e5-91da-b8763fd99c5f:2dd90298-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695eb-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90298-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695eb-9aeb-11e5-91da-b8763fd99c5f:2dd9029b-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695eb-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9029b-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695eb-9aeb-11e5-91da-b8763fd99c5f:2dd9029e-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695eb-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9029e-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695eb-9aeb-11e5-91da-b8763fd99c5f:2dd90364-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695eb-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90364-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695eb-9aeb-11e5-91da-b8763fd99c5f:2dd90367-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695eb-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90367-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695eb-9aeb-11e5-91da-b8763fd99c5f:2dd9038b-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695eb-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9038b-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695eb-9aeb-11e5-91da-b8763fd99c5f:2dd90399-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695eb-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90399-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695eb-9aeb-11e5-91da-b8763fd99c5f:2dd9039c-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695eb-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9039c-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f:2dd90171-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90171-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f:2dd901e1-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd901e1-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f:2dd901e4-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd901e4-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f:2dd901e7-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd901e7-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f:2dd901ea-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd901ea-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f:2dd902a2-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd902a2-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f:2dd902a5-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd902a5-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f:2dd902a8-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd902a8-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f:2dd902ab-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd902ab-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f:2dd902ae-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd902ae-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f:2dd902b1-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd902b1-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f:2dd90379-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90379-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f:2dd9037c-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9037c-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f:2dd9038e-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9038e-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f:2dd9039f-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9039f-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f:2dd903a2-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd903a2-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f:2dd903a5-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd903a5-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f:2dd903a8-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd903a8-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f:2dd90447-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695f1-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90447-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695f7-9aeb-11e5-91da-b8763fd99c5f:2dd90173-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695f7-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90173-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695f7-9aeb-11e5-91da-b8763fd99c5f:2dd901ed-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695f7-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd901ed-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695f7-9aeb-11e5-91da-b8763fd99c5f:2dd9044b-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695f7-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9044b-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695fe-9aeb-11e5-91da-b8763fd99c5f:2dd90176-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695fe-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90176-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695fe-9aeb-11e5-91da-b8763fd99c5f:2dd903ab-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695fe-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd903ab-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17695fe-9aeb-11e5-91da-b8763fd99c5f:2dd90440-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17695fe-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90440-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769614-9aeb-11e5-91da-b8763fd99c5f:2dd9017e-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769614-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9017e-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769614-9aeb-11e5-91da-b8763fd99c5f:2dd903b5-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769614-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd903b5-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769614-9aeb-11e5-91da-b8763fd99c5f:2dd903c3-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769614-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd903c3-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769614-9aeb-11e5-91da-b8763fd99c5f:2dd903c6-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769614-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd903c6-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769614-9aeb-11e5-91da-b8763fd99c5f:2dd903c9-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769614-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd903c9-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769614-9aeb-11e5-91da-b8763fd99c5f:2dd903cd-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769614-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd903cd-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769619-9aeb-11e5-91da-b8763fd99c5f:2dd9048c-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: belongsTo - - externalId: GeographicalRegion - - externalId: non-historic - - externalId: SubGeographicalRegion - - externalId: SubGeographicalRegion.Region - sourceExternalId: f1769619-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9048c-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769624-9aeb-11e5-91da-b8763fd99c5f:2dd90184-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769624-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90184-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769624-9aeb-11e5-91da-b8763fd99c5f:2dd901f3-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769624-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd901f3-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769624-9aeb-11e5-91da-b8763fd99c5f:2dd902bc-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769624-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd902bc-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769624-9aeb-11e5-91da-b8763fd99c5f:2dd902bf-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769624-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd902bf-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769624-9aeb-11e5-91da-b8763fd99c5f:2dd902c2-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769624-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd902c2-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769624-9aeb-11e5-91da-b8763fd99c5f:2dd902c5-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769624-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd902c5-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769624-9aeb-11e5-91da-b8763fd99c5f:2dd902c8-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769624-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd902c8-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769624-9aeb-11e5-91da-b8763fd99c5f:2dd902cb-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769624-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd902cb-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769624-9aeb-11e5-91da-b8763fd99c5f:2dd903d8-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769624-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd903d8-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769624-9aeb-11e5-91da-b8763fd99c5f:2dd90455-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769624-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90455-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176962a-9aeb-11e5-91da-b8763fd99c5f:2dd90186-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176962a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90186-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176962a-9aeb-11e5-91da-b8763fd99c5f:2dd903db-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176962a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd903db-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176962a-9aeb-11e5-91da-b8763fd99c5f:2dd903de-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176962a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd903de-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176962a-9aeb-11e5-91da-b8763fd99c5f:2dd903e1-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176962a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd903e1-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176962a-9aeb-11e5-91da-b8763fd99c5f:2dd90459-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176962a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90459-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769630-9aeb-11e5-91da-b8763fd99c5f:2dd90188-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769630-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90188-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769630-9aeb-11e5-91da-b8763fd99c5f:2dd903c7-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769630-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd903c7-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769630-9aeb-11e5-91da-b8763fd99c5f:2dd903d3-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769630-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd903d3-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769630-9aeb-11e5-91da-b8763fd99c5f:2dd903d6-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769630-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd903d6-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769630-9aeb-11e5-91da-b8763fd99c5f:2dd903dc-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769630-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd903dc-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769630-9aeb-11e5-91da-b8763fd99c5f:2dd903e4-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769630-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd903e4-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769630-9aeb-11e5-91da-b8763fd99c5f:2dd903e7-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769630-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd903e7-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769636-9aeb-11e5-91da-b8763fd99c5f:2dd9018a-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769636-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9018a-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769636-9aeb-11e5-91da-b8763fd99c5f:2dd903df-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769636-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd903df-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769636-9aeb-11e5-91da-b8763fd99c5f:2dd903e5-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769636-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd903e5-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769636-9aeb-11e5-91da-b8763fd99c5f:2dd903e8-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769636-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd903e8-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:0345061e-37c9-9a49-b632-2af77bdca3a2 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 0345061e-37c9-9a49-b632-2af77bdca3a2 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:0619c12e-abb9-b141-b3a7-e2788d3d375c - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 0619c12e-abb9-b141-b3a7-e2788d3d375c - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:093a3df5-0583-894f-99c9-016d752ff920 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 093a3df5-0583-894f-99c9-016d752ff920 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:0a122f63-f2c6-ca43-be49-76571474d98c - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 0a122f63-f2c6-ca43-be49-76571474d98c - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:0b50ae74-92eb-f244-872b-07e629f53494 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 0b50ae74-92eb-f244-872b-07e629f53494 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:0d7f39d0-5447-fb42-9d4f-3d0775bceef0 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 0d7f39d0-5447-fb42-9d4f-3d0775bceef0 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:0dae5d37-4d01-3645-9e94-607410c5ac34 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 0dae5d37-4d01-3645-9e94-607410c5ac34 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:13116d72-3969-1344-91a7-14fb404a00c0 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 13116d72-3969-1344-91a7-14fb404a00c0 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:16659ede-d69f-1d48-bcf2-ebb2ee6c259e - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 16659ede-d69f-1d48-bcf2-ebb2ee6c259e - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:292835be-c432-734c-8c28-3b8c61c0c077 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 292835be-c432-734c-8c28-3b8c61c0c077 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:2a67a243-6e4a-a84d-8aea-f250399166c9 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2a67a243-6e4a-a84d-8aea-f250399166c9 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:2dd90196-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90196-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:2dd901ff-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd901ff-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:2dd90202-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90202-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:2dd902dd-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd902dd-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:2dd902e0-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd902e0-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:2dd902e3-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd902e3-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:2dd902e6-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd902e6-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:2dd9040b-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9040b-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:2dd9040e-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9040e-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:2dd90411-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90411-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:2dd90414-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90414-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:2e98d68c-e0c5-6d47-b392-a1746743ef63 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2e98d68c-e0c5-6d47-b392-a1746743ef63 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:2f8c9437-e564-e948-8764-be3494565b1f - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2f8c9437-e564-e948-8764-be3494565b1f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:30ab0123-1669-1242-99ca-610e4bcdc6c5 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 30ab0123-1669-1242-99ca-610e4bcdc6c5 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:329965e7-b5de-c54f-856a-358c9e1f9c50 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 329965e7-b5de-c54f-856a-358c9e1f9c50 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:3718af45-a6ee-fb40-a4c6-f1118c114393 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 3718af45-a6ee-fb40-a4c6-f1118c114393 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:37c82b4c-7441-9946-98bf-7ade99732ecf - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 37c82b4c-7441-9946-98bf-7ade99732ecf - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:38b8e08a-112e-b046-8504-efef4beb0c17 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 38b8e08a-112e-b046-8504-efef4beb0c17 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:40725e5a-33bf-6046-8d11-da19f9c726b4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 40725e5a-33bf-6046-8d11-da19f9c726b4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:40c8340e-7ccf-d446-8a6b-bffa19ec88e6 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 40c8340e-7ccf-d446-8a6b-bffa19ec88e6 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:4a5b7813-14d3-fa4d-8907-edcdce7dbfd9 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 4a5b7813-14d3-fa4d-8907-edcdce7dbfd9 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:5831696b-e9e1-8e4a-a169-0e4e09f15f1f - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 5831696b-e9e1-8e4a-a169-0e4e09f15f1f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:5c3fa20d-4748-5e44-b915-481e6d2552cd - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 5c3fa20d-4748-5e44-b915-481e6d2552cd - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:5ddb272d-00bd-d74e-8c96-e2049351c3e4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 5ddb272d-00bd-d74e-8c96-e2049351c3e4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:5ffde614-bc34-384d-923e-e62ac9f3f584 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 5ffde614-bc34-384d-923e-e62ac9f3f584 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:67bc203e-13e4-534b-94b8-bf19f5b31080 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 67bc203e-13e4-534b-94b8-bf19f5b31080 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:67dfc70a-36cf-da4e-b97c-fd1eda9cd8ac - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 67dfc70a-36cf-da4e-b97c-fd1eda9cd8ac - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:692809cf-1bb4-8642-9b70-ee677f063304 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 692809cf-1bb4-8642-9b70-ee677f063304 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:6abb96f5-a71f-8e41-b563-7c569789d6bd - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 6abb96f5-a71f-8e41-b563-7c569789d6bd - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:6ac2f3cd-c906-0a4b-ba1b-5942e9c65307 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 6ac2f3cd-c906-0a4b-ba1b-5942e9c65307 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:70f1852f-186d-5e43-98bf-b69f028cfcc5 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 70f1852f-186d-5e43-98bf-b69f028cfcc5 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:714c775a-246f-e34f-9a6b-667f4b66138c - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 714c775a-246f-e34f-9a6b-667f4b66138c - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:71d09d39-a080-5d4f-86f3-7b3185e51b8e - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 71d09d39-a080-5d4f-86f3-7b3185e51b8e - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:79887cf1-6c94-3f4f-a1f8-7ddef866d55f - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 79887cf1-6c94-3f4f-a1f8-7ddef866d55f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:7af49a35-a119-5443-84be-af343b7761c8 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 7af49a35-a119-5443-84be-af343b7761c8 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:7d101d2b-ce05-9843-ab59-2c3e5e244cbb - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 7d101d2b-ce05-9843-ab59-2c3e5e244cbb - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:7f811234-10fe-e34f-a9af-9af61a6dd4c7 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 7f811234-10fe-e34f-a9af-9af61a6dd4c7 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:84775b5d-95c1-bd46-bb0d-0f74e4c06757 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 84775b5d-95c1-bd46-bb0d-0f74e4c06757 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:854b40ee-7109-d04c-b4ef-f0665722e451 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 854b40ee-7109-d04c-b4ef-f0665722e451 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:8574aebb-23c4-3e4a-bd04-b5651c97d27a - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 8574aebb-23c4-3e4a-bd04-b5651c97d27a - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:8d35b51b-06bd-7541-b3e6-2ddbbb1204fe - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 8d35b51b-06bd-7541-b3e6-2ddbbb1204fe - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:8e8e6b0c-ee96-7a4f-9790-c9ae62edafbe - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 8e8e6b0c-ee96-7a4f-9790-c9ae62edafbe - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:925f3ab7-54ce-b748-887a-fc6ae23bbdc2 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 925f3ab7-54ce-b748-887a-fc6ae23bbdc2 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:947636e9-e939-294e-a94b-2090060b74ca - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 947636e9-e939-294e-a94b-2090060b74ca - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:9b49634c-4910-9841-b595-0d0cb0d0d1de - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 9b49634c-4910-9841-b595-0d0cb0d0d1de - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:a0ad0036-fbee-6646-b195-3a86614529f2 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: a0ad0036-fbee-6646-b195-3a86614529f2 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:a2a690ef-b78a-3f46-90c5-5d434e26a64e - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: a2a690ef-b78a-3f46-90c5-5d434e26a64e - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:a4bc44aa-25e4-684a-ab2c-c7fc3378d9e5 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: a4bc44aa-25e4-684a-ab2c-c7fc3378d9e5 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:a749d698-52a2-564e-8c6f-068fe62045fe - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: a749d698-52a2-564e-8c6f-068fe62045fe - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:ad37e24a-9993-4f45-80ed-49a80b6a85d9 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: ad37e24a-9993-4f45-80ed-49a80b6a85d9 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:ad970e05-69d9-5340-9dbd-ce6a9a2e0996 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: ad970e05-69d9-5340-9dbd-ce6a9a2e0996 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:ae68b597-69fb-e14b-a070-67c7dcc1d698 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: ae68b597-69fb-e14b-a070-67c7dcc1d698 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:b016bc42-5a9f-ec42-b326-e9b9eed9e6d3 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: b016bc42-5a9f-ec42-b326-e9b9eed9e6d3 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:b3ff19da-83b8-5e40-b38c-38982b748732 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: b3ff19da-83b8-5e40-b38c-38982b748732 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:bc0f852a-3cc8-e143-8174-f0854a2d38aa - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: bc0f852a-3cc8-e143-8174-f0854a2d38aa - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:c1aa295d-e817-db46-b14e-bee2739fcd9d - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: c1aa295d-e817-db46-b14e-bee2739fcd9d - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:c1ca1160-be67-9448-a16b-e92a6112e380 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: c1ca1160-be67-9448-a16b-e92a6112e380 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:c8ac9d5f-d60e-a345-8790-12b86184c31b - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: c8ac9d5f-d60e-a345-8790-12b86184c31b - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:c8f95fee-e9a6-a44b-97ac-23e272a05ecd - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: c8f95fee-e9a6-a44b-97ac-23e272a05ecd - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:c95039b2-aea2-ad4d-b444-c6efc77461e5 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: c95039b2-aea2-ad4d-b444-c6efc77461e5 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:caa10f15-4591-e241-ac0b-81675acf5687 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: caa10f15-4591-e241-ac0b-81675acf5687 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:cd25f47c-ad0c-f74d-8c0e-fd91a95505b6 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: cd25f47c-ad0c-f74d-8c0e-fd91a95505b6 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:cf654436-a05a-7948-8cc3-f3a288608964 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: cf654436-a05a-7948-8cc3-f3a288608964 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:d7a472cc-6b91-564d-93b4-0f3c24f2342c - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: d7a472cc-6b91-564d-93b4-0f3c24f2342c - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:d92cb2eb-fd81-194c-9e43-618df025d6b4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: d92cb2eb-fd81-194c-9e43-618df025d6b4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:da4b0f71-a251-f947-be12-2de53a2d272a - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: da4b0f71-a251-f947-be12-2de53a2d272a - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:dae3bb4b-0a86-9243-99c1-a1d5b163774d - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: dae3bb4b-0a86-9243-99c1-a1d5b163774d - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:e035c87e-704d-404f-8ea8-91c62e0d7d8a - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: e035c87e-704d-404f-8ea8-91c62e0d7d8a - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:e25c03e3-96a7-a64f-b776-a155f009d5f8 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: e25c03e3-96a7-a64f-b776-a155f009d5f8 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:e3d56d8b-5f71-3042-9734-7118977e887c - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: e3d56d8b-5f71-3042-9734-7118977e887c - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:e3ef93a1-723a-224e-8863-472e4f269633 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: e3ef93a1-723a-224e-8863-472e4f269633 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:e65c4130-3a4d-db42-9020-c9f7c447d473 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: e65c4130-3a4d-db42-9020-c9f7c447d473 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:ea014fdb-b96f-2a4b-b1df-d38e846d4941 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: ea014fdb-b96f-2a4b-b1df-d38e846d4941 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:eaf768b1-3d82-c64d-a1c6-5bd7f9a72751 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: eaf768b1-3d82-c64d-a1c6-5bd7f9a72751 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:ecfaf384-4581-4249-8646-b5d31943ad61 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: ecfaf384-4581-4249-8646-b5d31943ad61 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:efe663e2-7518-3044-88d4-209bf597536a - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: efe663e2-7518-3044-88d4-209bf597536a - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:f08f910c-300e-8941-aa85-3106d93e3429 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f08f910c-300e-8941-aa85-3106d93e3429 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:f4dba262-3ff3-9f41-961d-7fbe1f8a0cd7 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f4dba262-3ff3-9f41-961d-7fbe1f8a0cd7 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:f710cc4a-50dd-e443-8ac7-4c56b70041e8 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f710cc4a-50dd-e443-8ac7-4c56b70041e8 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:f723530d-ae1e-df4f-9c7f-21e42da147d6 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f723530d-ae1e-df4f-9c7f-21e42da147d6 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965a-9aeb-11e5-91da-b8763fd99c5f:f9e8d6d1-76e1-6c4d-a640-e128ecfa723e - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f9e8d6d1-76e1-6c4d-a640-e128ecfa723e - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176965f-9aeb-11e5-91da-b8763fd99c5f:2dd9048c-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: belongsTo - - externalId: GeographicalRegion - - externalId: non-historic - - externalId: SubGeographicalRegion - - externalId: SubGeographicalRegion.Region - sourceExternalId: f176965f-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9048c-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769664-9aeb-11e5-91da-b8763fd99c5f:2dd9019a-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769664-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9019a-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769664-9aeb-11e5-91da-b8763fd99c5f:2dd9040c-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769664-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9040c-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769664-9aeb-11e5-91da-b8763fd99c5f:2dd90471-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769664-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90471-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176966a-9aeb-11e5-91da-b8763fd99c5f:02b41435-bd3e-bb4b-9b89-24e3a8983c0f - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176966a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 02b41435-bd3e-bb4b-9b89-24e3a8983c0f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176966a-9aeb-11e5-91da-b8763fd99c5f:2dd9019c-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176966a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9019c-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176966a-9aeb-11e5-91da-b8763fd99c5f:2dd903f4-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176966a-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd903f4-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769670-9aeb-11e5-91da-b8763fd99c5f:076da31b-080d-c742-9cb1-134a6ba5b1ed - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 076da31b-080d-c742-9cb1-134a6ba5b1ed - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769670-9aeb-11e5-91da-b8763fd99c5f:0ef62251-5e2e-d14a-aa44-396d4a885c4f - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 0ef62251-5e2e-d14a-aa44-396d4a885c4f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769670-9aeb-11e5-91da-b8763fd99c5f:1a6d8d77-f2ca-f043-b475-f7db6304fe3e - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 1a6d8d77-f2ca-f043-b475-f7db6304fe3e - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769670-9aeb-11e5-91da-b8763fd99c5f:1ef7a41b-2cff-6046-b48f-c60a16fd20b0 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 1ef7a41b-2cff-6046-b48f-c60a16fd20b0 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769670-9aeb-11e5-91da-b8763fd99c5f:22c3512a-fdf2-464c-b1f4-85d173fa2d18 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 22c3512a-fdf2-464c-b1f4-85d173fa2d18 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769670-9aeb-11e5-91da-b8763fd99c5f:2dd9019e-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9019e-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769670-9aeb-11e5-91da-b8763fd99c5f:2dd90403-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90403-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769670-9aeb-11e5-91da-b8763fd99c5f:2dd90409-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90409-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769670-9aeb-11e5-91da-b8763fd99c5f:2dd9040f-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9040f-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769670-9aeb-11e5-91da-b8763fd99c5f:2dd90417-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90417-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769670-9aeb-11e5-91da-b8763fd99c5f:2dd90478-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90478-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769670-9aeb-11e5-91da-b8763fd99c5f:2dd9047c-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9047c-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769670-9aeb-11e5-91da-b8763fd99c5f:2e64900b-94a8-6e40-bd63-3e69510c316f - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2e64900b-94a8-6e40-bd63-3e69510c316f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769670-9aeb-11e5-91da-b8763fd99c5f:356c6f11-da4d-3f4d-b3fd-49234106345c - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 356c6f11-da4d-3f4d-b3fd-49234106345c - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769670-9aeb-11e5-91da-b8763fd99c5f:3916ae31-9f3d-af4e-8d87-87d373d8200a - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 3916ae31-9f3d-af4e-8d87-87d373d8200a - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769670-9aeb-11e5-91da-b8763fd99c5f:51883d43-111e-f24b-89f7-0573426fa32f - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 51883d43-111e-f24b-89f7-0573426fa32f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769670-9aeb-11e5-91da-b8763fd99c5f:5906b2a0-4ec9-1949-98b8-709380af0060 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 5906b2a0-4ec9-1949-98b8-709380af0060 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769670-9aeb-11e5-91da-b8763fd99c5f:669c1d0c-87dd-a943-9316-88308bd36f15 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 669c1d0c-87dd-a943-9316-88308bd36f15 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769670-9aeb-11e5-91da-b8763fd99c5f:6da89f37-cc6f-4e4e-8b20-975295017390 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 6da89f37-cc6f-4e4e-8b20-975295017390 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769670-9aeb-11e5-91da-b8763fd99c5f:761a49da-2b71-2d4e-877f-3cf7fa86ef63 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 761a49da-2b71-2d4e-877f-3cf7fa86ef63 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769670-9aeb-11e5-91da-b8763fd99c5f:8a0babd6-6d6f-854b-b65f-4245e65c9e9c - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 8a0babd6-6d6f-854b-b65f-4245e65c9e9c - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769670-9aeb-11e5-91da-b8763fd99c5f:8f8094c3-5c43-1241-9014-6c7a3ae10213 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 8f8094c3-5c43-1241-9014-6c7a3ae10213 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769670-9aeb-11e5-91da-b8763fd99c5f:9acd76b4-8d78-2b4b-81d5-e404e244997d - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 9acd76b4-8d78-2b4b-81d5-e404e244997d - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769670-9aeb-11e5-91da-b8763fd99c5f:a533d3fd-822f-3b40-9030-e18b571cd1ea - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: a533d3fd-822f-3b40-9030-e18b571cd1ea - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769670-9aeb-11e5-91da-b8763fd99c5f:a949cafd-39f3-8e4c-bca5-9928b004b4da - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: a949cafd-39f3-8e4c-bca5-9928b004b4da - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769670-9aeb-11e5-91da-b8763fd99c5f:aadfa134-666a-ae4f-8c09-6f3ae114c3b5 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: aadfa134-666a-ae4f-8c09-6f3ae114c3b5 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769670-9aeb-11e5-91da-b8763fd99c5f:ab88a3a2-a572-3440-be06-757e0225ea42 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: ab88a3a2-a572-3440-be06-757e0225ea42 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769670-9aeb-11e5-91da-b8763fd99c5f:b77c60e8-fe4f-9149-9bc6-c3eb8f899de2 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: b77c60e8-fe4f-9149-9bc6-c3eb8f899de2 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769670-9aeb-11e5-91da-b8763fd99c5f:bff806b2-7825-af4c-a34b-be542dbe5ae6 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: bff806b2-7825-af4c-a34b-be542dbe5ae6 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769670-9aeb-11e5-91da-b8763fd99c5f:c3458e72-1c7a-a74b-b561-8afe0a3aa2be - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: c3458e72-1c7a-a74b-b561-8afe0a3aa2be - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769670-9aeb-11e5-91da-b8763fd99c5f:d288bd96-ae6e-cf46-9775-f38e2699f1ce - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: d288bd96-ae6e-cf46-9775-f38e2699f1ce - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769670-9aeb-11e5-91da-b8763fd99c5f:d28bc89e-0592-1440-9721-5cbe124c1f1b - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: d28bc89e-0592-1440-9721-5cbe124c1f1b - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769670-9aeb-11e5-91da-b8763fd99c5f:db724627-9dae-4748-b4e3-2daf627ffa17 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: db724627-9dae-4748-b4e3-2daf627ffa17 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769670-9aeb-11e5-91da-b8763fd99c5f:dc791b3b-fe63-a14b-b144-953748088613 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: dc791b3b-fe63-a14b-b144-953748088613 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769670-9aeb-11e5-91da-b8763fd99c5f:e2f56599-a78e-494f-8db3-c0b0bdab1d70 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: e2f56599-a78e-494f-8db3-c0b0bdab1d70 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769670-9aeb-11e5-91da-b8763fd99c5f:eb8b2a90-9bc8-154f-92dd-3887e3676498 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: eb8b2a90-9bc8-154f-92dd-3887e3676498 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769670-9aeb-11e5-91da-b8763fd99c5f:eba035f1-7c96-0142-8561-37b28625abf7 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: eba035f1-7c96-0142-8561-37b28625abf7 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769670-9aeb-11e5-91da-b8763fd99c5f:ee4223ab-0538-dc4a-9b79-d463c048bb08 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: ee4223ab-0538-dc4a-9b79-d463c048bb08 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769670-9aeb-11e5-91da-b8763fd99c5f:fe798f30-0725-e94e-8142-c06ca7701b14 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: fe798f30-0725-e94e-8142-c06ca7701b14 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769676-9aeb-11e5-91da-b8763fd99c5f:2dd901a0-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769676-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd901a0-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769676-9aeb-11e5-91da-b8763fd99c5f:2dd90205-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769676-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90205-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769676-9aeb-11e5-91da-b8763fd99c5f:2dd90418-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769676-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90418-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176967c-9aeb-11e5-91da-b8763fd99c5f:2dd901a2-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176967c-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd901a2-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176967c-9aeb-11e5-91da-b8763fd99c5f:2dd90208-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176967c-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90208-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176967c-9aeb-11e5-91da-b8763fd99c5f:2dd90412-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176967c-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90412-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769682-9aeb-11e5-91da-b8763fd99c5f:2dd901a4-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769682-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd901a4-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769682-9aeb-11e5-91da-b8763fd99c5f:2dd902ea-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769682-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd902ea-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769682-9aeb-11e5-91da-b8763fd99c5f:2dd902ed-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769682-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd902ed-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769682-9aeb-11e5-91da-b8763fd99c5f:2dd902f0-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769682-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd902f0-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769682-9aeb-11e5-91da-b8763fd99c5f:2dd902f3-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769682-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd902f3-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769682-9aeb-11e5-91da-b8763fd99c5f:2dd903ee-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769682-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd903ee-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769682-9aeb-11e5-91da-b8763fd99c5f:2dd90415-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769682-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90415-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769682-9aeb-11e5-91da-b8763fd99c5f:2dd9041a-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769682-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9041a-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769682-9aeb-11e5-91da-b8763fd99c5f:2dd9047f-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769682-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9047f-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769688-9aeb-11e5-91da-b8763fd99c5f:2dd903ca-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769688-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd903ca-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769688-9aeb-11e5-91da-b8763fd99c5f:2dd903d0-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769688-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd903d0-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769688-9aeb-11e5-91da-b8763fd99c5f:2dd903e2-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769688-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd903e2-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769688-9aeb-11e5-91da-b8763fd99c5f:2dd903f7-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769688-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd903f7-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769688-9aeb-11e5-91da-b8763fd99c5f:2dd903fd-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769688-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd903fd-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769688-9aeb-11e5-91da-b8763fd99c5f:2dd90400-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769688-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90400-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769688-9aeb-11e5-91da-b8763fd99c5f:2dd90475-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769688-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90475-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769688-9aeb-11e5-91da-b8763fd99c5f:2dd90483-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769688-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90483-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769688-9aeb-11e5-91da-b8763fd99c5f:terminal-without-name-property - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769688-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: terminal-without-name-property - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176968e-9aeb-11e5-91da-b8763fd99c5f:2dd901a8-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176968e-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd901a8-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176968e-9aeb-11e5-91da-b8763fd99c5f:2dd9020b-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176968e-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9020b-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176968e-9aeb-11e5-91da-b8763fd99c5f:2dd9020e-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176968e-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9020e-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176968e-9aeb-11e5-91da-b8763fd99c5f:2dd902f7-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176968e-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd902f7-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176968e-9aeb-11e5-91da-b8763fd99c5f:2dd902fa-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176968e-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd902fa-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176968e-9aeb-11e5-91da-b8763fd99c5f:2dd902fd-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176968e-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd902fd-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176968e-9aeb-11e5-91da-b8763fd99c5f:2dd90300-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176968e-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90300-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176968e-9aeb-11e5-91da-b8763fd99c5f:2dd90303-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176968e-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90303-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176968e-9aeb-11e5-91da-b8763fd99c5f:2dd903d9-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176968e-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd903d9-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176968e-9aeb-11e5-91da-b8763fd99c5f:2dd9041b-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176968e-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9041b-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769694-9aeb-11e5-91da-b8763fd99c5f:2dd901aa-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769694-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd901aa-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769694-9aeb-11e5-91da-b8763fd99c5f:2dd90211-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769694-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90211-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769694-9aeb-11e5-91da-b8763fd99c5f:2dd90214-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769694-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90214-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769694-9aeb-11e5-91da-b8763fd99c5f:2dd90217-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769694-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90217-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769694-9aeb-11e5-91da-b8763fd99c5f:2dd90307-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769694-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90307-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769694-9aeb-11e5-91da-b8763fd99c5f:2dd9030a-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769694-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9030a-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769694-9aeb-11e5-91da-b8763fd99c5f:2dd9030d-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769694-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9030d-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769694-9aeb-11e5-91da-b8763fd99c5f:2dd90310-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769694-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90310-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769694-9aeb-11e5-91da-b8763fd99c5f:2dd90394-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769694-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90394-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769694-9aeb-11e5-91da-b8763fd99c5f:2dd903b2-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769694-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd903b2-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769694-9aeb-11e5-91da-b8763fd99c5f:2dd9041d-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769694-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9041d-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769694-9aeb-11e5-91da-b8763fd99c5f:2dd90420-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f1769694-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90420-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f1769699-9aeb-11e5-91da-b8763fd99c5f:2dd9048c-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: belongsTo - - externalId: GeographicalRegion - - externalId: non-historic - - externalId: SubGeographicalRegion - - externalId: SubGeographicalRegion.Region - sourceExternalId: f1769699-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9048c-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176969e-9aeb-11e5-91da-b8763fd99c5f:2dd901ae-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176969e-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd901ae-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176969e-9aeb-11e5-91da-b8763fd99c5f:2dd9021a-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176969e-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9021a-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176969e-9aeb-11e5-91da-b8763fd99c5f:2dd90314-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176969e-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90314-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176969e-9aeb-11e5-91da-b8763fd99c5f:2dd90317-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176969e-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90317-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176969e-9aeb-11e5-91da-b8763fd99c5f:2dd9031a-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176969e-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9031a-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176969e-9aeb-11e5-91da-b8763fd99c5f:2dd9031d-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176969e-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9031d-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176969e-9aeb-11e5-91da-b8763fd99c5f:2dd903ac-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176969e-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd903ac-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176969e-9aeb-11e5-91da-b8763fd99c5f:2dd9041e-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176969e-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9041e-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176969e-9aeb-11e5-91da-b8763fd99c5f:2dd90421-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176969e-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90421-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f176969e-9aeb-11e5-91da-b8763fd99c5f:2dd90486-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f176969e-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90486-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17696a3-9aeb-11e5-91da-b8763fd99c5f:2dd9048c-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: belongsTo - - externalId: GeographicalRegion - - externalId: non-historic - - externalId: SubGeographicalRegion - - externalId: SubGeographicalRegion.Region - sourceExternalId: f17696a3-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9048c-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17696a8-9aeb-11e5-91da-b8763fd99c5f:2dd901b2-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17696a8-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd901b2-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17696a8-9aeb-11e5-91da-b8763fd99c5f:2dd9021d-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17696a8-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9021d-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17696a8-9aeb-11e5-91da-b8763fd99c5f:2dd90220-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17696a8-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90220-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17696a8-9aeb-11e5-91da-b8763fd99c5f:2dd90385-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17696a8-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90385-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17696a8-9aeb-11e5-91da-b8763fd99c5f:2dd9048a-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17696a8-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9048a-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f:2dd901be-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd901be-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f:2dd90244-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90244-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f:2dd90247-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90247-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f:2dd9024a-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9024a-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f:2dd9024d-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9024d-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f:2dd90347-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90347-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f:2dd9034a-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9034a-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f:2dd9034d-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9034d-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f:2dd90350-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90350-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f:2dd90353-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90353-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f:2dd90356-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90356-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f:2dd90391-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90391-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f:2dd9039a-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9039a-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f:2dd9039d-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9039d-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f:2dd903a6-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd903a6-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f:2dd903a9-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd903a9-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f:2dd90432-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90432-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f:2dd90435-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17696ca-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90435-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17696cf-9aeb-11e5-91da-b8763fd99c5f:2dd9048c-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: belongsTo - - externalId: GeographicalRegion - - externalId: non-historic - - externalId: SubGeographicalRegion - - externalId: SubGeographicalRegion.Region - sourceExternalId: f17696cf-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd9048c-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17696d4-9aeb-11e5-91da-b8763fd99c5f:2dd901c2-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17696d4-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd901c2-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17696d4-9aeb-11e5-91da-b8763fd99c5f:2dd90250-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17696d4-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90250-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17696d4-9aeb-11e5-91da-b8763fd99c5f:2dd90433-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17696d4-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90433-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17696da-9aeb-11e5-91da-b8763fd99c5f:2dd901c4-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17696da-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd901c4-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17696da-9aeb-11e5-91da-b8763fd99c5f:2dd90253-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17696da-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90253-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f17696da-9aeb-11e5-91da-b8763fd99c5f:2dd90436-bdfb-11e5-94fa-c8f73332c8f4 - labels: - - externalId: connectsTo - - externalId: non-historic - - externalId: Substation - - externalId: Substation.Terminal - - externalId: Terminal - sourceExternalId: f17696da-9aeb-11e5-91da-b8763fd99c5f - sourceType: Asset - startTime: 1705795200000 - targetExternalId: 2dd90436-bdfb-11e5-94fa-c8f73332c8f4 - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f4dba262-3ff3-9f41-961d-7fbe1f8a0cd7:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: f4dba262-3ff3-9f41-961d-7fbe1f8a0cd7 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f710cc4a-50dd-e443-8ac7-4c56b70041e8:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: f710cc4a-50dd-e443-8ac7-4c56b70041e8 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f723530d-ae1e-df4f-9c7f-21e42da147d6:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: f723530d-ae1e-df4f-9c7f-21e42da147d6 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: f9e8d6d1-76e1-6c4d-a640-e128ecfa723e:f176965a-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: f9e8d6d1-76e1-6c4d-a640-e128ecfa723e - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f176965a-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: fe798f30-0725-e94e-8142-c06ca7701b14:f1769670-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: fe798f30-0725-e94e-8142-c06ca7701b14 - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769670-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset -- dataSetId: 2626756768281823 - externalId: terminal-without-name-property:f1769688-9aeb-11e5-91da-b8763fd99c5f - labels: - - externalId: belongsTo - - externalId: non-historic - - externalId: Substation - - externalId: Terminal - - externalId: Terminal.Substation - sourceExternalId: terminal-without-name-property - sourceType: Asset - startTime: 1705795200000 - targetExternalId: f1769688-9aeb-11e5-91da-b8763fd99c5f - targetType: Asset diff --git a/tests/tests_unit/app/api/test_workflows/Extract_RDF_Graph_and_Generate_Assets_workflow.yml b/tests/tests_unit/app/api/test_workflows/Extract_RDF_Graph_and_Generate_Assets_workflow.yml deleted file mode 100644 index 169cdb69e..000000000 --- a/tests/tests_unit/app/api/test_workflows/Extract_RDF_Graph_and_Generate_Assets_workflow.yml +++ /dev/null @@ -1,3 +0,0 @@ -assets: [] -labels: [] -relationships: [] diff --git a/tests/tests_unit/app/api/test_workflows/sheet2cdf_workflow.yml b/tests/tests_unit/app/api/test_workflows/sheet2cdf_workflow.yml deleted file mode 100644 index c6df5d3b2..000000000 --- a/tests/tests_unit/app/api/test_workflows/sheet2cdf_workflow.yml +++ /dev/null @@ -1,468 +0,0 @@ -assets: -- data_set_id: 2626756768281823 - external_id: playground_NO1.NO2 - labels: - - externalId: PriceAreaConnection - - externalId: non-historic - metadata: - active: 'true' - identifier: NO1.NO2 - priceArea: Nordics.Norway.NO1, Nordics.Norway.NO2 - type: PriceAreaConnection - name: NO1 to NO2 -- data_set_id: 2626756768281823 - external_id: playground_NO1.NO3 - labels: - - externalId: PriceAreaConnection - - externalId: non-historic - metadata: - active: 'true' - identifier: NO1.NO3 - priceArea: Nordics.Norway.NO1, Nordics.Norway.NO3 - type: PriceAreaConnection - name: NO1 to NO3 -- data_set_id: 2626756768281823 - external_id: playground_NO1.NO5 - labels: - - externalId: PriceAreaConnection - - externalId: non-historic - metadata: - active: 'true' - identifier: NO1.NO5 - priceArea: Nordics.Norway.NO1, Nordics.Norway.NO5 - type: PriceAreaConnection - name: NO1 to NO5 -- data_set_id: 2626756768281823 - external_id: playground_NO2.NO5 - labels: - - externalId: PriceAreaConnection - - externalId: non-historic - metadata: - active: 'true' - identifier: NO2.NO5 - priceArea: Nordics.Norway.NO2, Nordics.Norway.NO5 - type: PriceAreaConnection - name: NO2 to NO5 -- data_set_id: 2626756768281823 - external_id: playground_NO3.NO4 - labels: - - externalId: PriceAreaConnection - - externalId: non-historic - metadata: - active: 'true' - identifier: NO3.NO4 - priceArea: Nordics.Norway.NO3, Nordics.Norway.NO4 - type: PriceAreaConnection - name: NO3 to NO4 -- data_set_id: 2626756768281823 - external_id: playground_NO3.NO5 - labels: - - externalId: PriceAreaConnection - - externalId: non-historic - metadata: - active: 'true' - identifier: NO3.NO5 - priceArea: Nordics.Norway.NO3, Nordics.Norway.NO5 - type: PriceAreaConnection - name: NO3 to NO5 -- data_set_id: 2626756768281823 - external_id: playground_Nordics - labels: - - externalId: CountryGroup - - externalId: non-historic - metadata: - active: 'true' - identifier: Nordics - type: CountryGroup - name: Nordics -- data_set_id: 2626756768281823 - external_id: playground_Nordics.Norway - labels: - - externalId: Country - - externalId: non-historic - metadata: - TSO: Statnett - active: 'true' - countryGroup: Nordics - identifier: Nordics.Norway - type: Country - name: Norway - parent_external_id: playground_Nordics -- data_set_id: 2626756768281823 - external_id: playground_Nordics.Norway.NO1 - labels: - - externalId: PriceArea - - externalId: non-historic - metadata: - active: 'true' - country: Nordics.Norway - identifier: Nordics.Norway.NO1 - priceAreaConnection: NO1.NO2, NO1.NO5, NO1.NO3 - type: PriceArea - name: NO1 - parent_external_id: playground_Nordics.Norway -- data_set_id: 2626756768281823 - external_id: playground_Nordics.Norway.NO2 - labels: - - externalId: PriceArea - - externalId: non-historic - metadata: - active: 'true' - country: Nordics.Norway - identifier: Nordics.Norway.NO2 - priceAreaConnection: NO1.NO2, NO2.NO5 - type: PriceArea - name: NO2 - parent_external_id: playground_Nordics.Norway -- data_set_id: 2626756768281823 - external_id: playground_Nordics.Norway.NO3 - labels: - - externalId: PriceArea - - externalId: non-historic - metadata: - active: 'true' - country: Nordics.Norway - identifier: Nordics.Norway.NO3 - priceAreaConnection: NO1.NO3, NO3.NO4, NO3.NO5 - type: PriceArea - name: NO3 - parent_external_id: playground_Nordics.Norway -- data_set_id: 2626756768281823 - external_id: playground_Nordics.Norway.NO4 - labels: - - externalId: PriceArea - - externalId: non-historic - metadata: - active: 'true' - country: Nordics.Norway - identifier: Nordics.Norway.NO4 - priceAreaConnection: NO3.NO4 - type: PriceArea - name: NO4 - parent_external_id: playground_Nordics.Norway -- data_set_id: 2626756768281823 - external_id: playground_Nordics.Norway.NO5 - labels: - - externalId: PriceArea - - externalId: non-historic - metadata: - active: 'true' - country: Nordics.Norway - identifier: Nordics.Norway.NO5 - priceAreaConnection: NO1.NO5, NO2.NO5, NO3.NO5 - type: PriceArea - name: NO5 - parent_external_id: playground_Nordics.Norway -- data_set_id: 2626756768281823 - description: Used to store all assets which parent does not exist - external_id: playground_orphanage-2626756768281823 - labels: - - externalId: non-historic - metadata: - active: 'true' - cdfResourceType: Asset - identifier: orphanage - type: Orphanage - name: Orphanage -labels: -- data_set_id: 2626756768281823 - external_id: Country - name: Country -- data_set_id: 2626756768281823 - external_id: CountryGroup - name: CountryGroup -- data_set_id: 2626756768281823 - external_id: PriceArea - name: PriceArea -- data_set_id: 2626756768281823 - external_id: PriceAreaConnection - name: PriceAreaConnection -- data_set_id: 2626756768281823 - external_id: TSO - name: TSO -- data_set_id: 2626756768281823 - external_id: country - name: country -- data_set_id: 2626756768281823 - external_id: countryGroup - name: countryGroup -- data_set_id: 2626756768281823 - external_id: historic - name: historic -- data_set_id: 2626756768281823 - external_id: name - name: name -- data_set_id: 2626756768281823 - external_id: non-historic - name: non-historic -- data_set_id: 2626756768281823 - external_id: priceArea - name: priceArea -- data_set_id: 2626756768281823 - external_id: priceAreaConnection - name: priceAreaConnection -relationships: -- data_set_id: 2626756768281823 - external_id: playground_NO1.NO2:playground_Nordics.Norway.NO1 - labels: - - externalId: PriceArea - - externalId: PriceAreaConnection - - externalId: non-historic - - externalId: priceArea - source_external_id: playground_NO1.NO2 - source_type: Asset - target_external_id: playground_Nordics.Norway.NO1 - target_type: Asset -- data_set_id: 2626756768281823 - external_id: playground_NO1.NO2:playground_Nordics.Norway.NO2 - labels: - - externalId: PriceArea - - externalId: PriceAreaConnection - - externalId: non-historic - - externalId: priceArea - source_external_id: playground_NO1.NO2 - source_type: Asset - target_external_id: playground_Nordics.Norway.NO2 - target_type: Asset -- data_set_id: 2626756768281823 - external_id: playground_NO1.NO3:playground_Nordics.Norway.NO1 - labels: - - externalId: PriceArea - - externalId: PriceAreaConnection - - externalId: non-historic - - externalId: priceArea - source_external_id: playground_NO1.NO3 - source_type: Asset - target_external_id: playground_Nordics.Norway.NO1 - target_type: Asset -- data_set_id: 2626756768281823 - external_id: playground_NO1.NO3:playground_Nordics.Norway.NO3 - labels: - - externalId: PriceArea - - externalId: PriceAreaConnection - - externalId: non-historic - - externalId: priceArea - source_external_id: playground_NO1.NO3 - source_type: Asset - target_external_id: playground_Nordics.Norway.NO3 - target_type: Asset -- data_set_id: 2626756768281823 - external_id: playground_NO1.NO5:playground_Nordics.Norway.NO1 - labels: - - externalId: PriceArea - - externalId: PriceAreaConnection - - externalId: non-historic - - externalId: priceArea - source_external_id: playground_NO1.NO5 - source_type: Asset - target_external_id: playground_Nordics.Norway.NO1 - target_type: Asset -- data_set_id: 2626756768281823 - external_id: playground_NO1.NO5:playground_Nordics.Norway.NO5 - labels: - - externalId: PriceArea - - externalId: PriceAreaConnection - - externalId: non-historic - - externalId: priceArea - source_external_id: playground_NO1.NO5 - source_type: Asset - target_external_id: playground_Nordics.Norway.NO5 - target_type: Asset -- data_set_id: 2626756768281823 - external_id: playground_NO2.NO5:playground_Nordics.Norway.NO2 - labels: - - externalId: PriceArea - - externalId: PriceAreaConnection - - externalId: non-historic - - externalId: priceArea - source_external_id: playground_NO2.NO5 - source_type: Asset - target_external_id: playground_Nordics.Norway.NO2 - target_type: Asset -- data_set_id: 2626756768281823 - external_id: playground_NO2.NO5:playground_Nordics.Norway.NO5 - labels: - - externalId: PriceArea - - externalId: PriceAreaConnection - - externalId: non-historic - - externalId: priceArea - source_external_id: playground_NO2.NO5 - source_type: Asset - target_external_id: playground_Nordics.Norway.NO5 - target_type: Asset -- data_set_id: 2626756768281823 - external_id: playground_NO3.NO4:playground_Nordics.Norway.NO3 - labels: - - externalId: PriceArea - - externalId: PriceAreaConnection - - externalId: non-historic - - externalId: priceArea - source_external_id: playground_NO3.NO4 - source_type: Asset - target_external_id: playground_Nordics.Norway.NO3 - target_type: Asset -- data_set_id: 2626756768281823 - external_id: playground_NO3.NO4:playground_Nordics.Norway.NO4 - labels: - - externalId: PriceArea - - externalId: PriceAreaConnection - - externalId: non-historic - - externalId: priceArea - source_external_id: playground_NO3.NO4 - source_type: Asset - target_external_id: playground_Nordics.Norway.NO4 - target_type: Asset -- data_set_id: 2626756768281823 - external_id: playground_NO3.NO5:playground_Nordics.Norway.NO3 - labels: - - externalId: PriceArea - - externalId: PriceAreaConnection - - externalId: non-historic - - externalId: priceArea - source_external_id: playground_NO3.NO5 - source_type: Asset - target_external_id: playground_Nordics.Norway.NO3 - target_type: Asset -- data_set_id: 2626756768281823 - external_id: playground_NO3.NO5:playground_Nordics.Norway.NO5 - labels: - - externalId: PriceArea - - externalId: PriceAreaConnection - - externalId: non-historic - - externalId: priceArea - source_external_id: playground_NO3.NO5 - source_type: Asset - target_external_id: playground_Nordics.Norway.NO5 - target_type: Asset -- data_set_id: 2626756768281823 - external_id: playground_Nordics.Norway.NO1:playground_NO1.NO2 - labels: - - externalId: PriceArea - - externalId: PriceAreaConnection - - externalId: non-historic - - externalId: priceAreaConnection - source_external_id: playground_Nordics.Norway.NO1 - source_type: Asset - target_external_id: playground_NO1.NO2 - target_type: Asset -- data_set_id: 2626756768281823 - external_id: playground_Nordics.Norway.NO1:playground_NO1.NO3 - labels: - - externalId: PriceArea - - externalId: PriceAreaConnection - - externalId: non-historic - - externalId: priceAreaConnection - source_external_id: playground_Nordics.Norway.NO1 - source_type: Asset - target_external_id: playground_NO1.NO3 - target_type: Asset -- data_set_id: 2626756768281823 - external_id: playground_Nordics.Norway.NO1:playground_NO1.NO5 - labels: - - externalId: PriceArea - - externalId: PriceAreaConnection - - externalId: non-historic - - externalId: priceAreaConnection - source_external_id: playground_Nordics.Norway.NO1 - source_type: Asset - target_external_id: playground_NO1.NO5 - target_type: Asset -- data_set_id: 2626756768281823 - external_id: playground_Nordics.Norway.NO2:playground_NO1.NO2 - labels: - - externalId: PriceArea - - externalId: PriceAreaConnection - - externalId: non-historic - - externalId: priceAreaConnection - source_external_id: playground_Nordics.Norway.NO2 - source_type: Asset - target_external_id: playground_NO1.NO2 - target_type: Asset -- data_set_id: 2626756768281823 - external_id: playground_Nordics.Norway.NO2:playground_NO2.NO5 - labels: - - externalId: PriceArea - - externalId: PriceAreaConnection - - externalId: non-historic - - externalId: priceAreaConnection - source_external_id: playground_Nordics.Norway.NO2 - source_type: Asset - target_external_id: playground_NO2.NO5 - target_type: Asset -- data_set_id: 2626756768281823 - external_id: playground_Nordics.Norway.NO3:playground_NO1.NO3 - labels: - - externalId: PriceArea - - externalId: PriceAreaConnection - - externalId: non-historic - - externalId: priceAreaConnection - source_external_id: playground_Nordics.Norway.NO3 - source_type: Asset - target_external_id: playground_NO1.NO3 - target_type: Asset -- data_set_id: 2626756768281823 - external_id: playground_Nordics.Norway.NO3:playground_NO3.NO4 - labels: - - externalId: PriceArea - - externalId: PriceAreaConnection - - externalId: non-historic - - externalId: priceAreaConnection - source_external_id: playground_Nordics.Norway.NO3 - source_type: Asset - target_external_id: playground_NO3.NO4 - target_type: Asset -- data_set_id: 2626756768281823 - external_id: playground_Nordics.Norway.NO3:playground_NO3.NO5 - labels: - - externalId: PriceArea - - externalId: PriceAreaConnection - - externalId: non-historic - - externalId: priceAreaConnection - source_external_id: playground_Nordics.Norway.NO3 - source_type: Asset - target_external_id: playground_NO3.NO5 - target_type: Asset -- data_set_id: 2626756768281823 - external_id: playground_Nordics.Norway.NO4:playground_NO3.NO4 - labels: - - externalId: PriceArea - - externalId: PriceAreaConnection - - externalId: non-historic - - externalId: priceAreaConnection - source_external_id: playground_Nordics.Norway.NO4 - source_type: Asset - target_external_id: playground_NO3.NO4 - target_type: Asset -- data_set_id: 2626756768281823 - external_id: playground_Nordics.Norway.NO5:playground_NO1.NO5 - labels: - - externalId: PriceArea - - externalId: PriceAreaConnection - - externalId: non-historic - - externalId: priceAreaConnection - source_external_id: playground_Nordics.Norway.NO5 - source_type: Asset - target_external_id: playground_NO1.NO5 - target_type: Asset -- data_set_id: 2626756768281823 - external_id: playground_Nordics.Norway.NO5:playground_NO2.NO5 - labels: - - externalId: PriceArea - - externalId: PriceAreaConnection - - externalId: non-historic - - externalId: priceAreaConnection - source_external_id: playground_Nordics.Norway.NO5 - source_type: Asset - target_external_id: playground_NO2.NO5 - target_type: Asset -- data_set_id: 2626756768281823 - external_id: playground_Nordics.Norway.NO5:playground_NO3.NO5 - labels: - - externalId: PriceArea - - externalId: PriceAreaConnection - - externalId: non-historic - - externalId: priceAreaConnection - source_external_id: playground_Nordics.Norway.NO5 - source_type: Asset - target_external_id: playground_NO3.NO5 - target_type: Asset diff --git a/tests/tests_unit/app/conftest.py b/tests/tests_unit/app/conftest.py deleted file mode 100644 index 6441359f9..000000000 --- a/tests/tests_unit/app/conftest.py +++ /dev/null @@ -1,10 +0,0 @@ -import pytest - -from cognite.neat.legacy.rules import importers -from cognite.neat.legacy.rules.models.rules import Rules -from tests import config as config - - -@pytest.fixture(scope="session") -def transformation_rules() -> Rules: - return importers.ExcelImporter(config.SIMPLECIM_TRANSFORMATION_RULES).to_rules() From 5ae8d58af4644dc483988f2e44fcdd2ca04c109b Mon Sep 17 00:00:00 2001 From: nikokaoja Date: Tue, 23 Jul 2024 11:34:51 +0200 Subject: [PATCH 3/3] bump version, changelog --- Makefile | 2 +- cognite/neat/_version.py | 2 +- docs/CHANGELOG.md | 6 ++++++ pyproject.toml | 2 +- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index a2b5ee047..1d0cba8e4 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ .PHONY: run-explorer run-tests run-linters build-ui build-python build-docker run-docker compose-up -version="0.87.6" +version="0.88.0" run-explorer: @echo "Running explorer API server..." # open "http://localhost:8000/static/index.html" || true diff --git a/cognite/neat/_version.py b/cognite/neat/_version.py index 7ca92d8d7..b0af29dd5 100644 --- a/cognite/neat/_version.py +++ b/cognite/neat/_version.py @@ -1 +1 @@ -__version__ = "0.87.6" +__version__ = "0.88.0" diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index af8e3f92b..59597c7c0 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -16,6 +16,12 @@ Changes are grouped as follows: - `Security` in case of vulnerabilities. +## [0.88.0] - 22-07-24 +### Removed +- [BREAKING] Legacy neat has been removed from the codebase. This includes `legacy` module, + steps, and UI capabilities (such as explorer and rules editor). + + ## [0.87.6] - 22-07-24 ### Added - Labels generation from NeatGraphStore in AssetLoader diff --git a/pyproject.toml b/pyproject.toml index f3b7caff3..bcad85170 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "cognite-neat" -version = "0.87.6" +version = "0.88.0" readme = "README.md" description = "Knowledge graph transformation" authors = [