mage-ai 0.9.74__py3-none-any.whl → 0.9.76__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 (374) hide show
  1. mage_ai/ai/llm_pipeline_wizard.py +6 -4
  2. mage_ai/ai/openai_client.py +7 -5
  3. mage_ai/api/policies/PipelineSchedulePolicy.py +1 -0
  4. mage_ai/api/presenters/PipelineSchedulePresenter.py +11 -2
  5. mage_ai/api/resources/PipelineScheduleResource.py +11 -3
  6. mage_ai/api/resources/PipelineTriggerResource.py +3 -1
  7. mage_ai/api/resources/SessionResource.py +2 -2
  8. mage_ai/api/resources/SyncResource.py +1 -1
  9. mage_ai/api/resources/UserResource.py +1 -1
  10. mage_ai/data_integrations/destinations/constants.py +3 -0
  11. mage_ai/data_integrations/sources/constants.py +2 -0
  12. mage_ai/data_preparation/models/block/__init__.py +4 -5
  13. mage_ai/data_preparation/models/block/dbt/dbt_adapter.py +20 -8
  14. mage_ai/data_preparation/models/block/dynamic/constants.py +0 -1
  15. mage_ai/data_preparation/models/block/dynamic/counter.py +1 -3
  16. mage_ai/data_preparation/models/block/r/__init__.py +16 -5
  17. mage_ai/data_preparation/models/block/sql/__init__.py +2 -0
  18. mage_ai/data_preparation/models/block/sql/mssql.py +8 -0
  19. mage_ai/data_preparation/models/block/sql/utils/shared.py +6 -2
  20. mage_ai/data_preparation/models/constants.py +2 -1
  21. mage_ai/data_preparation/models/pipeline.py +5 -0
  22. mage_ai/data_preparation/templates/constants.py +7 -0
  23. mage_ai/data_preparation/templates/data_exporters/streaming/elasticsearch.yaml +3 -0
  24. mage_ai/data_preparation/templates/data_loaders/airtable.py +28 -0
  25. mage_ai/data_preparation/templates/data_loaders/streaming/nats.yaml +6 -3
  26. mage_ai/data_preparation/templates/repo/io_config.yaml +2 -0
  27. mage_ai/io/airtable.py +104 -0
  28. mage_ai/io/base.py +30 -1
  29. mage_ai/io/bigquery.py +36 -0
  30. mage_ai/io/config.py +5 -0
  31. mage_ai/io/mssql.py +5 -0
  32. mage_ai/io/mysql.py +6 -1
  33. mage_ai/io/oracledb.py +2 -4
  34. mage_ai/io/postgres.py +38 -19
  35. mage_ai/io/redshift.py +13 -0
  36. mage_ai/io/sql.py +1 -0
  37. mage_ai/io/utils.py +18 -0
  38. mage_ai/orchestration/db/__init__.py +23 -3
  39. mage_ai/orchestration/db/models/schedules.py +2 -5
  40. mage_ai/orchestration/db/models/secrets.py +11 -1
  41. mage_ai/orchestration/metrics/pipeline_run.py +1 -1
  42. mage_ai/orchestration/pipeline_scheduler_original.py +4 -5
  43. mage_ai/orchestration/pipeline_scheduler_project_platform.py +4 -5
  44. mage_ai/orchestration/utils/distributed_lock.py +8 -1
  45. mage_ai/server/api/triggers.py +9 -0
  46. mage_ai/server/constants.py +1 -1
  47. mage_ai/server/frontend_dist/404.html +3 -3
  48. mage_ai/server/frontend_dist/_next/static/chunks/{1187-839336d276186105.js → 1187-4560c3895e1d7099.js} +1 -1
  49. mage_ai/server/frontend_dist/_next/static/chunks/{1598-0adca9dce3ba4c60.js → 1598-cbf3f5a6078fc3f5.js} +1 -1
  50. mage_ai/server/frontend_dist/_next/static/chunks/2717-638a944d24d5abde.js +1 -0
  51. mage_ai/server/frontend_dist/_next/static/chunks/3548-36f746b1824004f2.js +1 -0
  52. mage_ai/server/frontend_dist/_next/static/chunks/{3763-39a5174f6a3924db.js → 3763-aabe2703076636b0.js} +1 -1
  53. mage_ai/server/frontend_dist/_next/static/chunks/3782-3e2acb5ed45b582b.js +1 -0
  54. mage_ai/server/frontend_dist/_next/static/chunks/449-5e2253c6aba42557.js +1 -0
  55. mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/5627-d5e559859dd0e1e0.js → frontend_dist/_next/static/chunks/5627-10e76bafa5a26f5f.js} +1 -1
  56. mage_ai/server/frontend_dist/_next/static/chunks/{5699-e49718dfc9eb2854.js → 5699-e99379e332bd0b41.js} +1 -1
  57. mage_ai/server/frontend_dist/_next/static/chunks/{7966-163da2621b8c987c.js → 7966-a5a7db345ce81263.js} +1 -1
  58. mage_ai/server/frontend_dist/_next/static/chunks/pages/_app-782dd4a6b13e1c42.js +2 -0
  59. mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/pages/compute-ed67fa8e81662e8b.js → frontend_dist/_next/static/chunks/pages/compute-9e2dea78024e3bb4.js} +1 -1
  60. mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/pages/files-e0ecd7ced09a63b2.js → frontend_dist/_next/static/chunks/pages/files-e08c7fe76f968f9c.js} +1 -1
  61. mage_ai/server/frontend_dist/_next/static/chunks/pages/global-data-products/{[...slug]-c7a729477ecda50e.js → [...slug]-30c3807057a4e65b.js} +1 -1
  62. mage_ai/server/frontend_dist/_next/static/chunks/pages/{global-data-products-fd6ae6a358a60a0c.js → global-data-products-8dcb3b31af9e0e39.js} +1 -1
  63. mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/pages/global-hooks/[...slug]-8e50243797a7fe59.js → frontend_dist/_next/static/chunks/pages/global-hooks/[...slug]-85a64b64d27214b6.js} +1 -1
  64. mage_ai/server/frontend_dist/_next/static/chunks/pages/{global-hooks-d0c003446332dc0d.js → global-hooks-4ff959d51b8a9502.js} +1 -1
  65. mage_ai/server/frontend_dist/_next/static/chunks/pages/manage/{files-a69ed8e9f814490c.js → files-d08a460641d0efaa.js} +1 -1
  66. mage_ai/server/frontend_dist/_next/static/chunks/pages/manage/{overview-1aad7093c6d39257.js → overview-aae747f487e08d51.js} +1 -1
  67. mage_ai/server/frontend_dist/_next/static/chunks/pages/manage/{pipeline-runs-528d30e0d13b0cc7.js → pipeline-runs-09a842d64a6ada62.js} +1 -1
  68. mage_ai/server/frontend_dist/_next/static/chunks/pages/manage/{settings-fb9201d9cf63031d.js → settings-2e98e57d9376a458.js} +1 -1
  69. mage_ai/server/frontend_dist/_next/static/chunks/pages/manage/users/{[user]-000f5a980a07da39.js → [user]-7be6e41ad66089bb.js} +1 -1
  70. mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/pages/manage/users/new-e4e613f6e817a733.js → frontend_dist/_next/static/chunks/pages/manage/users/new-4c088833063bfa07.js} +1 -1
  71. mage_ai/server/frontend_dist/_next/static/chunks/pages/manage/users-5db54821a3059c69.js +1 -0
  72. mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/pages/manage-34d718b8a4066c23.js → frontend_dist/_next/static/chunks/pages/manage-868fcd8cbeb265f0.js} +1 -1
  73. mage_ai/server/frontend_dist/_next/static/chunks/pages/{oauth-3bfd1b8d7f036726.js → oauth-6ceceb62191dfe8a.js} +1 -1
  74. mage_ai/server/frontend_dist/_next/static/chunks/pages/overview-f65416f6dbe30ad3.js +1 -0
  75. mage_ai/server/frontend_dist/_next/static/chunks/pages/{pipeline-runs-5f8c100e648efa8a.js → pipeline-runs-2d0136b51b57de93.js} +1 -1
  76. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/backfills/{[...slug]-688c652f3296bb9c.js → [...slug]-1ad5238742e25b4c.js} +1 -1
  77. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/backfills-bd11e87d026bfbf9.js +1 -0
  78. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/{dashboard-1236e36d39b1637d.js → dashboard-0f4f47f721b0723f.js} +1 -1
  79. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/edit-432da20df91511fb.js +1 -0
  80. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/logs-fe91dfb0091f6bc6.js +1 -0
  81. mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/monitors/block-runs-0d68d4bf6290fefb.js → frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/monitors/block-runs-cf794b2d22a80f31.js} +1 -1
  82. mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/monitors/block-runtime-9254358d58f07714.js → frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/monitors/block-runtime-a964caef91bed9e1.js} +1 -1
  83. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/{monitors-821001e690caebe2.js → monitors-80bebb4401eefe25.js} +1 -1
  84. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/runs/[run]-2eae7cb017027682.js +1 -0
  85. mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/runs-2d4b2a0800a66b33.js → frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/runs-e3d7fdfeb7f806f7.js} +1 -1
  86. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/settings-03d9bca3bc5e6088.js +1 -0
  87. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/syncs-707ed8ca942ca802.js +1 -0
  88. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/triggers/{[...slug]-259143ed3cf59e31.js → [...slug]-8429f17d4146e1ec.js} +1 -1
  89. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/triggers-193045d9836d8d80.js +1 -0
  90. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines-d25d07db166cbb04.js +1 -0
  91. mage_ai/server/frontend_dist/_next/static/chunks/pages/platform/global-hooks/{[...slug]-5eeec927e4202b63.js → [...slug]-6834ae87bd668cb2.js} +1 -1
  92. mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/pages/platform/global-hooks-fbe9ad995d46d837.js → frontend_dist/_next/static/chunks/pages/platform/global-hooks-b3f7309a23e592b2.js} +1 -1
  93. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/account/{profile-fc659962d4015cb3.js → profile-f8b7374385e1f1bf.js} +1 -1
  94. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/platform/preferences-8de68502a9afa299.js +1 -0
  95. mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/pages/settings/platform/settings-a4f88c334414402b.js → frontend_dist/_next/static/chunks/pages/settings/platform/settings-50fb6a34f3913f1f.js} +1 -1
  96. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/permissions/{[...slug]-4deb9579ef99a3c6.js → [...slug]-2e5c098c21ea32b7.js} +1 -1
  97. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/{permissions-e0cda2f2bfce8d61.js → permissions-54e4b15b9585bfc4.js} +1 -1
  98. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/preferences-040f83d75d0f6537.js +1 -0
  99. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/roles/{[...slug]-910257d16c604ebd.js → [...slug]-95088f43034e3c95.js} +1 -1
  100. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/{roles-4f7a0756806cee34.js → roles-e9149e1fcf218f42.js} +1 -1
  101. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/{sync-data-208d6f955204d704.js → sync-data-75b67ae4a00818ef.js} +1 -1
  102. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/users/{[...slug]-c89dc67e5a1706a8.js → [...slug]-557dda05ca6c6124.js} +1 -1
  103. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/users-fa61dc6c1370e6a5.js +1 -0
  104. mage_ai/server/frontend_dist/_next/static/chunks/pages/{sign-in-054b33312d3193c3.js → sign-in-593c40985d63fcf7.js} +1 -1
  105. mage_ai/server/frontend_dist/_next/static/chunks/pages/templates/{[...slug]-b6ed6a5d818bfd20.js → [...slug]-252c4b6b818345d5.js} +1 -1
  106. mage_ai/server/frontend_dist/_next/static/chunks/pages/{templates-852357bc983af2ea.js → templates-ca528bc607753ab8.js} +1 -1
  107. mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/pages/terminal-1f9c56d671bbc67d.js → frontend_dist/_next/static/chunks/pages/terminal-287362c1defcc96b.js} +1 -1
  108. mage_ai/server/frontend_dist/_next/static/chunks/pages/test-2f83af8c9f1378fe.js +1 -0
  109. mage_ai/server/frontend_dist/_next/static/chunks/pages/triggers-d9de73fb799efed8.js +1 -0
  110. mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/pages/version-control-ae3469b992a341d6.js → frontend_dist/_next/static/chunks/pages/version-control-573f0225d7a703ed.js} +1 -1
  111. mage_ai/server/frontend_dist/_next/static/qR0jauUABqPaFMjUsYeoG/_buildManifest.js +1 -0
  112. mage_ai/server/frontend_dist/block-layout.html +3 -3
  113. mage_ai/server/frontend_dist/compute.html +6 -6
  114. mage_ai/server/frontend_dist/files.html +6 -6
  115. mage_ai/server/frontend_dist/global-data-products/[...slug].html +6 -6
  116. mage_ai/server/frontend_dist/global-data-products.html +6 -6
  117. mage_ai/server/frontend_dist/global-hooks/[...slug].html +6 -6
  118. mage_ai/server/frontend_dist/global-hooks.html +6 -6
  119. mage_ai/server/frontend_dist/index.html +3 -3
  120. mage_ai/server/frontend_dist/manage/files.html +6 -6
  121. mage_ai/server/frontend_dist/manage/overview.html +6 -6
  122. mage_ai/server/frontend_dist/manage/pipeline-runs.html +6 -6
  123. mage_ai/server/frontend_dist/manage/settings.html +6 -6
  124. mage_ai/server/frontend_dist/manage/users/[user].html +6 -6
  125. mage_ai/server/frontend_dist/manage/users/new.html +6 -6
  126. mage_ai/server/frontend_dist/manage/users.html +6 -6
  127. mage_ai/server/frontend_dist/manage.html +6 -6
  128. mage_ai/server/frontend_dist/oauth.html +5 -5
  129. mage_ai/server/frontend_dist/overview.html +6 -6
  130. mage_ai/server/frontend_dist/pipeline-runs.html +6 -6
  131. mage_ai/server/frontend_dist/pipelines/[pipeline]/backfills/[...slug].html +6 -6
  132. mage_ai/server/frontend_dist/pipelines/[pipeline]/backfills.html +6 -6
  133. mage_ai/server/frontend_dist/pipelines/[pipeline]/dashboard.html +6 -6
  134. mage_ai/server/frontend_dist/pipelines/[pipeline]/edit.html +3 -3
  135. mage_ai/server/frontend_dist/pipelines/[pipeline]/logs.html +6 -6
  136. mage_ai/server/frontend_dist/pipelines/[pipeline]/monitors/block-runs.html +6 -6
  137. mage_ai/server/frontend_dist/pipelines/[pipeline]/monitors/block-runtime.html +6 -6
  138. mage_ai/server/frontend_dist/pipelines/[pipeline]/monitors.html +6 -6
  139. mage_ai/server/frontend_dist/pipelines/[pipeline]/runs/[run].html +6 -6
  140. mage_ai/server/frontend_dist/pipelines/[pipeline]/runs.html +6 -6
  141. mage_ai/server/frontend_dist/pipelines/[pipeline]/settings.html +6 -6
  142. mage_ai/server/frontend_dist/pipelines/[pipeline]/syncs.html +6 -6
  143. mage_ai/server/frontend_dist/pipelines/[pipeline]/triggers/[...slug].html +6 -6
  144. mage_ai/server/frontend_dist/pipelines/[pipeline]/triggers.html +6 -6
  145. mage_ai/server/frontend_dist/pipelines/[pipeline].html +3 -3
  146. mage_ai/server/frontend_dist/pipelines.html +6 -6
  147. mage_ai/server/frontend_dist/platform/global-hooks/[...slug].html +6 -6
  148. mage_ai/server/frontend_dist/platform/global-hooks.html +6 -6
  149. mage_ai/server/frontend_dist/settings/account/profile.html +6 -6
  150. mage_ai/server/frontend_dist/settings/platform/preferences.html +6 -6
  151. mage_ai/server/frontend_dist/settings/platform/settings.html +6 -6
  152. mage_ai/server/frontend_dist/settings/workspace/permissions/[...slug].html +6 -6
  153. mage_ai/server/frontend_dist/settings/workspace/permissions.html +6 -6
  154. mage_ai/server/frontend_dist/settings/workspace/preferences.html +6 -6
  155. mage_ai/server/frontend_dist/settings/workspace/roles/[...slug].html +6 -6
  156. mage_ai/server/frontend_dist/settings/workspace/roles.html +6 -6
  157. mage_ai/server/frontend_dist/settings/workspace/sync-data.html +6 -6
  158. mage_ai/server/frontend_dist/settings/workspace/users/[...slug].html +6 -6
  159. mage_ai/server/frontend_dist/settings/workspace/users.html +6 -6
  160. mage_ai/server/frontend_dist/settings.html +3 -3
  161. mage_ai/server/frontend_dist/sign-in.html +7 -7
  162. mage_ai/server/frontend_dist/templates/[...slug].html +6 -6
  163. mage_ai/server/frontend_dist/templates.html +6 -6
  164. mage_ai/server/frontend_dist/terminal.html +6 -6
  165. mage_ai/server/frontend_dist/test.html +3 -3
  166. mage_ai/server/frontend_dist/triggers.html +6 -6
  167. mage_ai/server/frontend_dist/v2/canvas.html +2 -2
  168. mage_ai/server/frontend_dist/v2.html +2 -2
  169. mage_ai/server/frontend_dist/version-control.html +6 -6
  170. mage_ai/server/frontend_dist_base_path_template/404.html +3 -3
  171. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/{1187-839336d276186105.js → 1187-4560c3895e1d7099.js} +1 -1
  172. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/{1598-0adca9dce3ba4c60.js → 1598-cbf3f5a6078fc3f5.js} +1 -1
  173. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/2717-638a944d24d5abde.js +1 -0
  174. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/3548-36f746b1824004f2.js +1 -0
  175. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/{3763-39a5174f6a3924db.js → 3763-aabe2703076636b0.js} +1 -1
  176. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/3782-3e2acb5ed45b582b.js +1 -0
  177. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/449-5e2253c6aba42557.js +1 -0
  178. mage_ai/server/{frontend_dist/_next/static/chunks/5627-d5e559859dd0e1e0.js → frontend_dist_base_path_template/_next/static/chunks/5627-10e76bafa5a26f5f.js} +1 -1
  179. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/{5699-e49718dfc9eb2854.js → 5699-e99379e332bd0b41.js} +1 -1
  180. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/{7966-163da2621b8c987c.js → 7966-a5a7db345ce81263.js} +1 -1
  181. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/_app-ee5e328aaf51c698.js +2 -0
  182. mage_ai/server/{frontend_dist/_next/static/chunks/pages/compute-ed67fa8e81662e8b.js → frontend_dist_base_path_template/_next/static/chunks/pages/compute-9e2dea78024e3bb4.js} +1 -1
  183. mage_ai/server/{frontend_dist/_next/static/chunks/pages/files-e0ecd7ced09a63b2.js → frontend_dist_base_path_template/_next/static/chunks/pages/files-e08c7fe76f968f9c.js} +1 -1
  184. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/global-data-products/{[...slug]-c7a729477ecda50e.js → [...slug]-30c3807057a4e65b.js} +1 -1
  185. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/{global-data-products-fd6ae6a358a60a0c.js → global-data-products-8dcb3b31af9e0e39.js} +1 -1
  186. mage_ai/server/{frontend_dist/_next/static/chunks/pages/global-hooks/[...slug]-8e50243797a7fe59.js → frontend_dist_base_path_template/_next/static/chunks/pages/global-hooks/[...slug]-85a64b64d27214b6.js} +1 -1
  187. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/{global-hooks-d0c003446332dc0d.js → global-hooks-4ff959d51b8a9502.js} +1 -1
  188. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage/{files-a69ed8e9f814490c.js → files-d08a460641d0efaa.js} +1 -1
  189. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage/{overview-1aad7093c6d39257.js → overview-aae747f487e08d51.js} +1 -1
  190. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage/{pipeline-runs-528d30e0d13b0cc7.js → pipeline-runs-09a842d64a6ada62.js} +1 -1
  191. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage/{settings-fb9201d9cf63031d.js → settings-2e98e57d9376a458.js} +1 -1
  192. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage/users/{[user]-000f5a980a07da39.js → [user]-7be6e41ad66089bb.js} +1 -1
  193. mage_ai/server/{frontend_dist/_next/static/chunks/pages/manage/users/new-e4e613f6e817a733.js → frontend_dist_base_path_template/_next/static/chunks/pages/manage/users/new-4c088833063bfa07.js} +1 -1
  194. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage/users-5db54821a3059c69.js +1 -0
  195. mage_ai/server/{frontend_dist/_next/static/chunks/pages/manage-34d718b8a4066c23.js → frontend_dist_base_path_template/_next/static/chunks/pages/manage-868fcd8cbeb265f0.js} +1 -1
  196. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/{oauth-3bfd1b8d7f036726.js → oauth-6ceceb62191dfe8a.js} +1 -1
  197. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/overview-f65416f6dbe30ad3.js +1 -0
  198. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/{pipeline-runs-5f8c100e648efa8a.js → pipeline-runs-2d0136b51b57de93.js} +1 -1
  199. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/backfills/{[...slug]-688c652f3296bb9c.js → [...slug]-1ad5238742e25b4c.js} +1 -1
  200. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/backfills-bd11e87d026bfbf9.js +1 -0
  201. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/{dashboard-1236e36d39b1637d.js → dashboard-0f4f47f721b0723f.js} +1 -1
  202. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/edit-432da20df91511fb.js +1 -0
  203. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/logs-fe91dfb0091f6bc6.js +1 -0
  204. mage_ai/server/{frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/monitors/block-runs-0d68d4bf6290fefb.js → frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/monitors/block-runs-cf794b2d22a80f31.js} +1 -1
  205. mage_ai/server/{frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/monitors/block-runtime-9254358d58f07714.js → frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/monitors/block-runtime-a964caef91bed9e1.js} +1 -1
  206. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/{monitors-821001e690caebe2.js → monitors-80bebb4401eefe25.js} +1 -1
  207. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/runs/[run]-2eae7cb017027682.js +1 -0
  208. mage_ai/server/{frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/runs-2d4b2a0800a66b33.js → frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/runs-e3d7fdfeb7f806f7.js} +1 -1
  209. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/settings-03d9bca3bc5e6088.js +1 -0
  210. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/syncs-707ed8ca942ca802.js +1 -0
  211. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/triggers/{[...slug]-259143ed3cf59e31.js → [...slug]-8429f17d4146e1ec.js} +1 -1
  212. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/triggers-193045d9836d8d80.js +1 -0
  213. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines-d25d07db166cbb04.js +1 -0
  214. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/platform/global-hooks/{[...slug]-5eeec927e4202b63.js → [...slug]-6834ae87bd668cb2.js} +1 -1
  215. mage_ai/server/{frontend_dist/_next/static/chunks/pages/platform/global-hooks-fbe9ad995d46d837.js → frontend_dist_base_path_template/_next/static/chunks/pages/platform/global-hooks-b3f7309a23e592b2.js} +1 -1
  216. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/account/{profile-fc659962d4015cb3.js → profile-f8b7374385e1f1bf.js} +1 -1
  217. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/platform/preferences-8de68502a9afa299.js +1 -0
  218. mage_ai/server/{frontend_dist/_next/static/chunks/pages/settings/platform/settings-a4f88c334414402b.js → frontend_dist_base_path_template/_next/static/chunks/pages/settings/platform/settings-50fb6a34f3913f1f.js} +1 -1
  219. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/permissions/{[...slug]-4deb9579ef99a3c6.js → [...slug]-2e5c098c21ea32b7.js} +1 -1
  220. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/{permissions-e0cda2f2bfce8d61.js → permissions-54e4b15b9585bfc4.js} +1 -1
  221. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/preferences-040f83d75d0f6537.js +1 -0
  222. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/roles/{[...slug]-910257d16c604ebd.js → [...slug]-95088f43034e3c95.js} +1 -1
  223. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/{roles-4f7a0756806cee34.js → roles-e9149e1fcf218f42.js} +1 -1
  224. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/{sync-data-208d6f955204d704.js → sync-data-75b67ae4a00818ef.js} +1 -1
  225. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/users/{[...slug]-c89dc67e5a1706a8.js → [...slug]-557dda05ca6c6124.js} +1 -1
  226. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/users-fa61dc6c1370e6a5.js +1 -0
  227. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/{sign-in-054b33312d3193c3.js → sign-in-593c40985d63fcf7.js} +1 -1
  228. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/templates/{[...slug]-b6ed6a5d818bfd20.js → [...slug]-252c4b6b818345d5.js} +1 -1
  229. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/{templates-852357bc983af2ea.js → templates-ca528bc607753ab8.js} +1 -1
  230. mage_ai/server/{frontend_dist/_next/static/chunks/pages/terminal-1f9c56d671bbc67d.js → frontend_dist_base_path_template/_next/static/chunks/pages/terminal-287362c1defcc96b.js} +1 -1
  231. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/test-2f83af8c9f1378fe.js +1 -0
  232. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/triggers-d9de73fb799efed8.js +1 -0
  233. mage_ai/server/{frontend_dist/_next/static/chunks/pages/version-control-ae3469b992a341d6.js → frontend_dist_base_path_template/_next/static/chunks/pages/version-control-573f0225d7a703ed.js} +1 -1
  234. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/{webpack-12ad70eb5c31aa92.js → webpack-5f4be622608d9267.js} +1 -1
  235. mage_ai/server/frontend_dist_base_path_template/_next/static/iCySon3_GCldnbC5U7C-s/_buildManifest.js +1 -0
  236. mage_ai/server/frontend_dist_base_path_template/block-layout.html +3 -3
  237. mage_ai/server/frontend_dist_base_path_template/compute.html +6 -6
  238. mage_ai/server/frontend_dist_base_path_template/files.html +6 -6
  239. mage_ai/server/frontend_dist_base_path_template/global-data-products/[...slug].html +6 -6
  240. mage_ai/server/frontend_dist_base_path_template/global-data-products.html +6 -6
  241. mage_ai/server/frontend_dist_base_path_template/global-hooks/[...slug].html +6 -6
  242. mage_ai/server/frontend_dist_base_path_template/global-hooks.html +6 -6
  243. mage_ai/server/frontend_dist_base_path_template/index.html +3 -3
  244. mage_ai/server/frontend_dist_base_path_template/manage/files.html +6 -6
  245. mage_ai/server/frontend_dist_base_path_template/manage/overview.html +6 -6
  246. mage_ai/server/frontend_dist_base_path_template/manage/pipeline-runs.html +6 -6
  247. mage_ai/server/frontend_dist_base_path_template/manage/settings.html +6 -6
  248. mage_ai/server/frontend_dist_base_path_template/manage/users/[user].html +6 -6
  249. mage_ai/server/frontend_dist_base_path_template/manage/users/new.html +6 -6
  250. mage_ai/server/frontend_dist_base_path_template/manage/users.html +6 -6
  251. mage_ai/server/frontend_dist_base_path_template/manage.html +6 -6
  252. mage_ai/server/frontend_dist_base_path_template/oauth.html +5 -5
  253. mage_ai/server/frontend_dist_base_path_template/overview.html +6 -6
  254. mage_ai/server/frontend_dist_base_path_template/pipeline-runs.html +6 -6
  255. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/backfills/[...slug].html +6 -6
  256. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/backfills.html +6 -6
  257. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/dashboard.html +6 -6
  258. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/edit.html +3 -3
  259. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/logs.html +6 -6
  260. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/monitors/block-runs.html +6 -6
  261. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/monitors/block-runtime.html +6 -6
  262. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/monitors.html +6 -6
  263. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/runs/[run].html +6 -6
  264. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/runs.html +6 -6
  265. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/settings.html +6 -6
  266. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/syncs.html +6 -6
  267. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/triggers/[...slug].html +6 -6
  268. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/triggers.html +6 -6
  269. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline].html +3 -3
  270. mage_ai/server/frontend_dist_base_path_template/pipelines.html +6 -6
  271. mage_ai/server/frontend_dist_base_path_template/platform/global-hooks/[...slug].html +6 -6
  272. mage_ai/server/frontend_dist_base_path_template/platform/global-hooks.html +6 -6
  273. mage_ai/server/frontend_dist_base_path_template/settings/account/profile.html +6 -6
  274. mage_ai/server/frontend_dist_base_path_template/settings/platform/preferences.html +6 -6
  275. mage_ai/server/frontend_dist_base_path_template/settings/platform/settings.html +6 -6
  276. mage_ai/server/frontend_dist_base_path_template/settings/workspace/permissions/[...slug].html +6 -6
  277. mage_ai/server/frontend_dist_base_path_template/settings/workspace/permissions.html +6 -6
  278. mage_ai/server/frontend_dist_base_path_template/settings/workspace/preferences.html +6 -6
  279. mage_ai/server/frontend_dist_base_path_template/settings/workspace/roles/[...slug].html +6 -6
  280. mage_ai/server/frontend_dist_base_path_template/settings/workspace/roles.html +6 -6
  281. mage_ai/server/frontend_dist_base_path_template/settings/workspace/sync-data.html +6 -6
  282. mage_ai/server/frontend_dist_base_path_template/settings/workspace/users/[...slug].html +6 -6
  283. mage_ai/server/frontend_dist_base_path_template/settings/workspace/users.html +6 -6
  284. mage_ai/server/frontend_dist_base_path_template/settings.html +3 -3
  285. mage_ai/server/frontend_dist_base_path_template/sign-in.html +7 -7
  286. mage_ai/server/frontend_dist_base_path_template/templates/[...slug].html +6 -6
  287. mage_ai/server/frontend_dist_base_path_template/templates.html +6 -6
  288. mage_ai/server/frontend_dist_base_path_template/terminal.html +6 -6
  289. mage_ai/server/frontend_dist_base_path_template/test.html +3 -3
  290. mage_ai/server/frontend_dist_base_path_template/triggers.html +6 -6
  291. mage_ai/server/frontend_dist_base_path_template/v2/canvas.html +2 -2
  292. mage_ai/server/frontend_dist_base_path_template/v2.html +2 -2
  293. mage_ai/server/frontend_dist_base_path_template/version-control.html +6 -6
  294. mage_ai/server/server.py +9 -0
  295. mage_ai/services/teams/config.py +13 -2
  296. mage_ai/services/teams/teams.py +13 -11
  297. mage_ai/settings/server.py +2 -0
  298. mage_ai/shared/constants.py +3 -1
  299. mage_ai/shared/enum.py +2 -5
  300. mage_ai/shared/environments.py +27 -3
  301. mage_ai/streaming/sinks/elasticsearch.py +15 -5
  302. mage_ai/tests/api/endpoints/test_blocks.py +1 -101
  303. mage_ai/tests/api/endpoints/test_configuration_options.py +1 -48
  304. mage_ai/tests/api/endpoints/test_configuration_options_project_platform.py +68 -0
  305. mage_ai/tests/api/endpoints/test_custom_designs.py +1 -106
  306. mage_ai/tests/api/endpoints/test_custom_designs_project_platform.py +132 -0
  307. mage_ai/tests/api/endpoints/test_dbt_blocks.py +111 -0
  308. mage_ai/tests/api/endpoints/test_file_contents.py +0 -48
  309. mage_ai/tests/api/endpoints/test_file_contents_with_project_platform.py +66 -0
  310. mage_ai/tests/api/endpoints/test_pipelines.py +0 -134
  311. mage_ai/tests/api/endpoints/test_pipelines_with_project_platform.py +143 -0
  312. mage_ai/tests/data_preparation/models/block/dynamic/test_counter.py +1 -3
  313. mage_ai/tests/data_preparation/models/block/hook/test_hook_block.py +3 -3
  314. mage_ai/tests/data_preparation/models/test_block.py +7 -0
  315. mage_ai/tests/data_preparation/test_repo_manager.py +0 -63
  316. mage_ai/tests/data_preparation/test_repo_manager_project_platform.py +67 -0
  317. mage_ai/tests/data_preparation/test_variable_manager.py +0 -51
  318. mage_ai/tests/data_preparation/test_variable_manager_project_platform.py +41 -0
  319. mage_ai/tests/io/create_table/test_postgresql.py +27 -0
  320. mage_ai/tests/orchestration/notification/test_config.py +3 -3
  321. mage_ai/usage_statistics/logger.py +99 -15
  322. mage_ai-0.9.76.dist-info/METADATA +393 -0
  323. {mage_ai-0.9.74.dist-info → mage_ai-0.9.76.dist-info}/RECORD +331 -322
  324. {mage_ai-0.9.74.dist-info → mage_ai-0.9.76.dist-info}/WHEEL +1 -1
  325. mage_ai/server/frontend_dist/_next/static/chunks/1557-1ad0c64c2d08e569.js +0 -1
  326. mage_ai/server/frontend_dist/_next/static/chunks/2717-d65056b6b5e124eb.js +0 -1
  327. mage_ai/server/frontend_dist/_next/static/chunks/3548-b2c5edfb710886a6.js +0 -1
  328. mage_ai/server/frontend_dist/_next/static/chunks/3782-4b3091e550f809a2.js +0 -1
  329. mage_ai/server/frontend_dist/_next/static/chunks/pages/_app-5bdff745074fb350.js +0 -2
  330. mage_ai/server/frontend_dist/_next/static/chunks/pages/manage/users-a5e9d77ed5b50205.js +0 -1
  331. mage_ai/server/frontend_dist/_next/static/chunks/pages/overview-69af3253ad0d0d89.js +0 -1
  332. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/backfills-fe112809feb25b05.js +0 -1
  333. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/edit-e9ca358209cdf93d.js +0 -1
  334. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/logs-a29f1615d2e7d330.js +0 -1
  335. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/runs/[run]-6d382ae5bad9745c.js +0 -1
  336. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/settings-cd49372ae1702963.js +0 -1
  337. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/syncs-135be8974f7f5f2b.js +0 -1
  338. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/triggers-3af13e89adff4d6c.js +0 -1
  339. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines-b578b075a8d857e3.js +0 -1
  340. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/platform/preferences-058d283ee178c038.js +0 -1
  341. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/preferences-7b02bb70462144cb.js +0 -1
  342. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/users-5212c01a9dc558da.js +0 -1
  343. mage_ai/server/frontend_dist/_next/static/chunks/pages/test-86b12cc12d4a625f.js +0 -1
  344. mage_ai/server/frontend_dist/_next/static/chunks/pages/triggers-2481c40b18d5b6d4.js +0 -1
  345. mage_ai/server/frontend_dist/_next/static/pLWT6Sqd09xYpufCVIqnz/_buildManifest.js +0 -1
  346. mage_ai/server/frontend_dist_base_path_template/_next/static/JQewSAObpbhO0wrdAM6Ng/_buildManifest.js +0 -1
  347. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/1557-1ad0c64c2d08e569.js +0 -1
  348. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/2717-d65056b6b5e124eb.js +0 -1
  349. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/3548-b2c5edfb710886a6.js +0 -1
  350. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/3782-4b3091e550f809a2.js +0 -1
  351. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/_app-90de19bc03f1484b.js +0 -2
  352. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage/users-a5e9d77ed5b50205.js +0 -1
  353. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/overview-69af3253ad0d0d89.js +0 -1
  354. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/backfills-fe112809feb25b05.js +0 -1
  355. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/edit-e9ca358209cdf93d.js +0 -1
  356. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/logs-a29f1615d2e7d330.js +0 -1
  357. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/runs/[run]-6d382ae5bad9745c.js +0 -1
  358. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/settings-cd49372ae1702963.js +0 -1
  359. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/syncs-135be8974f7f5f2b.js +0 -1
  360. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/triggers-3af13e89adff4d6c.js +0 -1
  361. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines-b578b075a8d857e3.js +0 -1
  362. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/platform/preferences-058d283ee178c038.js +0 -1
  363. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/preferences-7b02bb70462144cb.js +0 -1
  364. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/users-5212c01a9dc558da.js +0 -1
  365. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/test-86b12cc12d4a625f.js +0 -1
  366. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/triggers-2481c40b18d5b6d4.js +0 -1
  367. mage_ai-0.9.74.dist-info/METADATA +0 -544
  368. /mage_ai/server/frontend_dist/_next/static/chunks/pages/{_app-5bdff745074fb350.js.LICENSE.txt → _app-782dd4a6b13e1c42.js.LICENSE.txt} +0 -0
  369. /mage_ai/server/frontend_dist/_next/static/{pLWT6Sqd09xYpufCVIqnz → qR0jauUABqPaFMjUsYeoG}/_ssgManifest.js +0 -0
  370. /mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/{_app-90de19bc03f1484b.js.LICENSE.txt → _app-ee5e328aaf51c698.js.LICENSE.txt} +0 -0
  371. /mage_ai/server/frontend_dist_base_path_template/_next/static/{JQewSAObpbhO0wrdAM6Ng → iCySon3_GCldnbC5U7C-s}/_ssgManifest.js +0 -0
  372. {mage_ai-0.9.74.dist-info → mage_ai-0.9.76.dist-info}/entry_points.txt +0 -0
  373. {mage_ai-0.9.74.dist-info → mage_ai-0.9.76.dist-info/licenses}/LICENSE +0 -0
  374. {mage_ai-0.9.74.dist-info → mage_ai-0.9.76.dist-info}/top_level.txt +0 -0
