mage-ai 0.9.58__py3-none-any.whl → 0.9.59__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.

Potentially problematic release.


This version of mage-ai might be problematic. Click here for more details.

Files changed (682) hide show
  1. mage_ai/api/logging.py +11 -7
  2. mage_ai/api/policies/BlockPolicy.py +1 -0
  3. mage_ai/api/policies/CommandCenterItemPolicy.py +56 -0
  4. mage_ai/api/policies/CustomTemplatePolicy.py +0 -3
  5. mage_ai/api/policies/GitBranchPolicy.py +8 -1
  6. mage_ai/api/policies/GitFilePolicy.py +1 -0
  7. mage_ai/api/policies/PipelinePolicy.py +2 -0
  8. mage_ai/api/policies/PipelineSchedulePolicy.py +1 -0
  9. mage_ai/api/policies/VersionControlBranchPolicy.py +98 -0
  10. mage_ai/api/policies/VersionControlFilePolicy.py +96 -0
  11. mage_ai/api/policies/VersionControlProjectPolicy.py +92 -0
  12. mage_ai/api/policies/VersionControlRemotePolicy.py +94 -0
  13. mage_ai/api/presenters/CommandCenterItemPresenter.py +19 -0
  14. mage_ai/api/presenters/GitBranchPresenter.py +2 -0
  15. mage_ai/api/presenters/GitCustomBranchPresenter.py +1 -0
  16. mage_ai/api/presenters/PipelinePresenter.py +5 -5
  17. mage_ai/api/presenters/PipelineSchedulePresenter.py +4 -0
  18. mage_ai/api/presenters/VersionControlBranchPresenter.py +15 -0
  19. mage_ai/api/presenters/VersionControlFilePresenter.py +15 -0
  20. mage_ai/api/presenters/VersionControlProjectPresenter.py +14 -0
  21. mage_ai/api/presenters/VersionControlRemotePresenter.py +15 -0
  22. mage_ai/api/resources/BlockResource.py +110 -80
  23. mage_ai/api/resources/CommandCenterItemResource.py +29 -0
  24. mage_ai/api/resources/FileResource.py +15 -1
  25. mage_ai/api/resources/FolderResource.py +2 -1
  26. mage_ai/api/resources/GitBranchResource.py +183 -108
  27. mage_ai/api/resources/GitCustomBranchResource.py +9 -2
  28. mage_ai/api/resources/GitFileResource.py +26 -13
  29. mage_ai/api/resources/KernelResource.py +9 -1
  30. mage_ai/api/resources/LogResource.py +1 -1
  31. mage_ai/api/resources/OauthResource.py +4 -5
  32. mage_ai/api/resources/PipelineResource.py +71 -38
  33. mage_ai/api/resources/PipelineRunResource.py +7 -2
  34. mage_ai/api/resources/PullRequestResource.py +12 -3
  35. mage_ai/api/resources/SearchResultResource.py +4 -4
  36. mage_ai/api/resources/VersionControlBranchResource.py +85 -0
  37. mage_ai/api/resources/VersionControlFileResource.py +65 -0
  38. mage_ai/api/resources/VersionControlProjectResource.py +46 -0
  39. mage_ai/api/resources/VersionControlRemoteResource.py +64 -0
  40. mage_ai/authentication/oauth/constants.py +13 -16
  41. mage_ai/authentication/operation_history/models.py +11 -32
  42. mage_ai/authentication/operation_history/utils.py +14 -14
  43. mage_ai/authentication/permissions/constants.py +5 -0
  44. mage_ai/authentication/providers/active_directory.py +2 -2
  45. mage_ai/authentication/providers/bitbucket.py +2 -2
  46. mage_ai/authentication/providers/constants.py +6 -12
  47. mage_ai/authentication/providers/ghe.py +2 -2
  48. mage_ai/authentication/providers/google.py +2 -2
  49. mage_ai/authentication/providers/okta.py +2 -2
  50. mage_ai/cache/base.py +11 -3
  51. mage_ai/cache/block.py +116 -32
  52. mage_ai/cache/block_action_object/__init__.py +52 -43
  53. mage_ai/cache/constants.py +1 -0
  54. mage_ai/cache/file.py +114 -0
  55. mage_ai/cache/pipeline.py +34 -8
  56. mage_ai/cache/tag.py +51 -10
  57. mage_ai/cache/utils.py +44 -3
  58. mage_ai/cluster_manager/kubernetes/workload_manager.py +9 -1
  59. mage_ai/command_center/__init__.py +0 -0
  60. mage_ai/command_center/applications/__init__.py +0 -0
  61. mage_ai/command_center/applications/constants.py +96 -0
  62. mage_ai/command_center/applications/factory.py +54 -0
  63. mage_ai/command_center/blocks/__init__.py +0 -0
  64. mage_ai/command_center/blocks/factory.py +39 -0
  65. mage_ai/command_center/blocks/utils.py +127 -0
  66. mage_ai/command_center/constants.py +66 -0
  67. mage_ai/command_center/factory.py +161 -0
  68. mage_ai/command_center/files/__init__.py +0 -0
  69. mage_ai/command_center/files/constants.py +235 -0
  70. mage_ai/command_center/files/factory.py +86 -0
  71. mage_ai/command_center/models.py +442 -0
  72. mage_ai/command_center/pipeline_runs/__init__.py +0 -0
  73. mage_ai/command_center/pipeline_runs/utils.py +141 -0
  74. mage_ai/command_center/pipelines/__init__.py +0 -0
  75. mage_ai/command_center/pipelines/constants.py +118 -0
  76. mage_ai/command_center/pipelines/factory.py +132 -0
  77. mage_ai/command_center/pipelines/utils.py +120 -0
  78. mage_ai/command_center/service.py +45 -0
  79. mage_ai/command_center/settings.py +37 -0
  80. mage_ai/command_center/support/__init__.py +0 -0
  81. mage_ai/command_center/support/constants.py +46 -0
  82. mage_ai/command_center/triggers/__init__.py +0 -0
  83. mage_ai/command_center/triggers/factory.py +49 -0
  84. mage_ai/command_center/triggers/utils.py +463 -0
  85. mage_ai/command_center/utils.py +30 -0
  86. mage_ai/data_preparation/executors/azure_container_instance_executor.py +1 -1
  87. mage_ai/data_preparation/executors/block_executor.py +68 -19
  88. mage_ai/data_preparation/executors/ecs_block_executor.py +2 -2
  89. mage_ai/data_preparation/executors/executor_factory.py +2 -0
  90. mage_ai/data_preparation/executors/gcp_cloud_run_block_executor.py +1 -1
  91. mage_ai/data_preparation/executors/k8s_block_executor.py +2 -2
  92. mage_ai/data_preparation/executors/pyspark_block_executor.py +7 -1
  93. mage_ai/data_preparation/git/__init__.py +29 -11
  94. mage_ai/data_preparation/git/api.py +14 -13
  95. mage_ai/data_preparation/git/clients/base.py +4 -8
  96. mage_ai/data_preparation/git/clients/github.py +1 -0
  97. mage_ai/data_preparation/git/utils.py +5 -7
  98. mage_ai/data_preparation/models/block/__init__.py +556 -329
  99. mage_ai/data_preparation/models/block/content.py +111 -0
  100. mage_ai/data_preparation/models/block/data_integration/mixins.py +1 -0
  101. mage_ai/data_preparation/models/block/dbt/block.py +29 -19
  102. mage_ai/data_preparation/models/block/dbt/block_sql.py +50 -260
  103. mage_ai/data_preparation/models/block/dbt/block_yaml.py +74 -156
  104. mage_ai/data_preparation/models/block/dbt/dbt_adapter.py +22 -10
  105. mage_ai/data_preparation/models/block/dbt/profiles.py +19 -15
  106. mage_ai/data_preparation/models/block/dbt/project.py +3 -0
  107. mage_ai/data_preparation/models/block/dynamic/constants.py +0 -0
  108. mage_ai/data_preparation/models/block/dynamic/dynamic_child.py +507 -47
  109. mage_ai/data_preparation/models/block/dynamic/utils.py +432 -1
  110. mage_ai/data_preparation/models/block/platform/mixins.py +68 -44
  111. mage_ai/data_preparation/models/block/r/__init__.py +19 -9
  112. mage_ai/data_preparation/models/block/sql/__init__.py +34 -20
  113. mage_ai/data_preparation/models/block/sql/utils/shared.py +4 -1
  114. mage_ai/data_preparation/models/block/utils.py +176 -71
  115. mage_ai/data_preparation/models/constants.py +7 -0
  116. mage_ai/data_preparation/models/file.py +21 -2
  117. mage_ai/data_preparation/models/pipeline.py +20 -8
  118. mage_ai/data_preparation/models/project/__init__.py +12 -7
  119. mage_ai/data_preparation/models/project/constants.py +1 -0
  120. mage_ai/data_preparation/models/triggers/__init__.py +7 -0
  121. mage_ai/data_preparation/models/utils.py +20 -0
  122. mage_ai/data_preparation/models/variable.py +113 -15
  123. mage_ai/data_preparation/shared/utils.py +6 -3
  124. mage_ai/data_preparation/storage/base_storage.py +17 -0
  125. mage_ai/data_preparation/storage/gcs_storage.py +4 -0
  126. mage_ai/data_preparation/storage/local_storage.py +15 -0
  127. mage_ai/data_preparation/storage/s3_storage.py +4 -0
  128. mage_ai/data_preparation/templates/data_exporters/qdrant.py +0 -1
  129. mage_ai/data_preparation/templates/data_loaders/qdrant.py +2 -1
  130. mage_ai/data_preparation/variable_manager.py +1 -0
  131. mage_ai/io/base.py +4 -1
  132. mage_ai/io/bigquery.py +25 -16
  133. mage_ai/io/clickhouse.py +3 -0
  134. mage_ai/io/druid.py +3 -0
  135. mage_ai/io/mongodb.py +1 -1
  136. mage_ai/io/pinot.py +3 -0
  137. mage_ai/io/postgres.py +13 -0
  138. mage_ai/io/snowflake.py +4 -0
  139. mage_ai/io/spark.py +8 -3
  140. mage_ai/io/sql.py +6 -0
  141. mage_ai/orchestration/db/__init__.py +26 -19
  142. mage_ai/orchestration/db/cache.py +4 -3
  143. mage_ai/orchestration/db/models/schedules.py +72 -16
  144. mage_ai/orchestration/db/models/schedules_project_platform.py +2 -3
  145. mage_ai/orchestration/pipeline_scheduler_original.py +19 -11
  146. mage_ai/orchestration/pipeline_scheduler_project_platform.py +20 -11
  147. mage_ai/presenters/interactions/constants.py +1 -0
  148. mage_ai/presenters/interactions/models.py +4 -3
  149. mage_ai/presenters/pages/models/client_pages/pipeline_schedules.py +1 -1
  150. mage_ai/server/constants.py +1 -1
  151. mage_ai/server/frontend_dist/404.html +11 -11
  152. mage_ai/server/frontend_dist/_next/static/PPQxHOmWVJ0iQFzG2rc8m/_buildManifest.js +1 -0
  153. mage_ai/server/frontend_dist/_next/static/chunks/1235.c9ed47779baccc05.js +1 -0
  154. mage_ai/server/frontend_dist/_next/static/chunks/125-029194ff22eb33a5.js +1 -0
  155. mage_ai/server/frontend_dist/_next/static/chunks/1557-4f7485c2e2228f77.js +1 -0
  156. mage_ai/server/frontend_dist/_next/static/chunks/2474-8e702cea23ddd16d.js +1 -0
  157. mage_ai/server/frontend_dist/_next/static/chunks/2479-727fa69ee9be6fab.js +1 -0
  158. mage_ai/server/frontend_dist/_next/static/chunks/26-38bc9380422f3900.js +1 -0
  159. mage_ai/server/frontend_dist/_next/static/chunks/2717-3e7ea8543b3825b7.js +1 -0
  160. mage_ai/server/frontend_dist/_next/static/chunks/3366-420721116ea5a665.js +1 -0
  161. mage_ai/server/frontend_dist/_next/static/chunks/3449-a9c98239582cd6f0.js +1 -0
  162. mage_ai/server/frontend_dist/_next/static/chunks/3548-13563a1ff815f922.js +1 -0
  163. mage_ai/server/frontend_dist/_next/static/chunks/3943-9e0a8ba0db34b35f.js +1 -0
  164. mage_ai/server/frontend_dist/_next/static/chunks/4371-ef23f95888d6cd11.js +1 -0
  165. mage_ai/server/frontend_dist/_next/static/chunks/4632.a4171dbf46b31c7c.js +1 -0
  166. mage_ai/server/frontend_dist/_next/static/chunks/523-be11ad59861d00ca.js +1 -0
  167. mage_ai/server/frontend_dist/_next/static/chunks/5457-d582b00545b4cb5e.js +1 -0
  168. mage_ai/server/frontend_dist/_next/static/chunks/5831-2abc4063e887a03e.js +1 -0
  169. mage_ai/server/frontend_dist/_next/static/chunks/6085-692d2f784c0504f2.js +1 -0
  170. mage_ai/server/frontend_dist/_next/static/chunks/635-1e8857c2b6dde289.js +1 -0
  171. mage_ai/server/frontend_dist/_next/static/chunks/7022-0d52dd8868621fb0.js +1 -0
  172. mage_ai/server/frontend_dist/_next/static/chunks/7264-d05f4b7088533f6f.js +1 -0
  173. mage_ai/server/frontend_dist/_next/static/chunks/7361-b3d661ec743bfadf.js +1 -0
  174. mage_ai/server/frontend_dist/_next/static/chunks/737-cc6467310a130dd9.js +1 -0
  175. mage_ai/server/frontend_dist/_next/static/chunks/{4495-4f0340aa82e0c623.js → 7674-9ec4fe64b5bf64d5.js} +1 -1
  176. mage_ai/server/frontend_dist/_next/static/chunks/8038-85d715c8c8394827.js +1 -0
  177. mage_ai/server/frontend_dist/_next/static/chunks/8095-bdce03896ef9639a.js +1 -0
  178. mage_ai/server/frontend_dist/_next/static/chunks/8146-6bed4e7401e067e6.js +1 -0
  179. mage_ai/server/frontend_dist/_next/static/chunks/8192-0c0b91523a38c4ca.js +1 -0
  180. mage_ai/server/frontend_dist/_next/static/chunks/8432-f736d101fc6d9215.js +1 -0
  181. mage_ai/server/frontend_dist/_next/static/chunks/8513-d207733b485c8d03.js +1 -0
  182. mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/9264-e7e040a54d34360c.js → frontend_dist/_next/static/chunks/9264-7cd9cd0ba86ec5e4.js} +1 -1
  183. mage_ai/server/frontend_dist/_next/static/chunks/9265-d2a1aaec75ec69b8.js +1 -0
  184. mage_ai/server/frontend_dist/_next/static/chunks/9440-54add041c392517f.js +1 -0
  185. mage_ai/server/frontend_dist/_next/static/chunks/9624-59b2f803f9c88cd6.js +1 -0
  186. mage_ai/server/frontend_dist/_next/static/chunks/9832-67896490f6e8a014.js +1 -0
  187. mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/bd1a647f.fe5d87096be24a62.js → frontend_dist/_next/static/chunks/bd1a647f.04f7913662092327.js} +1 -1
  188. mage_ai/server/frontend_dist/_next/static/chunks/pages/404-7cabe357906f44f2.js +1 -0
  189. mage_ai/server/frontend_dist/_next/static/chunks/pages/_app-a11bd2ebe11b3fd2.js +1 -0
  190. mage_ai/server/frontend_dist/_next/static/chunks/pages/block-layout-ffca51945cd154ef.js +1 -0
  191. mage_ai/server/frontend_dist/_next/static/chunks/pages/compute-3ba957769292db80.js +1 -0
  192. mage_ai/server/frontend_dist/_next/static/chunks/pages/files-45679fd18b3edd82.js +1 -0
  193. mage_ai/server/frontend_dist/_next/static/chunks/pages/global-data-products/[...slug]-95735a218106aa99.js +1 -0
  194. mage_ai/server/frontend_dist/_next/static/chunks/pages/{global-data-products-cf98c2e4d20fd92c.js → global-data-products-81d6c7fd9a9cff38.js} +1 -1
  195. mage_ai/server/frontend_dist/_next/static/chunks/pages/global-hooks/[...slug]-eb4421d51fb7368f.js +1 -0
  196. mage_ai/server/frontend_dist/_next/static/chunks/pages/global-hooks-a8c712e20512fede.js +1 -0
  197. mage_ai/server/frontend_dist/_next/static/chunks/pages/manage/files-f1be1d4735ba6919.js +1 -0
  198. mage_ai/server/frontend_dist/_next/static/chunks/pages/manage/settings-febe676687966276.js +1 -0
  199. mage_ai/server/frontend_dist/_next/static/chunks/pages/manage/users/[user]-70efe222130490b5.js +1 -0
  200. mage_ai/server/frontend_dist/_next/static/chunks/pages/manage/users/new-0914c36b3c8fd59f.js +1 -0
  201. mage_ai/server/frontend_dist/_next/static/chunks/pages/manage/{users-81e6e5319a59cd07.js → users-4f12e989e7809ef9.js} +1 -1
  202. mage_ai/server/frontend_dist/_next/static/chunks/pages/manage-809d4c99b44991a1.js +1 -0
  203. mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/pages/oauth-0436d395963cd27a.js → frontend_dist/_next/static/chunks/pages/oauth-aa9079c8dbaea3a5.js} +1 -1
  204. mage_ai/server/frontend_dist/_next/static/chunks/pages/overview-4e30fb5b18383b71.js +1 -0
  205. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipeline-runs-5d31c3aace39182d.js +1 -0
  206. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/backfills/[...slug]-85a80ee66f60a65c.js +1 -0
  207. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/backfills-0bd74047e257e6d9.js +1 -0
  208. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/dashboard-80619dc504d0103b.js +1 -0
  209. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/edit-3cc8d74ff8525bfd.js +1 -0
  210. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/logs-cdc379ff24ff8858.js +1 -0
  211. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/monitors/block-runs-03396d6086003e16.js +1 -0
  212. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/monitors/block-runtime-d6d920758654f6d9.js +1 -0
  213. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/monitors-27727bb87be506c3.js +1 -0
  214. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/runs/[run]-16aab67743651a0b.js +1 -0
  215. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/runs-e3ca1bedb660b252.js +1 -0
  216. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/settings-9d58701fe7213727.js +1 -0
  217. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/syncs-0041fd76ee15e49f.js +1 -0
  218. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/triggers/[...slug]-780be9a2da697915.js +1 -0
  219. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/triggers-db8bb970b1f2273f.js +1 -0
  220. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines-a3422b0ab1dbacd7.js +1 -0
  221. mage_ai/server/frontend_dist/_next/static/chunks/pages/platform/global-hooks/[...slug]-987a1603f4201943.js +1 -0
  222. mage_ai/server/frontend_dist/_next/static/chunks/pages/platform/global-hooks-3dc8bf89cab2722b.js +1 -0
  223. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/account/profile-43d3f2a5071b9537.js +1 -0
  224. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/platform/preferences-c97b1a39b22c112a.js +1 -0
  225. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/platform/settings-49efb66da67554ca.js +1 -0
  226. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/permissions/{[...slug]-be6aaec07c138656.js → [...slug]-5117d3555972484c.js} +1 -1
  227. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/permissions-ee1c19d4a192ae4d.js +1 -0
  228. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/preferences-4dc5724486ee3396.js +1 -0
  229. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/roles/{[...slug]-f380d293cff7592b.js → [...slug]-fa095aac732368c3.js} +1 -1
  230. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/{roles-ddce04fae28bd7f6.js → roles-db6d71f8692a33e7.js} +1 -1
  231. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/sync-data-99c6a263d2ab9553.js +1 -0
  232. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/users/{[...slug]-213a43564fdfbe17.js → [...slug]-9d881ac0a3db9baa.js} +1 -1
  233. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/users-bee414d57c4b55d7.js +1 -0
  234. mage_ai/server/frontend_dist/_next/static/chunks/pages/sign-in-e7030e1a7a9e88e1.js +1 -0
  235. mage_ai/server/frontend_dist/_next/static/chunks/pages/templates/[...slug]-312afc9a3f8d0a4f.js +1 -0
  236. mage_ai/server/frontend_dist/_next/static/chunks/pages/templates-e5300c340aa94c6b.js +1 -0
  237. mage_ai/server/frontend_dist/_next/static/chunks/pages/terminal-fa7ca0c86142d146.js +1 -0
  238. mage_ai/server/frontend_dist/_next/static/chunks/pages/test-bd51b3918b39329e.js +1 -0
  239. mage_ai/server/frontend_dist/_next/static/chunks/pages/triggers-5158a9d98d2459e8.js +1 -0
  240. mage_ai/server/frontend_dist/_next/static/chunks/pages/version-control-5ce099a7abb354ff.js +1 -0
  241. mage_ai/server/frontend_dist/_next/static/chunks/{webpack-fea697dd168c6d0c.js → webpack-efa55343114c8128.js} +1 -1
  242. mage_ai/server/{frontend_dist_base_path_template/_next/static/css/d1e8e64d0b07af2f.css → frontend_dist/_next/static/css/b59541b123fd7191.css} +1 -1
  243. mage_ai/server/frontend_dist/block-layout.html +3 -3
  244. mage_ai/server/frontend_dist/compute.html +11 -11
  245. mage_ai/server/frontend_dist/files.html +11 -11
  246. mage_ai/server/frontend_dist/global-data-products/[...slug].html +11 -11
  247. mage_ai/server/frontend_dist/global-data-products.html +11 -11
  248. mage_ai/server/frontend_dist/global-hooks/[...slug].html +11 -11
  249. mage_ai/server/frontend_dist/global-hooks.html +11 -11
  250. mage_ai/server/frontend_dist/index.html +3 -3
  251. mage_ai/server/frontend_dist/manage/files.html +11 -11
  252. mage_ai/server/frontend_dist/manage/settings.html +11 -11
  253. mage_ai/server/frontend_dist/manage/users/[user].html +11 -11
  254. mage_ai/server/frontend_dist/manage/users/new.html +11 -11
  255. mage_ai/server/frontend_dist/manage/users.html +11 -11
  256. mage_ai/server/frontend_dist/manage.html +11 -11
  257. mage_ai/server/frontend_dist/oauth.html +8 -8
  258. mage_ai/server/frontend_dist/overview.html +11 -11
  259. mage_ai/server/frontend_dist/pipeline-runs.html +11 -11
  260. mage_ai/server/frontend_dist/pipelines/[pipeline]/backfills/[...slug].html +11 -11
  261. mage_ai/server/frontend_dist/pipelines/[pipeline]/backfills.html +11 -11
  262. mage_ai/server/frontend_dist/pipelines/[pipeline]/dashboard.html +11 -11
  263. mage_ai/server/frontend_dist/pipelines/[pipeline]/edit.html +3 -3
  264. mage_ai/server/frontend_dist/pipelines/[pipeline]/logs.html +11 -11
  265. mage_ai/server/frontend_dist/pipelines/[pipeline]/monitors/block-runs.html +11 -11
  266. mage_ai/server/frontend_dist/pipelines/[pipeline]/monitors/block-runtime.html +11 -11
  267. mage_ai/server/frontend_dist/pipelines/[pipeline]/monitors.html +11 -11
  268. mage_ai/server/frontend_dist/pipelines/[pipeline]/runs/[run].html +11 -11
  269. mage_ai/server/frontend_dist/pipelines/[pipeline]/runs.html +11 -11
  270. mage_ai/server/frontend_dist/pipelines/[pipeline]/settings.html +11 -11
  271. mage_ai/server/frontend_dist/pipelines/[pipeline]/syncs.html +11 -11
  272. mage_ai/server/frontend_dist/pipelines/[pipeline]/triggers/[...slug].html +11 -11
  273. mage_ai/server/frontend_dist/pipelines/[pipeline]/triggers.html +11 -11
  274. mage_ai/server/frontend_dist/pipelines/[pipeline].html +3 -3
  275. mage_ai/server/frontend_dist/pipelines.html +11 -11
  276. mage_ai/server/frontend_dist/platform/global-hooks/[...slug].html +11 -11
  277. mage_ai/server/frontend_dist/platform/global-hooks.html +11 -11
  278. mage_ai/server/frontend_dist/settings/account/profile.html +11 -11
  279. mage_ai/server/frontend_dist/settings/platform/preferences.html +11 -11
  280. mage_ai/server/frontend_dist/settings/platform/settings.html +11 -11
  281. mage_ai/server/frontend_dist/settings/workspace/permissions/[...slug].html +11 -11
  282. mage_ai/server/frontend_dist/settings/workspace/permissions.html +11 -11
  283. mage_ai/server/frontend_dist/settings/workspace/preferences.html +11 -11
  284. mage_ai/server/frontend_dist/settings/workspace/roles/[...slug].html +11 -11
  285. mage_ai/server/frontend_dist/settings/workspace/roles.html +11 -11
  286. mage_ai/server/frontend_dist/settings/workspace/sync-data.html +11 -11
  287. mage_ai/server/frontend_dist/settings/workspace/users/[...slug].html +11 -11
  288. mage_ai/server/frontend_dist/settings/workspace/users.html +11 -11
  289. mage_ai/server/frontend_dist/settings.html +3 -3
  290. mage_ai/server/frontend_dist/sign-in.html +41 -41
  291. mage_ai/server/frontend_dist/templates/[...slug].html +11 -11
  292. mage_ai/server/frontend_dist/templates.html +11 -11
  293. mage_ai/server/frontend_dist/terminal.html +11 -11
  294. mage_ai/server/frontend_dist/test.html +3 -5
  295. mage_ai/server/frontend_dist/triggers.html +11 -11
  296. mage_ai/server/frontend_dist/version-control.html +11 -11
  297. mage_ai/server/frontend_dist_base_path_template/404.html +11 -11
  298. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/1235.c9ed47779baccc05.js +1 -0
  299. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/125-029194ff22eb33a5.js +1 -0
  300. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/1557-4f7485c2e2228f77.js +1 -0
  301. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/2474-8e702cea23ddd16d.js +1 -0
  302. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/2479-727fa69ee9be6fab.js +1 -0
  303. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/26-38bc9380422f3900.js +1 -0
  304. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/2717-3e7ea8543b3825b7.js +1 -0
  305. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/3366-420721116ea5a665.js +1 -0
  306. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/3449-a9c98239582cd6f0.js +1 -0
  307. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/3548-13563a1ff815f922.js +1 -0
  308. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/3943-9e0a8ba0db34b35f.js +1 -0
  309. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/4371-ef23f95888d6cd11.js +1 -0
  310. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/4632.a4171dbf46b31c7c.js +1 -0
  311. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/523-be11ad59861d00ca.js +1 -0
  312. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/5457-d582b00545b4cb5e.js +1 -0
  313. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/5831-2abc4063e887a03e.js +1 -0
  314. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/6085-692d2f784c0504f2.js +1 -0
  315. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/635-1e8857c2b6dde289.js +1 -0
  316. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/7022-0d52dd8868621fb0.js +1 -0
  317. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/7264-d05f4b7088533f6f.js +1 -0
  318. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/7361-b3d661ec743bfadf.js +1 -0
  319. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/737-cc6467310a130dd9.js +1 -0
  320. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/{4495-4f0340aa82e0c623.js → 7674-9ec4fe64b5bf64d5.js} +1 -1
  321. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/8038-85d715c8c8394827.js +1 -0
  322. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/8095-bdce03896ef9639a.js +1 -0
  323. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/8146-6bed4e7401e067e6.js +1 -0
  324. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/8192-0c0b91523a38c4ca.js +1 -0
  325. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/8432-f736d101fc6d9215.js +1 -0
  326. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/8513-d207733b485c8d03.js +1 -0
  327. mage_ai/server/{frontend_dist/_next/static/chunks/9264-e7e040a54d34360c.js → frontend_dist_base_path_template/_next/static/chunks/9264-7cd9cd0ba86ec5e4.js} +1 -1
  328. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/9265-d2a1aaec75ec69b8.js +1 -0
  329. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/9440-54add041c392517f.js +1 -0
  330. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/9624-59b2f803f9c88cd6.js +1 -0
  331. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/9832-67896490f6e8a014.js +1 -0
  332. mage_ai/server/{frontend_dist/_next/static/chunks/bd1a647f.fe5d87096be24a62.js → frontend_dist_base_path_template/_next/static/chunks/bd1a647f.04f7913662092327.js} +1 -1
  333. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/404-7cabe357906f44f2.js +1 -0
  334. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/_app-a11bd2ebe11b3fd2.js +1 -0
  335. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/block-layout-ffca51945cd154ef.js +1 -0
  336. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/compute-3ba957769292db80.js +1 -0
  337. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/files-45679fd18b3edd82.js +1 -0
  338. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/global-data-products/[...slug]-95735a218106aa99.js +1 -0
  339. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/{global-data-products-cf98c2e4d20fd92c.js → global-data-products-81d6c7fd9a9cff38.js} +1 -1
  340. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/global-hooks/[...slug]-eb4421d51fb7368f.js +1 -0
  341. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/global-hooks-a8c712e20512fede.js +1 -0
  342. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage/files-f1be1d4735ba6919.js +1 -0
  343. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage/settings-febe676687966276.js +1 -0
  344. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage/users/[user]-70efe222130490b5.js +1 -0
  345. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage/users/new-0914c36b3c8fd59f.js +1 -0
  346. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage/{users-81e6e5319a59cd07.js → users-4f12e989e7809ef9.js} +1 -1
  347. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage-809d4c99b44991a1.js +1 -0
  348. mage_ai/server/{frontend_dist/_next/static/chunks/pages/oauth-0436d395963cd27a.js → frontend_dist_base_path_template/_next/static/chunks/pages/oauth-aa9079c8dbaea3a5.js} +1 -1
  349. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/overview-4e30fb5b18383b71.js +1 -0
  350. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipeline-runs-5d31c3aace39182d.js +1 -0
  351. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/backfills/[...slug]-85a80ee66f60a65c.js +1 -0
  352. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/backfills-0bd74047e257e6d9.js +1 -0
  353. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/dashboard-80619dc504d0103b.js +1 -0
  354. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/edit-3cc8d74ff8525bfd.js +1 -0
  355. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/logs-cdc379ff24ff8858.js +1 -0
  356. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/monitors/block-runs-03396d6086003e16.js +1 -0
  357. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/monitors/block-runtime-d6d920758654f6d9.js +1 -0
  358. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/monitors-27727bb87be506c3.js +1 -0
  359. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/runs/[run]-16aab67743651a0b.js +1 -0
  360. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/runs-e3ca1bedb660b252.js +1 -0
  361. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/settings-9d58701fe7213727.js +1 -0
  362. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/syncs-0041fd76ee15e49f.js +1 -0
  363. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/triggers/[...slug]-780be9a2da697915.js +1 -0
  364. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/triggers-db8bb970b1f2273f.js +1 -0
  365. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines-a3422b0ab1dbacd7.js +1 -0
  366. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/platform/global-hooks/[...slug]-987a1603f4201943.js +1 -0
  367. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/platform/global-hooks-3dc8bf89cab2722b.js +1 -0
  368. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/account/profile-43d3f2a5071b9537.js +1 -0
  369. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/platform/preferences-c97b1a39b22c112a.js +1 -0
  370. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/platform/settings-49efb66da67554ca.js +1 -0
  371. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/permissions/{[...slug]-be6aaec07c138656.js → [...slug]-5117d3555972484c.js} +1 -1
  372. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/permissions-ee1c19d4a192ae4d.js +1 -0
  373. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/preferences-4dc5724486ee3396.js +1 -0
  374. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/roles/{[...slug]-f380d293cff7592b.js → [...slug]-fa095aac732368c3.js} +1 -1
  375. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/{roles-ddce04fae28bd7f6.js → roles-db6d71f8692a33e7.js} +1 -1
  376. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/sync-data-99c6a263d2ab9553.js +1 -0
  377. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/users/{[...slug]-213a43564fdfbe17.js → [...slug]-9d881ac0a3db9baa.js} +1 -1
  378. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/users-bee414d57c4b55d7.js +1 -0
  379. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/sign-in-e7030e1a7a9e88e1.js +1 -0
  380. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/templates/[...slug]-312afc9a3f8d0a4f.js +1 -0
  381. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/templates-e5300c340aa94c6b.js +1 -0
  382. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/terminal-fa7ca0c86142d146.js +1 -0
  383. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/test-bd51b3918b39329e.js +1 -0
  384. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/triggers-5158a9d98d2459e8.js +1 -0
  385. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/version-control-5ce099a7abb354ff.js +1 -0
  386. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/{webpack-d30cb09c85b4c4f0.js → webpack-bd35e1c7bd7b5a9a.js} +1 -1
  387. mage_ai/server/{frontend_dist/_next/static/css/d1e8e64d0b07af2f.css → frontend_dist_base_path_template/_next/static/css/b59541b123fd7191.css} +1 -1
  388. mage_ai/server/frontend_dist_base_path_template/_next/static/do4WOsw7lNhouR0mtPTFi/_buildManifest.js +1 -0
  389. mage_ai/server/frontend_dist_base_path_template/block-layout.html +3 -3
  390. mage_ai/server/frontend_dist_base_path_template/compute.html +11 -11
  391. mage_ai/server/frontend_dist_base_path_template/files.html +11 -11
  392. mage_ai/server/frontend_dist_base_path_template/global-data-products/[...slug].html +11 -11
  393. mage_ai/server/frontend_dist_base_path_template/global-data-products.html +11 -11
  394. mage_ai/server/frontend_dist_base_path_template/global-hooks/[...slug].html +11 -11
  395. mage_ai/server/frontend_dist_base_path_template/global-hooks.html +11 -11
  396. mage_ai/server/frontend_dist_base_path_template/index.html +3 -3
  397. mage_ai/server/frontend_dist_base_path_template/manage/files.html +11 -11
  398. mage_ai/server/frontend_dist_base_path_template/manage/settings.html +11 -11
  399. mage_ai/server/frontend_dist_base_path_template/manage/users/[user].html +11 -11
  400. mage_ai/server/frontend_dist_base_path_template/manage/users/new.html +11 -11
  401. mage_ai/server/frontend_dist_base_path_template/manage/users.html +11 -11
  402. mage_ai/server/frontend_dist_base_path_template/manage.html +11 -11
  403. mage_ai/server/frontend_dist_base_path_template/oauth.html +8 -8
  404. mage_ai/server/frontend_dist_base_path_template/overview.html +11 -11
  405. mage_ai/server/frontend_dist_base_path_template/pipeline-runs.html +11 -11
  406. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/backfills/[...slug].html +11 -11
  407. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/backfills.html +11 -11
  408. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/dashboard.html +11 -11
  409. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/edit.html +3 -3
  410. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/logs.html +11 -11
  411. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/monitors/block-runs.html +11 -11
  412. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/monitors/block-runtime.html +11 -11
  413. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/monitors.html +11 -11
  414. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/runs/[run].html +11 -11
  415. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/runs.html +11 -11
  416. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/settings.html +11 -11
  417. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/syncs.html +11 -11
  418. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/triggers/[...slug].html +11 -11
  419. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/triggers.html +11 -11
  420. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline].html +3 -3
  421. mage_ai/server/frontend_dist_base_path_template/pipelines.html +11 -11
  422. mage_ai/server/frontend_dist_base_path_template/platform/global-hooks/[...slug].html +11 -11
  423. mage_ai/server/frontend_dist_base_path_template/platform/global-hooks.html +11 -11
  424. mage_ai/server/frontend_dist_base_path_template/settings/account/profile.html +11 -11
  425. mage_ai/server/frontend_dist_base_path_template/settings/platform/preferences.html +11 -11
  426. mage_ai/server/frontend_dist_base_path_template/settings/platform/settings.html +11 -11
  427. mage_ai/server/frontend_dist_base_path_template/settings/workspace/permissions/[...slug].html +11 -11
  428. mage_ai/server/frontend_dist_base_path_template/settings/workspace/permissions.html +11 -11
  429. mage_ai/server/frontend_dist_base_path_template/settings/workspace/preferences.html +11 -11
  430. mage_ai/server/frontend_dist_base_path_template/settings/workspace/roles/[...slug].html +11 -11
  431. mage_ai/server/frontend_dist_base_path_template/settings/workspace/roles.html +11 -11
  432. mage_ai/server/frontend_dist_base_path_template/settings/workspace/sync-data.html +11 -11
  433. mage_ai/server/frontend_dist_base_path_template/settings/workspace/users/[...slug].html +11 -11
  434. mage_ai/server/frontend_dist_base_path_template/settings/workspace/users.html +11 -11
  435. mage_ai/server/frontend_dist_base_path_template/settings.html +3 -3
  436. mage_ai/server/frontend_dist_base_path_template/sign-in.html +39 -39
  437. mage_ai/server/frontend_dist_base_path_template/templates/[...slug].html +11 -11
  438. mage_ai/server/frontend_dist_base_path_template/templates.html +11 -11
  439. mage_ai/server/frontend_dist_base_path_template/terminal.html +11 -11
  440. mage_ai/server/frontend_dist_base_path_template/test.html +3 -5
  441. mage_ai/server/frontend_dist_base_path_template/triggers.html +11 -11
  442. mage_ai/server/frontend_dist_base_path_template/version-control.html +11 -11
  443. mage_ai/server/logger.py +8 -2
  444. mage_ai/server/server.py +18 -1
  445. mage_ai/services/aws/ecs/config.py +3 -3
  446. mage_ai/services/aws/ecs/ecs.py +2 -2
  447. mage_ai/services/search/block_action_objects.py +59 -5
  448. mage_ai/settings/models/configuration_option.py +3 -1
  449. mage_ai/settings/platform/__init__.py +54 -18
  450. mage_ai/settings/repo.py +5 -4
  451. mage_ai/shared/custom_logger.py +213 -0
  452. mage_ai/shared/environments.py +4 -0
  453. mage_ai/shared/files.py +52 -1
  454. mage_ai/shared/models.py +2 -1
  455. mage_ai/shared/parsers.py +8 -1
  456. mage_ai/shared/path_fixer.py +62 -5
  457. mage_ai/shared/retry.py +4 -0
  458. mage_ai/shared/strings.py +4 -0
  459. mage_ai/tests/api/endpoints/test_blocks.py +101 -1
  460. mage_ai/tests/api/operations/base/test_base_with_user_permissions.py +4 -1
  461. mage_ai/tests/base_test.py +21 -2
  462. mage_ai/tests/cache/test_pipeline_cache.py +11 -8
  463. mage_ai/tests/data_preparation/executors/test_block_executor.py +55 -45
  464. mage_ai/tests/data_preparation/git/test_api.py +4 -4
  465. mage_ai/tests/data_preparation/models/block/dbt/test_block.py +75 -43
  466. mage_ai/tests/data_preparation/models/block/dbt/test_block_sql.py +329 -329
  467. mage_ai/tests/data_preparation/models/block/dbt/test_block_yaml.py +296 -296
  468. mage_ai/tests/data_preparation/models/block/dbt/test_dbt_adapter.py +2 -2
  469. mage_ai/tests/data_preparation/models/block/dbt/test_dbt_cli.py +4 -4
  470. mage_ai/tests/data_preparation/models/block/dbt/test_profiles.py +2 -2
  471. mage_ai/tests/data_preparation/models/block/dbt/test_project.py +2 -2
  472. mage_ai/tests/data_preparation/models/block/dbt/test_sources.py +2 -2
  473. mage_ai/tests/data_preparation/models/block/dynamic/test_dynamic_child_block_factory.py +477 -474
  474. mage_ai/tests/data_preparation/models/block/dynamic/test_dynamic_helpers.py +25 -24
  475. mage_ai/tests/data_preparation/models/block/platform/test_mixins.py +16 -25
  476. mage_ai/tests/data_preparation/models/block/test_utils.py +8 -8
  477. mage_ai/tests/data_preparation/models/test_block.py +6 -8
  478. mage_ai/tests/data_preparation/models/test_project.py +10 -5
  479. mage_ai/tests/data_preparation/models/test_variable.py +51 -4
  480. mage_ai/tests/orchestration/test_pipeline_scheduler_project_platform.py +1 -0
  481. mage_ai/version_control/__init__.py +0 -0
  482. mage_ai/version_control/models.py +288 -0
  483. {mage_ai-0.9.58.dist-info → mage_ai-0.9.59.dist-info}/METADATA +6 -6
  484. {mage_ai-0.9.58.dist-info → mage_ai-0.9.59.dist-info}/RECORD +490 -476
  485. mage_ai/server/frontend_dist/_next/static/0XnQ0C0nTr1w0o9CGC7wQ/_buildManifest.js +0 -1
  486. mage_ai/server/frontend_dist/_next/static/chunks/1124-d8fc76201b83b376.js +0 -1
  487. mage_ai/server/frontend_dist/_next/static/chunks/1154-2f262f7eb38ebaa1.js +0 -1
  488. mage_ai/server/frontend_dist/_next/static/chunks/1235.53172e14801844e5.js +0 -1
  489. mage_ai/server/frontend_dist/_next/static/chunks/1550-32333b36ac6b061b.js +0 -1
  490. mage_ai/server/frontend_dist/_next/static/chunks/1557-7cfe722a9b83b935.js +0 -1
  491. mage_ai/server/frontend_dist/_next/static/chunks/1598-dd776e3a92e9cccd.js +0 -1
  492. mage_ai/server/frontend_dist/_next/static/chunks/1751-5adf859690505d7b.js +0 -1
  493. mage_ai/server/frontend_dist/_next/static/chunks/1952-b2197a87dd67d8e0.js +0 -1
  494. mage_ai/server/frontend_dist/_next/static/chunks/2056-0ea1faa46646a77e.js +0 -1
  495. mage_ai/server/frontend_dist/_next/static/chunks/2213-52267f1e9ef5751c.js +0 -1
  496. mage_ai/server/frontend_dist/_next/static/chunks/2474-352ae192b826d896.js +0 -1
  497. mage_ai/server/frontend_dist/_next/static/chunks/2512-46141494a1b75897.js +0 -1
  498. mage_ai/server/frontend_dist/_next/static/chunks/2717-accc279ae116b1b9.js +0 -1
  499. mage_ai/server/frontend_dist/_next/static/chunks/2852872c-15b31a7081e6a868.js +0 -1
  500. mage_ai/server/frontend_dist/_next/static/chunks/2886-9e73e1f48f248741.js +0 -1
  501. mage_ai/server/frontend_dist/_next/static/chunks/2920.86a65e5f26ff1795.js +0 -1
  502. mage_ai/server/frontend_dist/_next/static/chunks/3437-6055fd5aa82880f6.js +0 -1
  503. mage_ai/server/frontend_dist/_next/static/chunks/3449-214fce1a0e623b47.js +0 -1
  504. mage_ai/server/frontend_dist/_next/static/chunks/3494-23a9cc2cd649abac.js +0 -1
  505. mage_ai/server/frontend_dist/_next/static/chunks/3745-3662911c364d917b.js +0 -1
  506. mage_ai/server/frontend_dist/_next/static/chunks/3763-70c7eb1fc203c903.js +0 -1
  507. mage_ai/server/frontend_dist/_next/static/chunks/3943-a49377acc514e77f.js +0 -1
  508. mage_ai/server/frontend_dist/_next/static/chunks/4052-f89c85d2c8f51931.js +0 -1
  509. mage_ai/server/frontend_dist/_next/static/chunks/4296-14eaf96d6479e673.js +0 -1
  510. mage_ai/server/frontend_dist/_next/static/chunks/4804-1254a474f238d078.js +0 -1
  511. mage_ai/server/frontend_dist/_next/static/chunks/5283-9df961e430a79bea.js +0 -1
  512. mage_ai/server/frontend_dist/_next/static/chunks/5457-b373ebdf4d05d8e2.js +0 -1
  513. mage_ai/server/frontend_dist/_next/static/chunks/5499-bc2528f8e18f61a6.js +0 -1
  514. mage_ai/server/frontend_dist/_next/static/chunks/5810-e66141c1271bced6.js +0 -1
  515. mage_ai/server/frontend_dist/_next/static/chunks/5849-f30d8694e3318a81.js +0 -1
  516. mage_ai/server/frontend_dist/_next/static/chunks/5896-7b8e36634d7d94eb.js +0 -1
  517. mage_ai/server/frontend_dist/_next/static/chunks/6043-728790621ca9014c.js +0 -1
  518. mage_ai/server/frontend_dist/_next/static/chunks/6050-e48b7f97e96bbcb6.js +0 -1
  519. mage_ai/server/frontend_dist/_next/static/chunks/6285-648f9a732e100b2f.js +0 -1
  520. mage_ai/server/frontend_dist/_next/static/chunks/6639-74eefed142e14ea6.js +0 -1
  521. mage_ai/server/frontend_dist/_next/static/chunks/6965-c613d1834c8ed92d.js +0 -1
  522. mage_ai/server/frontend_dist/_next/static/chunks/7022-ea92cb336bd1aab8.js +0 -1
  523. mage_ai/server/frontend_dist/_next/static/chunks/7361-06ef4c642a8fe8c4.js +0 -1
  524. mage_ai/server/frontend_dist/_next/static/chunks/7858-677c641010ac9160.js +0 -1
  525. mage_ai/server/frontend_dist/_next/static/chunks/8125-b9d53e425b6ddc7e.js +0 -1
  526. mage_ai/server/frontend_dist/_next/static/chunks/8146-fb8f445644e573b0.js +0 -1
  527. mage_ai/server/frontend_dist/_next/static/chunks/8432-0ace6fb7bdbc6864.js +0 -1
  528. mage_ai/server/frontend_dist/_next/static/chunks/844-1e171f361e63b36d.js +0 -1
  529. mage_ai/server/frontend_dist/_next/static/chunks/9269-40e2101f56399dcc.js +0 -1
  530. mage_ai/server/frontend_dist/_next/static/chunks/9302-913007e2f801ad65.js +0 -1
  531. mage_ai/server/frontend_dist/_next/static/chunks/9440-c51bf1e8f271cb25.js +0 -1
  532. mage_ai/server/frontend_dist/_next/static/chunks/9624-89a4b20eb3b3c754.js +0 -1
  533. mage_ai/server/frontend_dist/_next/static/chunks/976-0a8c2c4d7acd957b.js +0 -1
  534. mage_ai/server/frontend_dist/_next/static/chunks/9775-c1142b1312e8f95d.js +0 -1
  535. mage_ai/server/frontend_dist/_next/static/chunks/pages/404-5ddd12931e0e3e41.js +0 -1
  536. mage_ai/server/frontend_dist/_next/static/chunks/pages/_app-ec5e62e8e5bb4e4c.js +0 -1
  537. mage_ai/server/frontend_dist/_next/static/chunks/pages/block-layout-44d7291d8f29eb36.js +0 -1
  538. mage_ai/server/frontend_dist/_next/static/chunks/pages/compute-a6661a9c3647532a.js +0 -1
  539. mage_ai/server/frontend_dist/_next/static/chunks/pages/files-26b899d3adb4ed19.js +0 -1
  540. mage_ai/server/frontend_dist/_next/static/chunks/pages/global-data-products/[...slug]-9bd164317e652311.js +0 -1
  541. mage_ai/server/frontend_dist/_next/static/chunks/pages/global-hooks/[...slug]-c87d46dc53e35638.js +0 -1
  542. mage_ai/server/frontend_dist/_next/static/chunks/pages/global-hooks-24785a5ed4eb340d.js +0 -1
  543. mage_ai/server/frontend_dist/_next/static/chunks/pages/manage/files-2c5fd116ecfacb33.js +0 -1
  544. mage_ai/server/frontend_dist/_next/static/chunks/pages/manage/settings-8c9285af31efa3fd.js +0 -1
  545. mage_ai/server/frontend_dist/_next/static/chunks/pages/manage/users/[user]-ec7e0419a64ae3b2.js +0 -1
  546. mage_ai/server/frontend_dist/_next/static/chunks/pages/manage/users/new-039e083cc068da54.js +0 -1
  547. mage_ai/server/frontend_dist/_next/static/chunks/pages/manage-8e4d4f6edc515265.js +0 -1
  548. mage_ai/server/frontend_dist/_next/static/chunks/pages/overview-b73185fdcb7a131c.js +0 -1
  549. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipeline-runs-97b3335bf60f8b07.js +0 -1
  550. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/backfills/[...slug]-83de126136539668.js +0 -1
  551. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/backfills-50f45e96892cfb63.js +0 -1
  552. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/dashboard-5fe0f1de6fd365f8.js +0 -1
  553. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/edit-306db9e11fa94d21.js +0 -1
  554. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/logs-ebea4be0562b08e3.js +0 -1
  555. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/monitors/block-runs-f09d21d5c2661504.js +0 -1
  556. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/monitors/block-runtime-61e770f155ae22ef.js +0 -1
  557. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/monitors-d347a4614130e2ab.js +0 -1
  558. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/runs/[run]-056c5e5080a3754f.js +0 -1
  559. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/runs-208fa8de91f152bc.js +0 -1
  560. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/settings-a32e02f46a816eb3.js +0 -1
  561. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/syncs-8012388b15f0e0a5.js +0 -1
  562. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/triggers/[...slug]-6714c72f90923cfb.js +0 -1
  563. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/triggers-afc0525ef27fe1f2.js +0 -1
  564. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines-a91e7d86cd0540d9.js +0 -1
  565. mage_ai/server/frontend_dist/_next/static/chunks/pages/platform/global-hooks/[...slug]-7c8e5a8908fe9c83.js +0 -1
  566. mage_ai/server/frontend_dist/_next/static/chunks/pages/platform/global-hooks-be0684d1527587d8.js +0 -1
  567. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/account/profile-147660a34351147c.js +0 -1
  568. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/platform/preferences-ec5f01b683920ced.js +0 -1
  569. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/platform/settings-28aa4512c0e23ec5.js +0 -1
  570. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/permissions-a2850b2e0a282d16.js +0 -1
  571. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/preferences-b1df80674dca4713.js +0 -1
  572. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/sync-data-41abcd2241958dc8.js +0 -1
  573. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/users-a2c5859f53b5e9e1.js +0 -1
  574. mage_ai/server/frontend_dist/_next/static/chunks/pages/sign-in-1aea2a0c373bc6ae.js +0 -1
  575. mage_ai/server/frontend_dist/_next/static/chunks/pages/templates/[...slug]-e7485742708215d2.js +0 -1
  576. mage_ai/server/frontend_dist/_next/static/chunks/pages/templates-b4086d30a89d6801.js +0 -1
  577. mage_ai/server/frontend_dist/_next/static/chunks/pages/terminal-3cd171f43f0a0df8.js +0 -1
  578. mage_ai/server/frontend_dist/_next/static/chunks/pages/test-6a22493285c47ba2.js +0 -1
  579. mage_ai/server/frontend_dist/_next/static/chunks/pages/triggers-ad20a31dde4fe59b.js +0 -1
  580. mage_ai/server/frontend_dist/_next/static/chunks/pages/version-control-267952b4c87927c7.js +0 -1
  581. mage_ai/server/frontend_dist_base_path_template/_next/static/RC0Yenm06LvZI-RWFoT5C/_buildManifest.js +0 -1
  582. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/1124-d8fc76201b83b376.js +0 -1
  583. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/1154-2f262f7eb38ebaa1.js +0 -1
  584. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/1235.53172e14801844e5.js +0 -1
  585. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/1550-32333b36ac6b061b.js +0 -1
  586. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/1557-7cfe722a9b83b935.js +0 -1
  587. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/1598-dd776e3a92e9cccd.js +0 -1
  588. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/1751-5adf859690505d7b.js +0 -1
  589. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/1952-b2197a87dd67d8e0.js +0 -1
  590. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/2056-0ea1faa46646a77e.js +0 -1
  591. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/2213-52267f1e9ef5751c.js +0 -1
  592. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/2474-352ae192b826d896.js +0 -1
  593. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/2512-46141494a1b75897.js +0 -1
  594. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/2717-accc279ae116b1b9.js +0 -1
  595. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/2852872c-15b31a7081e6a868.js +0 -1
  596. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/2886-9e73e1f48f248741.js +0 -1
  597. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/2920.86a65e5f26ff1795.js +0 -1
  598. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/3437-6055fd5aa82880f6.js +0 -1
  599. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/3449-214fce1a0e623b47.js +0 -1
  600. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/3494-23a9cc2cd649abac.js +0 -1
  601. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/3745-3662911c364d917b.js +0 -1
  602. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/3763-70c7eb1fc203c903.js +0 -1
  603. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/3943-a49377acc514e77f.js +0 -1
  604. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/4052-f89c85d2c8f51931.js +0 -1
  605. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/4296-14eaf96d6479e673.js +0 -1
  606. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/4804-1254a474f238d078.js +0 -1
  607. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/5283-9df961e430a79bea.js +0 -1
  608. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/5457-b373ebdf4d05d8e2.js +0 -1
  609. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/5499-bc2528f8e18f61a6.js +0 -1
  610. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/5810-e66141c1271bced6.js +0 -1
  611. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/5849-f30d8694e3318a81.js +0 -1
  612. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/5896-7b8e36634d7d94eb.js +0 -1
  613. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/6043-728790621ca9014c.js +0 -1
  614. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/6050-e48b7f97e96bbcb6.js +0 -1
  615. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/6285-648f9a732e100b2f.js +0 -1
  616. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/6639-74eefed142e14ea6.js +0 -1
  617. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/6965-c613d1834c8ed92d.js +0 -1
  618. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/7022-ea92cb336bd1aab8.js +0 -1
  619. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/7361-06ef4c642a8fe8c4.js +0 -1
  620. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/7858-677c641010ac9160.js +0 -1
  621. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/8125-b9d53e425b6ddc7e.js +0 -1
  622. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/8146-fb8f445644e573b0.js +0 -1
  623. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/8432-0ace6fb7bdbc6864.js +0 -1
  624. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/844-1e171f361e63b36d.js +0 -1
  625. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/9269-40e2101f56399dcc.js +0 -1
  626. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/9302-913007e2f801ad65.js +0 -1
  627. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/9440-c51bf1e8f271cb25.js +0 -1
  628. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/9624-89a4b20eb3b3c754.js +0 -1
  629. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/976-0a8c2c4d7acd957b.js +0 -1
  630. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/9775-c1142b1312e8f95d.js +0 -1
  631. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/404-5ddd12931e0e3e41.js +0 -1
  632. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/_app-ec5e62e8e5bb4e4c.js +0 -1
  633. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/block-layout-44d7291d8f29eb36.js +0 -1
  634. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/compute-a6661a9c3647532a.js +0 -1
  635. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/files-26b899d3adb4ed19.js +0 -1
  636. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/global-data-products/[...slug]-9bd164317e652311.js +0 -1
  637. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/global-hooks/[...slug]-c87d46dc53e35638.js +0 -1
  638. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/global-hooks-24785a5ed4eb340d.js +0 -1
  639. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage/files-2c5fd116ecfacb33.js +0 -1
  640. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage/settings-8c9285af31efa3fd.js +0 -1
  641. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage/users/[user]-ec7e0419a64ae3b2.js +0 -1
  642. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage/users/new-039e083cc068da54.js +0 -1
  643. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage-8e4d4f6edc515265.js +0 -1
  644. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/overview-b73185fdcb7a131c.js +0 -1
  645. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipeline-runs-97b3335bf60f8b07.js +0 -1
  646. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/backfills/[...slug]-83de126136539668.js +0 -1
  647. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/backfills-50f45e96892cfb63.js +0 -1
  648. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/dashboard-5fe0f1de6fd365f8.js +0 -1
  649. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/edit-306db9e11fa94d21.js +0 -1
  650. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/logs-ebea4be0562b08e3.js +0 -1
  651. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/monitors/block-runs-f09d21d5c2661504.js +0 -1
  652. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/monitors/block-runtime-61e770f155ae22ef.js +0 -1
  653. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/monitors-d347a4614130e2ab.js +0 -1
  654. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/runs/[run]-056c5e5080a3754f.js +0 -1
  655. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/runs-208fa8de91f152bc.js +0 -1
  656. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/settings-a32e02f46a816eb3.js +0 -1
  657. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/syncs-8012388b15f0e0a5.js +0 -1
  658. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/triggers/[...slug]-6714c72f90923cfb.js +0 -1
  659. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/triggers-afc0525ef27fe1f2.js +0 -1
  660. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines-a91e7d86cd0540d9.js +0 -1
  661. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/platform/global-hooks/[...slug]-7c8e5a8908fe9c83.js +0 -1
  662. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/platform/global-hooks-be0684d1527587d8.js +0 -1
  663. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/account/profile-147660a34351147c.js +0 -1
  664. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/platform/preferences-ec5f01b683920ced.js +0 -1
  665. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/platform/settings-28aa4512c0e23ec5.js +0 -1
  666. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/permissions-a2850b2e0a282d16.js +0 -1
  667. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/preferences-b1df80674dca4713.js +0 -1
  668. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/sync-data-41abcd2241958dc8.js +0 -1
  669. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/users-a2c5859f53b5e9e1.js +0 -1
  670. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/sign-in-1aea2a0c373bc6ae.js +0 -1
  671. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/templates/[...slug]-e7485742708215d2.js +0 -1
  672. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/templates-b4086d30a89d6801.js +0 -1
  673. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/terminal-3cd171f43f0a0df8.js +0 -1
  674. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/test-6a22493285c47ba2.js +0 -1
  675. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/triggers-ad20a31dde4fe59b.js +0 -1
  676. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/version-control-267952b4c87927c7.js +0 -1
  677. /mage_ai/server/frontend_dist/_next/static/{0XnQ0C0nTr1w0o9CGC7wQ → PPQxHOmWVJ0iQFzG2rc8m}/_ssgManifest.js +0 -0
  678. /mage_ai/server/frontend_dist_base_path_template/_next/static/{RC0Yenm06LvZI-RWFoT5C → do4WOsw7lNhouR0mtPTFi}/_ssgManifest.js +0 -0
  679. {mage_ai-0.9.58.dist-info → mage_ai-0.9.59.dist-info}/LICENSE +0 -0
  680. {mage_ai-0.9.58.dist-info → mage_ai-0.9.59.dist-info}/WHEEL +0 -0
  681. {mage_ai-0.9.58.dist-info → mage_ai-0.9.59.dist-info}/entry_points.txt +0 -0
  682. {mage_ai-0.9.58.dist-info → mage_ai-0.9.59.dist-info}/top_level.txt +0 -0
