cognite-neat 0.70.1__py3-none-any.whl → 0.127.19__py3-none-any.whl

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