mage_ai/io/airtable.py ADDED
@@ -0,0 +1,104 @@
1
+ from typing import Union
2
+
3
+ import pandas as pd
4
+ import polars as pl
5
+ from pyairtable.api import Api
6
+
7
+ from mage_ai.io.base import BaseIO
8
+ from mage_ai.io.config import BaseConfigLoader, ConfigKey
9
+
10
+
11
+ class Airtable(BaseIO):
12
+ """
13
+ Handles data transfer between Airtable tables and the Mage app.
14
+ """
15
+
16
+ def __init__(
17
+ self,
18
+ token: str,
19
+ verbose: bool = True,
20
+ **kwargs) -> None:
21
+ """
22
+ Initializes a connection to Airtable.
23
+
24
+ Args:
25
+ token (str): Airtable API token.
26
+ verbose (bool, optional): Whether to print verbose output. Defaults to True.
27
+ **kwargs: Additional keyword arguments to pass to the pyairtable Api constructor.
28
+ """
29
+ super().__init__(verbose=verbose)
30
+ self.client = Api(token, **kwargs) # Create the Airtable API client
31
+
32
+ @classmethod
33
+ def with_config(
34
+ cls,
35
+ config: BaseConfigLoader
36
+ ) -> 'Airtable':
37
+ """
38
+ Initializes an Airtable client from a configuration loader.
39
+
40
+ Args:
41
+ config (BaseConfigLoader): Configuration loader object.
42
+
43
+ Raises:
44
+ ValueError: If no valid Airtable API token is found in the configuration.
45
+
46
+ Returns:
47
+ Airtable: An instance of the Airtable class.
48
+ """
49
+ if ConfigKey.AIRTABLE_ACCESS_TOKEN not in config:
50
+ raise ValueError(
51
+ 'No valid API token found for Airtable.'
52
+ 'You must specify your access token in your config.'
53
+ )
54
+
55
+ return cls(
56
+ token=config[ConfigKey.AIRTABLE_ACCESS_TOKEN]
57
+ )
58
+
59
+ def load(
60
+ self,
61
+ base_id: str,
62
+ table_name: str,
63
+ **kwargs,
64
+ ) -> pd.DataFrame:
65
+ """
66
+ Loads data from an Airtable table into a Pandas DataFrame.
67
+
68
+ Args:
69
+ base_id (str): The ID of the Airtable base (e.g., 'app*****').
70
+ table_name (str): The name or ID of the Airtable table (e.g., 'tbl*****').
71
+ **kwargs: Additional keyword arguments to pass to the pyairtable table.all() method.
72
+
73
+ Returns:
74
+ DataFrame: A Pandas DataFrame containing the data from the Airtable table.
75
+ """
76
+ with self.printer.print_msg(
77
+ f'Loading data frame from table \'{table_name}\' at airtable app \'{base_id}\''
78
+ ):
79
+ table = self.client.table(base_id, table_name) # Get the Airtable table
80
+ data = table.all(**kwargs) # Fetch all records from the table
81
+
82
+ # Flatten the Airtable data structure into a list of dictionaries
83
+ flattened_data = []
84
+ for record in data:
85
+ flattened_record = {
86
+ 'id': record['id'],
87
+ 'createdTime': record['createdTime']
88
+ }
89
+ fields = record['fields']
90
+ flattened_record.update(fields)
91
+ flattened_data.append(flattened_record)
92
+
93
+ return pd.DataFrame(flattened_data) # Create and return a DataFrame
94
+
95
+ def export(
96
+ self,
97
+ df: Union[pd.DataFrame, pl.DataFrame],
98
+ *args,
99
+ **kwargs
100
+ ) -> None:
101
+ """
102
+ Not implemented yet. This method is intended to export data to Airtable.
103
+ """
104
+ pass
mage_ai/io/base.py CHANGED
@@ -16,6 +16,7 @@ QUERY_ROW_LIMIT = 10_000_000
16
16
 