@@ -18,6 +18,7 @@ from typing import Any, Callable, Dict, Generator, List, Set, Tuple, Union
18
18
 
19
19
  import inflection
20
20
  import pandas as pd
21
+ import polars as pl
21
22
  import simplejson
22
23
  import yaml
23
24
  from jinja2 import Template
@@ -28,12 +29,20 @@ from mage_ai.data_cleaner.shared.utils import is_geo_dataframe, is_spark_datafra
28
29
  from mage_ai.data_integrations.sources.constants import SQL_SOURCES_MAPPING
29
30
  from mage_ai.data_preparation.logging.logger import DictLogger
30
31
  from mage_ai.data_preparation.logging.logger_manager_factory import LoggerManagerFactory
32
+ from mage_ai.data_preparation.models.block.content import hydrate_block_outputs
31
33
  from mage_ai.data_preparation.models.block.data_integration.mixins import (
32
34
  DataIntegrationMixin,
33
35
  )
34
36
  from mage_ai.data_preparation.models.block.data_integration.utils import (
35
37
  execute_data_integration,
36
38
  )
39
+ from mage_ai.data_preparation.models.block.dynamic.utils import (
40
+ DynamicBlockFlag,
41
+ is_dynamic_block,
42
+ is_dynamic_block_child,
43
+ should_reduce_output,
44
+ uuid_for_output_variables,
45
+ )
37
46
  from mage_ai.data_preparation.models.block.errors import HasDownstreamDependencies
