simstack 2.0.1__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 (354) hide show
  1. simstack/__init__.py +0 -0
  2. simstack/_version.py +24 -0
  3. simstack/core/__init__.py +0 -0
  4. simstack/core/artifacts.py +406 -0
  5. simstack/core/asnyc_helper.py +14 -0
  6. simstack/core/config_file.py +60 -0
  7. simstack/core/context.py +283 -0
  8. simstack/core/definitions.py +22 -0
  9. simstack/core/engine.py +115 -0
  10. simstack/core/find_simstack_modules.py +41 -0
  11. simstack/core/hash.py +179 -0
  12. simstack/core/node.py +1137 -0
  13. simstack/core/node_claim.py +31 -0
  14. simstack/core/node_runner.py +362 -0
  15. simstack/core/process_results.py +102 -0
  16. simstack/core/recompute_artifacts.py +117 -0
  17. simstack/core/reset_db.py +12 -0
  18. simstack/core/resource_assignment.py +222 -0
  19. simstack/core/resources.py +90 -0
  20. simstack/core/route_table.py +24 -0
  21. simstack/core/run_docker.py +109 -0
  22. simstack/core/run_node.py +71 -0
  23. simstack/core/runner.py +169 -0
  24. simstack/core/services/__init__.py +11 -0
  25. simstack/core/services/base_service.py +191 -0
  26. simstack/core/services/file_transfer_service.py +117 -0
  27. simstack/core/services/git_restart_service.py +12 -0
  28. simstack/core/services/git_uv_update_service.py +105 -0
  29. simstack/core/services/node_execution_service.py +182 -0
  30. simstack/core/services/resource_branch_monitor_service.py +71 -0
  31. simstack/core/services/runner_cleanup_service.py +149 -0
  32. simstack/core/services/runner_manager.py +163 -0
  33. simstack/core/services/runner_status_service.py +10 -0
  34. simstack/core/services/slurm_status_service.py +67 -0
  35. simstack/core/services/stop_check_service.py +27 -0
  36. simstack/core/services/timeout_restart_service.py +26 -0
  37. simstack/core/simstack_result.py +39 -0
  38. simstack/core/submit_node.py +217 -0
  39. simstack/core/task_id.py +22 -0
  40. simstack/methods/__init__.py +0 -0
  41. simstack/methods/archive_files.py +292 -0
  42. simstack/methods/fire_and_forget_runner.py +113 -0
  43. simstack/methods/generate_test.py +125 -0
  44. simstack/methods/get_file.py +26 -0
  45. simstack/methods/mass_runner.py +154 -0
  46. simstack/methods/multiple_executor.py +307 -0
  47. simstack/methods/switch_git.py +16 -0
  48. simstack/methods/upload_helpers.py +81 -0
  49. simstack/models/__init__.py +74 -0
  50. simstack/models/array_list.py +21 -0
  51. simstack/models/array_storage.py +83 -0
  52. simstack/models/artifact_models.py +127 -0
  53. simstack/models/base_lists.py +51 -0
  54. simstack/models/base_types.py +209 -0
  55. simstack/models/charts_artifact.py +690 -0
  56. simstack/models/dataset.py +451 -0
  57. simstack/models/dataset_metadata.py +390 -0
  58. simstack/models/datasettuple.py +587 -0
  59. simstack/models/file_instance.py +150 -0
  60. simstack/models/file_list.py +45 -0
  61. simstack/models/files.py +424 -0
  62. simstack/models/fire_and_forget_result.py +19 -0
  63. simstack/models/images2d.py +113 -0
  64. simstack/models/log_entry_model.py +37 -0
  65. simstack/models/models.py +52 -0
  66. simstack/models/named_data_reference.py +36 -0
  67. simstack/models/node_registry.py +96 -0
  68. simstack/models/pandas_model.py +239 -0
  69. simstack/models/parameters.py +402 -0
  70. simstack/models/pickle_models.py +300 -0
  71. simstack/models/project.py +13 -0
  72. simstack/models/resource_assignment.py +170 -0
  73. simstack/models/resource_definition.py +125 -0
  74. simstack/models/runner_model.py +39 -0
  75. simstack/models/simple_table.py +79 -0
  76. simstack/models/simstack_model.py +97 -0
  77. simstack/models/slurm_info.py +17 -0
  78. simstack/models/table_artifact.py +87 -0
  79. simstack/models/tag.py +12 -0
  80. simstack/models/unused_test_file.py +5 -0
  81. simstack/simstack_marker.com +1 -0
  82. simstack/static/docs/.buildinfo +4 -0
  83. simstack/static/docs/_images/GUI Concept.webp +0 -0
  84. simstack/static/docs/_modules/index.html +434 -0
  85. simstack/static/docs/_modules/simstack/core/artifacts.html +743 -0
  86. simstack/static/docs/_modules/simstack/core/asnyc_helper.html +330 -0
  87. simstack/static/docs/_modules/simstack/core/config_file.html +379 -0
  88. simstack/static/docs/_modules/simstack/core/definitions.html +341 -0
  89. simstack/static/docs/_modules/simstack/core/engine.html +437 -0
  90. simstack/static/docs/_modules/simstack/core/find_simstack_modules.html +360 -0
  91. simstack/static/docs/_modules/simstack/core/hash.html +528 -0
  92. simstack/static/docs/_modules/simstack/core/node.html +1392 -0
  93. simstack/static/docs/_modules/simstack/core/node_claim.html +347 -0
  94. simstack/static/docs/_modules/simstack/core/node_runner.html +711 -0
  95. simstack/static/docs/_modules/simstack/core/process_results.html +418 -0
  96. simstack/static/docs/_modules/simstack/core/recompute_artifacts.html +433 -0
  97. simstack/static/docs/_modules/simstack/core/reset_db.html +328 -0
  98. simstack/static/docs/_modules/simstack/core/resource_assignment.html +553 -0
  99. simstack/static/docs/_modules/simstack/core/resources.html +424 -0
  100. simstack/static/docs/_modules/simstack/core/route_table.html +349 -0
  101. simstack/static/docs/_modules/simstack/core/run_docker.html +425 -0
  102. simstack/static/docs/_modules/simstack/core/run_node.html +390 -0
  103. simstack/static/docs/_modules/simstack/core/runner.html +491 -0
  104. simstack/static/docs/_modules/simstack/core/services/base_service.html +522 -0
  105. simstack/static/docs/_modules/simstack/core/services/file_transfer_service.html +436 -0
  106. simstack/static/docs/_modules/simstack/core/services/git_restart_service.html +331 -0
  107. simstack/static/docs/_modules/simstack/core/services/git_uv_update_service.html +427 -0
  108. simstack/static/docs/_modules/simstack/core/services/node_execution_service.html +507 -0
  109. simstack/static/docs/_modules/simstack/core/services/resource_branch_monitor_service.html +390 -0
  110. simstack/static/docs/_modules/simstack/core/services/runner_cleanup_service.html +468 -0
  111. simstack/static/docs/_modules/simstack/core/services/runner_manager.html +485 -0
  112. simstack/static/docs/_modules/simstack/core/services/runner_status_service.html +329 -0
  113. simstack/static/docs/_modules/simstack/core/services/slurm_status_service.html +386 -0
  114. simstack/static/docs/_modules/simstack/core/services/stop_check_service.html +346 -0
  115. simstack/static/docs/_modules/simstack/core/services/timeout_restart_service.html +345 -0
  116. simstack/static/docs/_modules/simstack/core/simstack_result.html +355 -0
  117. simstack/static/docs/_modules/simstack/core/submit_node.html +536 -0
  118. simstack/static/docs/_modules/simstack/core/task_id.html +344 -0
  119. simstack/static/docs/_modules/simstack/methods/archive_files.html +598 -0
  120. simstack/static/docs/_modules/simstack/methods/fire_and_forget_runner.html +432 -0
  121. simstack/static/docs/_modules/simstack/methods/generate_test.html +443 -0
  122. simstack/static/docs/_modules/simstack/methods/get_file.html +342 -0
  123. simstack/static/docs/_modules/simstack/methods/mass_runner.html +476 -0
  124. simstack/static/docs/_modules/simstack/methods/multiple_executor.html +674 -0
  125. simstack/static/docs/_modules/simstack/methods/switch_git.html +332 -0
  126. simstack/static/docs/_modules/simstack/methods/upload_helpers.html +403 -0
  127. simstack/static/docs/_modules/simstack/models/array_list.html +337 -0
  128. simstack/static/docs/_modules/simstack/models/array_storage.html +417 -0
  129. simstack/static/docs/_modules/simstack/models/base_lists.html +374 -0
  130. simstack/static/docs/_modules/simstack/models/base_types.html +588 -0
  131. simstack/static/docs/_modules/simstack/models/charts_artifact.html +1035 -0
  132. simstack/static/docs/_modules/simstack/models/dataset.html +866 -0
  133. simstack/static/docs/_modules/simstack/models/dataset_metadata.html +711 -0
  134. simstack/static/docs/_modules/simstack/models/datasettuple.html +1005 -0
  135. simstack/static/docs/_modules/simstack/models/file_instance.html +472 -0
  136. simstack/static/docs/_modules/simstack/models/file_list.html +367 -0
  137. simstack/static/docs/_modules/simstack/models/files.html +773 -0
  138. simstack/static/docs/_modules/simstack/models/fire_and_forget_result.html +335 -0
  139. simstack/static/docs/_modules/simstack/models/images2d.html +447 -0
  140. simstack/static/docs/_modules/simstack/models/log_entry_model.html +356 -0
  141. simstack/static/docs/_modules/simstack/models/models.html +374 -0
  142. simstack/static/docs/_modules/simstack/models/named_data_reference.html +355 -0
  143. simstack/static/docs/_modules/simstack/models/node_registry.html +415 -0
  144. simstack/static/docs/_modules/simstack/models/pandas_model.html +570 -0
  145. simstack/static/docs/_modules/simstack/models/parameters.html +739 -0
  146. simstack/static/docs/_modules/simstack/models/pickle_models.html +631 -0
  147. simstack/static/docs/_modules/simstack/models/project.html +329 -0
  148. simstack/static/docs/_modules/simstack/models/resource_assignment.html +501 -0
  149. simstack/static/docs/_modules/simstack/models/resource_definition.html +477 -0
  150. simstack/static/docs/_modules/simstack/models/runner_model.html +361 -0
  151. simstack/static/docs/_modules/simstack/models/simple_table.html +413 -0
  152. simstack/static/docs/_modules/simstack/models/simstack_model.html +416 -0
  153. simstack/static/docs/_modules/simstack/models/slurm_info.html +333 -0
  154. simstack/static/docs/_modules/simstack/models/table_artifact.html +406 -0
  155. simstack/static/docs/_modules/simstack/models/tag.html +328 -0
  156. simstack/static/docs/_modules/simstack/tables/model_table.html +519 -0
  157. simstack/static/docs/_modules/simstack/tables/node_children.html +538 -0
  158. simstack/static/docs/_modules/simstack/tables/node_table.html +854 -0
  159. simstack/static/docs/_modules/simstack/tables/table_builder.html +642 -0
  160. simstack/static/docs/_modules/simstack/util/async_zip_utils.html +337 -0
  161. simstack/static/docs/_modules/simstack/util/b64mixin.html +339 -0
  162. simstack/static/docs/_modules/simstack/util/cleaned_json_schema.html +336 -0
  163. simstack/static/docs/_modules/simstack/util/config_reader.html +498 -0
  164. simstack/static/docs/_modules/simstack/util/custom_model_dump.html +391 -0
  165. simstack/static/docs/_modules/simstack/util/database_information.html +488 -0
  166. simstack/static/docs/_modules/simstack/util/db.html +1392 -0
  167. simstack/static/docs/_modules/simstack/util/db_logger.html +448 -0
  168. simstack/static/docs/_modules/simstack/util/default_from_dict.html +376 -0
  169. simstack/static/docs/_modules/simstack/util/directory_iterator.html +553 -0
  170. simstack/static/docs/_modules/simstack/util/docstring_parser.html +526 -0
  171. simstack/static/docs/_modules/simstack/util/file_hashing.html +394 -0
  172. simstack/static/docs/_modules/simstack/util/file_transfer_client.html +791 -0
  173. simstack/static/docs/_modules/simstack/util/files.html +733 -0
  174. simstack/static/docs/_modules/simstack/util/find_python_files.html +349 -0
  175. simstack/static/docs/_modules/simstack/util/generate_ui_schema.html +396 -0
  176. simstack/static/docs/_modules/simstack/util/generic_list_mixin.html +487 -0
  177. simstack/static/docs/_modules/simstack/util/get_module_path.html +336 -0
  178. simstack/static/docs/_modules/simstack/util/git_pull.html +381 -0
  179. simstack/static/docs/_modules/simstack/util/git_repository_status.html +411 -0
  180. simstack/static/docs/_modules/simstack/util/import_module.html +377 -0
  181. simstack/static/docs/_modules/simstack/util/importer.html +637 -0
  182. simstack/static/docs/_modules/simstack/util/init_data_source.html +357 -0
  183. simstack/static/docs/_modules/simstack/util/make_graph.html +423 -0
  184. simstack/static/docs/_modules/simstack/util/make_table.html +1171 -0
  185. simstack/static/docs/_modules/simstack/util/mappings.html +378 -0
  186. simstack/static/docs/_modules/simstack/util/minimal_route_finder.html +388 -0
  187. simstack/static/docs/_modules/simstack/util/mock_db.html +472 -0
  188. simstack/static/docs/_modules/simstack/util/module_path_checker.html +354 -0
  189. simstack/static/docs/_modules/simstack/util/object_list_mixin.html +649 -0
  190. simstack/static/docs/_modules/simstack/util/path_manager.html +566 -0
  191. simstack/static/docs/_modules/simstack/util/project_root_finder.html +357 -0
  192. simstack/static/docs/_modules/simstack/util/queue_watcher.html +593 -0
  193. simstack/static/docs/_modules/simstack/util/resource_config.html +532 -0
  194. simstack/static/docs/_modules/simstack/util/route_finder.html +390 -0
  195. simstack/static/docs/_modules/simstack/util/runner_util.html +487 -0
  196. simstack/static/docs/_modules/simstack/util/runner_utils.html +468 -0
  197. simstack/static/docs/_modules/simstack/util/safe_code_executor.html +510 -0
  198. simstack/static/docs/_modules/simstack/util/sample_config.html +414 -0
  199. simstack/static/docs/_modules/simstack/util/sensitive_content_filter.html +364 -0
  200. simstack/static/docs/_modules/simstack/util/setup_logging.html +365 -0
  201. simstack/static/docs/_modules/simstack/util/submit_to_watchdog.html +425 -0
  202. simstack/static/docs/_modules/simstack/util/toml_reader.html +482 -0
  203. simstack/static/docs/_modules/simstack/util/transform_file_name.html +357 -0
  204. simstack/static/docs/_modules/simstack/util/ui_tools.html +541 -0
  205. simstack/static/docs/_sources/architecture.rst.txt +25 -0
  206. simstack/static/docs/_sources/array_lists.rst.txt +60 -0
  207. simstack/static/docs/_sources/ci_pipeline.rst.txt +34 -0
  208. simstack/static/docs/_sources/data.rst.txt +175 -0
  209. simstack/static/docs/_sources/database_migrations.rst.txt +310 -0
  210. simstack/static/docs/_sources/datasets.rst.txt +93 -0
  211. simstack/static/docs/_sources/developer_guide.rst.txt +25 -0
  212. simstack/static/docs/_sources/docker_nodes.rst.txt +149 -0
  213. simstack/static/docs/_sources/files.rst.txt +159 -0
  214. simstack/static/docs/_sources/fire_and_forget_runner.rst.txt +67 -0
  215. simstack/static/docs/_sources/index.rst.txt +25 -0
  216. simstack/static/docs/_sources/installation.rst.txt +213 -0
  217. simstack/static/docs/_sources/lists.rst.txt +111 -0
  218. simstack/static/docs/_sources/mass_runner.rst.txt +91 -0
  219. simstack/static/docs/_sources/misc.rst.txt +106 -0
  220. simstack/static/docs/_sources/overview.rst.txt +91 -0
  221. simstack/static/docs/_sources/recovery.rst.txt +38 -0
  222. simstack/static/docs/_sources/resource_config.rst.txt +141 -0
  223. simstack/static/docs/_sources/simstack/modules/modules.rst.txt +7 -0
  224. simstack/static/docs/_sources/simstack/modules/simstack.core.rst.txt +205 -0
  225. simstack/static/docs/_sources/simstack/modules/simstack.core.services.rst.txt +109 -0
  226. simstack/static/docs/_sources/simstack/modules/simstack.methods.rst.txt +77 -0
  227. simstack/static/docs/_sources/simstack/modules/simstack.models.rst.txt +261 -0
  228. simstack/static/docs/_sources/simstack/modules/simstack.rst.txt +22 -0
  229. simstack/static/docs/_sources/simstack/modules/simstack.tables.rst.txt +45 -0
  230. simstack/static/docs/_sources/simstack/modules/simstack.util.rst.txt +381 -0
  231. simstack/static/docs/_sources/submit.rst.txt +141 -0
  232. simstack/static/docs/_sources/tables.rst.txt +50 -0
  233. simstack/static/docs/_sources/testing_complex_nodes.rst.txt +92 -0
  234. simstack/static/docs/_sources/ui_documentation.rst.txt +272 -0
  235. simstack/static/docs/_sources/workflows.rst.txt +446 -0
  236. simstack/static/docs/_static/base-stemmer.js +476 -0
  237. simstack/static/docs/_static/basic.css +906 -0
  238. simstack/static/docs/_static/cobalt2-theme.css +1 -0
  239. simstack/static/docs/_static/debug.css +69 -0
  240. simstack/static/docs/_static/docs-build-badge.css +51 -0
  241. simstack/static/docs/_static/docs-build-badge.js +51 -0
  242. simstack/static/docs/_static/doctools.js +150 -0
  243. simstack/static/docs/_static/documentation_options.js +13 -0
  244. simstack/static/docs/_static/english-stemmer.js +1066 -0
  245. simstack/static/docs/_static/file.png +0 -0
  246. simstack/static/docs/_static/language_data.js +13 -0
  247. simstack/static/docs/_static/minus.png +0 -0
  248. simstack/static/docs/_static/plus.png +0 -0
  249. simstack/static/docs/_static/pygments.css +250 -0
  250. simstack/static/docs/_static/scripts/furo-extensions.js +0 -0
  251. simstack/static/docs/_static/scripts/furo.js +3 -0
  252. simstack/static/docs/_static/scripts/furo.js.LICENSE.txt +7 -0
  253. simstack/static/docs/_static/scripts/furo.js.map +1 -0
  254. simstack/static/docs/_static/searchtools.js +693 -0
  255. simstack/static/docs/_static/skeleton.css +296 -0
  256. simstack/static/docs/_static/sphinx_highlight.js +159 -0
  257. simstack/static/docs/_static/styles/furo-extensions.css +2 -0
  258. simstack/static/docs/_static/styles/furo-extensions.css.map +1 -0
  259. simstack/static/docs/_static/styles/furo.css +2 -0
  260. simstack/static/docs/_static/styles/furo.css.map +1 -0
  261. simstack/static/docs/architecture.html +340 -0
  262. simstack/static/docs/array_lists.html +678 -0
  263. simstack/static/docs/ci_pipeline.html +385 -0
  264. simstack/static/docs/data.html +545 -0
  265. simstack/static/docs/database_migrations.html +652 -0
  266. simstack/static/docs/datasets.html +1268 -0
  267. simstack/static/docs/developer_guide.html +359 -0
  268. simstack/static/docs/docker_nodes.html +503 -0
  269. simstack/static/docs/files.html +718 -0
  270. simstack/static/docs/fire_and_forget_runner.html +641 -0
  271. simstack/static/docs/genindex.html +6390 -0
  272. simstack/static/docs/index.html +516 -0
  273. simstack/static/docs/installation.html +553 -0
  274. simstack/static/docs/lists.html +478 -0
  275. simstack/static/docs/mass_runner.html +481 -0
  276. simstack/static/docs/misc.html +454 -0
  277. simstack/static/docs/objects.inv +0 -0
  278. simstack/static/docs/overview.html +469 -0
  279. simstack/static/docs/py-modindex.html +1111 -0
  280. simstack/static/docs/recovery.html +371 -0
  281. simstack/static/docs/resource_config.html +590 -0
  282. simstack/static/docs/search.html +327 -0
  283. simstack/static/docs/searchindex.js +1 -0
  284. simstack/static/docs/simstack/modules/modules.html +489 -0
  285. simstack/static/docs/simstack/modules/simstack.core.html +1883 -0
  286. simstack/static/docs/simstack/modules/simstack.core.services.html +698 -0
  287. simstack/static/docs/simstack/modules/simstack.html +3060 -0
  288. simstack/static/docs/simstack/modules/simstack.methods.html +1150 -0
  289. simstack/static/docs/simstack/modules/simstack.models.html +13113 -0
  290. simstack/static/docs/simstack/modules/simstack.tables.html +635 -0
  291. simstack/static/docs/simstack/modules/simstack.util.html +3320 -0
  292. simstack/static/docs/submit.html +487 -0
  293. simstack/static/docs/tables.html +539 -0
  294. simstack/static/docs/testing_complex_nodes.html +458 -0
  295. simstack/static/docs/ui_documentation.html +788 -0
  296. simstack/static/docs/workflows.html +766 -0
  297. simstack/tables/__init__.py +0 -0
  298. simstack/tables/model_table.py +191 -0
  299. simstack/tables/node_children.py +219 -0
  300. simstack/tables/node_table.py +517 -0
  301. simstack/tables/table_builder.py +314 -0
  302. simstack/util/__init__.py +0 -0
  303. simstack/util/async_zip_utils.py +18 -0
  304. simstack/util/b64mixin.py +23 -0
  305. simstack/util/cleaned_json_schema.py +20 -0
  306. simstack/util/config_reader.py +179 -0
  307. simstack/util/custom_model_dump.py +75 -0
  308. simstack/util/database_information.py +160 -0
  309. simstack/util/db.py +1013 -0
  310. simstack/util/db_logger.py +117 -0
  311. simstack/util/default_from_dict.py +57 -0
  312. simstack/util/directory_iterator.py +216 -0
  313. simstack/util/docstring_parser.py +186 -0
  314. simstack/util/file_hashing.py +72 -0
  315. simstack/util/file_transfer_client.py +427 -0
  316. simstack/util/files.py +396 -0
  317. simstack/util/find_python_files.py +33 -0
  318. simstack/util/generate_ui_schema.py +80 -0
  319. simstack/util/generic_list_mixin.py +120 -0
  320. simstack/util/geometry_utils.py +118 -0
  321. simstack/util/get_module_path.py +20 -0
  322. simstack/util/git_pull.py +65 -0
  323. simstack/util/git_repository_status.py +95 -0
  324. simstack/util/import_module.py +63 -0
  325. simstack/util/importer.py +312 -0
  326. simstack/util/init_data_source.py +41 -0
  327. simstack/util/make_graph.py +107 -0
  328. simstack/util/make_table.py +837 -0
  329. simstack/util/mappings.py +41 -0
  330. simstack/util/minimal_route_finder.py +72 -0
  331. simstack/util/mock_db.py +144 -0
  332. simstack/util/module_path_checker.py +38 -0
  333. simstack/util/object_list_mixin.py +282 -0
  334. simstack/util/path_manager.py +223 -0
  335. simstack/util/project_root_finder.py +41 -0
  336. simstack/util/queue_watcher.py +262 -0
  337. simstack/util/resource_config.py +198 -0
  338. simstack/util/route_finder.py +74 -0
  339. simstack/util/runner_util.py +166 -0
  340. simstack/util/runner_utils.py +143 -0
  341. simstack/util/safe_code_executor.py +194 -0
  342. simstack/util/sample_config.py +98 -0
  343. simstack/util/sensitive_content_filter.py +48 -0
  344. simstack/util/setup_logging.py +49 -0
  345. simstack/util/submit_to_watchdog.py +106 -0
  346. simstack/util/toml_reader.py +142 -0
  347. simstack/util/transform_file_name.py +41 -0
  348. simstack/util/ui_tools.py +213 -0
  349. simstack-2.0.1.dist-info/METADATA +454 -0
  350. simstack-2.0.1.dist-info/RECORD +354 -0
  351. simstack-2.0.1.dist-info/WHEEL +5 -0
  352. simstack-2.0.1.dist-info/entry_points.txt +12 -0
  353. simstack-2.0.1.dist-info/licenses/LICENSE +21 -0
  354. simstack-2.0.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,390 @@
