cognite-neat 0.123.32__py3-none-any.whl → 0.127.30__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 (320) hide show
  1. cognite/neat/__init__.py +2 -2
  2. cognite/neat/_client/__init__.py +4 -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 +125 -0
  7. cognite/neat/_client/data_classes.py +44 -0
  8. cognite/neat/_client/data_model_api.py +115 -0
  9. cognite/neat/_client/spaces_api.py +115 -0
  10. cognite/neat/_client/statistics_api.py +24 -0
  11. cognite/neat/_client/views_api.py +129 -0
  12. cognite/neat/_config.py +185 -0
  13. cognite/neat/_data_model/_analysis.py +196 -0
  14. cognite/neat/_data_model/_constants.py +67 -0
  15. cognite/neat/_data_model/_identifiers.py +61 -0
  16. cognite/neat/_data_model/_shared.py +41 -0
  17. cognite/neat/_data_model/deployer/_differ.py +140 -0
  18. cognite/neat/_data_model/deployer/_differ_container.py +360 -0
  19. cognite/neat/_data_model/deployer/_differ_data_model.py +54 -0
  20. cognite/neat/_data_model/deployer/_differ_space.py +9 -0
  21. cognite/neat/_data_model/deployer/_differ_view.py +299 -0
  22. cognite/neat/_data_model/deployer/data_classes.py +529 -0
  23. cognite/neat/_data_model/deployer/deployer.py +401 -0
  24. cognite/neat/_data_model/exporters/__init__.py +15 -0
  25. cognite/neat/_data_model/exporters/_api_exporter.py +37 -0
  26. cognite/neat/_data_model/exporters/_base.py +24 -0
  27. cognite/neat/_data_model/exporters/_table_exporter/exporter.py +128 -0
  28. cognite/neat/_data_model/exporters/_table_exporter/workbook.py +409 -0
  29. cognite/neat/_data_model/exporters/_table_exporter/writer.py +421 -0
  30. cognite/neat/_data_model/importers/__init__.py +5 -0
  31. cognite/neat/_data_model/importers/_api_importer.py +166 -0
  32. cognite/neat/_data_model/importers/_base.py +16 -0
  33. cognite/neat/_data_model/importers/_table_importer/data_classes.py +295 -0
  34. cognite/neat/_data_model/importers/_table_importer/importer.py +192 -0
  35. cognite/neat/_data_model/importers/_table_importer/reader.py +1063 -0
  36. cognite/neat/_data_model/importers/_table_importer/source.py +94 -0
  37. cognite/neat/_data_model/models/conceptual/_base.py +18 -0
  38. cognite/neat/_data_model/models/conceptual/_concept.py +67 -0
  39. cognite/neat/_data_model/models/conceptual/_data_model.py +51 -0
  40. cognite/neat/_data_model/models/conceptual/_properties.py +104 -0
  41. cognite/neat/_data_model/models/conceptual/_property.py +105 -0
  42. cognite/neat/_data_model/models/dms/__init__.py +198 -2
  43. cognite/neat/_data_model/models/dms/_base.py +13 -9
  44. cognite/neat/_data_model/models/dms/_constants.py +47 -1
  45. cognite/neat/_data_model/models/dms/_constraints.py +42 -0
  46. cognite/neat/_data_model/models/dms/_container.py +159 -0
  47. cognite/neat/_data_model/models/dms/_data_model.py +95 -0
  48. cognite/neat/_data_model/models/dms/_data_types.py +195 -0
  49. cognite/neat/_data_model/models/dms/_http.py +28 -0
  50. cognite/neat/_data_model/models/dms/_indexes.py +30 -0
  51. cognite/neat/_data_model/models/dms/_limits.py +96 -0
  52. cognite/neat/_data_model/models/dms/_references.py +135 -0
  53. cognite/neat/_data_model/models/dms/_schema.py +18 -0
  54. cognite/neat/_data_model/models/dms/_space.py +14 -10
  55. cognite/neat/_data_model/models/dms/_types.py +17 -0
  56. cognite/neat/_data_model/models/dms/_view_filter.py +282 -0
  57. cognite/neat/_data_model/models/dms/_view_property.py +235 -0
  58. cognite/neat/_data_model/models/dms/_views.py +210 -0
  59. cognite/neat/_data_model/models/entities/__init__.py +50 -0
  60. cognite/neat/_data_model/models/entities/_base.py +101 -0
  61. cognite/neat/{data_model → _data_model}/models/entities/_constants.py +5 -0
  62. cognite/neat/_data_model/models/entities/_data_types.py +144 -0
  63. cognite/neat/{data_model → _data_model}/models/entities/_identifiers.py +1 -1
  64. cognite/neat/_data_model/models/entities/_parser.py +226 -0
  65. cognite/neat/_data_model/validation/dms/__init__.py +75 -0
  66. cognite/neat/_data_model/validation/dms/_ai_readiness.py +364 -0
  67. cognite/neat/_data_model/validation/dms/_base.py +307 -0
  68. cognite/neat/_data_model/validation/dms/_connections.py +638 -0
  69. cognite/neat/_data_model/validation/dms/_consistency.py +57 -0
  70. cognite/neat/_data_model/validation/dms/_containers.py +174 -0
  71. cognite/neat/_data_model/validation/dms/_limits.py +420 -0
  72. cognite/neat/_data_model/validation/dms/_orchestrator.py +222 -0
  73. cognite/neat/_data_model/validation/dms/_views.py +103 -0
  74. cognite/neat/_exceptions.py +56 -0
  75. cognite/neat/_issues.py +68 -0
  76. cognite/neat/_session/__init__.py +3 -0
  77. cognite/neat/_session/_html/_render.py +30 -0
  78. cognite/neat/_session/_html/static/__init__.py +8 -0
  79. cognite/neat/_session/_html/static/deployment.css +303 -0
  80. cognite/neat/_session/_html/static/deployment.js +150 -0
  81. cognite/neat/_session/_html/static/issues.css +211 -0
  82. cognite/neat/_session/_html/static/issues.js +168 -0
  83. cognite/neat/_session/_html/static/shared.css +186 -0
  84. cognite/neat/_session/_html/templates/__init__.py +4 -0
  85. cognite/neat/_session/_html/templates/deployment.html +75 -0
  86. cognite/neat/_session/_html/templates/issues.html +45 -0
  87. cognite/neat/_session/_issues.py +81 -0
  88. cognite/neat/_session/_opt.py +35 -0
  89. cognite/neat/_session/_physical.py +261 -0
  90. cognite/neat/_session/_result.py +236 -0
  91. cognite/neat/_session/_session.py +88 -0
  92. cognite/neat/_session/_usage_analytics/_collector.py +131 -0
  93. cognite/neat/_session/_usage_analytics/_constants.py +23 -0
  94. cognite/neat/_session/_usage_analytics/_storage.py +240 -0
  95. cognite/neat/_session/_wrappers.py +82 -0
  96. cognite/neat/_state_machine/__init__.py +10 -0
  97. cognite/neat/_state_machine/_base.py +37 -0
  98. cognite/neat/_state_machine/_states.py +52 -0
  99. cognite/neat/_store/__init__.py +3 -0
  100. cognite/neat/_store/_provenance.py +81 -0
  101. cognite/neat/_store/_store.py +156 -0
  102. cognite/neat/_utils/_reader.py +194 -0
  103. cognite/neat/_utils/auxiliary.py +7 -0
  104. cognite/neat/_utils/collection.py +11 -0
  105. cognite/neat/_utils/http_client/__init__.py +39 -0
  106. cognite/neat/_utils/http_client/_client.py +245 -0
  107. cognite/neat/_utils/http_client/_config.py +19 -0
  108. cognite/neat/_utils/http_client/_data_classes.py +294 -0
  109. cognite/neat/_utils/http_client/_tracker.py +31 -0
  110. cognite/neat/_utils/text.py +71 -0
  111. cognite/neat/_utils/useful_types.py +37 -0
  112. cognite/neat/_utils/validation.py +154 -0
  113. cognite/neat/_version.py +1 -1
  114. cognite/neat/v0/core/_client/_api/__init__.py +0 -0
  115. cognite/neat/{core → v0/core}/_client/_api/data_modeling_loaders.py +6 -6
  116. cognite/neat/{core → v0/core}/_client/_api/neat_instances.py +5 -5
  117. cognite/neat/{core → v0/core}/_client/_api/schema.py +5 -5
  118. cognite/neat/{core → v0/core}/_client/_api/statistics.py +3 -3
  119. cognite/neat/{core → v0/core}/_client/_api_client.py +1 -1
  120. cognite/neat/v0/core/_client/data_classes/__init__.py +0 -0
  121. cognite/neat/{core → v0/core}/_client/data_classes/schema.py +4 -4
  122. cognite/neat/{core → v0/core}/_client/testing.py +1 -1
  123. cognite/neat/{core → v0/core}/_constants.py +5 -3
  124. cognite/neat/v0/core/_data_model/__init__.py +0 -0
  125. cognite/neat/{core → v0/core}/_data_model/_constants.py +3 -0
  126. cognite/neat/{core → v0/core}/_data_model/_shared.py +4 -4
  127. cognite/neat/{core → v0/core}/_data_model/analysis/_base.py +8 -8
  128. cognite/neat/{core → v0/core}/_data_model/exporters/__init__.py +1 -2
  129. cognite/neat/{core → v0/core}/_data_model/exporters/_base.py +7 -7
  130. cognite/neat/{core → v0/core}/_data_model/exporters/_data_model2dms.py +9 -9
  131. cognite/neat/{core → v0/core}/_data_model/exporters/_data_model2excel.py +12 -12
  132. cognite/neat/{core → v0/core}/_data_model/exporters/_data_model2instance_template.py +4 -4
  133. cognite/neat/{core/_data_model/exporters/_data_model2ontology.py → v0/core/_data_model/exporters/_data_model2semantic_model.py} +126 -116
  134. cognite/neat/{core → v0/core}/_data_model/exporters/_data_model2yaml.py +1 -1
  135. cognite/neat/{core → v0/core}/_data_model/importers/_base.py +5 -5
  136. cognite/neat/{core → v0/core}/_data_model/importers/_base_file_reader.py +2 -2
  137. cognite/neat/{core → v0/core}/_data_model/importers/_dict2data_model.py +5 -5
  138. cognite/neat/{core → v0/core}/_data_model/importers/_dms2data_model.py +16 -15
  139. cognite/neat/{core → v0/core}/_data_model/importers/_graph2data_model.py +12 -12
  140. cognite/neat/{core → v0/core}/_data_model/importers/_rdf/_base.py +12 -12
  141. cognite/neat/{core → v0/core}/_data_model/importers/_rdf/_inference2rdata_model.py +14 -14
  142. cognite/neat/{core → v0/core}/_data_model/importers/_rdf/_owl2data_model.py +41 -21
  143. cognite/neat/{core → v0/core}/_data_model/importers/_rdf/_shared.py +9 -9
  144. cognite/neat/{core → v0/core}/_data_model/importers/_spreadsheet2data_model.py +92 -12
  145. cognite/neat/{core → v0/core}/_data_model/models/__init__.py +3 -3
  146. cognite/neat/{core → v0/core}/_data_model/models/_base_verified.py +5 -5
  147. cognite/neat/{core → v0/core}/_data_model/models/_import_contexts.py +1 -1
  148. cognite/neat/{core → v0/core}/_data_model/models/_types.py +5 -5
  149. cognite/neat/{core → v0/core}/_data_model/models/conceptual/_unverified.py +5 -5
  150. cognite/neat/{core → v0/core}/_data_model/models/conceptual/_validation.py +12 -12
  151. cognite/neat/{core → v0/core}/_data_model/models/conceptual/_verified.py +9 -9
  152. cognite/neat/{core → v0/core}/_data_model/models/data_types.py +4 -4
  153. cognite/neat/{core → v0/core}/_data_model/models/entities/__init__.py +2 -0
  154. cognite/neat/{core → v0/core}/_data_model/models/entities/_loaders.py +2 -2
  155. cognite/neat/{core → v0/core}/_data_model/models/entities/_multi_value.py +2 -2
  156. cognite/neat/{core → v0/core}/_data_model/models/entities/_restrictions.py +6 -6
  157. cognite/neat/{core → v0/core}/_data_model/models/entities/_single_value.py +17 -3
  158. cognite/neat/{core → v0/core}/_data_model/models/entities/_types.py +10 -0
  159. cognite/neat/{core → v0/core}/_data_model/models/mapping/_classic2core.py +5 -5
  160. cognite/neat/{core → v0/core}/_data_model/models/physical/__init__.py +1 -1
  161. cognite/neat/{core → v0/core}/_data_model/models/physical/_exporter.py +24 -19
  162. cognite/neat/{core → v0/core}/_data_model/models/physical/_unverified.py +69 -20
  163. cognite/neat/{core → v0/core}/_data_model/models/physical/_validation.py +24 -20
  164. cognite/neat/{core → v0/core}/_data_model/models/physical/_verified.py +95 -24
  165. cognite/neat/{core → v0/core}/_data_model/transformers/_base.py +4 -4
  166. cognite/neat/{core → v0/core}/_data_model/transformers/_converters.py +35 -28
  167. cognite/neat/{core → v0/core}/_data_model/transformers/_mapping.py +7 -7
  168. cognite/neat/{core → v0/core}/_data_model/transformers/_union_conceptual.py +5 -5
  169. cognite/neat/{core → v0/core}/_data_model/transformers/_verification.py +7 -7
  170. cognite/neat/v0/core/_instances/__init__.py +0 -0
  171. cognite/neat/{core → v0/core}/_instances/_tracking/base.py +1 -1
  172. cognite/neat/{core → v0/core}/_instances/_tracking/log.py +1 -1
  173. cognite/neat/{core → v0/core}/_instances/extractors/__init__.py +1 -1
  174. cognite/neat/{core → v0/core}/_instances/extractors/_base.py +6 -6
  175. cognite/neat/v0/core/_instances/extractors/_classic_cdf/__init__.py +0 -0
  176. cognite/neat/{core → v0/core}/_instances/extractors/_classic_cdf/_base.py +7 -7
  177. cognite/neat/{core → v0/core}/_instances/extractors/_classic_cdf/_classic.py +12 -12
  178. cognite/neat/{core → v0/core}/_instances/extractors/_classic_cdf/_relationships.py +3 -3
  179. cognite/neat/{core → v0/core}/_instances/extractors/_classic_cdf/_sequences.py +2 -2
  180. cognite/neat/{core → v0/core}/_instances/extractors/_dict.py +6 -3
  181. cognite/neat/{core → v0/core}/_instances/extractors/_dms.py +6 -6
  182. cognite/neat/{core → v0/core}/_instances/extractors/_dms_graph.py +11 -11
  183. cognite/neat/{core → v0/core}/_instances/extractors/_mock_graph_generator.py +10 -10
  184. cognite/neat/{core → v0/core}/_instances/extractors/_raw.py +3 -3
  185. cognite/neat/{core → v0/core}/_instances/extractors/_rdf_file.py +7 -7
  186. cognite/neat/{core → v0/core}/_instances/loaders/_base.py +5 -5
  187. cognite/neat/{core → v0/core}/_instances/loaders/_rdf2dms.py +17 -17
  188. cognite/neat/{core → v0/core}/_instances/loaders/_rdf_to_instance_space.py +11 -11
  189. cognite/neat/{core → v0/core}/_instances/queries/_select.py +29 -3
  190. cognite/neat/{core → v0/core}/_instances/queries/_update.py +1 -1
  191. cognite/neat/{core → v0/core}/_instances/transformers/_base.py +4 -4
  192. cognite/neat/{core → v0/core}/_instances/transformers/_classic_cdf.py +6 -6
  193. cognite/neat/{core → v0/core}/_instances/transformers/_prune_graph.py +4 -4
  194. cognite/neat/{core → v0/core}/_instances/transformers/_rdfpath.py +1 -1
  195. cognite/neat/{core → v0/core}/_instances/transformers/_value_type.py +4 -4
  196. cognite/neat/{core → v0/core}/_issues/_base.py +5 -5
  197. cognite/neat/{core → v0/core}/_issues/_contextmanagers.py +1 -1
  198. cognite/neat/{core → v0/core}/_issues/_factory.py +3 -3
  199. cognite/neat/{core → v0/core}/_issues/errors/__init__.py +1 -1
  200. cognite/neat/{core → v0/core}/_issues/errors/_external.py +1 -1
  201. cognite/neat/{core → v0/core}/_issues/errors/_general.py +1 -1
  202. cognite/neat/{core → v0/core}/_issues/errors/_properties.py +1 -1
  203. cognite/neat/{core → v0/core}/_issues/errors/_resources.py +2 -2
  204. cognite/neat/{core → v0/core}/_issues/errors/_wrapper.py +2 -2
  205. cognite/neat/{core → v0/core}/_issues/warnings/__init__.py +1 -1
  206. cognite/neat/{core → v0/core}/_issues/warnings/_external.py +1 -1
  207. cognite/neat/{core → v0/core}/_issues/warnings/_general.py +1 -1
  208. cognite/neat/{core → v0/core}/_issues/warnings/_models.py +2 -2
  209. cognite/neat/{core → v0/core}/_issues/warnings/_properties.py +2 -2
  210. cognite/neat/{core → v0/core}/_issues/warnings/_resources.py +1 -1
  211. cognite/neat/{core → v0/core}/_issues/warnings/user_modeling.py +1 -1
  212. cognite/neat/{core → v0/core}/_store/_data_model.py +12 -12
  213. cognite/neat/{core → v0/core}/_store/_instance.py +43 -10
  214. cognite/neat/{core → v0/core}/_store/_provenance.py +3 -3
  215. cognite/neat/{core → v0/core}/_store/exceptions.py +4 -4
  216. cognite/neat/v0/core/_utils/__init__.py +0 -0
  217. cognite/neat/{core → v0/core}/_utils/auth.py +1 -1
  218. cognite/neat/{core → v0/core}/_utils/auxiliary.py +1 -1
  219. cognite/neat/{core → v0/core}/_utils/collection_.py +2 -2
  220. cognite/neat/{core → v0/core}/_utils/graph_transformations_report.py +1 -1
  221. cognite/neat/{core → v0/core}/_utils/rdf_.py +1 -1
  222. cognite/neat/{core → v0/core}/_utils/reader/_base.py +1 -1
  223. cognite/neat/{core → v0/core}/_utils/spreadsheet.py +18 -4
  224. cognite/neat/{core → v0/core}/_utils/text.py +1 -1
  225. cognite/neat/{core → v0/core}/_utils/upload.py +3 -3
  226. cognite/neat/v0/plugins/__init__.py +4 -0
  227. cognite/neat/v0/plugins/_base.py +9 -0
  228. cognite/neat/v0/plugins/_data_model.py +48 -0
  229. cognite/neat/{plugins → v0/plugins}/_issues.py +1 -1
  230. cognite/neat/{plugins → v0/plugins}/_manager.py +7 -16
  231. cognite/neat/{session → v0/session}/_base.py +12 -10
  232. cognite/neat/{session → v0/session}/_collector.py +1 -1
  233. cognite/neat/v0/session/_diff.py +51 -0
  234. cognite/neat/{session → v0/session}/_drop.py +3 -3
  235. cognite/neat/{session → v0/session}/_explore.py +2 -2
  236. cognite/neat/{session → v0/session}/_fix.py +2 -2
  237. cognite/neat/{session → v0/session}/_inspect.py +3 -3
  238. cognite/neat/{session → v0/session}/_mapping.py +3 -3
  239. cognite/neat/{session → v0/session}/_plugin.py +4 -5
  240. cognite/neat/{session → v0/session}/_prepare.py +8 -8
  241. cognite/neat/{session → v0/session}/_read.py +33 -20
  242. cognite/neat/{session → v0/session}/_set.py +8 -8
  243. cognite/neat/{session → v0/session}/_show.py +5 -5
  244. cognite/neat/{session → v0/session}/_state.py +10 -10
  245. cognite/neat/{session → v0/session}/_subset.py +4 -4
  246. cognite/neat/{session → v0/session}/_template.py +11 -11
  247. cognite/neat/{session → v0/session}/_to.py +12 -12
  248. cognite/neat/{session → v0/session}/_wizard.py +1 -1
  249. cognite/neat/{session → v0/session}/engine/_load.py +1 -1
  250. cognite/neat/{session → v0/session}/exceptions.py +5 -5
  251. cognite/neat/v1.py +3 -0
  252. {cognite_neat-0.123.32.dist-info → cognite_neat-0.127.30.dist-info}/METADATA +7 -6
  253. cognite_neat-0.127.30.dist-info/RECORD +319 -0
  254. {cognite_neat-0.123.32.dist-info → cognite_neat-0.127.30.dist-info}/WHEEL +1 -1
  255. cognite/neat/data_model/models/entities/__init__.py +0 -9
  256. cognite/neat/plugins/__init__.py +0 -3
  257. cognite/neat/plugins/data_model/importers/__init__.py +0 -5
  258. cognite/neat/plugins/data_model/importers/_base.py +0 -28
  259. cognite_neat-0.123.32.dist-info/RECORD +0 -209
  260. /cognite/neat/{core → _data_model/deployer}/__init__.py +0 -0
  261. /cognite/neat/{core/_client/_api → _data_model/exporters/_table_exporter}/__init__.py +0 -0
  262. /cognite/neat/{core/_client/data_classes → _data_model/importers/_table_importer}/__init__.py +0 -0
  263. /cognite/neat/{core/_data_model → _data_model/models/conceptual}/__init__.py +0 -0
  264. /cognite/neat/{core/_instances → _data_model/validation}/__init__.py +0 -0
  265. /cognite/neat/{core/_instances/extractors/_classic_cdf → _session/_html}/__init__.py +0 -0
  266. /cognite/neat/{core/_utils → _session/_usage_analytics}/__init__.py +0 -0
  267. /cognite/neat/{data_model → v0}/__init__.py +0 -0
  268. /cognite/neat/{plugins/data_model → v0/core}/__init__.py +0 -0
  269. /cognite/neat/{core → v0/core}/_client/__init__.py +0 -0
  270. /cognite/neat/{core → v0/core}/_client/data_classes/data_modeling.py +0 -0
  271. /cognite/neat/{core → v0/core}/_client/data_classes/neat_sequence.py +0 -0
  272. /cognite/neat/{core → v0/core}/_client/data_classes/statistics.py +0 -0
  273. /cognite/neat/{core → v0/core}/_config.py +0 -0
  274. /cognite/neat/{core → v0/core}/_data_model/analysis/__init__.py +0 -0
  275. /cognite/neat/{core → v0/core}/_data_model/catalog/__init__.py +0 -0
  276. /cognite/neat/{core → v0/core}/_data_model/catalog/classic_model.xlsx +0 -0
  277. /cognite/neat/{core → v0/core}/_data_model/catalog/conceptual-imf-data-model.xlsx +0 -0
  278. /cognite/neat/{core → v0/core}/_data_model/catalog/hello_world_pump.xlsx +0 -0
  279. /cognite/neat/{core → v0/core}/_data_model/importers/__init__.py +0 -0
  280. /cognite/neat/{core → v0/core}/_data_model/importers/_rdf/__init__.py +0 -0
  281. /cognite/neat/{core → v0/core}/_data_model/models/_base_unverified.py +0 -0
  282. /cognite/neat/{core → v0/core}/_data_model/models/conceptual/__init__.py +0 -0
  283. /cognite/neat/{core → v0/core}/_data_model/models/entities/_constants.py +0 -0
  284. /cognite/neat/{core → v0/core}/_data_model/models/entities/_wrapped.py +0 -0
  285. /cognite/neat/{core → v0/core}/_data_model/models/mapping/__init__.py +0 -0
  286. /cognite/neat/{core → v0/core}/_data_model/models/mapping/_classic2core.yaml +0 -0
  287. /cognite/neat/{core → v0/core}/_data_model/transformers/__init__.py +0 -0
  288. /cognite/neat/{core → v0/core}/_instances/_shared.py +0 -0
  289. /cognite/neat/{core → v0/core}/_instances/_tracking/__init__.py +0 -0
  290. /cognite/neat/{core → v0/core}/_instances/examples/Knowledge-Graph-Nordic44-dirty.xml +0 -0
  291. /cognite/neat/{core → v0/core}/_instances/examples/Knowledge-Graph-Nordic44.xml +0 -0
  292. /cognite/neat/{core → v0/core}/_instances/examples/__init__.py +0 -0
  293. /cognite/neat/{core → v0/core}/_instances/examples/skos-capturing-sheet-wind-topics.xlsx +0 -0
  294. /cognite/neat/{core → v0/core}/_instances/extractors/_classic_cdf/_assets.py +0 -0
  295. /cognite/neat/{core → v0/core}/_instances/extractors/_classic_cdf/_data_sets.py +0 -0
  296. /cognite/neat/{core → v0/core}/_instances/extractors/_classic_cdf/_events.py +0 -0
  297. /cognite/neat/{core → v0/core}/_instances/extractors/_classic_cdf/_files.py +0 -0
  298. /cognite/neat/{core → v0/core}/_instances/extractors/_classic_cdf/_labels.py +0 -0
  299. /cognite/neat/{core → v0/core}/_instances/extractors/_classic_cdf/_timeseries.py +0 -0
  300. /cognite/neat/{core → v0/core}/_instances/loaders/__init__.py +0 -0
  301. /cognite/neat/{core → v0/core}/_instances/queries/__init__.py +0 -0
  302. /cognite/neat/{core → v0/core}/_instances/queries/_base.py +0 -0
  303. /cognite/neat/{core → v0/core}/_instances/queries/_queries.py +0 -0
  304. /cognite/neat/{core → v0/core}/_instances/transformers/__init__.py +0 -0
  305. /cognite/neat/{core → v0/core}/_issues/__init__.py +0 -0
  306. /cognite/neat/{core → v0/core}/_issues/formatters.py +0 -0
  307. /cognite/neat/{core → v0/core}/_shared.py +0 -0
  308. /cognite/neat/{core → v0/core}/_store/__init__.py +0 -0
  309. /cognite/neat/{core → v0/core}/_utils/io_.py +0 -0
  310. /cognite/neat/{core → v0/core}/_utils/reader/__init__.py +0 -0
  311. /cognite/neat/{core → v0/core}/_utils/tarjan.py +0 -0
  312. /cognite/neat/{core → v0/core}/_utils/time_.py +0 -0
  313. /cognite/neat/{core → v0/core}/_utils/xml_.py +0 -0
  314. /cognite/neat/{session → v0/session}/__init__.py +0 -0
  315. /cognite/neat/{session → v0/session}/_experimental.py +0 -0
  316. /cognite/neat/{session → v0/session}/_state/README.md +0 -0
  317. /cognite/neat/{session → v0/session}/engine/__init__.py +0 -0
  318. /cognite/neat/{session → v0/session}/engine/_import.py +0 -0
  319. /cognite/neat/{session → v0/session}/engine/_interface.py +0 -0
  320. {cognite_neat-0.123.32.dist-info → cognite_neat-0.127.30.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,185 @@
1
+ import sys
2
+ from pathlib import Path
3
+
4
+ from pydantic import BaseModel, Field
5
+
6
+ from cognite.neat._issues import ConsistencyError, ModelSyntaxError
7
+ from cognite.neat._utils.useful_types import ModusOperandi
8
+
9
+ if sys.version_info >= (3, 11):
10
+ import tomllib as tomli # Python 3.11+
11
+ else:
12
+ import tomli # type: ignore
13
+
14
+
15
+ class ValidationConfig(BaseModel, populate_by_name=True):
16
+ """Validation configuration."""
17
+
18
+ exclude: list[str] = Field(default_factory=list)
19
+
20
+ def can_run_validator(self, code: str, issue_type: type) -> bool:
21
+ """
22
+ Check if a specific validator should run.
23
+
24
+ Args:
25
+ code: Validation code (e.g., "NEAT-DMS-CONTAINER-001")
26
+ issue_type: Issue type (e.g., ModelSyntaxError, ConsistencyError, Recommendation)
27
+
28
+ Returns:
29
+ True if validator should run, False otherwise
30
+ """
31
+
32
+ is_excluded = self._is_excluded(code, self.exclude)
33
+
34
+ if issue_type in [ModelSyntaxError, ConsistencyError] and is_excluded:
35
+ print(f"Validator {code} was excluded however it is a critical validator and will still run.")
36
+ return True
37
+ else:
38
+ return not is_excluded
39
+
40
+ @classmethod
41
+ def _is_excluded(cls, code: str, patterns: list[str]) -> bool:
42
+ """Check if code matches any pattern (supports wildcards)."""
43
+ for pattern in patterns:
44
+ if "*" in pattern:
45
+ # Split both pattern and code by hyphens
46
+ pattern_parts = pattern.split("-")
47
+ code_parts = code.split("-")
48
+
49
+ # Pattern must have same or fewer parts than code
50
+ if len(pattern_parts) > len(code_parts):
51
+ continue
52
+
53
+ # Check if all pattern parts match (allowing wildcards)
54
+ match = True
55
+ for p_part, c_part in zip(pattern_parts, code_parts, strict=False):
56
+ if p_part != "*" and p_part != c_part:
57
+ match = False
58
+ break
59
+
60
+ if match:
61
+ return True
62
+ elif code == pattern:
63
+ return True
64
+
65
+ return False
66
+
67
+ def __str__(self) -> str:
68
+ """Human-readable configuration summary."""
69
+ if not self.exclude:
70
+ return "All validators enabled"
71
+ return f"Excluded Rules: {', '.join(self.exclude)}"
72
+
73
+
74
+ class ModelingConfig(BaseModel, populate_by_name=True):
75
+ """Modeling configuration."""
76
+
77
+ mode: ModusOperandi = "additive"
78
+
79
+
80
+ class NeatConfig(BaseModel, populate_by_name=True):
81
+ """Configuration for a custom profile."""
82
+
83
+ profile: str
84
+ validation: ValidationConfig
85
+ modeling: ModelingConfig
86
+
87
+ def __str__(self) -> str:
88
+ """Human-readable configuration summary."""
89
+ lines = [
90
+ f"Profile: {self.profile}",
91
+ f"Modeling Mode: {self.modeling.mode}",
92
+ f"Validation: {self.validation}",
93
+ ]
94
+ return "\n".join(lines)
95
+
96
+
97
+ def internal_profiles() -> dict[str, NeatConfig]:
98
+ """Get internal NeatConfig profile by name."""
99
+ return {
100
+ "legacy-additive": NeatConfig(
101
+ profile="legacy-additive",
102
+ modeling=ModelingConfig(mode="additive"),
103
+ validation=ValidationConfig(
104
+ exclude=[
105
+ "NEAT-DMS-AI-READINESS-*",
106
+ "NEAT-DMS-CONNECTIONS-002",
107
+ "NEAT-DMS-CONNECTIONS-REVERSE-007",
108
+ "NEAT-DMS-CONNECTIONS-REVERSE-008",
109
+ "NEAT-DMS-CONSISTENCY-001",
110
+ ]
111
+ ),
112
+ ),
113
+ "legacy-rebuild": NeatConfig(
114
+ profile="legacy-rebuild",
115
+ modeling=ModelingConfig(mode="rebuild"),
116
+ validation=ValidationConfig(
117
+ exclude=[
118
+ "NEAT-DMS-AI-READINESS-*",
119
+ "NEAT-DMS-CONNECTIONS-002",
120
+ "NEAT-DMS-CONNECTIONS-REVERSE-007",
121
+ "NEAT-DMS-CONNECTIONS-REVERSE-008",
122
+ "NEAT-DMS-CONSISTENCY-001",
123
+ ]
124
+ ),
125
+ ),
126
+ "deep-additive": NeatConfig(
127
+ profile="deep-additive",
128
+ modeling=ModelingConfig(mode="additive"),
129
+ validation=ValidationConfig(exclude=[]),
130
+ ),
131
+ "deep-rebuild": NeatConfig(
132
+ profile="deep-rebuild",
133
+ modeling=ModelingConfig(mode="rebuild"),
134
+ validation=ValidationConfig(exclude=[]),
135
+ ),
136
+ }
137
+
138
+
139
+ def get_neat_config(config_file_name: str, profile: str) -> NeatConfig:
140
+ """Get NeatConfig from file or internal profiles.
141
+
142
+ Args:
143
+ config_file_name: Path to configuration file.
144
+ profile: Profile name to use.
145
+ Returns:
146
+ NeatConfig instance.
147
+ """
148
+
149
+ if not config_file_name.endswith(".toml"):
150
+ raise ValueError("config_file_name must end with '.toml'")
151
+
152
+ file_path = Path.cwd() / config_file_name
153
+
154
+ if file_path.exists():
155
+ with file_path.open("rb") as f:
156
+ toml = tomli.load(f)
157
+
158
+ if "tool" in toml and "neat" in toml["tool"]:
159
+ data = toml["tool"]["neat"]
160
+ elif "neat" in toml:
161
+ data = toml["neat"]
162
+ else:
163
+ raise ValueError("No [tool.neat] or [neat] section found in the configuration file.")
164
+
165
+ toml_profile = data.get("profile")
166
+ toml_profiles = data.get("profiles")
167
+ hardcoded_profiles = internal_profiles()
168
+
169
+ if toml_profile and toml_profile in hardcoded_profiles:
170
+ raise ValueError(f"Internal profile '{toml_profile}' cannot be used in external configuration file.")
171
+
172
+ if toml_profiles and any(p in hardcoded_profiles for p in toml_profiles.keys()):
173
+ raise ValueError(
174
+ "Internal profiles cannot be redefined in external configuration file: "
175
+ f"{set(hardcoded_profiles.keys()).intersection(toml_profiles.keys())}"
176
+ )
177
+
178
+ if toml_profile and profile == toml_profile:
179
+ return NeatConfig(**data)
180
+ elif (built_in_profiles := data.get("profiles")) and profile in built_in_profiles:
181
+ return NeatConfig(profile=profile, **data["profiles"][profile])
182
+ else:
183
+ raise ValueError(f"Profile '{profile}' not found in configuration file.")
184
+ else:
185
+ raise FileNotFoundError(f"Configuration file '{file_path}' not found.")
@@ -0,0 +1,196 @@
1
+ from graphlib import TopologicalSorter
2
+
3
+ from cognite.neat._data_model.models.dms._constraints import RequiresConstraintDefinition
4
+ from cognite.neat._data_model.models.dms._container import ContainerPropertyDefinition, ContainerRequest
5
+ from cognite.neat._data_model.models.dms._data_types import DirectNodeRelation
6
+ from cognite.neat._data_model.models.dms._references import (
7
+ ContainerDirectReference,
8
+ ContainerReference,
9
+ ViewDirectReference,
10
+ ViewReference,
11
+ )
12
+ from cognite.neat._data_model.models.dms._schema import RequestSchema
13
+ from cognite.neat._data_model.models.dms._view_property import (
14
+ EdgeProperty,
15
+ ReverseDirectRelationProperty,
16
+ ViewCorePropertyRequest,
17
+ ViewRequestProperty,
18
+ )
19
+ from cognite.neat._data_model.models.dms._views import ViewRequest
20
+
21
+
22
+ class DataModelAnalysis:
23
+ def __init__(
24
+ self,
25
+ physical: RequestSchema | None = None,
26
+ ) -> None:
27
+ self._physical = physical
28
+
29
+ @property
30
+ def physical(self) -> RequestSchema:
31
+ if self._physical is None:
32
+ raise ValueError("Physical Data Model is required for this analysis")
33
+ return self._physical
34
+
35
+ def referenced_views(self, include_connection_end_node_types: bool = False) -> set[ViewReference]:
36
+ """Get all referenced views in the physical data model."""
37
+ referenced_views = set()
38
+
39
+ for view in self.physical.views:
40
+ referenced_views.add(view.as_reference())
41
+ if view.implements:
42
+ for implement in view.implements:
43
+ referenced_views.add(implement)
44
+
45
+ if include_connection_end_node_types:
46
+ referenced_views |= {view for view in self.connection_end_node_types.values() if view is not None}
47
+
48
+ return referenced_views
49
+
50
+ @property
51
+ def referenced_containers(self) -> set[ContainerReference]:
52
+ """Get all referenced containers in the physical data model."""
53
+ referenced_containers = set()
54
+
55
+ for view in self.physical.views:
56
+ for property_ in view.properties.values():
57
+ if isinstance(property_, ViewCorePropertyRequest):
58
+ referenced_containers.add(property_.container)
59
+
60
+ for container in self.physical.containers:
61
+ referenced_containers.add(container.as_reference())
62
+ if container.constraints:
63
+ for constraint in container.constraints.values():
64
+ if isinstance(constraint, RequiresConstraintDefinition):
65
+ referenced_containers.add(constraint.require)
66
+
67
+ return referenced_containers
68
+
69
+ def view_by_reference(self, include_inherited_properties: bool = True) -> dict[ViewReference, ViewRequest]:
70
+ """Get a mapping of view references to their corresponding ViewRequest objects."""
71
+ view_ancestors = self.ancestors_by_view(self.physical.views)
72
+
73
+ view_by_reference: dict[ViewReference, ViewRequest] = {
74
+ view.as_reference(): view.model_copy(deep=True) for view in self.physical.views
75
+ }
76
+
77
+ if include_inherited_properties:
78
+ for ref, view in view_by_reference.items():
79
+ for ancestor in view_ancestors.get(ref, set()):
80
+ if ancestor_view := view_by_reference.get(ancestor):
81
+ if ancestor_view.properties:
82
+ view.properties.update(ancestor_view.properties)
83
+
84
+ return view_by_reference
85
+
86
+ @property
87
+ def properties_by_view(self) -> dict[ViewReference, dict[str, ViewRequestProperty]]:
88
+ """Get a mapping of view references to their corresponding properties."""
89
+ view_by_reference = self.view_by_reference(include_inherited_properties=False)
90
+ return {
91
+ view_ref: {prop_name: prop for prop_name, prop in view.properties.items()}
92
+ for view_ref, view in view_by_reference.items()
93
+ }
94
+
95
+ @staticmethod
96
+ def ancestors_by_view(views: list[ViewRequest]) -> dict[ViewReference, set[ViewReference]]:
97
+ """Get a mapping of each view to its ancestors in the physical data model."""
98
+ implements_by_view = DataModelAnalysis.implements_by_view(views)
99
+
100
+ # Topological sort to ensure that concepts include all ancestors
101
+ for view in list(TopologicalSorter(implements_by_view).static_order()):
102
+ if view not in implements_by_view:
103
+ continue
104
+ implements_by_view[view] |= {
105
+ grand_parent
106
+ for parent in implements_by_view[view]
107
+ for grand_parent in implements_by_view.get(parent, set())
108
+ }
109
+
110
+ return implements_by_view
111
+
112
+ @staticmethod
113
+ def implements_by_view(views: list[ViewRequest]) -> dict[ViewReference, set[ViewReference]]:
114
+ """Get a mapping of each view to the views it implements."""
115
+ implements_mapping: dict[ViewReference, set[ViewReference]] = {}
116
+
117
+ for view in views:
118
+ view_ref = view.as_reference()
119
+ if view_ref not in implements_mapping:
120
+ implements_mapping[view_ref] = set()
121
+ if view.implements:
122
+ for implement in view.implements:
123
+ implements_mapping[view_ref].add(implement)
124
+
125
+ return implements_mapping
126
+
127
+ @property
128
+ def container_by_reference(self) -> dict[ContainerReference, ContainerRequest]:
129
+ """Get a mapping of container references to their corresponding ContainerRequest objects."""
130
+ return {container.as_reference(): container.model_copy(deep=True) for container in self.physical.containers}
131
+
132
+ @property
133
+ def container_properties(self) -> dict[tuple[ContainerReference, str], ContainerPropertyDefinition]:
134
+ """Get a mapping from (container reference, property name) to the property definition."""
135
+
136
+ return {
137
+ (container.as_reference(), prop_name): property_
138
+ for container in self.physical.containers
139
+ for prop_name, property_ in container.properties.items()
140
+ }
141
+
142
+ @property
143
+ def connection_end_node_types(self) -> dict[tuple[ViewReference, str], ViewReference | None]:
144
+ """Get a mapping of view references to their corresponding ViewRequest objects."""
145
+ view_by_reference = self.view_by_reference(include_inherited_properties=False)
146
+ connection_end_node_types: dict[tuple[ViewReference, str], ViewReference | None] = {}
147
+ container_properties = self.container_properties
148
+
149
+ for view_ref, view in view_by_reference.items():
150
+ if not view.properties:
151
+ continue
152
+ for prop_ref, property_ in view.properties.items():
153
+ # direct relation
154
+ if isinstance(property_, ViewCorePropertyRequest):
155
+ # explicit set of end node type via 'source' which is View reference
156
+ if property_.source:
157
+ connection_end_node_types[(view_ref, prop_ref)] = property_.source
158
+
159
+ # implicit end node type via container property, without actual knowledge of end node type
160
+ elif (
161
+ container_property := container_properties.get(
162
+ (property_.container, property_.container_property_identifier)
163
+ )
164
+ ) and isinstance(container_property.type, DirectNodeRelation):
165
+ connection_end_node_types[(view_ref, prop_ref)] = None
166
+
167
+ # reverse direct relation
168
+ if isinstance(property_, ReverseDirectRelationProperty) and property_.source:
169
+ connection_end_node_types[(view_ref, prop_ref)] = property_.source
170
+
171
+ # edge property
172
+ if isinstance(property_, EdgeProperty) and property_.source:
173
+ connection_end_node_types[(view_ref, prop_ref)] = property_.source
174
+
175
+ return connection_end_node_types
176
+
177
+ @property
178
+ def reverse_to_direct_mapping(
179
+ self,
180
+ ) -> dict[tuple[ViewReference, str], tuple[ViewReference, ContainerDirectReference | ViewDirectReference]]:
181
+ """Get a mapping of reverse direct relations to their corresponding source view and 'through' property."""
182
+ view_by_reference = self.view_by_reference(include_inherited_properties=False)
183
+ bidirectional_connections = {}
184
+
185
+ for view_ref, view in view_by_reference.items():
186
+ if not view.properties:
187
+ continue
188
+ for prop_ref, property_ in view.properties.items():
189
+ # reverse direct relation
190
+ if isinstance(property_, ReverseDirectRelationProperty):
191
+ bidirectional_connections[(view_ref, prop_ref)] = (
192
+ property_.source,
193
+ property_.through,
194
+ )
195
+
196
+ return bidirectional_connections
@@ -0,0 +1,67 @@
1
+ from ._identifiers import NameSpace
2
+
3
+ XML_SCHEMA_NAMESPACE = NameSpace("http://www.w3.org/2001/XMLSchema#")
4
+
5
+ CDF_CDM_SPACE = "cdf_cdm"
6
+ CDF_CDM_VERSION = "v1"
7
+
8
+ COGNITE_CONCEPTS_MAIN = (
9
+ "CogniteAsset",
10
+ "CogniteEquipment",
11
+ "CogniteActivity",
12
+ "CogniteTimeSeries",
13
+ "CogniteFile",
14
+ )
15
+
16
+ COGNITE_CONCEPTS_INTERFACES = (
17
+ "CogniteDescribable",
18
+ "CogniteSourceable",
19
+ "CogniteSchedulable",
20
+ "CogniteVisualizable",
21
+ )
22
+
23
+ COGNITE_CONCEPTS_CONFIGURATIONS = (
24
+ "CogniteSourceSystem",
25
+ "CogniteUnit",
26
+ "CogniteAssetClass",
27
+ "CogniteAssetType",
28
+ "CogniteEquipmentType",
29
+ "CogniteFileCategory",
30
+ )
31
+ COGNITE_CONCEPTS_ANNOTATIONS = (
32
+ "CogniteAnnotation",
33
+ "CogniteDiagramAnnotation",
34
+ )
35
+ COGNITE_CONCEPTS_3D = (
36
+ "CogniteCubeMap",
37
+ "CogniteCADRevision",
38
+ "CognitePointCloudVolume",
39
+ "Cognite360ImageAnnotation",
40
+ "Cognite3DObject",
41
+ "Cognite3DRevision",
42
+ "Cognite360Image",
43
+ "Cognite360ImageCollection",
44
+ "Cognite360ImageStation",
45
+ "CognitePointCloudModel",
46
+ "Cognite3DTransformation",
47
+ "Cognite360ImageModel",
48
+ "Cognite3DModel",
49
+ "CogniteCADModel",
50
+ "CognitePointCloudRevision",
51
+ "CogniteCADNode",
52
+ )
53
+
54
+ COGNITE_CONCEPTS: tuple[str, ...] = (
55
+ *COGNITE_CONCEPTS_MAIN,
56
+ *COGNITE_CONCEPTS_INTERFACES,
57
+ *COGNITE_CONCEPTS_CONFIGURATIONS,
58
+ *COGNITE_CONCEPTS_ANNOTATIONS,
59
+ *COGNITE_CONCEPTS_3D,
60
+ )
61
+
62
+ COGNITE_SPACES = (CDF_CDM_SPACE,)
63
+
64
+ # Defaults from https://docs.cognite.com/cdf/dm/dm_reference/dm_limits_and_restrictions#list-size-limits
65
+
66
+ DEFAULT_MAX_LIST_SIZE = 1000
67
+ DEFAULT_MAX_LIST_SIZE_DIRECT_RELATIONS = 100
@@ -0,0 +1,61 @@
1
+ from pydantic import HttpUrl, RootModel, ValidationError
2
+
3
+ from cognite.neat.v0.core._utils.auxiliary import local_import
4
+
5
+
6
+ class URI(RootModel[str]):
7
+ def __init__(self, value: str):
8
+ try:
9
+ # Use Pydantic's AnyUrl to validate the URI
10
+ _ = HttpUrl(value)
11
+ except ValidationError as e:
12
+ raise ValueError(f"Invalid URI: {value}") from e
13
+ super().__init__(value)
14
+
15
+ def __str__(self) -> str:
16
+ return self.root
17
+
18
+ def __repr__(self) -> str:
19
+ return f"URI({self.root!r})"
20
+
21
+ def as_rdflib_uriref(self): # type: ignore[no-untyped-def]
22
+ # rdflib is an optional dependency, so import here
23
+ local_import("rdflib", "rdflib")
24
+ from rdflib import URIRef
25
+
26
+ return URIRef(self.root)
27
+
28
+
29
+ class NameSpace(RootModel[str]):
30
+ def __init__(self, value: str):
31
+ try:
32
+ # Use Pydantic's AnyUrl to validate the URI
33
+ _ = HttpUrl(value)
34
+ except ValidationError as e:
35
+ raise ValueError(f"Invalid Namespace: {value}") from e
36
+ super().__init__(value)
37
+
38
+ def __str__(self) -> str:
39
+ return self.root
40
+
41
+ def __repr__(self) -> str:
42
+ return f"NameSpace({self.root!r})"
43
+
44
+ def term(self, name: str) -> URI:
45
+ # need to handle slices explicitly because of __getitem__ override
46
+ return URI(self.root + (name if isinstance(name, str) else ""))
47
+
48
+ def __getitem__(self, key: str) -> URI: # type: ignore[override]
49
+ return self.term(key)
50
+
51
+ def __getattr__(self, name: str) -> URI:
52
+ if name.startswith("__"): # ignore any special Python names!
53
+ raise AttributeError
54
+ return self.term(name)
55
+
56
+ def as_rdflib_namespace(self): # type: ignore[no-untyped-def]
57
+ # rdflib is an optional dependency, so import here
58
+ local_import("rdflib", "rdflib")
59
+ from rdflib import Namespace
60
+
61
+ return Namespace(self.root)
@@ -0,0 +1,41 @@
1
+ from abc import ABC, abstractmethod
2
+ from typing import Any
3
+
4
+ from cognite.neat._data_model.deployer.data_classes import DeploymentResult
5
+ from cognite.neat._issues import IssueList
6
+
7
+
8
+ class OnSuccess(ABC):
9
+ """Abstract base class for post-activity success handlers."""
10
+
11
+ @abstractmethod
12
+ def run(self, data_model: Any) -> None:
13
+ """Execute the success handler on the data model."""
14
+ pass
15
+
16
+
17
+ class OnSuccessIssuesChecker(OnSuccess, ABC):
18
+ """Abstract base class for post-activity success handlers that check for issues of the data model."""
19
+
20
+ def __init__(self) -> None:
21
+ self._issues = IssueList()
22
+ self._has_run = False
23
+
24
+ @property
25
+ def issues(self) -> IssueList:
26
+ if not self._has_run:
27
+ raise RuntimeError(f"{type(self).__name__} has not been run yet.")
28
+ return IssueList(self._issues)
29
+
30
+
31
+ class OnSuccessResultProducer(OnSuccess, ABC):
32
+ """Abstract base class for post-activity success handlers that produce desired outcomes using the data model."""
33
+
34
+ def __init__(self) -> None:
35
+ self._results: DeploymentResult | None = None
36
+
37
+ @property
38
+ def result(self) -> DeploymentResult:
39
+ if self._results is None:
40
+ raise RuntimeError("SchemaDeployer has not been run yet.")
41
+ return self._results
@@ -0,0 +1,140 @@
1
+ from abc import ABC, abstractmethod
2
+ from typing import Generic
3
+
4
+ from cognite.neat._utils.useful_types import T_Item
5
+
6
+ from .data_classes import (
7
+ AddedField,
8
+ ChangedField,
9
+ FieldChange,
10
+ FieldChanges,
11
+ RemovedField,
12
+ SeverityType,
13
+ )
14
+
15
+
16
+ class Differ(Generic[T_Item], ABC):
17
+ def __init__(self, parent_path: str | None = None) -> None:
18
+ self.parent_path = parent_path
19
+
20
+ def _get_path(self, field: str) -> str:
21
+ if self.parent_path:
22
+ return f"{self.parent_path}.{field}"
23
+ return field
24
+
25
+ def _diff_name_description(self, current: T_Item, new: T_Item, identifier: str | None = None) -> list[FieldChange]:
26
+ changes: list[FieldChange] = []
27
+ if hasattr(current, "name") and hasattr(new, "name"):
28
+ if current.name != new.name:
29
+ field_path = self._get_path(f"{identifier}.name" if identifier else "name")
30
+ changes.append(
31
+ ChangedField(
32
+ item_severity=SeverityType.SAFE,
33
+ field_path=field_path,
34
+ current_value=current.name,
35
+ new_value=new.name,
36
+ )
37
+ )
38
+ if hasattr(current, "description") and hasattr(new, "description"):
39
+ if current.description != new.description:
40
+ field_path = self._get_path(f"{identifier}.description" if identifier else "description")
41
+ changes.append(
42
+ ChangedField(
43
+ item_severity=SeverityType.SAFE,
44
+ field_path=field_path,
45
+ current_value=current.description,
46
+ new_value=new.description,
47
+ )
48
+ )
49
+ return changes
50
+
51
+
52
+ class ItemDiffer(Differ[T_Item], ABC):
53
+ """A generic class for comparing two items of the same type and reporting the differences."""
54
+
55
+ @abstractmethod
56
+ def diff(self, current: T_Item, new: T_Item) -> list[FieldChange]:
57
+ """Compare two items and return a list of changes.
58
+
59
+ Args:
60
+ current: The resource as it is in CDF.
61
+ new: The resource as it is desired to be.
62
+
63
+ Returns:
64
+ A list of changes between the two resources.
65
+ """
66
+ raise NotImplementedError()
67
+
68
+
69
+ class ObjectDiffer(Differ[T_Item], ABC):
70
+ @abstractmethod
71
+ def diff(self, current: T_Item, new: T_Item, identifier: str) -> list[FieldChange]:
72
+ """Compare two dict-like objects and return a list of changes.
73
+
74
+ Args:
75
+ current: The resource as it is in CDF.
76
+ new: The resource as it is desired to be.
77
+ identifier: The field used to identify individual items within the objects.
78
+
79
+ Returns:
80
+ A list of changes between the two resources.
81
+ """
82
+ raise NotImplementedError()
83
+
84
+
85
+ def field_differences(
86
+ parent_path: str,
87
+ current: dict[str, T_Item] | None,
88
+ new: dict[str, T_Item] | None,
89
+ add_severity: SeverityType,
90
+ remove_severity: SeverityType,
91
+ differ: ObjectDiffer[T_Item],
92
+ ) -> list[FieldChange]:
93
+ """Diff two containers of items.
94
+
95
+ A container is for example the properties, constraints, or indexes of a container,
96
+ properties of a space, views of a data model, etc.
97
+
98
+ Args:
99
+ parent_path: The JSON path to the container being compared.
100
+ current: The items as they are in CDF.
101
+ new: The items as they are desired to be.
102
+ add_severity: The severity to assign to added items.
103
+ remove_severity: The severity to assign to removed items.
104
+ differ: The differ to use for comparing individual items.
105
+
106
+ """
107
+ changes: list[FieldChange] = []
108
+ current_map = current or {}
109
+ new_map = new or {}
110
+ current_keys = set(current_map.keys())
111
+ new_keys = set(new_map.keys())
112
+
113
+ for key in sorted(new_keys - current_keys):
114
+ item_path = f"{parent_path}.{key}"
115
+ changes.append(
116
+ AddedField(
117
+ item_severity=add_severity,
118
+ field_path=item_path,
119
+ new_value=new_map[key],
120
+ )
121
+ )
122
+
123
+ for key in sorted(current_keys - new_keys):
124
+ changes.append(
125
+ RemovedField(
126
+ item_severity=remove_severity,
127
+ field_path=f"{parent_path}.{key}",
128
+ current_value=current_map[key],
129
+ )
130
+ )
131
+
132
+ for key in sorted(current_keys & new_keys):
133
+ item_path = f"{parent_path}.{key}"
134
+ cdf_item = current_map[key]
135
+ desired_item = new_map[key]
136
+ diffs = differ.diff(cdf_item, desired_item, identifier=key)
137
+ if diffs:
138
+ changes.append(FieldChanges(field_path=item_path, changes=diffs))
139
+
140
+ return changes