38
47
  from mage_ai.data_preparation.models.block.extension.utils import handle_run_tests
39
48
  from mage_ai.data_preparation.models.block.platform.mixins import (
@@ -45,12 +54,9 @@ from mage_ai.data_preparation.models.block.utils import (
45
54
  clean_name,
46
55
  fetch_input_variables,
47
56
  input_variables,
48
- is_dynamic_block,
49
- is_dynamic_block_child,
50
57
  is_output_variable,
51
58
  is_valid_print_variable,
52
59
  output_variables,
53
- should_reduce_output,
54
60
  )
55
61
  from mage_ai.data_preparation.models.constants import (
56
62
  BLOCK_LANGUAGE_TO_FILE_EXTENSION,
@@ -76,20 +82,22 @@ from mage_ai.data_preparation.shared.stream import StreamToLogger
76
82
  from mage_ai.data_preparation.shared.utils import get_template_vars
77
83
  from mage_ai.data_preparation.templates.data_integrations.utils import get_templates
78
84
  from mage_ai.data_preparation.templates.template import load_template
79
- from mage_ai.data_preparation.templates.utils import get_variable_for_template
80
85
  from mage_ai.server.kernel_output_parser import DataType
81
86
  from mage_ai.services.spark.config import SparkConfig
82
87
  from mage_ai.services.spark.spark import get_spark_session
83
88
  from mage_ai.settings.platform.constants import project_platform_activated
84
89
  from mage_ai.settings.repo import get_repo_path
85
90
  from mage_ai.shared.constants import ENV_DEV, ENV_TEST
91
+ from mage_ai.shared.custom_logger import DX_PRINTER
86
92
  from mage_ai.shared.environments import get_env, is_debug
87
93
  from mage_ai.shared.hash import extract, ignore_keys, merge_dict
88
94
  from mage_ai.shared.logger import BlockFunctionExec
89
95
  from mage_ai.shared.parsers import encode_complex
90
96
  from mage_ai.shared.path_fixer import (
97
+ add_absolute_path,
91
98
  add_root_repo_path_to_relative_path,
92
99
  get_path_parts,
100
+ remove_base_repo_path,
93
101
  )
94
102
  from mage_ai.shared.strings import format_enum
95
103
  from mage_ai.shared.utils import clean_name as clean_name_orig
@@ -337,14 +345,8 @@ class Block(DataIntegrationMixin, SparkBlock, ProjectPlatformAccessible):
337
345
 
338
346
  # Replicate block
339
347
  self.replicated_block = replicated_block
340
- self.replicated_block_object = None
341
- if replicated_block:
342
- self.replicated_block_object = Block(
343
- self.replicated_block,
344
- self.replicated_block,
345
- self.type,
346
- language=self.language,
347
- )
348
+ self.replicated_blocks = {}
349
+ self._replicated_block_object = None
348
350
 
349
351
  # Module for the block functions. Will be set when the block is executed from a notebook.
350
352
  self.module = None
@@ -399,9 +401,26 @@ class Block(DataIntegrationMixin, SparkBlock, ProjectPlatformAccessible):
399
401
  def configuration(self, x) -> None:
400
402
  self._configuration = self.clean_file_paths(x) if x else x
401
403
 
404
+ def get_original_block(self) -> 'Block':
405
+ if self.replicated_block:
406
+ return self.replicated_block_object.get_original_block()
407
+ return self
408
+
409
+ @property
410
+ def replicated_block_object(self) -> 'Block':
411
+ if self._replicated_block_object:
412
+ return self._replicated_block_object
413
+
414
+ if self.replicated_block and self.pipeline:
415
+ self._replicated_block_object = self.pipeline.get_block(self.replicated_block)
416
+ if self._replicated_block_object:
417
+ self._replicated_block_object.replicated_blocks[self.uuid] = self
418
+
419
+ return self._replicated_block_object
420
+
402
421
  @property
403
422
  def content(self) -> str:
404
- if self.replicated_block:
423
+ if self.replicated_block and self.replicated_block_object:
405
424
  self._content = self.replicated_block_object.content
406
425
 
407
426
  if self._content is None:
@@ -422,7 +441,7 @@ class Block(DataIntegrationMixin, SparkBlock, ProjectPlatformAccessible):
422
441
  return None
423
442
 
424
443
  async def content_async(self) -> str:
425
- if self.replicated_block:
444
+ if self.replicated_block and self.replicated_block_object:
426
445
  self._content = await self.replicated_block_object.content_async()
427
446
 
428
447
  if self._content is None:
@@ -430,6 +449,44 @@ class Block(DataIntegrationMixin, SparkBlock, ProjectPlatformAccessible):
430
449
 
431
450
  return self._content
432
451
 
452
+ def interpolate_content(
453
+ self,
454
+ content: str,
455
+ dynamic_block_index: int = None,
456
+ dynamic_block_indexes: int = None,
457
+ dynamic_upstream_block_uuids: int = None,
458
+ execution_partition: str = None,
459
+ from_notebook: bool = False,
460
+ outputs_from_input_vars: Dict = None,
461
+ upstream_block_uuids: List[str] = None,
462
+ variables: Dict = None,
463
+ **kwargs,
464
+ ) -> str:
465
+ variables = variables or {}
466
+ if self.pipeline and self.pipeline.variables:
467
+ variables.update(self.pipeline.variables)
468
+
469
+ if upstream_block_uuids is None:
470
+ upstream_block_uuids = self.upstream_block_uuids
471
+
472
+ if outputs_from_input_vars is None:
473
+ outputs_from_input_vars, _input_vars, _kwargs_vars, upstream_block_uuids = \
474
+ self.__get_outputs_from_input_vars(
475
+ dynamic_block_index=dynamic_block_index,
476
+ dynamic_block_indexes=dynamic_block_indexes,
477
+ dynamic_upstream_block_uuids=dynamic_upstream_block_uuids,
478
+ execution_partition=execution_partition,
479
+ from_notebook=from_notebook,
480
+ global_vars=variables,
481
+ )
482
+
483
+ return hydrate_block_outputs(
484
+ content,
485
+ outputs_from_input_vars=outputs_from_input_vars,
486
+ upstream_block_uuids=upstream_block_uuids,
487
+ variables=variables,
488
+ )
489
+
433
490
  async def metadata_async(self) -> Dict:
434
491
  if self.is_data_integration():
435
492
  grouped_templates = get_templates(group_templates=True)
@@ -437,59 +494,7 @@ class Block(DataIntegrationMixin, SparkBlock, ProjectPlatformAccessible):
437
494
  if BlockLanguage.YAML == self.language:
438
495
  content = await self.content_async()
439
496
  if content:
440
- variables = {}
441
- if self.pipeline and self.pipeline.variables:
442
- variables.update(self.pipeline.variables)
443
-
444
- def _block_output(
445
- block_uuid: str,
446
- parse: str = None,
447
- current_block=self,
448
- global_vars=variables,
449
- ) -> Any:
450
- data = None
451
-
452
- if not self.fetched_inputs_from_blocks:
453
- input_vars_fetched, _kwargs_vars, _upstream_block_uuids = \
454
- self.fetch_input_variables_and_catalog(
455
- None,
456
- None,
457
- global_vars=global_vars,
458
- from_notebook=True,
459
- )
460
- self.fetched_inputs_from_blocks = input_vars_fetched
461
-
462
- if block_uuid in self.upstream_block_uuids and \
463
- self.data_integration_inputs and \
464
- block_uuid in self.data_integration_inputs:
465
-
466
- up_uuids = [i for i in
467
- self.upstream_block_uuids if i in
468
- self.data_integration_inputs]
469
-
470
- index = up_uuids.index(block_uuid)
471
- data = self.fetched_inputs_from_blocks[index]
472
-
473
- if parse:
474
- results = {}
475
- exec(f'_parse_func = {parse}', results)
476
- try:
477
- return results['_parse_func'](data)
478
- except Exception:
479
- pass
480
-
481
- return data
482
-
483
- text = Template(content).render(
484
- block_output=_block_output,
485
- variables=lambda x, p=None, v=variables: get_variable_for_template(
486
- x,
487
- parse=p,
488
- variables=v,
489
- ),
490
- **get_template_vars(),
491
- )
492
-
497
+ text = self.interpolate_content(content)
493
498
  settings = yaml.safe_load(text)
494
499
  uuid = settings.get('source') or settings.get('destination')
495
500
  mapping = grouped_templates.get(uuid) or {}
@@ -570,6 +575,11 @@ class Block(DataIntegrationMixin, SparkBlock, ProjectPlatformAccessible):
570
575
 
571
576
  @property
572
577
  def repo_path(self) -> str:
578
+ if self.project_platform_activated:
579
+ repo_path = self.get_repo_path_from_configuration()
580
+ if repo_path:
581
+ return repo_path
582
+
573
583
  return self.pipeline.repo_path if self.pipeline is not None else get_repo_path()
574
584
 
575
585
  @classmethod
@@ -591,9 +601,15 @@ class Block(DataIntegrationMixin, SparkBlock, ProjectPlatformAccessible):
591
601
 
592
602
  @property
593
603
  def file_path(self) -> str:
604
+ if self.replicated_block and self.replicated_block_object:
605
+ return self.replicated_block_object.file_path
606
+
594
607
  file_path = self.get_file_path_from_source()
608
+ if not file_path:
609
+ file_path = self.configuration.get('file_path')
610
+
595
611
  if file_path:
596
- return add_root_repo_path_to_relative_path(file_path)
612
+ return add_absolute_path(file_path)
597
613
 
598
614
  return self.__build_file_path(
599
615
  self.repo_path or os.getcwd(),
@@ -604,6 +620,9 @@ class Block(DataIntegrationMixin, SparkBlock, ProjectPlatformAccessible):
604
620
 
605
621
  @property
606
622
  def file(self) -> File:
623
+ if self.replicated_block and self.replicated_block_object:
624
+ return self.replicated_block_object.file
625
+
607
626
  if self.project_platform_activated:
608
627
  file = self.build_file()
609
628
  if file:
@@ -660,23 +679,16 @@ class Block(DataIntegrationMixin, SparkBlock, ProjectPlatformAccessible):
660
679
  downstream_block_uuids = kwargs.get('downstream_block_uuids', [])
661
680
  upstream_block_uuids = kwargs.get('upstream_block_uuids', [])
662
681
 
663
- if BlockType.DBT == block.type and block.language == BlockLanguage.SQL:
664
- upstream_dbt_blocks = block.upstream_dbt_blocks() or []
665
- upstream_dbt_blocks_by_uuid = {
666
- block.uuid: block
667
- for block in upstream_dbt_blocks
668
- }
669
- pipeline.blocks_by_uuid.update(upstream_dbt_blocks_by_uuid)
670
- pipeline.validate('A cycle was formed while adding a block')
671
- pipeline.save()
672
- else:
673
- pipeline.add_block(
674
- block,
675
- downstream_block_uuids=downstream_block_uuids,
676
- upstream_block_uuids=upstream_block_uuids,
677
- priority=priority,
678
- widget=widget,
679
- )
682
+ if BlockType.DBT == block.type:
683
+ block.set_default_configurations()
684
+
685
+ pipeline.add_block(
686
+ block,
687
+ downstream_block_uuids=downstream_block_uuids,
688
+ upstream_block_uuids=upstream_block_uuids,
689
+ priority=priority,
690
+ widget=widget,
691
+ )
680
692
 
681
693
  @classmethod
682
694
  def block_class_from_type(self, block_type: str, language=None, pipeline=None) -> 'Block':
@@ -1052,8 +1064,10 @@ class Block(DataIntegrationMixin, SparkBlock, ProjectPlatformAccessible):
1052
1064
  logger=logger,
1053
1065
  logging_tags=logging_tags,
1054
1066
  )
1055
- conditional_message += \
1056
- f'Conditional block {conditional_block.uuid} evaluated to {block_result}.\n'
1067
+ conditional_message = (
1068
+ f'{conditional_message}Conditional block '
1069
+ f'{conditional_block.uuid} evaluated to {block_result}.\n'
1070
+ )
1057
1071
  result = result and block_result
1058
1072
 
1059
1073
  # Print result to block output
@@ -1071,6 +1085,33 @@ class Block(DataIntegrationMixin, SparkBlock, ProjectPlatformAccessible):
1071
1085
  callback_arr += self.callback_blocks
1072
1086
 
1073
1087
  try:
1088
+ # {
1089
+ # "dynamic_block_index": 1,
1090
+ # "dynamic_block_indexes": {
1091
+ # "dynamic_b": 0
1092
+ # },
1093
+ # "dynamic_upstream_block_uuids": [
1094
+ # "child_upstreams:0:b0_0"
1095
+ # ],
1096
+ # "metadata": {
1097
+ # "dynamic_b": {
1098
+ # "block_uuid": "b1_1",
1099
+ # "upstream_blocks": [
1100
+ # "b0_0"
1101
+ # ]
1102
+ # }
1103
+ # }
1104
+ # }
1105
+ if from_notebook:
1106
+ for upstream_block in self.upstream_blocks:
1107
+ if is_dynamic_block(upstream_block) or is_dynamic_block_child(upstream_block):
1108
+ if 'dynamic_block_index' not in kwargs:
1109
+ kwargs['dynamic_block_index'] = 0
1110
+ if 'dynamic_block_indexes' not in kwargs:
1111
+ kwargs['dynamic_block_indexes'] = {}
1112
+ if upstream_block.uuid not in kwargs['dynamic_block_indexes']:
1113
+ kwargs['dynamic_block_indexes'][upstream_block.uuid] = 0
1114
+
1074
1115
  output = self.execute_sync(
1075
1116
  global_vars=global_vars,
1076
1117
  logger=logger,
@@ -1129,6 +1170,7 @@ class Block(DataIntegrationMixin, SparkBlock, ProjectPlatformAccessible):
1129
1170
  disable_json_serialization: bool = False,
1130
1171
  data_integration_runtime_settings: Dict = None,
1131
1172
  execution_partition_previous: str = None,
1173
+ metadata: Dict = None,
1132
1174
  **kwargs,
1133
1175
  ) -> Dict:
1134
1176
  if logging_tags is None:
@@ -1185,55 +1227,68 @@ class Block(DataIntegrationMixin, SparkBlock, ProjectPlatformAccessible):
1185
1227
  run_settings=run_settings,
1186
1228
  data_integration_runtime_settings=data_integration_runtime_settings,
1187
1229
  execution_partition_previous=execution_partition_previous,
1230
+ metadata=metadata,
1188
1231
  **kwargs,
1189
1232
  )
1190
1233
 
1191
- block_output = self.post_process_output(output)
1192
- variable_mapping = dict()
1193
-
1194
- if BlockType.CHART == self.type:
1195
- variable_mapping = block_output
1196
- output = dict(
1197
- output=simplejson.dumps(
1198
- block_output,
1199
- default=encode_complex,
1200
- ignore_nan=True,
1201
- ) if not disable_json_serialization else block_output,
1202
- )
1234
+ if self.configuration and self.configuration.get('disable_query_preprocessing'):
1235
+ output = dict(output=None)
1203
1236
  else:
1204
- output_count = len(block_output)
1205
- variable_keys = [f'output_{idx}' for idx in range(output_count)]
1206
- variable_mapping = dict(zip(variable_keys, block_output))
1237
+ block_output = self.post_process_output(output)
1238
+ variable_mapping = dict()
1239
+
1240
+ if BlockType.CHART == self.type:
1241
+ variable_mapping = block_output
1242
+ output = dict(
1243
+ output=simplejson.dumps(
1244
+ block_output,
1245
+ default=encode_complex,
1246
+ ignore_nan=True,
1247
+ ) if not disable_json_serialization else block_output,
1248
+ )
1249
+ else:
1250
+ output_count = len(block_output)
1251
+ variable_keys = [f'output_{idx}' for idx in range(output_count)]
1252
+ variable_mapping = dict(zip(variable_keys, block_output))
1207
1253
 
1208
- if store_variables and \
1209
- self.pipeline and \
1210
- self.pipeline.type != PipelineType.INTEGRATION:
1254
+ if store_variables and \
1255
+ self.pipeline and \
1256
+ self.pipeline.type != PipelineType.INTEGRATION:
1211
1257
 
1212
- try:
1213
- self.store_variables(
1214
- variable_mapping,
1215
- execution_partition=execution_partition,
1216
- override_outputs=True,
1217
- spark=self.__get_spark_session_from_global_vars(global_vars=global_vars),
1218
- dynamic_block_uuid=dynamic_block_uuid,
1219
- )
1220
- except ValueError as e:
1221
- if str(e) == 'Circular reference detected':
1222
- raise ValueError(
1223
- 'Please provide dataframe or json serializable data as output.'
1258
+ try:
1259
+ self.store_variables(
1260
+ variable_mapping,
1261
+ execution_partition=execution_partition,
1262
+ override_outputs=True,
1263
+ spark=self.__get_spark_session_from_global_vars(
1264
+ global_vars=global_vars,
1265
+ ),
1266
+ dynamic_block_uuid=dynamic_block_uuid,
1267
+ )
1268
+ except ValueError as e:
1269
+ if str(e) == 'Circular reference detected':
1270
+ raise ValueError(
1271
+ 'Please provide dataframe or json serializable data as output.'
1272
+ )
1273
+ raise e
1274
+ # Reset outputs cache
1275
+ self._outputs = None
1276
+
1277
+ if BlockType.CHART != self.type:
1278
+ if analyze_outputs:
1279
+ self.analyze_outputs(
1280
+ variable_mapping,
1281
+ execution_partition=execution_partition,
1282
+ )
1283
+ else:
1284
+ self.analyze_outputs(
1285
+ variable_mapping,
1286
+ execution_partition=execution_partition,
1287
+ shape_only=True,
1224
1288
  )
1225
- raise e
1226
- # Reset outputs cache
1227
- self._outputs = None
1228
1289
 
1229
1290
  if update_status:
1230
1291
  self.status = BlockStatus.EXECUTED
1231
-
1232
- if BlockType.CHART != self.type:
1233
- if analyze_outputs:
1234
- self.analyze_outputs(variable_mapping)
1235
- else:
1236
- self.analyze_outputs(variable_mapping, shape_only=True)
1237
1292
  except Exception as err:
1238
1293
  if update_status:
1239
1294
  self.status = BlockStatus.FAILED
@@ -1348,6 +1403,59 @@ class Block(DataIntegrationMixin, SparkBlock, ProjectPlatformAccessible):
1348
1403
 
1349
1404
  return block_function
1350
1405
 
1406
+ def __get_outputs_from_input_vars(
1407
+ self,
1408
+ block_run_outputs_cache: Dict[str, List] = None,
1409
+ dynamic_block_index: int = None,
1410
+ dynamic_block_indexes: Dict = None,
1411
+ dynamic_upstream_block_uuids: List[str] = None,
1412
+ execution_partition: str = None,
1413
+ from_notebook: bool = False,
1414
+ global_vars: Dict = None,
1415
+ input_args: List = None,
1416
+ metadata: Dict = None,
1417
+ ) -> Tuple[Dict, List, Dict, List[str]]:
1418
+ # Only fetch the input variables that the destination block explicitly declares.
1419
+ # If all the input variables are fetched, there is a chance that a lot of data from
1420
+ # an upstream source block is loaded just to be used as inputs for the block’s
1421
+ # decorated functions. Only do this for the notebook because
1422
+ if from_notebook and self.is_data_integration():
1423
+ input_vars, kwargs_vars, upstream_block_uuids = \
1424
+ self.fetch_input_variables_and_catalog(
1425
+ input_args,
1426
+ execution_partition,
1427
+ global_vars,
1428
+ dynamic_block_index=dynamic_block_index,
1429
+ dynamic_block_indexes=dynamic_block_indexes,
1430
+ dynamic_upstream_block_uuids=dynamic_upstream_block_uuids,
1431
+ from_notebook=from_notebook,
1432
+ )
1433
+ else:
1434
+ input_vars, kwargs_vars, upstream_block_uuids = self.fetch_input_variables(
1435
+ input_args,
1436
+ block_run_outputs_cache=block_run_outputs_cache,
1437
+ dynamic_block_index=dynamic_block_index,
1438
+ dynamic_block_indexes=dynamic_block_indexes,
1439
+ dynamic_upstream_block_uuids=dynamic_upstream_block_uuids,
1440
+ execution_partition=execution_partition,
1441
+ from_notebook=from_notebook,
1442
+ global_vars=global_vars,
1443
+ metadata=metadata,
1444
+ )
1445
+
1446
+ outputs_from_input_vars = {}
1447
+ if input_args is None:
1448
+ upstream_block_uuids_length = len(upstream_block_uuids)
1449
+ for idx, input_var in enumerate(input_vars):
1450
+ if idx < upstream_block_uuids_length:
1451
+ upstream_block_uuid = upstream_block_uuids[idx]
1452
+ outputs_from_input_vars[upstream_block_uuid] = input_var
1453
+ outputs_from_input_vars[f'df_{idx + 1}'] = input_var
1454
+ else:
1455
+ outputs_from_input_vars = dict()
1456
+
1457
+ return outputs_from_input_vars, input_vars, kwargs_vars, upstream_block_uuids
1458
+
1351
1459
  def execute_block(
1352
1460
  self,
1353
1461
  block_run_outputs_cache: Dict[str, List] = None,
@@ -1367,6 +1475,7 @@ class Block(DataIntegrationMixin, SparkBlock, ProjectPlatformAccessible):
1367
1475
  run_settings: Dict = None,
1368
1476
  data_integration_runtime_settings: str = None,
1369
1477
  execution_partition_previous: str = None,
1478
+ metadata: Dict = None,
1370
1479
  **kwargs,
1371
1480
  ) -> Dict:
1372
1481
  if logging_tags is None:
@@ -1388,25 +1497,8 @@ class Block(DataIntegrationMixin, SparkBlock, ProjectPlatformAccessible):
1388
1497
  logging_tags=logging_tags,
1389
1498
  ):
1390
1499
  # Fetch input variables
1391
-
1392
- # Only fetch the input variables that the destination block explicitly declares.
1393
- # If all the input variables are fetched, there is a chance that a lot of data from
1394
- # an upstream source block is loaded just to be used as inputs for the block’s
1395
- # decorated functions. Only do this for the notebook because
1396
- if from_notebook and self.is_data_integration():
1397
- input_vars, kwargs_vars, upstream_block_uuids = \
1398
- self.fetch_input_variables_and_catalog(
1399
- input_args,
1400
- execution_partition,
1401
- global_vars,
1402
- dynamic_block_index=dynamic_block_index,
1403
- dynamic_block_indexes=dynamic_block_indexes,
1404
- dynamic_upstream_block_uuids=dynamic_upstream_block_uuids,
1405
- from_notebook=from_notebook,
1406
- )
1407
- else:
1408
- input_vars, kwargs_vars, upstream_block_uuids = self.fetch_input_variables(
1409
- input_args,
1500
+ outputs_from_input_vars, input_vars, kwargs_vars, upstream_block_uuids = \
1501
+ self.__get_outputs_from_input_vars(
1410
1502
  block_run_outputs_cache=block_run_outputs_cache,
1411
1503
  dynamic_block_index=dynamic_block_index,
1412
1504
  dynamic_block_indexes=dynamic_block_indexes,
@@ -1414,23 +1506,15 @@ class Block(DataIntegrationMixin, SparkBlock, ProjectPlatformAccessible):
1414
1506
  execution_partition=execution_partition,
1415
1507
  from_notebook=from_notebook,
1416
1508
  global_vars=global_vars,
1509
+ input_args=input_args,
1510
+ metadata=metadata,
1417
1511
  )
1418
1512
 
1419
- outputs_from_input_vars = {}
1420
- if input_args is None:
1421
- upstream_block_uuids_length = len(upstream_block_uuids)
1422
- for idx, input_var in enumerate(input_vars):
1423
- if idx < upstream_block_uuids_length:
1424
- upstream_block_uuid = upstream_block_uuids[idx]
1425
- outputs_from_input_vars[upstream_block_uuid] = input_var
1426
- outputs_from_input_vars[f'df_{idx + 1}'] = input_var
1427
- else:
1428
- outputs_from_input_vars = dict()
1429
-
1430
1513
  global_vars_copy = global_vars.copy()
1431
1514
  for kwargs_var in kwargs_vars:
1432
1515
  if kwargs_var:
1433
- global_vars_copy.update(kwargs_var)
1516
+ if isinstance(global_vars_copy, dict) and isinstance(kwargs_var, dict):
1517
+ global_vars_copy.update(kwargs_var)
1434
1518
 
1435
1519
  outputs = self._execute_block(
1436
1520
  outputs_from_input_vars,
@@ -1641,9 +1725,9 @@ class Block(DataIntegrationMixin, SparkBlock, ProjectPlatformAccessible):
1641
1725
  # Initialize module
1642
1726
  block_uuid = self.uuid
1643
1727
  block_file_path = self.file_path
1644
- if self.replicated_block:
1645
- block_uuid = self.replicated_block
1728
+ if self.replicated_block and self.replicated_block_object:
1646
1729
  block_file_path = self.replicated_block_object.file_path
1730
+ block_uuid = self.replicated_block
1647
1731
 
1648
1732
  spec = importlib.util.spec_from_file_location(block_uuid, block_file_path)
1649
1733
  module = importlib.util.module_from_spec(spec)
@@ -1686,6 +1770,7 @@ class Block(DataIntegrationMixin, SparkBlock, ProjectPlatformAccessible):
1686
1770
  from_notebook: bool = False,
1687
1771
  global_vars: Dict = None,
1688
1772
  upstream_block_uuids: List[str] = None,
1773
+ metadata: Dict = None,
1689
1774
  ) -> Tuple[List, List, List]:
1690
1775
  """
1691
1776
  Fetch input variables for the current block's execution.
@@ -1709,6 +1794,13 @@ class Block(DataIntegrationMixin, SparkBlock, ProjectPlatformAccessible):
1709
1794
  Tuple[List, List, List]: A tuple containing the input variables, kwargs variables, and
1710
1795
  upstream block UUIDs.
1711
1796
  """
1797
+
1798
+ dynamic_block_flags = []
1799
+ if is_dynamic_block(self):
1800
+ dynamic_block_flags.append(DynamicBlockFlag.DYNAMIC)
1801
+ if is_dynamic_block_child(self):
1802
+ dynamic_block_flags.append(DynamicBlockFlag.DYNAMIC_CHILD)
1803
+
1712
1804
  variables = fetch_input_variables(
1713
1805
  self.pipeline,
1714
1806
  upstream_block_uuids or self.upstream_block_uuids,
@@ -1721,6 +1813,22 @@ class Block(DataIntegrationMixin, SparkBlock, ProjectPlatformAccessible):
1721
1813
  execution_partition=execution_partition,
1722
1814
  from_notebook=from_notebook,
1723
1815
  global_vars=global_vars,
1816
+ dynamic_block_flags=dynamic_block_flags,
1817
+ metadata=metadata,
1818
+ )
1819
+
1820
+ DX_PRINTER.critical(
1821
+ block=self,
1822
+ metadata=metadata,
1823
+ dynamic_block_index=dynamic_block_index,
1824
+ dynamic_block_indexes=dynamic_block_indexes,
1825
+ dynamic_upstream_block_uuids=dynamic_upstream_block_uuids,
1826
+ dynamic_block_flags=dynamic_block_flags,
1827
+ execution_partition=execution_partition,
1828
+ upstream_block_uuids=self.upstream_block_uuids,
1829
+ upstream_block_uuids_override=upstream_block_uuids,
1830
+ variables=len(variables) if variables else 'nothing',
1831
+ __uuid='fetch_input_variables',
1724
1832
  )
1725
1833
 
1726
1834
  return variables
@@ -1753,19 +1861,30 @@ class Block(DataIntegrationMixin, SparkBlock, ProjectPlatformAccessible):
1753
1861
  ):
1754
1862
  variable_manager = self.pipeline.variable_manager
1755
1863
 
1756
- block_uuid_use = block_uuid
1757
- clean_block_uuid = True
1758
- if dynamic_block_index is not None or is_dynamic_block_child(self):
1759
- block_uuid_use = os.path.join(*block_uuid.split(':'))
1760
- clean_block_uuid = False
1864
+ block_uuid_use, changed = uuid_for_output_variables(
1865
+ self,
1866
+ block_uuid=block_uuid,
1867
+ dynamic_block_index=dynamic_block_index,
1868
+ )
1761
1869
 
1762
- return variable_manager.get_variables_by_block(
1870
+ res = variable_manager.get_variables_by_block(
1763
1871
  self.pipeline.uuid,
1764
1872
  block_uuid=block_uuid_use,
1765
- clean_block_uuid=clean_block_uuid,
1873
+ clean_block_uuid=not changed,
1766
1874
  partition=partition,
1767
1875
  )
1768
1876
 
1877
+ DX_PRINTER.debug(
1878
+ str(res),
1879
+ block=self,
1880
+ block_uuid_use=block_uuid_use,
1881
+ clean_block_uuid=not changed,
1882
+ partition=partition,
1883
+ __uuid='get_variables_by_block',
1884
+ )
1885
+
1886
+ return res
1887
+
1769
1888
  def get_variable(
1770
1889
  self,
1771
1890
  block_uuid: str,
@@ -1777,22 +1896,34 @@ class Block(DataIntegrationMixin, SparkBlock, ProjectPlatformAccessible):
1777
1896
  ):
1778
1897
  variable_manager = self.pipeline.variable_manager
1779
1898
 
1780
- block_uuid_use = block_uuid
1781
- clean_block_uuid = True
1782
- if dynamic_block_index is not None or is_dynamic_block_child(self):
1783
- block_uuid_use = os.path.join(*block_uuid.split(':'))
1784
- clean_block_uuid = False
1899
+ block_uuid_use, changed = uuid_for_output_variables(
1900
+ self,
1901
+ block_uuid=block_uuid,
1902
+ dynamic_block_index=dynamic_block_index,
1903
+ )
1785
1904
 
1786
- return variable_manager.get_variable(
1905
+ value = variable_manager.get_variable(
1787
1906
  self.pipeline.uuid,
1788
1907
  block_uuid=block_uuid_use,
1789
- clean_block_uuid=clean_block_uuid,
1908
+ clean_block_uuid=not changed,
1790
1909
  partition=partition,
1791
1910
  raise_exception=raise_exception,
1792
1911
  spark=spark,
1793
1912
  variable_uuid=variable_uuid,
1794
1913
  )
1795
1914
 
1915
+ DX_PRINTER.debug(
1916
+ 'get_variable',
1917
+ block=self,
1918
+ block_uuid_use=block_uuid_use,
1919
+ clean_block_uuid=not changed,
1920
+ partition=partition,
1921
+ value=value,
1922
+ __uuid='output_variables'
1923
+ )
1924
+
1925
+ return value
1926
+
1796
1927
  def get_variable_object(
1797
1928
  self,
1798
1929
  block_uuid: str,
@@ -1802,16 +1933,16 @@ class Block(DataIntegrationMixin, SparkBlock, ProjectPlatformAccessible):
1802
1933
  ):
1803
1934
  variable_manager = self.pipeline.variable_manager
1804
1935
 
1805
- block_uuid_use = block_uuid
1806
- clean_block_uuid = True
1807
- if dynamic_block_index is not None or is_dynamic_block_child(self):
1808
- block_uuid_use = os.path.join(*block_uuid.split(':'))
1809
- clean_block_uuid = False
1936
+ block_uuid, changed = uuid_for_output_variables(
1937
+ self,
1938
+ block_uuid=block_uuid,
1939
+ dynamic_block_index=dynamic_block_index,
1940
+ )
1810
1941
 
1811
1942
  return variable_manager.get_variable_object(
1812
1943
  self.pipeline.uuid,
1813
- block_uuid=block_uuid_use,
1814
- clean_block_uuid=clean_block_uuid,
1944
+ block_uuid=block_uuid,
1945
+ clean_block_uuid=not changed,
1815
1946
  partition=partition,
1816
1947
  spark=self.get_spark_session(),
1817
1948
  variable_uuid=variable_uuid,
@@ -1855,6 +1986,7 @@ class Block(DataIntegrationMixin, SparkBlock, ProjectPlatformAccessible):
1855
1986
  variable_type: VariableType = None,
1856
1987
  block_uuid: str = None,
1857
1988
  selected_variables: List[str] = None,
1989
+ metadata: Dict = None,
1858
1990
  ) -> List[Dict]:
1859
1991
  if self.pipeline is None:
1860
1992
  return
@@ -1862,9 +1994,17 @@ class Block(DataIntegrationMixin, SparkBlock, ProjectPlatformAccessible):
1862
1994
  if not block_uuid:
1863
1995
  block_uuid = self.uuid
1864
1996
 
1997
+ # The block_run’s block_uuid for replicated blocks will be in this format:
1998
+ # [block_uuid]:[replicated_block_uuid]
1999
+ # We need to use the original block_uuid to get the proper output.
2000
+
2001
+ # Block runs for dynamic child blocks will have the following block UUID:
2002
+ # [block.uuid]:[index]
2003
+ # Don’t use the original UUID even if the block is a replica because it will get rid of
2004
+ # the dynamic child block index.
2005
+
1865
2006
  data_products = []
1866
2007
  outputs = []
1867
- variable_manager = self.pipeline.variable_manager
1868
2008
 
1869
2009
  all_variables = self.get_variables_by_block(
1870
2010
  block_uuid=block_uuid,
@@ -1876,6 +2016,11 @@ class Block(DataIntegrationMixin, SparkBlock, ProjectPlatformAccessible):
1876
2016
  execution_partition=execution_partition,
1877
2017
  )
1878
2018
 
2019
+ DX_PRINTER.debug(
2020
+ all_variables=all_variables,
2021
+ __uuid='get_variables_by_block',
2022
+ )
2023
+
1879
2024
  for v in all_variables:
1880
2025
  if selected_variables and v not in selected_variables:
1881
2026
  continue
@@ -1894,91 +2039,22 @@ class Block(DataIntegrationMixin, SparkBlock, ProjectPlatformAccessible):
1894
2039
  sample_count=sample_count,
1895
2040
  spark=self.get_spark_session(),
1896
2041
  )
1897
- if type(data) is pd.DataFrame:
1898
- if csv_lines_only:
1899
- data = dict(
1900
- table=data.to_csv(header=True, index=False).strip('\n').split('\n')
1901
- )
1902
- else:
1903
- try:
1904
- analysis = variable_manager.get_variable(
1905
- self.pipeline.uuid,
1906
- block_uuid,
1907
- v,
1908
- dataframe_analysis_keys=['metadata', 'statistics'],
1909
- partition=execution_partition,
1910
- variable_type=VariableType.DATAFRAME_ANALYSIS,
1911
- )
1912
- except Exception:
1913
- analysis = None
1914
- if analysis is not None and \
1915
- (analysis.get('statistics') or analysis.get('metadata')):
1916
-
1917
- stats = analysis.get('statistics', {})
1918
- column_types = (analysis.get('metadata') or {}).get('column_types', {})
1919
- row_count = stats.get('original_row_count', stats.get('count'))
1920
- column_count = stats.get('original_column_count', len(column_types))
1921
- else:
1922
- row_count, column_count = data.shape
1923
-
1924
- columns_to_display = data.columns.tolist()[:DATAFRAME_ANALYSIS_MAX_COLUMNS]
1925
- data = dict(
1926
- sample_data=dict(
1927
- columns=columns_to_display,
1928
- rows=json.loads(
1929
- data[columns_to_display].to_json(orient='split')
1930
- )['data']
1931
- ),
1932
- shape=[row_count, column_count],
1933
- type=DataType.TABLE,
1934
- variable_uuid=v,
1935
- )
1936
- data_products.append(data)
1937
- continue
1938
- elif is_geo_dataframe(data):
1939
- data = dict(
1940
- text_data=f'''Use the code in a scratchpad to get the output of the block:
1941
-
1942
- from mage_ai.data_preparation.variable_manager import get_variable
1943
- df = get_variable('{self.pipeline.uuid}', '{self.uuid}', 'df')
1944
- ''',
1945
- type=DataType.TEXT,
1946
- variable_uuid=v,
1947
- )
1948
- elif type(data) is str:
1949
- data = dict(
1950
- text_data=data,
1951
- type=DataType.TEXT,
1952
- variable_uuid=v,
1953
- )
1954
- elif type(data) is dict or type(data) is list:
1955
- data = dict(
1956
- text_data=simplejson.dumps(
1957
- data,
1958
- default=datetime.isoformat,
1959
- ignore_nan=True,
1960
- ),
1961
- type=DataType.TEXT,
1962
- variable_uuid=v,
1963
- )
1964
- elif is_spark_dataframe(data):
1965
- df = data.toPandas()
1966
- columns_to_display = df.columns.tolist()[:DATAFRAME_ANALYSIS_MAX_COLUMNS]
1967
- data = dict(
1968
- sample_data=dict(
1969
- columns=columns_to_display,
1970
- rows=json.loads(df[columns_to_display].to_json(orient='split'))['data']
1971
- ),
1972
- type=DataType.TABLE,
1973
- variable_uuid=v,
1974
- )
2042
+ data, is_data_product = self.__format_output_data(
2043
+ data,
2044
+ v,
2045
+ block_uuid=block_uuid,
2046
+ csv_lines_only=csv_lines_only,
2047
+ execution_partition=execution_partition,
2048
+ )
2049
+ if is_data_product:
1975
2050
  data_products.append(data)
1976
- continue
1977
- outputs.append(data)
2051
+ else:
2052
+ outputs.append(data)
1978
2053
  return outputs + data_products
1979
2054
 
1980
2055
  async def __get_outputs_async(
1981
2056
  self,
2057
+ csv_lines_only: bool = False,
1982
2058
  execution_partition: str = None,
1983
2059
  include_print_outputs: bool = True,
1984
2060
  sample_count: int = DATAFRAME_SAMPLE_COUNT_PREVIEW,
@@ -2021,19 +2097,55 @@ df = get_variable('{self.pipeline.uuid}', '{self.uuid}', 'df')
2021
2097
  sample_count=sample_count,
2022
2098
  spark=self.get_spark_session(),
2023
2099
  )
2024
- if type(data) is pd.DataFrame:
2100
+ data, is_data_product = self.__format_output_data(
2101
+ data,
2102
+ v,
2103
+ block_uuid=block_uuid,
2104
+ csv_lines_only=csv_lines_only,
2105
+ execution_partition=execution_partition,
2106
+ )
2107
+ if is_data_product:
2108
+ data_products.append(data)
2109
+ else:
2110
+ outputs.append(data)
2111
+ return outputs + data_products
2112
+
2113
+ def __format_output_data(
2114
+ self,
2115
+ data: Any,
2116
+ variable_uuid: str,
2117
+ block_uuid: str = None,
2118
+ csv_lines_only: bool = False,
2119
+ execution_partition: str = None,
2120
+ ) -> Tuple[Dict, bool]:
2121
+ """
2122
+ Takes variable data and formats it to return to the frontend.
2123
+
2124
+ Returns:
2125
+ Tuple[Dict, bool]: Tuple of the formatted data and is_data_product boolean. Data product
2126
+ outputs and non data product outputs are handled slightly differently.
2127
+ """
2128
+ variable_manager = self.pipeline.variable_manager
2129
+ if isinstance(data, pd.DataFrame):
2130
+ if csv_lines_only:
2131
+ data = dict(
2132
+ table=data.to_csv(header=True, index=False).strip('\n').split('\n')
2133
+ )
2134
+ else:
2025
2135
  try:
2026
2136
  analysis = variable_manager.get_variable(
2027
2137
  self.pipeline.uuid,
2028
2138
  block_uuid,
2029
- v,
2139
+ variable_uuid,
2030
2140
  dataframe_analysis_keys=['metadata', 'statistics'],
2031
2141
  partition=execution_partition,
2032
2142
  variable_type=VariableType.DATAFRAME_ANALYSIS,
2033
2143
  )
2034
2144
  except Exception:
2035
2145
  analysis = None
2036
- if analysis is not None:
2146
+ if analysis is not None and \
2147
+ (analysis.get('statistics') or analysis.get('metadata')):
2148
+
2037
2149
  stats = analysis.get('statistics', {})
2038
2150
  column_types = (analysis.get('metadata') or {}).get('column_types', {})
2039
2151
  row_count = stats.get('original_row_count', stats.get('count'))
@@ -2045,55 +2157,90 @@ df = get_variable('{self.pipeline.uuid}', '{self.uuid}', 'df')
2045
2157
  data = dict(
2046
2158
  sample_data=dict(
2047
2159
  columns=columns_to_display,
2048
- rows=json.loads(data[columns_to_display].to_json(orient='split'))['data']
2160
+ rows=json.loads(
2161
+ data[columns_to_display].to_json(orient='split')
2162
+ )['data']
2049
2163
  ),
2050
2164
  shape=[row_count, column_count],
2051
2165
  type=DataType.TABLE,
2052
- variable_uuid=v,
2166
+ variable_uuid=variable_uuid,
2053
2167
  )
2054
- data_products.append(data)
2055
- continue
2056
- elif is_geo_dataframe(data):
2057
- data = dict(
2058
- text_data=f'''Use the code in a scratchpad to get the output of the block:
2168
+ return data, True
2169
+ elif isinstance(data, pl.DataFrame):
2170
+ try:
2171
+ analysis = variable_manager.get_variable(
2172
+ self.pipeline.uuid,
2173
+ block_uuid,
2174
+ variable_uuid,
2175
+ dataframe_analysis_keys=['statistics'],
2176
+ partition=execution_partition,
2177
+ variable_type=VariableType.DATAFRAME_ANALYSIS,
2178
+ )
2179
+ except Exception:
2180
+ analysis = None
2181
+ if analysis is not None:
2182
+ stats = analysis.get('statistics', {})
2183
+ row_count = stats.get('original_row_count')
2184
+ column_count = stats.get('original_column_count')
2185
+ else:
2186
+ row_count, column_count = data.shape
2187
+ columns_to_display = data.columns[:DATAFRAME_ANALYSIS_MAX_COLUMNS]
2188
+ data = dict(
2189
+ sample_data=dict(
2190
+ columns=columns_to_display,
2191
+ rows=[
2192
+ list(row.values()) for row in json.loads(
2193
+ data[columns_to_display].write_json(row_oriented=True)
2194
+ )
2195
+ ]
2196
+ ),
2197
+ shape=[row_count, column_count],
2198
+ type=DataType.TABLE,
2199
+ variable_uuid=variable_uuid,
2200
+ )
2201
+ return data, True
2202
+ elif is_geo_dataframe(data):
2203
+ data = dict(
2204
+ text_data=f'''Use the code in a scratchpad to get the output of the block:
2059
2205
 
2060
2206
  from mage_ai.data_preparation.variable_manager import get_variable
2061
- df = get_variable('{self.pipeline.uuid}', '{block_uuid}', 'df')
2207
+ df = get_variable('{self.pipeline.uuid}', '{self.uuid}', 'df')
2062
2208
  ''',
2063
- type=DataType.TEXT,
2064
- variable_uuid=v,
2065
- )
2066
- elif type(data) is str:
2067
- data = dict(
2068
- text_data=data,
2069
- type=DataType.TEXT,
2070
- variable_uuid=v,
2071
- )
2072
- elif type(data) is dict or type(data) is list:
2073
- data = dict(
2074
- text_data=simplejson.dumps(
2075
- data,
2076
- default=datetime.isoformat,
2077
- ignore_nan=True,
2078
- ),
2079
- type=DataType.TEXT,
2080
- variable_uuid=v,
2081
- )
2082
- elif is_spark_dataframe(data):
2083
- df = data.toPandas()
2084
- columns_to_display = df.columns.tolist()[:DATAFRAME_ANALYSIS_MAX_COLUMNS]
2085
- data = dict(
2086
- sample_data=dict(
2087
- columns=columns_to_display,
2088
- rows=json.loads(df[columns_to_display].to_json(orient='split'))['data']
2089
- ),
2090
- type=DataType.TABLE,
2091
- variable_uuid=v,
2092
- )
2093
- data_products.append(data)
2094
- continue
2095
- outputs.append(data)
2096
- return outputs + data_products
2209
+ type=DataType.TEXT,
2210
+ variable_uuid=variable_uuid,
2211
+ )
2212
+ return data, False
2213
+ elif type(data) is str:
2214
+ data = dict(
2215
+ text_data=data,
2216
+ type=DataType.TEXT,
2217
+ variable_uuid=variable_uuid,
2218
+ )
2219
+ return data, False
2220
+ elif type(data) is dict or type(data) is list:
2221
+ data = dict(
2222
+ text_data=simplejson.dumps(
2223
+ data,
2224
+ default=datetime.isoformat,
2225
+ ignore_nan=True,
2226
+ ),
2227
+ type=DataType.TEXT,
2228
+ variable_uuid=variable_uuid,
2229
+ )
2230
+ return data, False
2231
+ elif is_spark_dataframe(data):
2232
+ df = data.toPandas()
2233
+ columns_to_display = df.columns.tolist()[:DATAFRAME_ANALYSIS_MAX_COLUMNS]
2234
+ data = dict(
2235
+ sample_data=dict(
2236
+ columns=columns_to_display,
2237
+ rows=json.loads(df[columns_to_display].to_json(orient='split'))['data']
2238
+ ),
2239
+ type=DataType.TABLE,
2240
+ variable_uuid=variable_uuid,
2241
+ )
2242
+ return data, True
2243
+ return data, False
2097
2244
 
2098
2245
  def __save_outputs_prepare(self, outputs, override_output_variable: bool = False) -> Dict:
2099
2246
  variable_mapping = dict()
@@ -2225,7 +2372,7 @@ df = get_variable('{self.pipeline.uuid}', '{block_uuid}', 'df')
2225
2372
  self.replicated_block and \
2226
2373
  BlockType.GLOBAL_DATA_PRODUCT != self.type:
2227
2374
 
2228
- file_path = self.file.file_path
2375
+ file_path = self.file_path
2229
2376
  if not os.path.isfile(file_path):
2230
2377
  data['error'] = dict(
2231
2378
  error='No such file or directory',
@@ -2384,6 +2531,9 @@ df = get_variable('{self.pipeline.uuid}', '{block_uuid}', 'df')
2384
2531
  widget=False,
2385
2532
  error_if_file_missing: bool = True,
2386
2533
  ) -> 'Block':
2534
+ if self.replicated_block:
2535
+ return self
2536
+
2387
2537
  if error_if_file_missing and not self.file.exists():
2388
2538
  raise Exception(f'File for block {self.uuid} does not exist at {self.file.file_path}.')
2389
2539
 
@@ -2400,6 +2550,9 @@ df = get_variable('{self.pipeline.uuid}', '{block_uuid}', 'df')
2400
2550
  error_if_file_missing: bool = True,
2401
2551
  widget: bool = False,
2402
2552
  ) -> 'Block':
2553
+ if self.replicated_block:
2554
+ return self
2555
+
2403
2556
  if error_if_file_missing and not self.file.exists():
2404
2557
  raise Exception(f'File for block {self.uuid} does not exist at {self.file.file_path}.')
2405
2558
 
@@ -2583,7 +2736,12 @@ df = get_variable('{self.pipeline.uuid}', '{block_uuid}', 'df')
2583
2736
  logging_tags=logging_tags,
2584
2737
  )
2585
2738
 
2586
- def analyze_outputs(self, variable_mapping, shape_only: bool = False) -> None:
2739
+ def analyze_outputs(
2740
+ self,
2741
+ variable_mapping,
2742
+ execution_partition: str = None,
2743
+ shape_only: bool = False,
2744
+ ) -> None:
2587
2745
  if self.pipeline is None:
2588
2746
  return
2589
2747
  for uuid, data in variable_mapping.items():
@@ -2599,6 +2757,7 @@ df = get_variable('{self.pipeline.uuid}', '{block_uuid}', 'df')
2599
2757
  original_column_count=data.shape[1],
2600
2758
  ),
2601
2759
  ),
2760
+ partition=execution_partition,
2602
2761
  variable_type=VariableType.DATAFRAME_ANALYSIS,
2603
2762
  )
2604
2763
  continue
@@ -2626,6 +2785,7 @@ df = get_variable('{self.pipeline.uuid}', '{block_uuid}', 'df')
2626
2785
  insights=analysis['insights'],
2627
2786
  suggestions=analysis['suggestions'],
2628
2787
  ),
2788
+ partition=execution_partition,
2629
2789
  variable_type=VariableType.DATAFRAME_ANALYSIS,
2630
2790
  )
2631
2791
  except Exception:
@@ -2633,6 +2793,20 @@ df = get_variable('{self.pipeline.uuid}', '{block_uuid}', 'df')
2633
2793
  # TODO: we use to silently fail, but it looks bad when using BigQuery
2634
2794
  # print('\nFailed to analyze dataframe:')
2635
2795
  # print(traceback.format_exc())
2796
+ elif type(data) is pl.DataFrame:
2797
+ self.pipeline.variable_manager.add_variable(
2798
+ self.pipeline.uuid,
2799
+ self.uuid,
2800
+ uuid,
2801
+ dict(
2802
+ statistics=dict(
2803
+ original_row_count=data.shape[0],
2804
+ original_column_count=data.shape[1],
2805
+ ),
2806
+ ),
2807
+ partition=execution_partition,
2808
+ variable_type=VariableType.DATAFRAME_ANALYSIS,
2809
+ )
2636
2810
 
2637
2811
  def set_global_vars(self, global_vars: Dict) -> None:
2638
2812
  self.global_vars = global_vars
@@ -2794,11 +2968,11 @@ df = get_variable('{self.pipeline.uuid}', '{block_uuid}', 'df')
2794
2968
  ) -> Dict:
2795
2969
  self.dynamic_block_uuid = dynamic_block_uuid
2796
2970
 
2797
- block_uuid = self.uuid
2798
- clean_block_uuid = True
2799
- if dynamic_block_uuid is not None:
2800
- block_uuid = os.path.join(*dynamic_block_uuid.split(':'))
2801
- clean_block_uuid = False
2971
+ block_uuid, changed = uuid_for_output_variables(
2972
+ self,
2973
+ block_uuid=self.uuid,
2974
+ dynamic_block_uuid=dynamic_block_uuid,
2975
+ )
2802
2976
 
2803
2977
  if self.pipeline is None:
2804
2978
  return
@@ -2807,7 +2981,7 @@ df = get_variable('{self.pipeline.uuid}', '{block_uuid}', 'df')
2807
2981
  self.pipeline.uuid,
2808
2982
  block_uuid=block_uuid,
2809
2983
  partition=execution_partition,
2810
- clean_block_uuid=clean_block_uuid,
2984
+ clean_block_uuid=not changed,
2811
2985
  )
2812
2986
 
2813
2987
  variable_mapping = self.__consolidate_variables(variable_mapping)
@@ -2844,24 +3018,23 @@ df = get_variable('{self.pipeline.uuid}', '{block_uuid}', 'df')
2844
3018
  dynamic_block_uuid=dynamic_block_uuid,
2845
3019
  )
2846
3020
 
2847
- block_uuid = self.uuid
2848
- clean_block_uuid = True
2849
- if dynamic_block_uuid is not None:
2850
- block_uuid = os.path.join(*dynamic_block_uuid.split(':'))
2851
- clean_block_uuid = False
3021
+ block_uuid, changed = uuid_for_output_variables(
3022
+ self,
3023
+ block_uuid=self.uuid,
3024
+ dynamic_block_uuid=dynamic_block_uuid,
3025
+ )
2852
3026
 
2853
3027
  for uuid, data in variables_data['variable_mapping'].items():
2854
3028
  if spark is not None and self.pipeline.type == PipelineType.PYSPARK \
2855
3029
  and type(data) is pd.DataFrame:
2856
3030
  data = spark.createDataFrame(data)
2857
-
2858
3031
  self.pipeline.variable_manager.add_variable(
2859
3032
  self.pipeline.uuid,
2860
3033
  block_uuid,
2861
3034
  uuid,
2862
3035
  data,
2863
3036
  partition=execution_partition,
2864
- clean_block_uuid=clean_block_uuid,
3037
+ clean_block_uuid=not changed,
2865
3038
  )
2866
3039
 
2867
3040
  for uuid in variables_data['removed_variables']:
@@ -2888,11 +3061,11 @@ df = get_variable('{self.pipeline.uuid}', '{block_uuid}', 'df')
2888
3061
  dynamic_block_uuid,
2889
3062
  )
2890
3063
 
2891
- block_uuid = self.uuid
2892
- clean_block_uuid = True
2893
- if dynamic_block_uuid is not None:
2894
- block_uuid = os.path.join(*dynamic_block_uuid.split(':'))
2895
- clean_block_uuid = False
3064
+ block_uuid, changed = uuid_for_output_variables(
3065
+ self,
3066
+ block_uuid=self.uuid,
3067
+ dynamic_block_uuid=dynamic_block_uuid,
3068
+ )
2896
3069
 
2897
3070
  for uuid, data in variables_data['variable_mapping'].items():
2898
3071
  if spark is not None and type(data) is pd.DataFrame:
@@ -2904,7 +3077,7 @@ df = get_variable('{self.pipeline.uuid}', '{block_uuid}', 'df')
2904
3077
  uuid,
2905
3078
  data,
2906
3079
  partition=execution_partition,
2907
- clean_block_uuid=clean_block_uuid,
3080
+ clean_block_uuid=not changed,
2908
3081
  )
2909
3082
 
2910
3083
  for uuid in variables_data['removed_variables']:
@@ -2912,7 +3085,7 @@ df = get_variable('{self.pipeline.uuid}', '{block_uuid}', 'df')
2912
3085
  self.pipeline.uuid,
2913
3086
  block_uuid,
2914
3087
  uuid,
2915
- clean_block_uuid=clean_block_uuid,
3088
+ clean_block_uuid=not changed,
2916
3089
  )
2917
3090
 
2918
3091
  def input_variables(self, execution_partition: str = None) -> Dict[str, List[str]]:
@@ -3055,18 +3228,72 @@ df = get_variable('{self.pipeline.uuid}', '{block_uuid}', 'df')
3055
3228
  2. Update the folder of variable
3056
3229
  3. Update upstream and downstream relationships
3057
3230
  """
3231
+ file_extension = Path(self.file_path).suffix if self.file_path else ''
3232
+ directory_name = self.file_directory_name(self.type)
3233
+
3058
3234
  old_uuid = self.uuid
3059
3235
  # This has to be here
3060
3236
  old_file_path = self.file_path
3061
3237
  block_content = self.content
3062
3238
 
3239
+ # load_titanic
3063
3240
  new_uuid = clean_name(name)
3064
3241
  self.name = name
3065
3242
  self.uuid = new_uuid
3243
+
3244
+ # This file has a path in its file_source that must be updated.
3245
+ if project_platform_activated() and \
3246
+ self.file_source_path() and \
3247
+ add_absolute_path(self.file_source_path()) == self.file_path:
3248
+
3249
+ # /home/src/data-vault/perftools/mage/data_loaders/team/illusory_glitter
3250
+ old_file_path_without_extension = str(Path(old_file_path).with_suffix(''))
3251
+ # /home/src/data-vault/perftools/mage/data_loaders/team
3252
+ old_file_path_without_uuid = str(Path(old_file_path_without_extension.replace(
3253
+ str(Path(old_uuid)),
3254
+ '',
3255
+ )))
3256
+
3257
+ # perftools/mage/data_loaders/team
3258
+ old_file_path_without_repo_path = remove_base_repo_path(old_file_path_without_uuid)
3259
+ # perftools/mage
3260
+ path_without_block_directory = str(old_file_path_without_repo_path).split(
3261
+ directory_name,
3262
+ )[0]
3263
+
3264
+ file_extension_new = Path(self.uuid).suffix or file_extension
3265
+ # perftools/mage/data_loaders/load_titanic.py
3266
+ new_path = str(Path(os.path.join(
3267
+ path_without_block_directory,
3268
+ directory_name,
3269
+ str(Path(self.uuid).with_suffix('')),
3270
+ )).with_suffix(file_extension_new))
3271
+
3272
+ configuration = self.configuration or {}
3273
+ if not configuration.get('file_source'):
3274
+ configuration['file_source'] = {}
3275
+ configuration['file_source']['path'] = new_path
3276
+ self.configuration = configuration
3277
+
3066
3278
  # This has to be here
3067
3279
  new_file_path = self.file_path
3068
3280
 
3069
3281
  if self.pipeline is not None:
3282
+ DX_PRINTER.critical(
3283
+ block=self,
3284
+ old_uuid=old_uuid,
3285
+ old_file_path=old_file_path,
3286
+ block_content=block_content,
3287
+ new_uuid=new_uuid,
3288
+ name=self.name,
3289
+ uuid=self.uuid,
3290
+ file_path=new_file_path,
3291
+ pipeline=self.pipeline.uuid,
3292
+ repo_path=self.pipeline.repo_path,
3293
+ configuration=self.configuration,
3294
+ __uuid='__update_name',
3295
+ )
3296
+
3070
3297
  if self.pipeline.has_block(
3071
3298
  new_uuid,
3072
3299
  block_type=self.type,
@@ -3110,7 +3337,7 @@ df = get_variable('{self.pipeline.uuid}', '{block_uuid}', 'df')
3110
3337
  language=self.language,
3111
3338
  pipeline=self.pipeline,
3112
3339
  )
3113
- cache.remove_pipeline(old_block, self.pipeline.uuid)
3340
+ cache.remove_pipeline(old_block, self.pipeline.uuid, self.pipeline.repo_path)
3114
3341
  else:
3115
3342
  cache.move_pipelines(self, dict(
3116
3343
  type=self.type,