1
+ from datetime import datetime
2
+ from typing import Dict, Any, Union, List
3
+
4
+ from odmantic import EmbeddedModel, Field, Model
5
+
6
+ from simstack.core.asnyc_helper import async_helper
7
+ from simstack.models import simstack_model
8
+
9
+
10
+ def _get_json_schema(data: Dict) -> dict:
11
+ """
12
+ Inspects the current data dict and returns a JSON schema for that dict.
13
+
14
+ Returns:
15
+ dict: JSON schema describing the structure and types of the current data dict
16
+ """
17
+ if not data:
18
+ return {"type": "object", "properties": {}, "additionalProperties": False}
19
+
20
+ properties = {}
21
+
22
+ for key, value in data.items():
23
+ if isinstance(value, str):
24
+ properties[key] = {"type": "string"}
25
+ elif isinstance(value, bool):
26
+ # Check bool before int/float since bool is a subclass of int in Python
27
+ properties[key] = {"type": "boolean"}
28
+ elif isinstance(value, int):
29
+ properties[key] = {"type": "integer"}
30
+ elif isinstance(value, float):
31
+ properties[key] = {"type": "number"}
32
+ elif isinstance(value, datetime):
33
+ properties[key] = {"type": "string", "format": "date-time"}
34
+ else:
35
+ # Fallback for any unexpected types
36
+ properties[key] = {"type": "string"}
37
+
38
+ schema = {"type": "object", "properties": properties, "additionalProperties": False}
39
+
40
+ return schema
41
+
42
+
43
+ class DataSetMetadataTemplate(Model):
44
+ dataset_type: str
45
+ model_json: dict[str, Any]
46
+ structure: Dict[str, Dict[str, str]] = Field(default_factory=dict)
47
+
48
+
49
+ @simstack_model
50
+ class DataSetMetadata(EmbeddedModel):
51
+ field_name: str = Field(unique=True)
52
+ data: Dict[str, Union[str, int, float, bool, datetime]] = Field(
53
+ default_factory=dict
54
+ )
55
+ is_validated: bool = False
56
+ structure: Dict[str, Dict[str, str]] = Field(default_factory=dict)
57
+
58
+ def get_json_schema(self):
59
+ return _get_json_schema(self.data)
60
+
61
+ async def validate_dict(self, new_structure: Dict[str, Dict[str, str]], dataset_tuple: bool = False) -> bool:
62
+ """
63
+ Asynchronously validates and updates the structure of a dataset metadata template based on a provided dictionary.
64
+
65
+ This function ensures that the structure of the provided `new_structure` aligns
66
+ with the pre-existing metadata template stored in the database. If the metadata
67
+ template does not exist, it initializes a new one. Schema properties and section
68
+ contents are rigorously compared for consistency, and any mismatches result in
69
+ an exception being raised. The function also handles updates to the template when
70
+ new sections or keys are introduced.
71
+
72
+ Parameters:
73
+ new_structure (Dict[str, Dict[str, str]]): A dictionary describing the sections
74
+ and their respective key-type mappings for the new dataset structure.
75
+ dataset_tuple (bool, optional): A flag indicating whether the provided structure
76
+ should be treated as a tuple. Defaults to False.
77
+
78
+ Returns:
79
+ bool: True if the structure is successfully validated or updated.
80
+
81
+ Raises:
82
+ ValueError: If there is a mismatch between the existing and provided schema
83
+ properties or section content.
84
+ """
85
+ from simstack.core.context import context
86
+ reference_metadata = await context.db.find_one(
87
+ DataSetMetadataTemplate,
88
+ DataSetMetadataTemplate.dataset_type == self.field_name,
89
+ )
90
+ # remove empty sections without mutating the dict during iteration
91
+ new_structure = {
92
+ key: value for key, value in new_structure.items() if value
93
+ }
94
+
95
+ if reference_metadata is None:
96
+ metadata_template = DataSetMetadataTemplate(
97
+ dataset_type=self.field_name,
98
+ model_json=_get_json_schema(self.data),
99
+ structure=new_structure,
100
+ )
101
+ await context.db.save(metadata_template)
102
+ self.structure = new_structure
103
+ return True # first model of this type
104
+
105
+ new_data_json = _get_json_schema(self.data)
106
+
107
+ # Compare schemas element by element
108
+ # this section compares whether the data in the metadata is the same
109
+ ref_props = reference_metadata.model_json.get("properties", {})
110
+ new_props = new_data_json.get("properties", {})
111
+
112
+ # Check if property keys match
113
+ if set(ref_props.keys()) != set(new_props.keys()):
114
+ raise ValueError(
115
+ f"Data schema properties mismatch. Reference keys: {set(ref_props.keys())}, Current keys: {set(new_props.keys())}"
116
+ )
117
+
118
+ # Check each property type, allowing string format differences
119
+ for key in ref_props.keys():
120
+ ref_prop = ref_props[key]
121
+ new_prop = new_props[key]
122
+
123
+ ref_type = ref_prop.get("type")
124
+ new_type = new_prop.get("type")
125
+
126
+ if ref_type != new_type:
127
+ raise ValueError(
128
+ f"Property '{key}' type mismatch. Reference: {ref_type}, Current: {new_type}"
129
+ )
130
+
131
+ # For string types, allow the format field to differ or be missing
132
+ if ref_type == "string":
133
+ # Compare all fields except 'format'
134
+ ref_without_format = {k: v for k, v in ref_prop.items() if k != "format"}
135
+ new_without_format = {k: v for k, v in new_prop.items() if k != "format"}
136
+ if ref_without_format != new_without_format:
137
+ raise ValueError(
138
+ f"Property '{key}' schema mismatch (excluding format). Reference: {ref_without_format}, Current: {new_without_format}"
139
+ )
140
+ else:
141
+ # For non-string types, require exact match
142
+ if ref_prop != new_prop:
143
+ raise ValueError(
144
+ f"Property '{key}' schema mismatch. Reference: {ref_prop}, Current: {new_prop}"
145
+ )
146
+
147
+ # Check if model types in existing sections match, and add new keys
148
+ save_template = False
149
+ updated_structure = reference_metadata.structure.copy()
150
+
151
+ for section_name, new_section_content in new_structure.items():
152
+ if section_name in updated_structure:
153
+ ref_section_content = updated_structure[section_name]
154
+ # Check if all elements in new_section_content match ref_section_content exactly
155
+
156
+ if dataset_tuple: # for tuples the sections must be exactly the same
157
+ if ref_section_content != new_section_content:
158
+ raise ValueError(
159
+ f"Section '{section_name}' content mismatch. Reference: {ref_section_content}, Current: {new_section_content}"
160
+ )
161
+ continue
162
+ else:
163
+ ref_keys = set(ref_section_content.keys())
164
+ new_keys = set(new_section_content.keys())
165
+
166
+ extra_keys = new_keys - ref_keys
167
+
168
+ # Check for type mismatches in common keys
169
+ common_keys = ref_keys.intersection(new_keys)
170
+ for key in common_keys:
171
+ if ref_section_content[key] != new_section_content[key]:
172
+ error_msg = f"MetaData Validation failed: Section '{section_name}' has different value for key '{key}'.\n"
173
+ error_msg += f"Reference: {ref_section_content[key]}, Current: {new_section_content[key]}\n"
174
+ error_msg += f"Reference keys: {ref_keys}\n"
175
+ error_msg += f"Current keys: {new_keys}"
176
+ raise ValueError(error_msg)
177
+
178
+ if len(extra_keys) > 0:
179
+ for key in extra_keys:
180
+ updated_structure[section_name][key] = new_section_content[key]
181
+ save_template = True
182
+ else:
183
+ # Completely new section
184
+ updated_structure[section_name] = new_section_content
185
+ save_template = True
186
+
187
+ if save_template:
188
+ reference_metadata.structure = updated_structure
189
+ await context.db.save(reference_metadata)
190
+ self.structure = updated_structure
191
+ return True
192
+
193
+ @async_helper
194
+ async def freeze(self, new_structure: Dict[str, Dict[str, str]]) -> bool:
195
+ from simstack.core.context import context
196
+ db = context.db
197
+ reference_metadata = await db.find_one(
198
+ DataSetMetadataTemplate,
199
+ DataSetMetadataTemplate.dataset_type == self.field_name,
200
+ )
201
+ if not reference_metadata:
202
+ raise ValueError("Metadata does not exist")
203
+ if self.structure != reference_metadata.structure:
204
+ raise ValueError("Metadata structure has changed in the database")
205
+ if self.structure == {}:
206
+ self.structure = new_structure
207
+ reference_metadata.structure = new_structure
208
+ await db.save(reference_metadata)
209
+ # some structure exists already
210
+ return new_structure == self.structure
211
+
212
+ @property
213
+ def initialized(self) -> bool:
214
+ """Check if the model has been fully constructed."""
215
+ # A simple heuristic: if we have an ID or if type is set, we're initialized
216
+ return hasattr(self, "dataset_type") and self.field_name is not None
217
+
218
+ # Dict-like behavior methods
219
+ def __getitem__(self, key: str):
220
+ """Get item from data dict."""
221
+ return self.data[key]
222
+
223
+ def __setitem__(self, key: str, value: Union[str, int, float, bool, datetime]):
224
+ """Set item in data dict with validation."""
225
+ # Validate value type
226
+ if not isinstance(value, (str, int, float, bool, datetime)):
227
+ raise TypeError(
228
+ f"Value must be str, int, float, bool, or datetime, got {type(value).__name__}"
229
+ )
230
+
231
+ # Only check for structural changes after initialization
232
+ if self.initialized and key not in self.data:
233
+ raise KeyError(
234
+ f"Cannot add new key '{key}' after initialization. Existing keys: {list(self.data.keys())}"
235
+ )
236
+
237
+ # Only check for type changes after initialization
238
+ if self.initialized and key in self.data:
239
+ existing_value = self.data[key]
240
+ if type(existing_value) != type(value):
241
+ raise TypeError(
242
+ f"Cannot change type of key '{key}' from {type(existing_value).__name__} "
243
+ f"to {type(value).__name__}"
244
+ )
245
+
246
+ self.data[key] = value
247
+
248
+ def __delitem__(self, key: str):
249
+ """Delete item from data dict."""
250
+ if self.initialized:
251
+ raise KeyError(f"Cannot delete key '{key}' after initialization")
252
+ del self.data[key]
253
+
254
+ def __contains__(self, key: str) -> bool:
255
+ """Check if key exists in data dict."""
256
+ return key in self.data
257
+
258
+ def __iter__(self):
259
+ """Iterate over keys in data dict."""
260
+ return iter(self.data)
261
+
262
+ def __len__(self) -> int:
263
+ """Get number of items in data dict."""
264
+ return len(self.data)
265
+
266
+ def keys(self):
267
+ """Get keys from data dict."""
268
+ return self.data.keys()
269
+
270
+ def values(self):
271
+ """Get values from data dict."""
272
+ return self.data.values()
273
+
274
+ def items(self):
275
+ """Get items from data dict."""
276
+ return self.data.items()
277
+
278
+ def get(self, key: str, default=None):
279
+ """Get item from data dict with default."""
280
+ return self.data.get(key, default)
281
+
282
+ def pop(self, key: str, *args):
283
+ """Pop item from data dict."""
284
+ if self.initialized:
285
+ raise KeyError(f"Cannot pop key '{key}' after initialization")
286
+ return self.data.pop(key, *args)
287
+
288
+ def popitem(self):
289
+ """Pop item from data dict."""
290
+ if self.initialized:
291
+ raise KeyError("Cannot pop items after initialization")
292
+ return self.data.popitem()
293
+
294
+ def clear(self):
295
+ """Clear data dict."""
296
+ if self.initialized:
297
+ raise RuntimeError("Cannot clear data after initialization")
298
+ self.data.clear()
299
+
300
+ def update(self, *args, **kwargs):
301
+ """Update data dict with validation."""
302
+ # Handle different update signatures
303
+ if args:
304
+ other = args[0]
305
+ if hasattr(other, "items"):
306
+ items_to_update = other.items()
307
+ else:
308
+ items_to_update = other
309
+ else:
310
+ items_to_update = []
311
+
312
+ # Combine with kwargs
313
+ all_items = list(items_to_update) + list(kwargs.items())
314
+
315
+ # Validate all items before updating
316
+ for key, value in all_items:
317
+ # Type validation (always required)
318
+ if not isinstance(value, (str, int, float, bool, datetime)):
319
+ raise TypeError(
320
+ f"Value for key '{key}' must be str, int, float, bool, or datetime, got {type(value).__name__}"
321
+ )
322
+
323
+ # Structural validation (only after initialization)
324
+ if self.initialized:
325
+ if key not in self.data:
326
+ raise KeyError(f"Cannot add new key '{key}' after initialization")
327
+
328
+ existing_value = self.data[key]
329
+ if type(existing_value) != type(value):
330
+ raise TypeError(
331
+ f"Cannot change type of key '{key}' from {type(existing_value).__name__} "
332
+ f"to {type(value).__name__}"
333
+ )
334
+
335
+ # If all validations pass, update the data
336
+ for key, value in all_items:
337
+ self.data[key] = value
338
+
339
+ def setdefault(self, key: str, default=None):
340
+ """Set default value for key if not exists."""
341
+ if key not in self.data:
342
+ if self.initialized:
343
+ raise KeyError(f"Cannot add new key '{key}' after initialization")
344
+
345
+ if default is not None and not isinstance(
346
+ default, (str, int, float, bool, datetime)
347
+ ):
348
+ raise TypeError(
349
+ f"Default value must be str, int, float, bool, or datetime, got {type(default).__name__}"
350
+ )
351
+
352
+ self.data[key] = default
353
+
354
+ return self.data[key]
355
+
356
+ # Additional utility methods (no initialization checks needed)
357
+ def copy_data(self) -> Dict[str, Union[str, int, float, bool, datetime]]:
358
+ """Return a copy of the data dict."""
359
+ return self.data.copy()
360
+
361
+ def is_type_compatible(self, key: str, value) -> bool:
362
+ """Check if a value is type-compatible with existing key."""
363
+ if key not in self.data:
364
+ return isinstance(value, (str, int, float, bool, datetime))
365
+ return type(self.data[key]) == type(value)
366
+
367
+ def get_key_type(self, key: str) -> type:
368
+ """Get the type of a specific key."""
369
+ if key not in self.data:
370
+ raise KeyError(f"Key '{key}' not found")
371
+ return type(self.data[key])
372
+
373
+ def get_schema_for_key(self, key: str) -> dict:
374
+ """Get JSON schema for a specific key."""
375
+ if key not in self.data:
376
+ raise KeyError(f"Key '{key}' not found")
377
+
378
+ value = self.data[key]
379
+ if isinstance(value, str):
380
+ return {"type": "string"}
381
+ elif isinstance(value, bool):
382
+ return {"type": "boolean"}
383
+ elif isinstance(value, int):
384
+ return {"type": "integer"}
385
+ elif isinstance(value, float):
386
+ return {"type": "number"}
387
+ elif isinstance(value, datetime):
388
+ return {"type": "string", "format": "date-time"}
389
+ else:
390
+ return {"type": "string"}