17
17
 
18
18
  class DataSource(StrEnum):
19
+ AIRTABLE = 'airtable'
19
20
  ALGOLIA = 'algolia'
20
21
  API = 'api'
21
22
  BIGQUERY = 'bigquery'
@@ -47,6 +48,26 @@ class FileFormat(StrEnum):
47
48
  PARQUET = 'parquet'
48
49
  HDF5 = 'hdf5'
49
50
  XML = 'xml'
51
+ EXCEL = 'excel'
52
+
53
+ @classmethod
54
+ def from_extension(cls, ext: str):
55
+ if ext == 'csv':
56
+ return cls.CSV
57
+ elif ext == 'json':
58
+ return cls.JSON
59
+ elif ext == 'parquet':
60
+ return cls.PARQUET
61
+ elif ext == 'hdf5':
62
+ return cls.HDF5
63
+ elif ext == 'xml':
64
+ return cls.XML
65
+ elif ext in ('xls', 'xlsx'):
66
+ return cls.EXCEL
67
+ else:
68
+ raise ValueError(
69
+ f'None file format found for this file extension: {ext}'
70
+ )
50
71
 
51
72
 
52
73
  class ExportWritePolicy(BaseEnum):
@@ -126,7 +147,9 @@ class BaseFile(BaseIO):
126
147
  pass
