mage-ai 0.9.13__py3-none-any.whl → 0.9.15__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.
- mage_ai/ai/generator.py +1 -1
- mage_ai/ai/generator_cmds.py +1 -1
- mage_ai/ai/llm_pipeline_wizard.py +31 -17
- mage_ai/api/policies/PipelineRunPolicy.py +1 -0
- mage_ai/api/presenters/BlockRunPresenter.py +4 -1
- mage_ai/api/presenters/PipelineRunPresenter.py +2 -0
- mage_ai/api/resources/BlockRunResource.py +18 -12
- mage_ai/api/resources/GitBranchResource.py +8 -1
- mage_ai/api/resources/PipelineResource.py +30 -14
- mage_ai/api/resources/PipelineRunResource.py +8 -5
- mage_ai/data_integrations/sources/constants.py +1 -0
- mage_ai/data_preparation/executors/block_executor.py +5 -7
- mage_ai/data_preparation/git/__init__.py +18 -0
- mage_ai/data_preparation/models/block/__init__.py +13 -1
- mage_ai/data_preparation/models/block/dbt/utils/__init__.py +5 -2
- mage_ai/data_preparation/models/block/integration/__init__.py +1 -1
- mage_ai/data_preparation/models/custom_templates/utils.py +14 -4
- mage_ai/data_preparation/models/variable.py +5 -2
- mage_ai/data_preparation/templates/constants.py +16 -0
- mage_ai/data_preparation/templates/data_exporters/mssql.py +32 -0
- mage_ai/data_preparation/templates/data_loaders/mssql.py +27 -0
- mage_ai/io/base.py +5 -0
- mage_ai/io/mssql.py +1 -0
- mage_ai/orchestration/db/models/base.py +15 -15
- mage_ai/orchestration/db/models/oauth.py +35 -4
- mage_ai/orchestration/db/models/schedules.py +4 -0
- mage_ai/orchestration/pipeline_scheduler.py +12 -6
- mage_ai/server/constants.py +1 -1
- mage_ai/server/frontend_dist/404.html +2 -2
- mage_ai/server/frontend_dist/404.html.html +2 -2
- mage_ai/server/frontend_dist/_next/static/chunks/{1005-ee665ba499795660.js → 1005-a2f0e3ee378ef02b.js} +1 -1
- mage_ai/server/frontend_dist/_next/static/chunks/1424-fca78f21a81a7183.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/2786-756b3834d10b8711.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/3391-e1578fe1bdb5a557.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/6299-9b785c07dacf22b3.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/7496-7e4dd11e3f3b8e79.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/8952-050e60a8b1eaa32a.js +1 -0
- mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/9605-e40d076d9fc36d83.js → frontend_dist/_next/static/chunks/9605-9332e1686c46da7d.js} +1 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/{_app-ccc78f4c29a2a431.js → _app-9dae6fa5126cf001.js} +1 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/files-03841b6dd7d240f6.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/manage/settings-6e2c0e3f818fd4de.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/edit-6e59dc4e57dd2680.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/logs-02295848c811e26a.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/runs-4a2671811a153411.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/{syncs-ddaca2c267def72a.js → syncs-484581ae34a1c596.js} +1 -1
- mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/triggers/[...slug]-ce11db27e4af7869.js → frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/triggers/[...slug]-91889109af36a793.js} +1 -1
- mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/triggers-ff1faac7a72b1af1.js → frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/triggers-624a2d7cbe6303b4.js} +1 -1
- mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/pages/triggers-eee16a91f281f92d.js → frontend_dist/_next/static/chunks/pages/triggers-71bbb30dd9b57f80.js} +1 -1
- mage_ai/server/{frontend_dist_base_path_template/_next/static/gJQ7p6K0VGDZzX1HXtRED → frontend_dist/_next/static/h5mR3pjfn_EQy348CZ_ok}/_buildManifest.js +1 -1
- mage_ai/server/frontend_dist/files.html +2 -2
- mage_ai/server/frontend_dist/global-data-products/[...slug].html +2 -2
- mage_ai/server/frontend_dist/global-data-products.html +2 -2
- mage_ai/server/frontend_dist/index.html +2 -2
- mage_ai/server/frontend_dist/manage/settings.html +2 -2
- mage_ai/server/frontend_dist/manage/users/[user].html +2 -2
- mage_ai/server/frontend_dist/manage/users/new.html +2 -2
- mage_ai/server/frontend_dist/manage/users.html +2 -2
- mage_ai/server/frontend_dist/manage.html +2 -2
- mage_ai/server/frontend_dist/overview.html +2 -2
- mage_ai/server/frontend_dist/pipeline-runs.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/backfills/[...slug].html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/backfills.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/edit.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/logs.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/monitors/block-runs.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/monitors/block-runtime.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/monitors.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/runs/[run].html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/runs.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/settings.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/syncs.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/triggers/[...slug].html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/triggers.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline].html +2 -2
- mage_ai/server/frontend_dist/pipelines.html +2 -2
- mage_ai/server/frontend_dist/settings/account/profile.html +2 -2
- mage_ai/server/frontend_dist/settings/workspace/preferences.html +2 -2
- mage_ai/server/frontend_dist/settings/workspace/sync-data.html +2 -2
- mage_ai/server/frontend_dist/settings/workspace/users.html +2 -2
- mage_ai/server/frontend_dist/settings.html +2 -2
- mage_ai/server/frontend_dist/sign-in.html +11 -11
- mage_ai/server/frontend_dist/templates/[...slug].html +2 -2
- mage_ai/server/frontend_dist/templates.html +2 -2
- mage_ai/server/frontend_dist/terminal.html +2 -2
- mage_ai/server/frontend_dist/test.html +3 -3
- mage_ai/server/frontend_dist/triggers.html +2 -2
- mage_ai/server/frontend_dist/version-control.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/404.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/404.html.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/{1005-ee665ba499795660.js → 1005-a2f0e3ee378ef02b.js} +1 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/1424-fca78f21a81a7183.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/2786-756b3834d10b8711.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/3391-e1578fe1bdb5a557.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/6299-9b785c07dacf22b3.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/7496-7e4dd11e3f3b8e79.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/8952-050e60a8b1eaa32a.js +1 -0
- mage_ai/server/{frontend_dist/_next/static/chunks/9605-e40d076d9fc36d83.js → frontend_dist_base_path_template/_next/static/chunks/9605-9332e1686c46da7d.js} +1 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/{_app-ccc78f4c29a2a431.js → _app-9dae6fa5126cf001.js} +1 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/files-03841b6dd7d240f6.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage/settings-6e2c0e3f818fd4de.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/edit-6e59dc4e57dd2680.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/logs-02295848c811e26a.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/runs-4a2671811a153411.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/{syncs-ddaca2c267def72a.js → syncs-484581ae34a1c596.js} +1 -1
- mage_ai/server/{frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/triggers/[...slug]-ce11db27e4af7869.js → frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/triggers/[...slug]-91889109af36a793.js} +1 -1
- mage_ai/server/{frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/triggers-ff1faac7a72b1af1.js → frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/triggers-624a2d7cbe6303b4.js} +1 -1
- mage_ai/server/{frontend_dist/_next/static/chunks/pages/triggers-eee16a91f281f92d.js → frontend_dist_base_path_template/_next/static/chunks/pages/triggers-71bbb30dd9b57f80.js} +1 -1
- mage_ai/server/{frontend_dist/_next/static/O4HzlRY2U3Q47jF8s3D-E → frontend_dist_base_path_template/_next/static/xykYLW_ARw5SevdiponLI}/_buildManifest.js +1 -1
- mage_ai/server/frontend_dist_base_path_template/files.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/global-data-products/[...slug].html +2 -2
- mage_ai/server/frontend_dist_base_path_template/global-data-products.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/index.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/manage/settings.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/manage/users/[user].html +2 -2
- mage_ai/server/frontend_dist_base_path_template/manage/users/new.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/manage/users.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/manage.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/overview.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipeline-runs.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/backfills/[...slug].html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/backfills.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/edit.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/logs.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/monitors/block-runs.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/monitors/block-runtime.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/monitors.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/runs/[run].html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/runs.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/settings.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/syncs.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/triggers/[...slug].html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/triggers.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline].html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/settings/account/profile.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/settings/workspace/preferences.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/settings/workspace/sync-data.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/settings/workspace/users.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/settings.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/sign-in.html +11 -11
- mage_ai/server/frontend_dist_base_path_template/templates/[...slug].html +2 -2
- mage_ai/server/frontend_dist_base_path_template/templates.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/terminal.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/test.html +3 -3
- mage_ai/server/frontend_dist_base_path_template/triggers.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/version-control.html +2 -2
- mage_ai/server/server.py +12 -1
- mage_ai/services/spark/spark.py +75 -2
- mage_ai/shared/retry.py +1 -1
- mage_ai/tests/data_preparation/models/custom_templates/__init__.py +0 -0
- mage_ai/tests/data_preparation/models/custom_templates/test_utils.py +36 -0
- mage_ai/tests/data_preparation/models/test_block.py +48 -0
- mage_ai/tests/services/spark/__init__.py +0 -0
- mage_ai/tests/services/spark/test_spark.py +52 -0
- {mage_ai-0.9.13.dist-info → mage_ai-0.9.15.dist-info}/METADATA +1 -1
- {mage_ai-0.9.13.dist-info → mage_ai-0.9.15.dist-info}/RECORD +164 -159
- mage_ai/server/frontend_dist/_next/static/chunks/1424-846b754e84e6eac1.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/2786-ba6f486fcaf52ba0.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/3391-6f0a0a5c254cd7f2.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/6299-fcb702d0f3d3291b.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/7496-7d0f9adf0b333801.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/8952-9d6fa18fa9378989.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/files-2dc2a0dfc0ff620d.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/manage/settings-6577159a0af52a78.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/edit-615ab9e61f0f39ed.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/logs-d331e98ad103b13e.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/runs-df014ddb14ebcef4.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/1424-846b754e84e6eac1.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/2786-ba6f486fcaf52ba0.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/3391-6f0a0a5c254cd7f2.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/6299-fcb702d0f3d3291b.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/7496-7d0f9adf0b333801.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/8952-9d6fa18fa9378989.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/files-2dc2a0dfc0ff620d.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage/settings-6577159a0af52a78.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/edit-615ab9e61f0f39ed.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/logs-d331e98ad103b13e.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/runs-df014ddb14ebcef4.js +0 -1
- mage_ai/tests/orchestration/db/test_models.py +0 -710
- /mage_ai/server/frontend_dist/_next/static/{O4HzlRY2U3Q47jF8s3D-E → h5mR3pjfn_EQy348CZ_ok}/_middlewareManifest.js +0 -0
- /mage_ai/server/frontend_dist/_next/static/{O4HzlRY2U3Q47jF8s3D-E → h5mR3pjfn_EQy348CZ_ok}/_ssgManifest.js +0 -0
- /mage_ai/server/frontend_dist_base_path_template/_next/static/{gJQ7p6K0VGDZzX1HXtRED → xykYLW_ARw5SevdiponLI}/_middlewareManifest.js +0 -0
- /mage_ai/server/frontend_dist_base_path_template/_next/static/{gJQ7p6K0VGDZzX1HXtRED → xykYLW_ARw5SevdiponLI}/_ssgManifest.js +0 -0
- {mage_ai-0.9.13.dist-info → mage_ai-0.9.15.dist-info}/LICENSE +0 -0
- {mage_ai-0.9.13.dist-info → mage_ai-0.9.15.dist-info}/WHEEL +0 -0
- {mage_ai-0.9.13.dist-info → mage_ai-0.9.15.dist-info}/entry_points.txt +0 -0
- {mage_ai-0.9.13.dist-info → mage_ai-0.9.15.dist-info}/top_level.txt +0 -0
mage_ai/ai/generator.py
CHANGED
|
@@ -33,7 +33,7 @@ class Generator:
|
|
|
33
33
|
elif use_case == LLMUseCase.GENERATE_PIPELINE_WITH_DESCRIPTION:
|
|
34
34
|
from mage_ai.ai.llm_pipeline_wizard import LLMPipelineWizard
|
|
35
35
|
|
|
36
|
-
return await LLMPipelineWizard().
|
|
36
|
+
return await LLMPipelineWizard().async_generate_pipeline_from_description(
|
|
37
37
|
request.get('pipeline_description'),
|
|
38
38
|
)
|
|
39
39
|
elif use_case == LLMUseCase.GENERATE_COMMENT_FOR_CODE:
|
mage_ai/ai/generator_cmds.py
CHANGED
|
@@ -49,7 +49,7 @@ def generate_block_with_description(block_description: str):
|
|
|
49
49
|
|
|
50
50
|
@app.command()
|
|
51
51
|
def generate_pipeline_with_description(pipeline_description: str):
|
|
52
|
-
print(asyncio.run(LLMPipelineWizard().
|
|
52
|
+
print(asyncio.run(LLMPipelineWizard().async_generate_pipeline_from_description(
|
|
53
53
|
pipeline_description)))
|
|
54
54
|
|
|
55
55
|
|
|
@@ -2,6 +2,7 @@ import ast
|
|
|
2
2
|
import asyncio
|
|
3
3
|
import json
|
|
4
4
|
import os
|
|
5
|
+
import re
|
|
5
6
|
from typing import Dict
|
|
6
7
|
|
|
7
8
|
import openai
|
|
@@ -53,23 +54,24 @@ PROMPT_TO_SPLIT_BLOCKS = """
|
|
|
53
54
|
A BLOCK does one action either reading data from one data source, transforming the data from
|
|
54
55
|
one format to another or exporting data into a data source.
|
|
55
56
|
Based on the code description delimited by triple backticks, your task is to identify
|
|
56
|
-
how many BLOCKS required
|
|
57
|
+
how many BLOCKS required, function for each BLOCK and upstream blocks between BLOCKs.
|
|
57
58
|
|
|
58
59
|
Use the following format:
|
|
59
|
-
BLOCK 1: <block function>
|
|
60
|
-
BLOCK 2: <block function>
|
|
61
|
-
BLOCK 3: <block function>
|
|
60
|
+
BLOCK 1: function: <block function>. upstream: <upstream blocks>
|
|
61
|
+
BLOCK 2: function: <block function>. upstream: <upstream blocks>
|
|
62
|
+
BLOCK 3: function: <block function>. upstream: <upstream blocks>
|
|
62
63
|
...
|
|
63
64
|
|
|
64
65
|
Example:
|
|
65
66
|
<code description>: ```
|
|
66
|
-
Read data from MySQL, filter out rows with book_price > 100, and save data to BigQuery.
|
|
67
|
+
Read data from MySQL and Postgres, filter out rows with book_price > 100, and save data to BigQuery.
|
|
67
68
|
```
|
|
68
69
|
|
|
69
70
|
Answer:
|
|
70
|
-
BLOCK 1: load data from MySQL
|
|
71
|
-
BLOCK 2:
|
|
72
|
-
BLOCK 3:
|
|
71
|
+
BLOCK 1: function: load data from MySQL. upstream:
|
|
72
|
+
BLOCK 2: function: load data from Postgres. upstream:
|
|
73
|
+
BLOCK 3: function: filter out rows with book_price > 100. upstream: 1, 2
|
|
74
|
+
BLOCK 4: function: export data to BigQuery. upstream: 3
|
|
73
75
|
|
|
74
76
|
<code description>: ```{code_description}```"""
|
|
75
77
|
PROMPT_FOR_FUNCTION_COMMENT = """
|
|
@@ -81,6 +83,7 @@ Your task is to write comments for each function inside.
|
|
|
81
83
|
The comment should follow Google Docstring format.
|
|
82
84
|
Return your response in JSON format with function name as key and the comment as value.
|
|
83
85
|
"""
|
|
86
|
+
BLOCK_SPLIT_PATTERN = r"BLOCK\s+(\w+):\s+function:\s+(.*?)\.\s+upstream:\s*(.*?)$"
|
|
84
87
|
TRANSFORMERS_FOLDER = 'transformers'
|
|
85
88
|
CLASSIFICATION_FUNCTION_NAME = "classify_description"
|
|
86
89
|
TEMPLATE_CLASSIFICATION_FUNCTION = [
|
|
@@ -197,7 +200,10 @@ class LLMPipelineWizard:
|
|
|
197
200
|
function_args.get(DataSource.__name__))
|
|
198
201
|
return block_type, block_language, pipeline_type, config
|
|
199
202
|
|
|
200
|
-
async def async_generate_block_with_description(
|
|
203
|
+
async def async_generate_block_with_description(
|
|
204
|
+
self,
|
|
205
|
+
block_description: str,
|
|
206
|
+
upstream_blocks: [str]) -> dict:
|
|
201
207
|
messages = [{"role": "user", "content": block_description}]
|
|
202
208
|
response = await openai.ChatCompletion.acreate(
|
|
203
209
|
model="gpt-3.5-turbo-0613",
|
|
@@ -220,6 +226,7 @@ class LLMPipelineWizard:
|
|
|
220
226
|
pipeline_type=pipeline_type,
|
|
221
227
|
),
|
|
222
228
|
language=block_language,
|
|
229
|
+
upstream_blocks=upstream_blocks,
|
|
223
230
|
)
|
|
224
231
|
else:
|
|
225
232
|
logger.error("Failed to interpret the description as a block template.")
|
|
@@ -238,11 +245,12 @@ class LLMPipelineWizard:
|
|
|
238
245
|
async def __async_generate_blocks(self,
|
|
239
246
|
block_dict: dict,
|
|
240
247
|
block_id: int,
|
|
241
|
-
block_description: str
|
|
242
|
-
|
|
248
|
+
block_description: str,
|
|
249
|
+
upstream_blocks: [str]) -> dict:
|
|
250
|
+
block = await self.async_generate_block_with_description(block_description, upstream_blocks)
|
|
243
251
|
block_dict[block_id] = block
|
|
244
252
|
|
|
245
|
-
async def
|
|
253
|
+
async def async_generate_pipeline_from_description(self, pipeline_description: str) -> dict:
|
|
246
254
|
splited_block_descriptions = await self.__async_split_description_by_blocks(
|
|
247
255
|
pipeline_description)
|
|
248
256
|
blocks = {}
|
|
@@ -250,11 +258,17 @@ class LLMPipelineWizard:
|
|
|
250
258
|
for line in splited_block_descriptions.strip().split('\n'):
|
|
251
259
|
if line.startswith("BLOCK") and ":" in line:
|
|
252
260
|
# Extract the block_id and block_description from the line
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
261
|
+
match = re.search(BLOCK_SPLIT_PATTERN, line)
|
|
262
|
+
if match:
|
|
263
|
+
block_id = match.group(1)
|
|
264
|
+
block_description = match.group(2).strip()
|
|
265
|
+
upstream_blocks = match.group(3).split(", ")
|
|
266
|
+
block_tasks.append(
|
|
267
|
+
self.__async_generate_blocks(
|
|
268
|
+
blocks,
|
|
269
|
+
block_id,
|
|
270
|
+
block_description,
|
|
271
|
+
upstream_blocks))
|
|
258
272
|
await asyncio.gather(*block_tasks)
|
|
259
273
|
return blocks
|
|
260
274
|
|
|
@@ -47,6 +47,7 @@ PipelineRunPolicy.allow_read(PipelineRunPresenter.default_attributes + [
|
|
|
47
47
|
PipelineRunPolicy.allow_read(PipelineRunPresenter.default_attributes + [
|
|
48
48
|
'block_runs',
|
|
49
49
|
'block_runs_count',
|
|
50
|
+
'completed_block_runs_count',
|
|
50
51
|
'pipeline_schedule_name',
|
|
51
52
|
'pipeline_schedule_token',
|
|
52
53
|
'pipeline_schedule_type',
|
|
@@ -27,6 +27,7 @@ class PipelineRunPresenter(BasePresenter):
|
|
|
27
27
|
additional_attributes = [
|
|
28
28
|
'block_runs',
|
|
29
29
|
'block_runs_count',
|
|
30
|
+
'completed_block_runs_count',
|
|
30
31
|
'pipeline_schedule_name',
|
|
31
32
|
'pipeline_schedule_token',
|
|
32
33
|
'pipeline_schedule_type',
|
|
@@ -66,6 +67,7 @@ PipelineRunPresenter.register_format(
|
|
|
66
67
|
PipelineRunPresenter.default_attributes + [
|
|
67
68
|
'block_runs',
|
|
68
69
|
'block_runs_count',
|
|
70
|
+
'completed_block_runs_count',
|
|
69
71
|
'pipeline_schedule_name',
|
|
70
72
|
'pipeline_schedule_token',
|
|
71
73
|
'pipeline_schedule_type',
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
+
from sqlalchemy.orm import aliased
|
|
2
|
+
|
|
1
3
|
from mage_ai.api.resources.DatabaseResource import DatabaseResource
|
|
2
|
-
from mage_ai.orchestration.db.models.schedules import BlockRun, PipelineRun, PipelineSchedule
|
|
3
4
|
from mage_ai.orchestration.db import safe_db_query
|
|
4
|
-
from
|
|
5
|
+
from mage_ai.orchestration.db.models.schedules import (
|
|
6
|
+
BlockRun,
|
|
7
|
+
PipelineRun,
|
|
8
|
+
PipelineSchedule,
|
|
9
|
+
)
|
|
5
10
|
|
|
6
11
|
|
|
7
12
|
class BlockRunResource(DatabaseResource):
|
|
@@ -24,16 +29,17 @@ class BlockRunResource(DatabaseResource):
|
|
|
24
29
|
pipeline_schedule_name,
|
|
25
30
|
) = tup
|
|
26
31
|
|
|
27
|
-
block_run =
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
32
|
+
block_run = dict(
|
|
33
|
+
block_uuid=block_uuid,
|
|
34
|
+
completed_at=completed_at,
|
|
35
|
+
created_at=created_at,
|
|
36
|
+
id=id,
|
|
37
|
+
pipeline_run_id=pipeline_run_id,
|
|
38
|
+
status=status,
|
|
39
|
+
updated_at=updated_at,
|
|
40
|
+
pipeline_schedule_id=pipeline_schedule_id,
|
|
41
|
+
pipeline_schedule_name=pipeline_schedule_name,
|
|
42
|
+
)
|
|
37
43
|
|
|
38
44
|
block_runs.append(block_run)
|
|
39
45
|
|
|
@@ -265,7 +265,14 @@ class GitBranchResource(GenericResource):
|
|
|
265
265
|
untracked_files: List[str],
|
|
266
266
|
limit: int = None
|
|
267
267
|
) -> Dict:
|
|
268
|
-
arr =
|
|
268
|
+
arr = []
|
|
269
|
+
|
|
270
|
+
if modified_files and isinstance(modified_files, list):
|
|
271
|
+
arr += modified_files
|
|
272
|
+
if staged_files and isinstance(staged_files, list):
|
|
273
|
+
arr += staged_files
|
|
274
|
+
if untracked_files and isinstance(untracked_files, list):
|
|
275
|
+
arr += untracked_files
|
|
269
276
|
|
|
270
277
|
if limit:
|
|
271
278
|
arr = arr[:limit]
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
+
from typing import Dict, List
|
|
2
3
|
|
|
3
4
|
from sqlalchemy import or_
|
|
4
5
|
from sqlalchemy.orm import aliased
|
|
@@ -312,17 +313,28 @@ class PipelineResource(BaseResource):
|
|
|
312
313
|
schedule.update(status=status)
|
|
313
314
|
|
|
314
315
|
@safe_db_query
|
|
315
|
-
def cancel_pipeline_runs(
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
PipelineRun.
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
316
|
+
def cancel_pipeline_runs(
|
|
317
|
+
pipeline_uuid: str = None,
|
|
318
|
+
pipeline_runs: List[Dict] = None,
|
|
319
|
+
):
|
|
320
|
+
if pipeline_runs is not None:
|
|
321
|
+
pipeline_run_ids = [run.get('id') for run in pipeline_runs]
|
|
322
|
+
pipeline_runs_to_cancel = (
|
|
323
|
+
PipelineRun.
|
|
324
|
+
query.
|
|
325
|
+
filter(PipelineRun.id.in_(pipeline_run_ids))
|
|
326
|
+
)
|
|
327
|
+
else:
|
|
328
|
+
pipeline_runs_to_cancel = (
|
|
329
|
+
PipelineRun.
|
|
330
|
+
query.
|
|
331
|
+
filter(PipelineRun.pipeline_uuid == pipeline_uuid).
|
|
332
|
+
filter(PipelineRun.status.in_([
|
|
333
|
+
PipelineRun.PipelineRunStatus.INITIAL,
|
|
334
|
+
PipelineRun.PipelineRunStatus.RUNNING,
|
|
335
|
+
]))
|
|
336
|
+
)
|
|
337
|
+
for pipeline_run in pipeline_runs_to_cancel:
|
|
326
338
|
PipelineScheduler(pipeline_run).stop()
|
|
327
339
|
|
|
328
340
|
def retry_pipeline_runs(pipeline_runs):
|
|
@@ -334,15 +346,19 @@ class PipelineResource(BaseResource):
|
|
|
334
346
|
|
|
335
347
|
def _update_callback(resource):
|
|
336
348
|
if status:
|
|
349
|
+
pipeline_runs = payload.get('pipeline_runs')
|
|
337
350
|
if status in [
|
|
338
351
|
ScheduleStatus.ACTIVE.value,
|
|
339
352
|
ScheduleStatus.INACTIVE.value,
|
|
340
353
|
]:
|
|
341
354
|
update_schedule_status(status, pipeline_uuid)
|
|
342
355
|
elif status == PipelineRun.PipelineRunStatus.CANCELLED.value:
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
356
|
+
if pipeline_runs is None:
|
|
357
|
+
cancel_pipeline_runs(pipeline_uuid=pipeline_uuid)
|
|
358
|
+
else:
|
|
359
|
+
cancel_pipeline_runs(pipeline_runs=pipeline_runs)
|
|
360
|
+
elif status == 'retry' and pipeline_runs:
|
|
361
|
+
retry_pipeline_runs(pipeline_runs)
|
|
346
362
|
|
|
347
363
|
self.on_update_callback = _update_callback
|
|
348
364
|
|
|
@@ -60,12 +60,15 @@ class PipelineRunResource(DatabaseResource):
|
|
|
60
60
|
else:
|
|
61
61
|
order_by.append((parts[0], 'asc'))
|
|
62
62
|
|
|
63
|
+
repo_pipeline_schedule_ids = [s.id for s in PipelineSchedule.repo_query]
|
|
64
|
+
|
|
63
65
|
results = (
|
|
64
|
-
PipelineRun
|
|
65
|
-
query
|
|
66
|
-
|
|
67
|
-
options(selectinload(PipelineRun.
|
|
68
|
-
|
|
66
|
+
PipelineRun
|
|
67
|
+
.query
|
|
68
|
+
.filter(PipelineRun.pipeline_schedule_id.in_(repo_pipeline_schedule_ids))
|
|
69
|
+
.options(selectinload(PipelineRun.block_runs))
|
|
70
|
+
.options(selectinload(PipelineRun.pipeline_schedule))
|
|
71
|
+
.join(PipelineSchedule, PipelineRun.pipeline_schedule_id == PipelineSchedule.id)
|
|
69
72
|
)
|
|
70
73
|
|
|
71
74
|
if global_data_product_uuid is not None:
|
|
@@ -252,20 +252,18 @@ class BlockExecutor:
|
|
|
252
252
|
)
|
|
253
253
|
|
|
254
254
|
result = __execute_with_retry()
|
|
255
|
-
except Exception as
|
|
255
|
+
except Exception as error:
|
|
256
256
|
self.logger.exception(
|
|
257
257
|
f'Failed to execute block {self.block.uuid}',
|
|
258
258
|
**merge_dict(tags, dict(
|
|
259
|
-
|
|
260
|
-
block_uuid=self.block.uuid,
|
|
261
|
-
error=e
|
|
259
|
+
error=error,
|
|
262
260
|
)),
|
|
263
261
|
)
|
|
264
262
|
if on_failure is not None:
|
|
265
263
|
on_failure(
|
|
266
264
|
self.block_uuid,
|
|
267
265
|
error=dict(
|
|
268
|
-
error=
|
|
266
|
+
error=error,
|
|
269
267
|
errors=traceback.format_stack(),
|
|
270
268
|
message=traceback.format_exc(),
|
|
271
269
|
),
|
|
@@ -285,7 +283,7 @@ class BlockExecutor:
|
|
|
285
283
|
logging_tags=tags,
|
|
286
284
|
pipeline_run=pipeline_run,
|
|
287
285
|
)
|
|
288
|
-
raise
|
|
286
|
+
raise error
|
|
289
287
|
self.logger.info(f'Finish executing block with {self.__class__.__name__}.', **tags)
|
|
290
288
|
if on_complete is not None:
|
|
291
289
|
on_complete(self.block_uuid)
|
|
@@ -537,7 +535,7 @@ class BlockExecutor:
|
|
|
537
535
|
tags: Dict = None,
|
|
538
536
|
):
|
|
539
537
|
"""
|
|
540
|
-
Update the status of block run by
|
|
538
|
+
Update the status of block run by either updating the BlockRun db object or making
|
|
541
539
|
API call
|
|
542
540
|
|
|
543
541
|
Args:
|
|
@@ -434,6 +434,24 @@ class Git:
|
|
|
434
434
|
for url in remote.urls:
|
|
435
435
|
if url.lower().startswith('https'):
|
|
436
436
|
repository_names.append('/'.join(url.split('/')[-2:]).replace('.git', ''))
|
|
437
|
+
|
|
438
|
+
# Remove the token from the URL
|
|
439
|
+
# e.g. https://[user]:[token]@[netloc]
|
|
440
|
+
parts = url.split('@')
|
|
441
|
+
|
|
442
|
+
parts_arr = []
|
|
443
|
+
if len(parts) >= 2:
|
|
444
|
+
# https://[user]:[token]
|
|
445
|
+
parts2 = parts[0].split(':')
|
|
446
|
+
# ['https', '', 'user', 'token']
|
|
447
|
+
parts2[len(parts2) - 1] = '[token]'
|
|
448
|
+
parts_arr.append(':'.join(parts2))
|
|
449
|
+
parts_arr += parts[1:]
|
|
450
|
+
else:
|
|
451
|
+
parts_arr += parts
|
|
452
|
+
|
|
453
|
+
url = '@'.join(parts_arr)
|
|
454
|
+
|
|
437
455
|
urls.append(url)
|
|
438
456
|
except GitCommandError as err:
|
|
439
457
|
print('WARNING (mage_ai.data_preparation.git.remotes):')
|
|
@@ -17,6 +17,7 @@ import pandas as pd
|
|
|
17
17
|
import simplejson
|
|
18
18
|
from jinja2 import Template
|
|
19
19
|
|
|
20
|
+
import mage_ai.data_preparation.decorators
|
|
20
21
|
from mage_ai.cache.block import BlockCache
|
|
21
22
|
from mage_ai.data_cleaner.shared.utils import is_geo_dataframe, is_spark_dataframe
|
|
22
23
|
from mage_ai.data_preparation.logging.logger import DictLogger
|
|
@@ -1110,6 +1111,8 @@ class Block:
|
|
|
1110
1111
|
) -> List:
|
|
1111
1112
|
if logging_tags is None:
|
|
1112
1113
|
logging_tags = dict()
|
|
1114
|
+
if input_vars is None:
|
|
1115
|
+
input_vars = list()
|
|
1113
1116
|
|
|
1114
1117
|
decorated_functions = []
|
|
1115
1118
|
test_functions = []
|
|
@@ -1177,9 +1180,18 @@ class Block:
|
|
|
1177
1180
|
block_uuid = self.replicated_block
|
|
1178
1181
|
block_file_path = self.replicated_block_object.file_path
|
|
1179
1182
|
spec = importlib.util.spec_from_file_location(
|
|
1180
|
-
block_uuid,
|
|
1183
|
+
block_uuid,
|
|
1184
|
+
block_file_path,
|
|
1181
1185
|
)
|
|
1182
1186
|
module = importlib.util.module_from_spec(spec)
|
|
1187
|
+
# Set the decorators in the module in case they are not defined in the block
|
|
1188
|
+
# code
|
|
1189
|
+
setattr(
|
|
1190
|
+
module,
|
|
1191
|
+
self.type,
|
|
1192
|
+
getattr(mage_ai.data_preparation.decorators, self.type),
|
|
1193
|
+
)
|
|
1194
|
+
module.test = mage_ai.data_preparation.decorators.test
|
|
1183
1195
|
spec.loader.exec_module(module)
|
|
1184
1196
|
block_function_updated = getattr(module, block_function.__name__)
|
|
1185
1197
|
self.module = module
|
|
@@ -501,9 +501,12 @@ def config_file_loader_and_configuration(
|
|
|
501
501
|
database = kwargs.get('database') or profile.get('project')
|
|
502
502
|
schema = profile.get('dataset')
|
|
503
503
|
|
|
504
|
-
|
|
504
|
+
config_file_loader_kwargs = dict(
|
|
505
505
|
GOOGLE_SERVICE_ACC_KEY_FILEPATH=keyfile,
|
|
506
|
-
)
|
|
506
|
+
)
|
|
507
|
+
if profile.get('location'):
|
|
508
|
+
config_file_loader_kwargs['GOOGLE_LOCATION'] = profile.get('location')
|
|
509
|
+
config_file_loader = ConfigFileLoader(config=config_file_loader_kwargs)
|
|
507
510
|
configuration = dict(
|
|
508
511
|
data_provider=profile_type,
|
|
509
512
|
data_provider_database=database,
|
|
@@ -122,7 +122,7 @@ class IntegrationBlock(Block):
|
|
|
122
122
|
proc = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
|
123
123
|
|
|
124
124
|
for line in proc.stdout:
|
|
125
|
-
f.write(
|
|
125
|
+
f.write(line.decode()),
|
|
126
126
|
print_log_from_line(
|
|
127
127
|
line,
|
|
128
128
|
config=config,
|
|
@@ -56,13 +56,23 @@ def group_and_hydrate_files(
|
|
|
56
56
|
file_dicts: List[Dict],
|
|
57
57
|
custom_template_class,
|
|
58
58
|
) -> List:
|
|
59
|
-
|
|
59
|
+
def _func(x):
|
|
60
|
+
arr = ['']
|
|
60
61
|
|
|
61
|
-
|
|
62
|
+
if x:
|
|
63
|
+
parent_names = x.get('parent_names', []) or []
|
|
64
|
+
if parent_names and len(parent_names) >= 1:
|
|
65
|
+
arr = [str(parent_name) for parent_name in parent_names]
|
|
66
|
+
|
|
67
|
+
return os.path.join(*arr)
|
|
68
|
+
|
|
69
|
+
groups = group_by(_func, file_dicts)
|
|
70
|
+
|
|
71
|
+
custom_templates = []
|
|
62
72
|
|
|
63
73
|
for template_uuid, _ in groups.items():
|
|
64
74
|
custom_template = custom_template_class.load(template_uuid=template_uuid)
|
|
65
75
|
if custom_template:
|
|
66
|
-
|
|
76
|
+
custom_templates.append(custom_template)
|
|
67
77
|
|
|
68
|
-
return
|
|
78
|
+
return custom_templates
|
|
@@ -430,11 +430,14 @@ class Variable:
|
|
|
430
430
|
df_output = data.copy()
|
|
431
431
|
# Clean up data types since parquet doesn't support mixed data types
|
|
432
432
|
for c in df_output.columns:
|
|
433
|
-
|
|
433
|
+
df_col = df_output[c]
|
|
434
|
+
if type(df_col) is pd.DataFrame:
|
|
435
|
+
raise Exception(f'Please do not use duplicate column name: "{c}"')
|
|
436
|
+
c_dtype = df_col.dtype
|
|
434
437
|
if not is_object_dtype(c_dtype):
|
|
435
438
|
column_types[c] = str(c_dtype)
|
|
436
439
|
else:
|
|
437
|
-
series_non_null =
|
|
440
|
+
series_non_null = df_col.dropna()
|
|
438
441
|
if len(series_non_null) > 0:
|
|
439
442
|
coltype = type(series_non_null.iloc[0])
|
|
440
443
|
if is_object_dtype(series_non_null.dtype):
|
|
@@ -51,6 +51,14 @@ TEMPLATES = [
|
|
|
51
51
|
name='MongoDB',
|
|
52
52
|
path='data_loaders/mongodb.py',
|
|
53
53
|
),
|
|
54
|
+
dict(
|
|
55
|
+
block_type=BlockType.DATA_LOADER,
|
|
56
|
+
description='Load data from MSSQL.',
|
|
57
|
+
groups=[GROUP_DATABASES],
|
|
58
|
+
language=BlockLanguage.PYTHON,
|
|
59
|
+
name='MSSQL',
|
|
60
|
+
path='data_loaders/mssql.py',
|
|
61
|
+
),
|
|
54
62
|
dict(
|
|
55
63
|
block_type=BlockType.DATA_EXPORTER,
|
|
56
64
|
description='Export data to a Delta Table in Amazon S3.',
|
|
@@ -82,6 +90,14 @@ TEMPLATES = [
|
|
|
82
90
|
name='MongoDB',
|
|
83
91
|
path='data_exporters/mongodb.py',
|
|
84
92
|
),
|
|
93
|
+
dict(
|
|
94
|
+
block_type=BlockType.DATA_EXPORTER,
|
|
95
|
+
description='Export data to MSSQL.',
|
|
96
|
+
groups=[GROUP_DATABASES],
|
|
97
|
+
language=BlockLanguage.PYTHON,
|
|
98
|
+
name='MSSQL',
|
|
99
|
+
path='data_exporters/mssql.py',
|
|
100
|
+
),
|
|
85
101
|
dict(
|
|
86
102
|
block_type=BlockType.DATA_LOADER,
|
|
87
103
|
description='Trigger another pipeline to run.',
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from mage_ai.settings.repo import get_repo_path
|
|
2
|
+
from mage_ai.io.config import ConfigFileLoader
|
|
3
|
+
from mage_ai.io.mssql import MSSQL
|
|
4
|
+
from pandas import DataFrame
|
|
5
|
+
from os import path
|
|
6
|
+
|
|
7
|
+
if 'data_exporter' not in globals():
|
|
8
|
+
from mage_ai.data_preparation.decorators import data_exporter
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@data_exporter
|
|
12
|
+
def export_data_to_mssql(df: DataFrame, **kwargs) -> None:
|
|
13
|
+
"""
|
|
14
|
+
Template for exporting data to a MSSQL database.
|
|
15
|
+
Specify your configuration settings in 'io_config.yaml'.
|
|
16
|
+
Set the following in your io_config:
|
|
17
|
+
|
|
18
|
+
Docs: https://docs.mage.ai/integrations/databases/MicrosoftSQLServer
|
|
19
|
+
"""
|
|
20
|
+
schema_name = 'dbo' # Specify the name of the schema to export data to
|
|
21
|
+
table_name = 'your_table_name' # Specify the name of the table to export data to
|
|
22
|
+
config_path = path.join(get_repo_path(), 'io_config.yaml')
|
|
23
|
+
config_profile = 'default'
|
|
24
|
+
|
|
25
|
+
with MSSQL.with_config(ConfigFileLoader(config_path, config_profile)) as loader:
|
|
26
|
+
loader.export(
|
|
27
|
+
df,
|
|
28
|
+
schema_name,
|
|
29
|
+
table_name,
|
|
30
|
+
index=False, # Specifies whether to include index in exported table
|
|
31
|
+
if_exists='replace', # Specify resolution policy if table name already exists
|
|
32
|
+
)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{% extends "data_loaders/default.jinja" %}
|
|
2
|
+
{% block imports %}
|
|
3
|
+
from mage_ai.settings.repo import get_repo_path
|
|
4
|
+
from mage_ai.io.config import ConfigFileLoader
|
|
5
|
+
from mage_ai.io.mssql import MSSQL
|
|
6
|
+
from os import path
|
|
7
|
+
{{ super() -}}
|
|
8
|
+
{% endblock %}
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
{% block content %}
|
|
12
|
+
@data_loader
|
|
13
|
+
def load_data_from_mssql(*args, **kwargs):
|
|
14
|
+
"""
|
|
15
|
+
Template for loading data from a MSSQL database.
|
|
16
|
+
Specify your configuration settings in 'io_config.yaml'.
|
|
17
|
+
Set the following in your io_config:
|
|
18
|
+
|
|
19
|
+
Docs: https://docs.mage.ai/integrations/databases/MicrosoftSQLServer
|
|
20
|
+
"""
|
|
21
|
+
query = 'Your MSSQL query' # Specify your SQL query here
|
|
22
|
+
config_path = path.join(get_repo_path(), 'io_config.yaml')
|
|
23
|
+
config_profile = 'default'
|
|
24
|
+
|
|
25
|
+
with MSSQL.with_config(ConfigFileLoader(config_path, config_profile)) as loader:
|
|
26
|
+
return loader.load(query)
|
|
27
|
+
{% endblock %}
|
mage_ai/io/base.py
CHANGED
|
@@ -35,6 +35,7 @@ class FileFormat(str, Enum):
|
|
|
35
35
|
JSON = 'json'
|
|
36
36
|
PARQUET = 'parquet'
|
|
37
37
|
HDF5 = 'hdf5'
|
|
38
|
+
XML = 'xml'
|
|
38
39
|
|
|
39
40
|
|
|
40
41
|
class ExportWritePolicy(str, Enum):
|
|
@@ -137,6 +138,8 @@ class BaseFile(BaseIO):
|
|
|
137
138
|
return pd.read_parquet
|
|
138
139
|
elif format == FileFormat.HDF5:
|
|
139
140
|
return pd.read_hdf
|
|
141
|
+
elif format == FileFormat.XML:
|
|
142
|
+
return pd.read_xml
|
|
140
143
|
else:
|
|
141
144
|
raise ValueError(f'Invalid format \'{format}\' specified.')
|
|
142
145
|
|
|
@@ -226,6 +229,8 @@ class BaseFile(BaseIO):
|
|
|
226
229
|
return df.to_json
|
|
227
230
|
elif format == FileFormat.HDF5:
|
|
228
231
|
return df.to_hdf
|
|
232
|
+
elif format == FileFormat.XML:
|
|
233
|
+
return df.to_xml
|
|
229
234
|
return df.to_parquet
|
|
230
235
|
|
|
231
236
|
def __del__(self):
|
mage_ai/io/mssql.py
CHANGED
|
@@ -142,6 +142,7 @@ class MSSQL(BaseSQL):
|
|
|
142
142
|
# Remove extraneous surrounding double quotes
|
|
143
143
|
# that get added while performing conversion to string.
|
|
144
144
|
df_[col] = df_[col].apply(lambda x: x.strip('"') if x and isinstance(x, str) else x)
|
|
145
|
+
df_.replace({np.NaN: None}, inplace=True)
|
|
145
146
|
for _, row in df_.iterrows():
|
|
146
147
|
values.append(tuple(row))
|
|
147
148
|
|