cognite-neat 0.123.26__py3-none-any.whl → 1.0.22__py3-none-any.whl

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