127
148
 
128
149
  def _get_file_format(self, filepath: Union[os.PathLike, str]) -> str:
129
- return os.path.splitext(os.path.basename(filepath))[-1][1:]
150
+ return FileFormat.from_extension(
151
+ os.path.splitext(os.path.basename(filepath))[-1][1:]
152
+ )
130
153
 
131
154
  def __get_reader(self, format: Union[FileFormat, str, None]) -> Callable:
132
155
  """
@@ -151,6 +174,8 @@ class BaseFile(BaseIO):
151
174
  return pd.read_hdf
152
175
  elif format == FileFormat.XML:
153
176
  return pd.read_xml
177
+ elif format == FileFormat.EXCEL:
178
+ return pd.read_excel
154
179
  else:
155
180
  raise ValueError(f"Invalid format '{format}' specified.")
156
181
 
@@ -249,6 +274,8 @@ class BaseFile(BaseIO):
249
274
  return df.write_hdf5
250
275
  elif format == FileFormat.XML:
251
276
  return df.write_xml
277
+ elif format == FileFormat.EXCEL:
278
+ return df.write_excel
252
279
  return df.write_parquet
253
280
 
254
281
  elif isinstance(df, DataFrame): # pandas DataFrame
@@ -260,6 +287,8 @@ class BaseFile(BaseIO):
260
287
  return df.to_hdf
261
288
  elif format == FileFormat.XML:
262
289
  return df.to_xml
290
+ elif format == FileFormat.EXCEL:
291
+ return df.to_excel
263
292
  return df.to_parquet
264
293
 
265
294
  def __del__(self):
mage_ai/io/bigquery.py CHANGED
@@ -1,7 +1,9 @@
1
+ import traceback
1
2
  import uuid
2
3
  from typing import Dict, List, Mapping, Union
3
4
 
4
5
  import numpy as np
6
+ import pandas as pd
5
7
  from google.cloud.bigquery import (
6
8
  Client,
7
9
  LoadJobConfig,
@@ -399,7 +401,41 @@ WHERE table_id = '{table_name}'
399
401
  # Clean column names
400
402
  if type(df) is DataFrame:
401
403
  df.columns = df.columns.str.replace(' ', '_')
404
+ table = None
405
+ try:
406
+ # Cast column types
407
+ table = self.client.get_table(table_id)
408
+ except Exception:
409
+ print(f'Table {table_id} does not exist.')
410
+ pass
402
411
 
412
+ if table is not None:
413
+ try:
414
+ timestamp_columns = [field.name for field in table.schema
415
+ if field.field_type == 'TIMESTAMP']
416
+
417
+ # Convert TIMESTAMP columns in DataFrame
418
+ for col in timestamp_columns:
419
+ if col in df.columns:
420
+ df[col] = pd.to_datetime(df[col])
421
+ except Exception:
422
+ print('Fail to cast column types in dataframe.')
423
+ traceback.print_exc()
424
+ if (
425
+ not config.schema and
426
+ config.write_disposition == WriteDisposition.WRITE_TRUNCATE
427
+ ):
428
+ df_columns = df.columns.tolist()
429
+ config.schema = [
430
+ SchemaField(
431
+ field.name,
432
+ field.field_type,
433
+ mode=field.mode,
434
+ fields=field.fields,
435
+ )
436
+ for field in table.schema
437
+ if field.name in df_columns
438
+ ]
403
439
  return self.client.load_table_from_dataframe(df, table_id, job_config=config).result()
404
440
 
405
441
  def execute(self, query_string: str, **kwargs) -> None:
mage_ai/io/config.py CHANGED
@@ -16,6 +16,8 @@ class ConfigKey(StrEnum):
16
16
  List of configuration settings for use with data IO clients.
17
17
  """
