cognite-neat 0.123.2__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 (333) 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 +206 -0
  43. cognite/neat/_data_model/models/dms/_base.py +31 -0
  44. cognite/neat/_data_model/models/dms/_constants.py +48 -0
  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 +48 -0
  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/models/entities/_constants.py +22 -0
  62. cognite/neat/_data_model/models/entities/_data_types.py +144 -0
  63. cognite/neat/_data_model/models/entities/_identifiers.py +61 -0
  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/__init__.py +0 -0
  93. cognite/neat/_session/_usage_analytics/_collector.py +131 -0
  94. cognite/neat/_session/_usage_analytics/_constants.py +23 -0
  95. cognite/neat/_session/_usage_analytics/_storage.py +240 -0
  96. cognite/neat/_session/_wrappers.py +82 -0
  97. cognite/neat/_state_machine/__init__.py +10 -0
  98. cognite/neat/_state_machine/_base.py +37 -0
  99. cognite/neat/_state_machine/_states.py +52 -0
  100. cognite/neat/_store/__init__.py +3 -0
  101. cognite/neat/_store/_provenance.py +81 -0
  102. cognite/neat/_store/_store.py +156 -0
  103. cognite/neat/_utils/__init__.py +0 -0
  104. cognite/neat/_utils/_reader.py +194 -0
  105. cognite/neat/_utils/auxiliary.py +39 -0
  106. cognite/neat/_utils/collection.py +11 -0
  107. cognite/neat/_utils/http_client/__init__.py +39 -0
  108. cognite/neat/_utils/http_client/_client.py +245 -0
  109. cognite/neat/_utils/http_client/_config.py +19 -0
  110. cognite/neat/_utils/http_client/_data_classes.py +294 -0
  111. cognite/neat/_utils/http_client/_tracker.py +31 -0
  112. cognite/neat/_utils/text.py +71 -0
  113. cognite/neat/_utils/useful_types.py +37 -0
  114. cognite/neat/_utils/validation.py +154 -0
  115. cognite/neat/_version.py +1 -1
  116. cognite/neat/v0/__init__.py +0 -0
  117. cognite/neat/v0/core/__init__.py +0 -0
  118. cognite/neat/v0/core/_client/_api/__init__.py +0 -0
  119. cognite/neat/{core → v0/core}/_client/_api/data_modeling_loaders.py +86 -7
  120. cognite/neat/{core → v0/core}/_client/_api/neat_instances.py +5 -5
  121. cognite/neat/{core → v0/core}/_client/_api/schema.py +5 -5
  122. cognite/neat/{core → v0/core}/_client/_api/statistics.py +3 -3
  123. cognite/neat/{core → v0/core}/_client/_api_client.py +1 -1
  124. cognite/neat/v0/core/_client/data_classes/__init__.py +0 -0
  125. cognite/neat/{core → v0/core}/_client/data_classes/schema.py +4 -4
  126. cognite/neat/{core → v0/core}/_client/testing.py +1 -1
  127. cognite/neat/{core → v0/core}/_constants.py +10 -3
  128. cognite/neat/v0/core/_data_model/__init__.py +0 -0
  129. cognite/neat/{core → v0/core}/_data_model/_constants.py +9 -6
  130. cognite/neat/{core → v0/core}/_data_model/_shared.py +5 -5
  131. cognite/neat/{core → v0/core}/_data_model/analysis/_base.py +12 -8
  132. cognite/neat/{core → v0/core}/_data_model/exporters/__init__.py +1 -2
  133. cognite/neat/{core → v0/core}/_data_model/exporters/_base.py +7 -7
  134. cognite/neat/{core → v0/core}/_data_model/exporters/_data_model2dms.py +9 -9
  135. cognite/neat/{core → v0/core}/_data_model/exporters/_data_model2excel.py +13 -13
  136. cognite/neat/{core → v0/core}/_data_model/exporters/_data_model2instance_template.py +4 -4
  137. cognite/neat/{core/_data_model/exporters/_data_model2ontology.py → v0/core/_data_model/exporters/_data_model2semantic_model.py} +126 -133
  138. cognite/neat/{core → v0/core}/_data_model/exporters/_data_model2yaml.py +1 -1
  139. cognite/neat/{core → v0/core}/_data_model/importers/__init__.py +4 -6
  140. cognite/neat/{core → v0/core}/_data_model/importers/_base.py +5 -5
  141. cognite/neat/{core → v0/core}/_data_model/importers/_base_file_reader.py +2 -2
  142. cognite/neat/{core → v0/core}/_data_model/importers/_dict2data_model.py +6 -6
  143. cognite/neat/{core → v0/core}/_data_model/importers/_dms2data_model.py +19 -16
  144. cognite/neat/v0/core/_data_model/importers/_graph2data_model.py +299 -0
  145. cognite/neat/v0/core/_data_model/importers/_rdf/__init__.py +4 -0
  146. cognite/neat/{core → v0/core}/_data_model/importers/_rdf/_base.py +13 -13
  147. cognite/neat/{core → v0/core}/_data_model/importers/_rdf/_inference2rdata_model.py +14 -14
  148. cognite/neat/v0/core/_data_model/importers/_rdf/_owl2data_model.py +144 -0
  149. cognite/neat/v0/core/_data_model/importers/_rdf/_shared.py +255 -0
  150. cognite/neat/{core → v0/core}/_data_model/importers/_spreadsheet2data_model.py +94 -13
  151. cognite/neat/{core → v0/core}/_data_model/models/__init__.py +3 -3
  152. cognite/neat/{core → v0/core}/_data_model/models/_base_verified.py +5 -5
  153. cognite/neat/v0/core/_data_model/models/_import_contexts.py +82 -0
  154. cognite/neat/{core → v0/core}/_data_model/models/_types.py +5 -5
  155. cognite/neat/{core → v0/core}/_data_model/models/conceptual/_unverified.py +18 -12
  156. cognite/neat/v0/core/_data_model/models/conceptual/_validation.py +308 -0
  157. cognite/neat/{core → v0/core}/_data_model/models/conceptual/_verified.py +13 -11
  158. cognite/neat/{core → v0/core}/_data_model/models/data_types.py +14 -4
  159. cognite/neat/{core → v0/core}/_data_model/models/entities/__init__.py +6 -0
  160. cognite/neat/v0/core/_data_model/models/entities/_loaders.py +155 -0
  161. cognite/neat/{core → v0/core}/_data_model/models/entities/_multi_value.py +2 -2
  162. cognite/neat/v0/core/_data_model/models/entities/_restrictions.py +230 -0
  163. cognite/neat/{core → v0/core}/_data_model/models/entities/_single_value.py +121 -16
  164. cognite/neat/{core → v0/core}/_data_model/models/entities/_types.py +10 -0
  165. cognite/neat/{core → v0/core}/_data_model/models/mapping/_classic2core.py +5 -5
  166. cognite/neat/{core → v0/core}/_data_model/models/physical/__init__.py +1 -1
  167. cognite/neat/{core → v0/core}/_data_model/models/physical/_exporter.py +28 -21
  168. cognite/neat/{core → v0/core}/_data_model/models/physical/_unverified.py +141 -38
  169. cognite/neat/{core → v0/core}/_data_model/models/physical/_validation.py +190 -24
  170. cognite/neat/{core → v0/core}/_data_model/models/physical/_verified.py +135 -15
  171. cognite/neat/{core → v0/core}/_data_model/transformers/__init__.py +2 -0
  172. cognite/neat/{core → v0/core}/_data_model/transformers/_base.py +4 -4
  173. cognite/neat/{core → v0/core}/_data_model/transformers/_converters.py +39 -32
  174. cognite/neat/{core → v0/core}/_data_model/transformers/_mapping.py +7 -7
  175. cognite/neat/v0/core/_data_model/transformers/_union_conceptual.py +208 -0
  176. cognite/neat/{core → v0/core}/_data_model/transformers/_verification.py +7 -7
  177. cognite/neat/v0/core/_instances/__init__.py +0 -0
  178. cognite/neat/{core → v0/core}/_instances/_tracking/base.py +1 -1
  179. cognite/neat/{core → v0/core}/_instances/_tracking/log.py +1 -1
  180. cognite/neat/{core → v0/core}/_instances/extractors/__init__.py +1 -1
  181. cognite/neat/{core → v0/core}/_instances/extractors/_base.py +6 -6
  182. cognite/neat/v0/core/_instances/extractors/_classic_cdf/__init__.py +0 -0
  183. cognite/neat/{core → v0/core}/_instances/extractors/_classic_cdf/_base.py +7 -7
  184. cognite/neat/{core → v0/core}/_instances/extractors/_classic_cdf/_classic.py +12 -12
  185. cognite/neat/{core → v0/core}/_instances/extractors/_classic_cdf/_relationships.py +3 -3
  186. cognite/neat/{core → v0/core}/_instances/extractors/_classic_cdf/_sequences.py +2 -2
  187. cognite/neat/{core → v0/core}/_instances/extractors/_dict.py +6 -3
  188. cognite/neat/{core → v0/core}/_instances/extractors/_dms.py +6 -6
  189. cognite/neat/{core → v0/core}/_instances/extractors/_dms_graph.py +11 -11
  190. cognite/neat/{core → v0/core}/_instances/extractors/_mock_graph_generator.py +10 -10
  191. cognite/neat/{core → v0/core}/_instances/extractors/_raw.py +3 -3
  192. cognite/neat/{core → v0/core}/_instances/extractors/_rdf_file.py +7 -7
  193. cognite/neat/{core → v0/core}/_instances/loaders/_base.py +5 -5
  194. cognite/neat/{core → v0/core}/_instances/loaders/_rdf2dms.py +17 -17
  195. cognite/neat/{core → v0/core}/_instances/loaders/_rdf_to_instance_space.py +11 -11
  196. cognite/neat/{core → v0/core}/_instances/queries/_select.py +29 -3
  197. cognite/neat/{core → v0/core}/_instances/queries/_update.py +1 -1
  198. cognite/neat/{core → v0/core}/_instances/transformers/_base.py +4 -4
  199. cognite/neat/{core → v0/core}/_instances/transformers/_classic_cdf.py +6 -6
  200. cognite/neat/{core → v0/core}/_instances/transformers/_prune_graph.py +4 -4
  201. cognite/neat/{core → v0/core}/_instances/transformers/_rdfpath.py +1 -1
  202. cognite/neat/{core → v0/core}/_instances/transformers/_value_type.py +4 -4
  203. cognite/neat/{core → v0/core}/_issues/_base.py +11 -6
  204. cognite/neat/{core → v0/core}/_issues/_contextmanagers.py +8 -6
  205. cognite/neat/{core → v0/core}/_issues/_factory.py +11 -8
  206. cognite/neat/{core → v0/core}/_issues/errors/__init__.py +3 -1
  207. cognite/neat/{core → v0/core}/_issues/errors/_external.py +1 -1
  208. cognite/neat/{core → v0/core}/_issues/errors/_general.py +1 -1
  209. cognite/neat/{core → v0/core}/_issues/errors/_properties.py +12 -1
  210. cognite/neat/{core → v0/core}/_issues/errors/_resources.py +2 -2
  211. cognite/neat/{core → v0/core}/_issues/errors/_wrapper.py +7 -3
  212. cognite/neat/{core → v0/core}/_issues/warnings/__init__.py +5 -1
  213. cognite/neat/{core → v0/core}/_issues/warnings/_external.py +1 -1
  214. cognite/neat/{core → v0/core}/_issues/warnings/_general.py +1 -1
  215. cognite/neat/{core → v0/core}/_issues/warnings/_models.py +39 -4
  216. cognite/neat/{core → v0/core}/_issues/warnings/_properties.py +13 -2
  217. cognite/neat/{core → v0/core}/_issues/warnings/_resources.py +1 -1
  218. cognite/neat/{core → v0/core}/_issues/warnings/user_modeling.py +1 -1
  219. cognite/neat/{core → v0/core}/_store/_data_model.py +13 -12
  220. cognite/neat/{core → v0/core}/_store/_instance.py +45 -12
  221. cognite/neat/{core → v0/core}/_store/_provenance.py +3 -3
  222. cognite/neat/{core → v0/core}/_store/exceptions.py +4 -4
  223. cognite/neat/v0/core/_utils/__init__.py +0 -0
  224. cognite/neat/{core → v0/core}/_utils/auth.py +1 -1
  225. cognite/neat/{core → v0/core}/_utils/auxiliary.py +7 -1
  226. cognite/neat/{core → v0/core}/_utils/collection_.py +2 -2
  227. cognite/neat/{core → v0/core}/_utils/graph_transformations_report.py +1 -1
  228. cognite/neat/{core → v0/core}/_utils/rdf_.py +38 -14
  229. cognite/neat/{core → v0/core}/_utils/reader/_base.py +1 -1
  230. cognite/neat/{core → v0/core}/_utils/spreadsheet.py +22 -4
  231. cognite/neat/v0/core/_utils/tarjan.py +44 -0
  232. cognite/neat/{core → v0/core}/_utils/text.py +1 -1
  233. cognite/neat/{core → v0/core}/_utils/upload.py +3 -3
  234. cognite/neat/v0/plugins/__init__.py +4 -0
  235. cognite/neat/v0/plugins/_base.py +9 -0
  236. cognite/neat/v0/plugins/_data_model.py +48 -0
  237. cognite/neat/{plugins → v0/plugins}/_issues.py +1 -1
  238. cognite/neat/{plugins → v0/plugins}/_manager.py +7 -16
  239. cognite/neat/{session → v0/session}/_base.py +13 -10
  240. cognite/neat/{session → v0/session}/_collector.py +1 -1
  241. cognite/neat/v0/session/_diff.py +51 -0
  242. cognite/neat/{session → v0/session}/_drop.py +3 -3
  243. cognite/neat/{session → v0/session}/_explore.py +2 -2
  244. cognite/neat/{session → v0/session}/_fix.py +2 -2
  245. cognite/neat/{session → v0/session}/_inspect.py +3 -3
  246. cognite/neat/{session → v0/session}/_mapping.py +3 -3
  247. cognite/neat/{session → v0/session}/_plugin.py +4 -5
  248. cognite/neat/{session → v0/session}/_prepare.py +8 -8
  249. cognite/neat/{session → v0/session}/_read.py +33 -43
  250. cognite/neat/{session → v0/session}/_set.py +8 -8
  251. cognite/neat/{session → v0/session}/_show.py +5 -5
  252. cognite/neat/{session → v0/session}/_state.py +22 -8
  253. cognite/neat/{session → v0/session}/_subset.py +4 -4
  254. cognite/neat/{session → v0/session}/_template.py +11 -11
  255. cognite/neat/{session → v0/session}/_to.py +12 -12
  256. cognite/neat/{session → v0/session}/_wizard.py +1 -1
  257. cognite/neat/{session → v0/session}/engine/_load.py +1 -1
  258. cognite/neat/{session → v0/session}/exceptions.py +5 -5
  259. cognite/neat/v1.py +3 -0
  260. {cognite_neat-0.123.2.dist-info → cognite_neat-0.127.30.dist-info}/METADATA +9 -8
  261. cognite_neat-0.127.30.dist-info/RECORD +319 -0
  262. {cognite_neat-0.123.2.dist-info → cognite_neat-0.127.30.dist-info}/WHEEL +1 -1
  263. cognite/neat/core/_data_model/importers/_dtdl2data_model/__init__.py +0 -3
  264. cognite/neat/core/_data_model/importers/_dtdl2data_model/_unit_lookup.py +0 -224
  265. cognite/neat/core/_data_model/importers/_dtdl2data_model/dtdl_converter.py +0 -320
  266. cognite/neat/core/_data_model/importers/_dtdl2data_model/dtdl_importer.py +0 -155
  267. cognite/neat/core/_data_model/importers/_dtdl2data_model/spec.py +0 -363
  268. cognite/neat/core/_data_model/importers/_rdf/__init__.py +0 -5
  269. cognite/neat/core/_data_model/importers/_rdf/_imf2data_model.py +0 -98
  270. cognite/neat/core/_data_model/importers/_rdf/_owl2data_model.py +0 -87
  271. cognite/neat/core/_data_model/importers/_rdf/_shared.py +0 -168
  272. cognite/neat/core/_data_model/models/conceptual/_validation.py +0 -294
  273. cognite/neat/core/_data_model/models/entities/_loaders.py +0 -75
  274. cognite/neat/plugins/__init__.py +0 -3
  275. cognite/neat/plugins/data_model/importers/__init__.py +0 -5
  276. cognite/neat/plugins/data_model/importers/_base.py +0 -28
  277. cognite_neat-0.123.2.dist-info/RECORD +0 -197
  278. /cognite/neat/{core → _data_model}/__init__.py +0 -0
  279. /cognite/neat/{core/_client/_api → _data_model/deployer}/__init__.py +0 -0
  280. /cognite/neat/{core/_client/data_classes → _data_model/exporters/_table_exporter}/__init__.py +0 -0
  281. /cognite/neat/{core/_data_model → _data_model/importers/_table_importer}/__init__.py +0 -0
  282. /cognite/neat/{core/_instances → _data_model/models}/__init__.py +0 -0
  283. /cognite/neat/{core/_instances/extractors/_classic_cdf → _data_model/models/conceptual}/__init__.py +0 -0
  284. /cognite/neat/{core/_utils → _data_model/validation}/__init__.py +0 -0
  285. /cognite/neat/{plugins/data_model → _session/_html}/__init__.py +0 -0
  286. /cognite/neat/{core → v0/core}/_client/__init__.py +0 -0
  287. /cognite/neat/{core → v0/core}/_client/data_classes/data_modeling.py +0 -0
  288. /cognite/neat/{core → v0/core}/_client/data_classes/neat_sequence.py +0 -0
  289. /cognite/neat/{core → v0/core}/_client/data_classes/statistics.py +0 -0
  290. /cognite/neat/{core → v0/core}/_config.py +0 -0
  291. /cognite/neat/{core → v0/core}/_data_model/analysis/__init__.py +0 -0
  292. /cognite/neat/{core → v0/core}/_data_model/catalog/__init__.py +0 -0
  293. /cognite/neat/{core → v0/core}/_data_model/catalog/classic_model.xlsx +0 -0
  294. /cognite/neat/{core → v0/core}/_data_model/catalog/conceptual-imf-data-model.xlsx +0 -0
  295. /cognite/neat/{core → v0/core}/_data_model/catalog/hello_world_pump.xlsx +0 -0
  296. /cognite/neat/{core → v0/core}/_data_model/models/_base_unverified.py +0 -0
  297. /cognite/neat/{core → v0/core}/_data_model/models/conceptual/__init__.py +0 -0
  298. /cognite/neat/{core → v0/core}/_data_model/models/entities/_constants.py +0 -0
  299. /cognite/neat/{core → v0/core}/_data_model/models/entities/_wrapped.py +0 -0
  300. /cognite/neat/{core → v0/core}/_data_model/models/mapping/__init__.py +0 -0
  301. /cognite/neat/{core → v0/core}/_data_model/models/mapping/_classic2core.yaml +0 -0
  302. /cognite/neat/{core → v0/core}/_instances/_shared.py +0 -0
  303. /cognite/neat/{core → v0/core}/_instances/_tracking/__init__.py +0 -0
  304. /cognite/neat/{core → v0/core}/_instances/examples/Knowledge-Graph-Nordic44-dirty.xml +0 -0
  305. /cognite/neat/{core → v0/core}/_instances/examples/Knowledge-Graph-Nordic44.xml +0 -0
  306. /cognite/neat/{core → v0/core}/_instances/examples/__init__.py +0 -0
  307. /cognite/neat/{core → v0/core}/_instances/examples/skos-capturing-sheet-wind-topics.xlsx +0 -0
  308. /cognite/neat/{core → v0/core}/_instances/extractors/_classic_cdf/_assets.py +0 -0
  309. /cognite/neat/{core → v0/core}/_instances/extractors/_classic_cdf/_data_sets.py +0 -0
  310. /cognite/neat/{core → v0/core}/_instances/extractors/_classic_cdf/_events.py +0 -0
  311. /cognite/neat/{core → v0/core}/_instances/extractors/_classic_cdf/_files.py +0 -0
  312. /cognite/neat/{core → v0/core}/_instances/extractors/_classic_cdf/_labels.py +0 -0
  313. /cognite/neat/{core → v0/core}/_instances/extractors/_classic_cdf/_timeseries.py +0 -0
  314. /cognite/neat/{core → v0/core}/_instances/loaders/__init__.py +0 -0
  315. /cognite/neat/{core → v0/core}/_instances/queries/__init__.py +0 -0
  316. /cognite/neat/{core → v0/core}/_instances/queries/_base.py +0 -0
  317. /cognite/neat/{core → v0/core}/_instances/queries/_queries.py +0 -0
  318. /cognite/neat/{core → v0/core}/_instances/transformers/__init__.py +0 -0
  319. /cognite/neat/{core → v0/core}/_issues/__init__.py +0 -0
  320. /cognite/neat/{core → v0/core}/_issues/formatters.py +0 -0
  321. /cognite/neat/{core → v0/core}/_shared.py +0 -0
  322. /cognite/neat/{core → v0/core}/_store/__init__.py +0 -0
  323. /cognite/neat/{core → v0/core}/_utils/io_.py +0 -0
  324. /cognite/neat/{core → v0/core}/_utils/reader/__init__.py +0 -0
  325. /cognite/neat/{core → v0/core}/_utils/time_.py +0 -0
  326. /cognite/neat/{core → v0/core}/_utils/xml_.py +0 -0
  327. /cognite/neat/{session → v0/session}/__init__.py +0 -0
  328. /cognite/neat/{session → v0/session}/_experimental.py +0 -0
  329. /cognite/neat/{session → v0/session}/_state/README.md +0 -0
  330. /cognite/neat/{session → v0/session}/engine/__init__.py +0 -0
  331. /cognite/neat/{session → v0/session}/engine/_import.py +0 -0
  332. /cognite/neat/{session → v0/session}/engine/_interface.py +0 -0
  333. {cognite_neat-0.123.2.dist-info → cognite_neat-0.127.30.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,131 @@
1
+ import os
2
+ import platform
3
+ import threading
4
+ import uuid
5
+ from contextlib import suppress
6
+ from typing import Any, Literal
7
+
8
+ from mixpanel import Consumer, Mixpanel # type: ignore[import-untyped]
9
+
10
+ from cognite.neat._session._usage_analytics._constants import IN_NOTEBOOK, IN_PYODIDE
11
+ from cognite.neat._session._usage_analytics._storage import ManualReadResult, ReadResult, get_storage
12
+
13
+ _NEAT_MIXPANEL_TOKEN: str = "bd630ad149d19999df3989c3a3750c4f"
14
+
15
+
16
+ class Collector:
17
+ """Collects usage data and sends it to Mixpanel."""
18
+
19
+ _instance: "Collector | None" = None
20
+ _initialized: bool = False
21
+
22
+ def __new__(cls, *args: Any, **kwargs: Any) -> "Collector":
23
+ # Implementing Singleton pattern
24
+ if cls._instance is None:
25
+ cls._instance = super().__new__(cls)
26
+ return cls._instance
27
+
28
+ def __init__(self, skip_tracking: bool = False) -> None:
29
+ if self._initialized:
30
+ return
31
+ self.mp = Mixpanel(_NEAT_MIXPANEL_TOKEN, consumer=Consumer(api_host="api-eu.mixpanel.com"))
32
+ self._storage = get_storage()
33
+ self._opt_status_key = "neat-opt-status"
34
+ self._distinct_id_key = "neat-distinct-id"
35
+ self.skip_tracking = skip_tracking
36
+ self._initialized = True
37
+ # We trigger these reads immediately to have them ready when needed.
38
+ # This is necessary in a Pyodide environment were the read will not be available
39
+ self._opt_status_read: ReadResult | None = self._storage.read(self._opt_status_key)
40
+ self._distinct_id_read: ReadResult | None = self._storage.read(self._distinct_id_key)
41
+
42
+ @property
43
+ def _opt_status(self) -> str:
44
+ if self._opt_status_read is None:
45
+ self._opt_status_read = self._storage.read(self._opt_status_key)
46
+ if self._opt_status_read.is_ready:
47
+ return self._opt_status_read.get_data()
48
+ return "" # We do not have an opt status yet.
49
+
50
+ @property
51
+ def _distinct_id(self) -> str | None:
52
+ if self._distinct_id_read is None:
53
+ self._distinct_id_read = self._storage.read(self._distinct_id_key)
54
+ if self._distinct_id_read.is_ready:
55
+ return self._distinct_id_read.get_data()
56
+ return None
57
+
58
+ def bust_opt_status(self) -> None:
59
+ self._storage.delete(self._opt_status_key)
60
+ self._opt_status_read = None
61
+
62
+ @property
63
+ def is_opted_out(self) -> bool:
64
+ return self._opt_status == "opted-out"
65
+
66
+ @property
67
+ def is_opted_in(self) -> bool:
68
+ return self._opt_status == "opted-in"
69
+
70
+ def enable(self) -> None:
71
+ self._storage.write(self._opt_status_key, "opted-in")
72
+ # Override cached property
73
+ self._opt_status_read = ManualReadResult("opted-in")
74
+
75
+ def disable(self) -> None:
76
+ self._storage.write(self._opt_status_key, "opted-out")
77
+ # Override cached property
78
+ self._opt_status_read = ManualReadResult("opted-out")
79
+
80
+ def get_distinct_id(self) -> str:
81
+ existing_id = self._distinct_id
82
+ if existing_id:
83
+ return existing_id
84
+
85
+ distinct_id = f"{platform.system()}-{platform.python_version()}-{uuid.uuid4()!s}"
86
+ self._storage.write(self._distinct_id_key, distinct_id)
87
+ with suppress(ConnectionError):
88
+ self.mp.people_set(
89
+ distinct_id,
90
+ {
91
+ "$os": platform.system(),
92
+ "$python_version": platform.python_version(),
93
+ "$distinct_id": distinct_id,
94
+ "environment": self._get_environment(),
95
+ },
96
+ )
97
+ self._distinct_id_read = ManualReadResult(distinct_id)
98
+ return distinct_id
99
+
100
+ @staticmethod
101
+ def _get_environment() -> Literal["pyodide", "notebook", "python"]:
102
+ """Get the current environment the user is running in."""
103
+ if IN_PYODIDE:
104
+ return "pyodide"
105
+ if IN_NOTEBOOK:
106
+ return "notebook"
107
+ return "python"
108
+
109
+ @property
110
+ def can_collect(self) -> bool:
111
+ """Check if tracking is possible."""
112
+ return not self.skip_tracking and self.is_opted_in and "PYTEST_CURRENT_TEST" not in os.environ
113
+
114
+ def collect(
115
+ self, event_name: Literal["action", "initSession", "deployment"], event_properties: dict[str, Any]
116
+ ) -> None:
117
+ distinct_id = self.get_distinct_id()
118
+
119
+ def track() -> None:
120
+ # If we are unable to connect to Mixpanel, we don't want to crash the program
121
+ with suppress(ConnectionError):
122
+ self.mp.track(distinct_id, event_name, event_properties)
123
+
124
+ if IN_PYODIDE:
125
+ # We cannot spawn threads in Pyodide
126
+ track()
127
+ return None
128
+
129
+ thread = threading.Thread(target=track, daemon=True)
130
+ thread.start()
131
+ return None
@@ -0,0 +1,23 @@
1
+ def _is_in_notebook() -> bool:
2
+ try:
3
+ from IPython import get_ipython
4
+
5
+ if "IPKernelApp" not in get_ipython().config: # pragma: no cover
6
+ return False
7
+ except ImportError:
8
+ return False
9
+ except AttributeError:
10
+ return False
11
+ return True
12
+
13
+
14
+ def _is_in_browser() -> bool:
15
+ try:
16
+ from pyodide.ffi import IN_BROWSER # type: ignore [import-not-found]
17
+ except ModuleNotFoundError:
18
+ return False
19
+ return IN_BROWSER
20
+
21
+
22
+ IN_PYODIDE = _is_in_browser()
23
+ IN_NOTEBOOK = _is_in_notebook() or IN_PYODIDE
@@ -0,0 +1,240 @@
1
+ """Storage abstraction for persisting data in both local filesystem and pyodide environments."""
2
+
3
+ from abc import ABC, abstractmethod
4
+ from dataclasses import dataclass
5
+ from pathlib import Path
6
+ from typing import TYPE_CHECKING, Protocol
7
+
8
+ if TYPE_CHECKING:
9
+ try:
10
+ import asyncio
11
+
12
+ from pyodide.webloop import PyodideTask # type: ignore[import-not-found]
13
+ except ImportError:
14
+ pass
15
+
16
+ from cognite.neat._session._usage_analytics._constants import IN_PYODIDE
17
+
18
+
19
+ @dataclass
20
+ class ReadResult(ABC):
21
+ @property
22
+ @abstractmethod
23
+ def is_ready(self) -> bool: ...
24
+
25
+ @abstractmethod
26
+ def get_data(self) -> str: ...
27
+
28
+
29
+ @dataclass
30
+ class ManualReadResult(ReadResult):
31
+ data: str
32
+
33
+ @property
34
+ def is_ready(self) -> bool:
35
+ return True
36
+
37
+ def get_data(self) -> str:
38
+ return self.data
39
+
40
+
41
+ class Storage(Protocol):
42
+ """Protocol for storage implementations."""
43
+
44
+ def read(self, key: str) -> ReadResult:
45
+ """Read a value from storage."""
46
+ ...
47
+
48
+ def write(self, key: str, value: str) -> None:
49
+ """Write a value to storage."""
50
+ ...
51
+
52
+ def delete(self, key: str) -> None:
53
+ """Delete a value from storage."""
54
+ ...
55
+
56
+
57
+ @dataclass
58
+ class FileReadResult(ReadResult):
59
+ data: str
60
+
61
+ @property
62
+ def is_ready(self) -> bool:
63
+ return True
64
+
65
+ def get_data(self) -> str:
66
+ return self.data
67
+
68
+
69
+ class FileSystemStorage:
70
+ """Storage implementation using the filesystem."""
71
+
72
+ ENCODING = "utf-8"
73
+
74
+ def __init__(self, base_dir: Path) -> None:
75
+ self._base_dir = base_dir
76
+
77
+ def _get_path(self, key: str) -> Path:
78
+ return self._base_dir / f"{key}.bin"
79
+
80
+ def read(self, key: str) -> ReadResult:
81
+ path = self._get_path(key)
82
+ if path.exists():
83
+ return FileReadResult(path.read_text(encoding=self.ENCODING))
84
+ return FileReadResult("")
85
+
86
+ def write(self, key: str, value: str) -> None:
87
+ path = self._get_path(key)
88
+ path.write_text(value, encoding=self.ENCODING)
89
+
90
+ def delete(self, key: str) -> None:
91
+ path = self._get_path(key)
92
+ path.unlink(missing_ok=True)
93
+
94
+
95
+ @dataclass
96
+ class PyodideResult(ReadResult):
97
+ task: "PyodideTask"
98
+
99
+ @property
100
+ def is_ready(self) -> bool:
101
+ return self.task.done()
102
+
103
+ def get_data(self) -> str:
104
+ return self.task.result()
105
+
106
+
107
+ class LocalStorageAdapter:
108
+ """
109
+ Storage implementation using browser IndexedDB for pyodide environments.
110
+
111
+ This adapter provides a synchronous interface to the asynchronous IndexedDB API
112
+ by managing an asyncio event loop.
113
+ """
114
+
115
+ _db_name = "neat-storage"
116
+ _store_name = "keyval"
117
+ _db_version = 1
118
+
119
+ def __init__(self) -> None:
120
+ self._loop = self._get_or_create_event_loop()
121
+
122
+ @staticmethod
123
+ def _get_or_create_event_loop() -> "asyncio.AbstractEventLoop":
124
+ """Gets the current asyncio event loop or creates a new one."""
125
+ import asyncio
126
+
127
+ try:
128
+ return asyncio.get_event_loop()
129
+ except RuntimeError:
130
+ loop = asyncio.new_event_loop()
131
+ asyncio.set_event_loop(loop)
132
+ return loop
133
+
134
+ def _execute_db_operation(self, operation: str, key: str, value: str | None = None) -> "PyodideTask":
135
+ """Executes a database operation by creating and calling a JS function."""
136
+
137
+ async def db_operation() -> str:
138
+ import js # type: ignore[import-not-found]
139
+ from pyodide.code import run_js # type: ignore[import-not-found]
140
+ from pyodide.ffi import to_js # type: ignore[import-not-found]
141
+
142
+ # Convert Python values to JS
143
+ js_key = to_js(key)
144
+ js_value = to_js(value if value is not None else "")
145
+
146
+ # Store values in js namespace temporarily to pass them safely
147
+ js._idb_key = js_key
148
+ js._idb_value = js_value
149
+
150
+ try:
151
+ # Create and execute the function directly in JavaScript context using run_js
152
+ js_code = f"""
153
+ (async () => {{
154
+ const dbName = "{self._db_name}";
155
+ const dbVersion = {self._db_version};
156
+ const storeName = "{self._store_name}";
157
+ const operation = "{operation}";
158
+
159
+ return new Promise((resolve, reject) => {{
160
+ const request = indexedDB.open(dbName, dbVersion);
161
+
162
+ request.onerror = (event) => reject(event.target.error);
163
+
164
+ request.onupgradeneeded = (event) => {{
165
+ const db = event.target.result;
166
+ if (!db.objectStoreNames.contains(storeName)) {{
167
+ db.createObjectStore(storeName);
168
+ }}
169
+ }};
170
+
171
+ request.onsuccess = (event) => {{
172
+ const db = event.target.result;
173
+ const mode = (operation === "read") ? "readonly" : "readwrite";
174
+ try {{
175
+ const transaction = db.transaction([storeName], mode);
176
+ const store = transaction.objectStore(storeName);
177
+
178
+ let storeRequest;
179
+ if (operation === "read") {{
180
+ storeRequest = store.get(_idb_key);
181
+ }} else if (operation === "write") {{
182
+ storeRequest = store.put(_idb_value, _idb_key);
183
+ }} else if (operation === "delete") {{
184
+ storeRequest = store.delete(_idb_key);
185
+ }} else {{
186
+ db.close();
187
+ return reject(new Error(`Unknown operation: ${{operation}}`));
188
+ }}
189
+
190
+ storeRequest.onsuccess = () => resolve(storeRequest.result ?? "");
191
+ storeRequest.onerror = (event) => reject(event.target.error);
192
+
193
+ transaction.oncomplete = () => db.close();
194
+ transaction.onerror = (event) => reject(event.target.error);
195
+
196
+ }} catch (error) {{
197
+ db.close();
198
+ reject(error);
199
+ }}
200
+ }};
201
+ }});
202
+ }})()
203
+ """
204
+
205
+ # Use run_js which properly returns an awaitable promise
206
+ result = await run_js(js_code)
207
+ return str(result) if result is not None else ""
208
+ finally:
209
+ # Clean up js namespace
210
+ try:
211
+ del js._idb_key
212
+ del js._idb_value
213
+ except Exception:
214
+ pass
215
+
216
+ try:
217
+ return self._loop.run_until_complete(db_operation())
218
+ except Exception:
219
+ return ""
220
+
221
+ def read(self, key: str) -> ReadResult:
222
+ return PyodideResult(self._execute_db_operation("read", key))
223
+
224
+ def write(self, key: str, value: str) -> None:
225
+ self._execute_db_operation("write", key, value)
226
+
227
+ def delete(self, key: str) -> None:
228
+ self._execute_db_operation("delete", key)
229
+
230
+
231
+ def get_storage(base_dir: Path | None = None) -> Storage:
232
+ """Get the appropriate storage implementation for the current environment."""
233
+ if IN_PYODIDE:
234
+ return LocalStorageAdapter()
235
+ else:
236
+ if base_dir is None:
237
+ import tempfile
238
+
239
+ base_dir = Path(tempfile.gettempdir()).resolve()
240
+ return FileSystemStorage(base_dir)
@@ -0,0 +1,82 @@
1
+ from collections.abc import Callable
2
+ from functools import wraps
3
+ from typing import Any, Protocol, TypeVar
4
+
5
+ from cognite.neat._session._usage_analytics._collector import Collector
6
+ from cognite.neat._store._store import NeatStore
7
+ from cognite.neat._utils.text import NEWLINE, split_on_capitals
8
+
9
+
10
+ class HasStore(Protocol):
11
+ _store: NeatStore
12
+
13
+
14
+ T_Class = TypeVar("T_Class", bound=HasStore)
15
+
16
+ _COLLECTOR = Collector()
17
+
18
+
19
+ def session_wrapper(cls: type[T_Class]) -> type[T_Class]:
20
+ # 1. Define the method decorator inside
21
+ def _handle_method_call(func: Callable[..., Any]) -> Callable[..., Any]:
22
+ """Decorator to handle exceptions and print provenance length"""
23
+
24
+ @wraps(func)
25
+ def wrapper(self: HasStore, *args: Any, **kwargs: Any) -> Any:
26
+ display_name = f"{' '.join(split_on_capitals(cls.__name__))} - {func.__name__}"
27
+ identifier = f"{cls.__name__}.{func.__name__}"
28
+ try:
29
+ res = func(self, *args, **kwargs)
30
+ if not self._store.provenance or "DataModel" not in identifier:
31
+ print(f"{display_name} ✅")
32
+ if _COLLECTOR.can_collect:
33
+ _COLLECTOR.collect("action", {"action": identifier, "success": True})
34
+ return res
35
+ change = self._store.provenance[-1]
36
+ issues_count = len(change.issues) if change.issues else 0
37
+ errors_count = len(change.errors) if change.errors else 0
38
+ total_issues = issues_count + errors_count
39
+
40
+ data_model_not_read = not change.successful and "ReadPhysicalDataModel" in identifier
41
+
42
+ print(
43
+ f"{display_name} "
44
+ f"{'✅' if change.successful else '❌'}"
45
+ f"{f' | Issues: {total_issues} (of which {errors_count} critical)' if total_issues > 0 else ''}"
46
+ f"{NEWLINE + '⚠️ Data model not read into session' if data_model_not_read else ''}"
47
+ f"{NEWLINE + '📋 For details on issues run neat.issues' if change.issues or change.errors else ''}"
48
+ f"{NEWLINE + '📊 For details on result run neat.result' if change.result else ''}"
49
+ )
50
+ if _COLLECTOR.can_collect:
51
+ event = change.as_mixpanel_event()
52
+ event["action"] = identifier
53
+ _COLLECTOR.collect("action", event)
54
+ if change.result:
55
+ event = change.result.as_mixpanel_event()
56
+ event["action"] = identifier
57
+ _COLLECTOR.collect("deployment", event)
58
+
59
+ return res
60
+
61
+ # if an error occurs, we catch it and print it out instead of
62
+ # getting a full traceback
63
+ except Exception as e:
64
+ print(f"{display_name} ❌")
65
+ print(f"{e!s}")
66
+ if _COLLECTOR.can_collect:
67
+ _COLLECTOR.collect("action", {"action": identifier, "success": False, "error_message": str(e)})
68
+
69
+ return wrapper
70
+
71
+ # Iterate through all attributes of the class
72
+ for attr_name in dir(cls):
73
+ # Skip private/protected methods (starting with _)
74
+ if not attr_name.startswith("_"):
75
+ attr = getattr(cls, attr_name)
76
+ # Only wrap callable methods
77
+ if callable(attr):
78
+ # Replace the original method with wrapped version
79
+ setattr(cls, attr_name, _handle_method_call(attr))
80
+
81
+ # Return the modified class
82
+ return cls
@@ -0,0 +1,10 @@
1
+ from ._base import State
2
+ from ._states import EmptyState, ForbiddenState, PhysicalState, Undo
3
+
4
+ __all__ = [
5
+ "EmptyState",
6
+ "ForbiddenState",
7
+ "PhysicalState",
8
+ "State",
9
+ "Undo",
10
+ ]
@@ -0,0 +1,37 @@
1
+ from abc import ABC, abstractmethod
2
+ from typing import Any
3
+
4
+
5
+ class State(ABC):
6
+ def __init__(self) -> None:
7
+ # this will be reference to the actual store in the session
8
+ # used to store data models and instances, here only as a placeholder
9
+ self._store = None
10
+
11
+ @abstractmethod
12
+ def transition(self, event: Any) -> "State":
13
+ """
14
+ Handle events that are delegated to this State.
15
+ """
16
+ raise NotImplementedError("on_event() must be implemented by the subclass.")
17
+
18
+ def can_transition(self, event: Any) -> bool:
19
+ """
20
+ Check if the state can transition on the given event.
21
+ """
22
+ # avoiding circular import
23
+ from cognite.neat._state_machine._states import ForbiddenState
24
+
25
+ return not isinstance(self.transition(event), ForbiddenState)
26
+
27
+ def __repr__(self) -> str:
28
+ """
29
+ Leverages the __str__ method to describe the State.
30
+ """
31
+ return self.__str__()
32
+
33
+ def __str__(self) -> str:
34
+ """
35
+ Returns the name of the State.
36
+ """
37
+ return self.__class__.__name__
@@ -0,0 +1,52 @@
1
+ from typing import Any
2
+
3
+ from cognite.neat._data_model.exporters import DMSExporter
4
+ from cognite.neat._data_model.importers import DMSImporter
5
+
6
+ from ._base import State
7
+
8
+
9
+ class Undo:
10
+ """
11
+ Event to trigger undoing the last action.
12
+ """
13
+
14
+ pass
15
+
16
+
17
+ class ForbiddenState(State):
18
+ """
19
+ State representing forbidden transitions - returns to previous state.
20
+ """
21
+
22
+ def __init__(self, previous_state: State):
23
+ self.previous_state = previous_state
24
+
25
+ def transition(self, event: Any) -> State:
26
+ # only "undo" to trigger going back to previous state
27
+ if isinstance(event, Undo):
28
+ return self.previous_state
29
+ return self
30
+
31
+
32
+ class EmptyState(State):
33
+ """
34
+ The initial state with empty NEAT store.
35
+ """
36
+
37
+ def transition(self, event: Any) -> State:
38
+ if isinstance(event, DMSImporter):
39
+ return PhysicalState()
40
+ return ForbiddenState(self)
41
+
42
+
43
+ class PhysicalState(State):
44
+ """
45
+ State with physical model loaded.
46
+ """
47
+
48
+ def transition(self, event: Any) -> State:
49
+ if isinstance(event, DMSExporter):
50
+ return PhysicalState()
51
+
52
+ return ForbiddenState(self)
@@ -0,0 +1,3 @@
1
+ from ._store import NeatStore
2
+
3
+ __all__ = ["NeatStore"]
@@ -0,0 +1,81 @@
1
+ import itertools
2
+ from collections import UserList
3
+ from dataclasses import dataclass, field
4
+ from datetime import datetime
5
+ from typing import Any
6
+
7
+ from cognite.neat._data_model.deployer.data_classes import DeploymentResult
8
+ from cognite.neat._issues import ConsistencyError, IssueList, ModelSyntaxError
9
+ from cognite.neat._state_machine import State
10
+
11
+
12
+ @dataclass
13
+ class Change:
14
+ agent: str
15
+ activity: str
16
+ source_state: State
17
+ start: datetime
18
+ end: datetime
19
+
20
+ target_state: State | None = field(default=None)
21
+ source_entity: str | None = field(default="ExternalEntity")
22
+ target_entity: str | None = field(default="FailedEntity")
23
+ issues: IssueList | None = field(default=None)
24
+ errors: IssueList | None = field(default=None)
25
+ # for time being setting to Any, can be refined later
26
+ result: DeploymentResult | None = field(default=None)
27
+ description: str | None = field(default=None)
28
+
29
+ @staticmethod
30
+ def standardize_activity_name(activity: str, start: datetime, end: datetime) -> str:
31
+ """Create standardized activity name"""
32
+ return f"{activity}_{start.timestamp()}-{end.timestamp()}"
33
+
34
+ @property
35
+ def successful(self) -> bool:
36
+ """Check if change was successful"""
37
+ return not self.errors
38
+
39
+ @property
40
+ def error_count(self) -> int:
41
+ """Get number of errors"""
42
+ return sum(
43
+ 1
44
+ for issue in itertools.chain(self.issues or [], self.errors or [])
45
+ if isinstance(issue, ModelSyntaxError | ConsistencyError)
46
+ )
47
+
48
+ def as_mixpanel_event(self) -> dict[str, Any]:
49
+ """Convert change to mixpanel event format"""
50
+ return {
51
+ "agent": self.agent,
52
+ "activity": self.activity,
53
+ "sourceEntity": self.source_entity,
54
+ "targetEntity": self.target_entity,
55
+ "sourceState": type(self.source_state).__name__,
56
+ "targetState": type(self.target_state).__name__ if self.target_state else "None",
57
+ "duration_ms": int((self.end - self.start).total_seconds() * 1000),
58
+ "successful": self.successful,
59
+ "issues": [issue.code or "<no code>" for issue in self.issues] if self.issues else [],
60
+ "errors": [error.code or "<no code>" for error in self.errors] if self.errors else [],
61
+ }
62
+
63
+
64
+ class Provenance(UserList[Change]):
65
+ def __delitem__(self, *args: Any, **kwargs: Any) -> None:
66
+ raise TypeError("Cannot delete change from provenance")
67
+
68
+ def __setitem__(self, *args: Any, **kwargs: Any) -> None:
69
+ raise TypeError("Cannot modify change from provenance")
70
+
71
+ @property
72
+ def last_change(self) -> Change | None:
73
+ return self[-1] if len(self) > 0 else None
74
+
75
+ def can_agent_do_activity(self, activity: Any) -> bool:
76
+ "Check if activity can be performed based on provenance"
77
+ raise NotImplementedError("Not implemented yet")
78
+
79
+ def provenance_without_failures(self) -> "Provenance":
80
+ """This method removes all the failed changes from the provenance list."""
81
+ raise NotImplementedError("Not implemented yet")