18
18
 
19
+ AIRTABLE_ACCESS_TOKEN = "AIRTABLE_ACCESS_TOKEN"
20
+
19
21
  ALGOLIA_APP_ID = 'ALGOLIA_APP_ID'
20
22
  ALGOLIA_API_KEY = 'ALGOLIA_API_KEY'
21
23
  ALGOLIA_INDEX_NAME = 'ALGOLIA_INDEX_NAME'
@@ -78,6 +80,7 @@ class ConfigKey(StrEnum):
78
80
  MYSQL_PASSWORD = 'MYSQL_PASSWORD'
79
81
  MYSQL_PORT = 'MYSQL_PORT'
80
82
  MYSQL_USER = 'MYSQL_USER'
83
+ MYSQL_ALLOW_LOCAL_INFILE = 'MYSQL_ALLOW_LOCAL_INFILE'
81
84
 
82
85
  ORACLEDB_USER = 'ORACLEDB_USER'
83
86
  ORACLEDB_PASSWORD = 'ORACLEDB_PASSWORD'
@@ -338,6 +341,7 @@ class VerboseConfigKey(StrEnum):
338
341
  Config key headers for the verbose configuration file format.
339
342
  """
340
343
 
344
+ AIRTABLE = 'Airtable'
341
345
  ALGOLIA = 'Algolia'
342
346
  AWS = 'AWS'
343
347
  BIGQUERY = 'BigQuery'
@@ -356,6 +360,7 @@ class VerboseConfigKey(StrEnum):
356
360
 
357
361
  class ConfigFileLoader(BaseConfigLoader):
358
362
  KEY_MAP = {
363
+ ConfigKey.AIRTABLE_ACCESS_TOKEN: VerboseConfigKey.AIRTABLE,
359
364
  ConfigKey.ALGOLIA_APP_ID: (
360
365
  VerboseConfigKey.ALGOLIA, 'app_id'),
361
366
  ConfigKey.ALGOLIA_API_KEY: (
mage_ai/io/mssql.py CHANGED
@@ -14,6 +14,7 @@ from mage_ai.io.config import BaseConfigLoader, ConfigKey
14
14
  from mage_ai.io.constants import UNIQUE_CONFLICT_METHOD_UPDATE
15
15
  from mage_ai.io.export_utils import PandasTypes
16
16
  from mage_ai.io.sql import BaseSQL
17
+ from mage_ai.shared.hash import extract
17
18
  from mage_ai.shared.parsers import encode_complex
18
19
 
19
20
  MERGE_TABLE_SQL = '''MERGE {table_name} AS t
@@ -197,9 +198,11 @@ class MSSQL(BaseSQL):
197
198
  TrustServerCertificate='yes',
198
199
  ),
199
200
  )
201
+ conn_kwargs = extract(kwargs, ['pool_size', 'max_overflow'])
200
202
  engine = create_engine(
201
203
  connection_url,
202
204
  fast_executemany=True,
205
+ **conn_kwargs,
203
206
  )
204
207
 
205
208
  unique_conflict_method = kwargs.get('unique_conflict_method')
@@ -242,6 +245,7 @@ class MSSQL(BaseSQL):
242
245
  method=merge_table,
243
246
  )
244
247
  return
248
+ sql_kwargs = extract(kwargs, ['chunksize', 'method'])
245
249
 
246
250
  df.to_sql(
247
251
  table_name,
@@ -249,6 +253,7 @@ class MSSQL(BaseSQL):
249
253
  schema=schema_name,
250
254
  if_exists=if_exists or ExportWritePolicy.REPLACE,
251
255
  index=False,
256
+ **sql_kwargs,
252
257
  )
253
258
 
254
259
  def get_type(self, column: Series, dtype: str) -> str:
mage_ai/io/mysql.py CHANGED
@@ -25,6 +25,7 @@ class MySQL(BaseSQL):
25
25
  password: str,
26
26
  user: str,
27
27
  port: int = 3306,
28
+ allow_local_infile: bool = False,
28
29
  verbose: bool = True,
29
30
  **kwargs,
30
31
  ) -> None:
@@ -35,18 +36,22 @@ class MySQL(BaseSQL):
35
36
  port=port or 3306,
36
37
  user=user,
37
38
  verbose=verbose,
39
+ allow_local_infile=allow_local_infile,
38
40
  **kwargs,
39
41
  )
40
42
 
41
43
  @classmethod
42
44
  def with_config(cls, config: BaseConfigLoader) -> 'MySQL':
43
- return cls(
45
+ conn_kwargs = dict(
44
46
  database=config[ConfigKey.MYSQL_DATABASE],
45
47
  host=config[ConfigKey.MYSQL_HOST],
46
48
  password=config[ConfigKey.MYSQL_PASSWORD],
47
49
  port=config[ConfigKey.MYSQL_PORT],
48
50
  user=config[ConfigKey.MYSQL_USER],
49
51
  )
52
+ if config[ConfigKey.MYSQL_ALLOW_LOCAL_INFILE] is not None:
53
+ conn_kwargs['allow_local_infile'] = config[ConfigKey.MYSQL_ALLOW_LOCAL_INFILE]
54
+ return cls(**conn_kwargs)
50
55
 
51
56
  def build_create_table_command(
52
57
  self,
mage_ai/io/oracledb.py CHANGED
@@ -244,10 +244,8 @@ FETCH FIRST {limit} ROWS ONLY
244
244
  # Remove extraneous surrounding double quotes
245
245
  # that get added while performing conversion to string.
246
246
  df_[col] = df_[col].apply(lambda x: x.strip('"') if x and isinstance(x, str) else x)
247
- df_.replace({np.NaN: None}, inplace=True)
248
- values = []
249
- for i in range(0, len(df_)):
250
- values.append(tuple(df_.fillna('').values[i]))
247
+ df_.fillna('', inplace=True)
248
+ values = list(df_.itertuples(index=False, name=None))
251
249
 
252
250
  # Create values placeholder
253
251
  colmn_names = df.columns.tolist()
mage_ai/io/postgres.py CHANGED
@@ -99,6 +99,8 @@ class Postgres(BaseSQL):
99
99
  password = self.settings['password']
100
100
  port = self.settings['port']
101
101
  user = self.settings['user']
102
+ keepalives = self.settings.get('keepalives', 1)
103
+ keepalives_idle = self.settings.get('keepalives_idle', 300)
102
104
  if self.settings['connection_method'] == 'ssh_tunnel':
103
105
  ssh_setting = dict(ssh_username=self.settings['ssh_username'])
104
106
  if self.settings['ssh_pkey'] is not None:
@@ -128,19 +130,35 @@ class Postgres(BaseSQL):
128
130
  host = '127.0.0.1'
129
131
  port = self.ssh_tunnel.local_bind_port
130
132
 
131
- connect_opts = dict(
132
- database=database,
133
- host=host,
134
- password=password,
135
- port=port,
136
- user=user,
137
- keepalives=1,
138
- keepalives_idle=300,
133
+ connect_opts = self.settings.copy()
134
+ # See recognized keyword parameters for psycopg2.connect()
135
+ # https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-PARAMKEYWORDS
136
+ unrecognized_keys = [
137
+ 'dbname',
138
+ 'schema',
139
+ 'connection_method',
140
+ 'ssh_host',
141
+ 'ssh_port',
142
+ 'ssh_username',
143
+ 'ssh_password',
144
+ 'ssh_pkey',
145
+ "verbose"
146
+ ]
147
+ for key in unrecognized_keys:
148
+ connect_opts.pop(key, None)
149
+
150
+ connect_opts.update(
151
+ {
152
+ 'database': database,
153
+ 'host': host,
154
+ 'password': password,
155
+ 'port': port,
156
+ 'user': user,
157
+ 'keepalives': keepalives,
158
+ 'keepalives_idle': keepalives_idle,
159
+ }
139
160
  )
140
161
 
141
- if self.settings.get('connect_timeout'):
142
- connect_opts['connect_timeout'] = self.settings['connect_timeout']
143
-
144
162
  try:
145
163
  self._ctx = connect(**connect_opts)
146
164
  except Exception:
@@ -277,6 +295,14 @@ class Postgres(BaseSQL):
277
295
 
278
296
  return 'text'
279
297
 
298
+ @staticmethod
299
+ def _clean_array_value(val: str) -> str:
300
+ if val is None or type(val) is not str or len(val) < 2:
301
+ return val
302
+ if val[0] == '[' and val[-1] == ']':
303
+ return str(val).replace('[', '{').replace(']', '}')
304
+ return val
305
+
280
306
  def upload_dataframe(
281
307
  self,
282
308
  cursor: _psycopg.cursor,
@@ -298,13 +324,6 @@ class Postgres(BaseSQL):
298
324
  # Use COPY command
299
325
  use_insert_command = False
300
326
 
301
- def clean_array_value(val):
302
- if val is None or type(val) is not str or len(val) < 2:
303
- return val
304
- if val[0] == '[' and val[-1] == ']':
305
- return '{' + val[1:-1] + '}'
306
- return val
307
-
308
327
  def serialize_obj(val):
309
328
  if type(val) is dict or type(val) is np.ndarray:
310
329
  return simplejson.dumps(
@@ -319,7 +338,7 @@ class Postgres(BaseSQL):
319
338
  ignore_nan=True,
320
339
  )
321
340
  elif not use_insert_command and type(val) is list:
322
- return clean_array_value(simplejson.dumps(
341
+ return self._clean_array_value(simplejson.dumps(
323
342
  val,
324
343
  default=encode_complex,
325
344
  ignore_nan=True,
mage_ai/io/redshift.py CHANGED
@@ -5,6 +5,7 @@ from typing import Dict, Union
5
5
  from pandas import DataFrame
6
6
  from redshift_connector import connect
7
7
 
8
+ from mage_ai.data_preparation.models.block.sql.utils.shared import split_query_string
8
9
  from mage_ai.io.base import QUERY_ROW_LIMIT, ExportWritePolicy
9
10
  from mage_ai.io.config import BaseConfigLoader, ConfigKey
10
11
  from mage_ai.io.export_utils import clean_df_for_export, infer_dtypes
@@ -67,6 +68,18 @@ class Redshift(BaseSQL):
67
68
  with self.conn.cursor() as cur:
68
69
  cur.execute(query_string, **kwargs)
69
70
 
71
+ def execute_query_raw(self, query: str, **kwargs) -> None:
72
+ """
73
+ Overwrite execute query to process multiple queries in one string.
74
+ """
75
+ results = []
76
+ with self.conn.cursor() as cursor:
77
+ for query_string in split_query_string(query):
78
+ result = cursor.execute(query_string)
79
+ results.append(result)
80
+ self.conn.commit()
81
+ return results
82
+
70
83
  def load(
71
84
  self,
72
85
  query_string: str,
mage_ai/io/sql.py CHANGED
@@ -301,6 +301,7 @@ class BaseSQL(BaseSQLConnection):
301
301
  if_exists=if_exists,
302
302
  unique_conflict_method=unique_conflict_method,
303
303
  unique_constraints=unique_constraints,
304
+ **kwargs,
304
305
  )
305
306
  return
306
307
 
mage_ai/io/utils.py CHANGED
@@ -29,3 +29,21 @@ def escape_quotes(line: str, single: bool = True, double: bool = True) -> str:
29
29
  if double:
30
30
  new_line = new_line.replace('\"', '\\"')
31
31
  return new_line
32
+
33
+
34
+ def map_json_to_airtable(data_types):
35
+ # Extract the non-null type (ignoring 'null')
36
+ data_type = next((t for t in data_types if t != 'null'), 'string')
37
+
38
+ # Mapping from JSON types to Airtable types
39
+ type_mapping = {
40
+ 'string': 'multilineText',
41
+ 'integer': 'number',
42
+ 'boolean': 'checkbox',
43
+ 'array': 'multipleSelects',
44
+ 'object': 'singleCollaborator',
45
+ 'number': 'number',
46
+ 'date-time': 'dateTime'
47
+ }
48
+
49
+ return type_mapping.get(data_type, 'str')
@@ -1,5 +1,6 @@
1
1
  import logging
2
2
  import os
3
+ from functools import wraps
3
4
  from urllib.parse import parse_qs, quote_plus, urlparse
4
5
 
5
6
  import sqlalchemy
@@ -13,7 +14,7 @@ from mage_ai.orchestration.db.setup import get_postgres_connection_url
13
14
  from mage_ai.orchestration.db.utils import get_user_info_from_db_connection_url
14
15
  from mage_ai.settings import OTEL_EXPORTER_OTLP_ENDPOINT
15
16
  from mage_ai.settings.repo import get_variables_dir
16
- from mage_ai.shared.environments import is_debug, is_test
17
+ from mage_ai.shared.environments import is_debug, is_test_mage
17
18
 
18
19
  DB_RETRY_COUNT = 2
19
20
  TEST_DB = 'test.db'
@@ -28,7 +29,7 @@ db_kwargs = dict(
28
29
  if OTEL_EXPORTER_OTLP_ENDPOINT:
29
30
  from opentelemetry.instrumentation.sqlalchemy import SQLAlchemyInstrumentor
30
31
 
31
- if is_test():
32
+ if is_test_mage():
32
33
  db_connection_url = f'sqlite:///{TEST_DB}'
33
34
  db_kwargs['connect_args']['check_same_thread'] = False
34
35
  elif not db_connection_url:
@@ -37,7 +38,7 @@ elif not db_connection_url:
37
38
  if pg_db_connection_url:
38
39
  db_connection_url = pg_db_connection_url
39
40
  else:
40
- if is_test():
41
+ if is_test_mage():
41
42
  db_connection_url = f'sqlite:///{TEST_DB}'
42
43
  elif os.path.exists(os.path.join('mage_ai', 'orchestration', 'db')):
43
44
  # For local dev environment
@@ -169,6 +170,25 @@ def safe_db_query(func):
169
170
  return func_with_rollback
170
171
 
171
172
 
173
+ def safe_db_query_async(func):
174
+ @wraps(func)
175
+ async def func_with_rollback_async(*args, **kwargs):
176
+ retry_count = 0
177
+ while True:
178
+ try:
179
+ return await func(*args, **kwargs)
180
+ except (
181
+ sqlalchemy.exc.OperationalError,
182
+ sqlalchemy.exc.PendingRollbackError,
183
+ sqlalchemy.exc.InternalError,
184
+ ) as e:
185
+ db_connection.session.rollback()
186
+ if retry_count >= DB_RETRY_COUNT:
187
+ raise e
188
+ retry_count += 1
189
+ return func_with_rollback_async
190
+
191
+
172
192
  logging.basicConfig()
173
193
 
174
194
  if is_debug() and not os.getenv('DISABLE_DATABASE_TERMINAL_OUTPUT'):
@@ -1,4 +1,3 @@
1
- import asyncio
2
1
  import collections
3
2
  import traceback
4
3
  import uuid
@@ -1400,10 +1399,8 @@ class PipelineRun(PipelineRunProjectPlatformMixin, BaseModel):
1400
1399
 
1401
1400
  from mage_ai.usage_statistics.logger import UsageStatisticLogger
1402
1401
 
1403
- asyncio.run(
1404
- UsageStatisticLogger().pipeline_runs_impression(
1405
- lambda: self.query.filter(self.status == self.PipelineRunStatus.COMPLETED).count(),
1406
- )
1402
+ UsageStatisticLogger().pipeline_runs_impression_sync(
1403
+ lambda: self.query.filter(self.status == self.PipelineRunStatus.COMPLETED).count(),
1407
1404
  )
1408
1405
 
1409
1406
  @safe_db_query
@@ -3,7 +3,8 @@ from typing import Optional
3
3
  from sqlalchemy import Column, String, Text, UniqueConstraint, or_
4
4
 
5
5
  from mage_ai.orchestration.db import safe_db_query
6
- from mage_ai.orchestration.db.models.base import BaseModel
6
+ from mage_ai.orchestration.db.models.base import BaseModel, classproperty
7
+ from mage_ai.settings.repo import get_repo_path
7
8
 
8
9
 
9
10
  class Secret(BaseModel):
@@ -15,6 +16,15 @@ class Secret(BaseModel):
15
16
  key_uuid = Column(String(255), nullable=True)
16
17
  __table_args__ = (UniqueConstraint('name', 'key_uuid', name='name_key_uuid_uc'),)
17
18
 
19
+ @classproperty
20
+ def repo_query(cls):
21
+ return cls.query.filter(
22
+ or_(
23
+ Secret.repo_name == get_repo_path(),
24
+ Secret.repo_name.is_(None),
25
+ )
26
+ )
27
+
18
28
  @classmethod
19
29
  @safe_db_query
20
30
  def get_secret(cls, name: str, key_uuid: str) -> Optional['Secret']:
@@ -220,7 +220,7 @@ def __calculate_metrics(pipeline_run: PipelineRun, pipeline) -> Dict:
220
220
 
221
221
  pipeline_metrics_by_stream = {}
222
222
  pipeline_logs_by_stream = {}
223
- pipeline_logs = pipeline_run.logs['content'].split('\n')
223
+ pipeline_logs = pipeline_run.logs[0]['content'].split('\n')
224
224
  for pipeline_log in pipeline_logs:
225
225
  tags = parse_line(pipeline_log)
226
226
  stream = tags.get('stream')
@@ -1,4 +1,3 @@
1
- import asyncio
2
1
  import collections
3
2
  import os
4
3
  import traceback
@@ -260,7 +259,7 @@ class PipelineScheduler:
260
259
  pipeline=self.pipeline,
261
260
  pipeline_run=self.pipeline_run,
262
261
  )
263
- asyncio.run(UsageStatisticLogger().pipeline_run_ended(self.pipeline_run))
262
+ UsageStatisticLogger().pipeline_run_ended_sync(self.pipeline_run)
264
263
 
265
264
  self.logger_manager.output_logs_to_destination()
266
265
 
@@ -339,7 +338,7 @@ class PipelineScheduler:
339
338
  error_msg: str,
340
339
  status=PipelineRun.PipelineRunStatus.FAILED,
341
340
  ) -> None:
342
- asyncio.run(UsageStatisticLogger().pipeline_run_ended(self.pipeline_run))
341
+ UsageStatisticLogger().pipeline_run_ended_sync(self.pipeline_run)
343
342
 
344
343
  if status == PipelineRun.PipelineRunStatus.FAILED:
345
344
  # Only send notification when pipeline run status is FAILED
@@ -1372,7 +1371,7 @@ def stop_pipeline_run(
1372
1371
  # Update pipeline run status to cancelled
1373
1372
  pipeline_run.update(status=status)
1374
1373
 
1375
- asyncio.run(UsageStatisticLogger().pipeline_run_ended(pipeline_run))
1374
+ UsageStatisticLogger().pipeline_run_ended_sync(pipeline_run)
1376
1375
 
1377
1376
  # Cancel all the block runs
1378
1377
  cancel_block_runs_and_jobs(pipeline_run, pipeline)
@@ -1584,7 +1583,7 @@ def schedule_all():
1584
1583
  pipeline_runs_excluded_by_limit = []
1585
1584
  for pipeline_schedule in active_schedules:
1586
1585
  lock_key = f'pipeline_schedule_{pipeline_schedule.id}'
1587
- if not lock.try_acquire_lock(lock_key):
1586
+ if not lock.try_acquire_lock(lock_key, timeout=30):
1588
1587
  continue
1589
1588
 
1590
1589
  trigger_pipeline_run_limit = pipeline_schedule.get_settings().pipeline_run_limit