MindsDB 25.2.2.1__py3-none-any.whl → 25.2.3.0__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 MindsDB might be problematic. Click here for more details.
- {MindsDB-25.2.2.1.dist-info → MindsDB-25.2.3.0.dist-info}/METADATA +229 -229
- {MindsDB-25.2.2.1.dist-info → MindsDB-25.2.3.0.dist-info}/RECORD +16 -15
- mindsdb/__about__.py +1 -1
- mindsdb/api/http/namespaces/databases.py +69 -1
- mindsdb/integrations/handlers/file_handler/file_handler.py +28 -46
- mindsdb/integrations/handlers/file_handler/tests/test_file_handler.py +8 -11
- mindsdb/integrations/handlers/ms_one_drive_handler/ms_one_drive_tables.py +1 -1
- mindsdb/integrations/handlers/timegpt_handler/requirements.txt +1 -1
- mindsdb/integrations/utilities/files/file_reader.py +66 -14
- mindsdb/interfaces/file/file_controller.py +140 -11
- mindsdb/interfaces/storage/db.py +1 -0
- mindsdb/migrations/versions/2025-02-09_4943359e354a_file_metadata.py +31 -0
- mindsdb/utilities/render/sqlalchemy_render.py +11 -5
- {MindsDB-25.2.2.1.dist-info → MindsDB-25.2.3.0.dist-info}/LICENSE +0 -0
- {MindsDB-25.2.2.1.dist-info → MindsDB-25.2.3.0.dist-info}/WHEEL +0 -0
- {MindsDB-25.2.2.1.dist-info → MindsDB-25.2.3.0.dist-info}/top_level.txt +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
mindsdb/__about__.py,sha256=
|
|
1
|
+
mindsdb/__about__.py,sha256=uLW9FOsa76914MCeZsPLrQCq8Xx-uChOe89R4xgef8g,444
|
|
2
2
|
mindsdb/__init__.py,sha256=fZopLiAYa9MzMZ0d48JgHc_LddfFKDzh7n_8icsjrVs,54
|
|
3
3
|
mindsdb/__main__.py,sha256=VQ3RetGs34NhFRT9d76o5S3UpKxdr-G3c0138kz3f8Y,21435
|
|
4
4
|
mindsdb/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -66,7 +66,7 @@ mindsdb/api/http/namespaces/analysis.py,sha256=Dp3izdLkG35cSAKNUPJfuGoFSRe34CzFP
|
|
|
66
66
|
mindsdb/api/http/namespaces/auth.py,sha256=Qm1ZUBdbv_nTDzSQHdzXEosdpYCRzIa17k1yYErOeuk,5483
|
|
67
67
|
mindsdb/api/http/namespaces/chatbots.py,sha256=9UBguchWd04wfYCrsNcFkkt0RCbg-eJcv79RIcJa5Zk,11738
|
|
68
68
|
mindsdb/api/http/namespaces/config.py,sha256=msgxhD9pBnit1w7WD4OYWaq-YtgNCzraxN7ibAvcGcU,9880
|
|
69
|
-
mindsdb/api/http/namespaces/databases.py,sha256=
|
|
69
|
+
mindsdb/api/http/namespaces/databases.py,sha256=HAr2pvEuj3n2t1Wmxje0ZsSRxrY3sz9k_MGQ-yvgYWg,18570
|
|
70
70
|
mindsdb/api/http/namespaces/default.py,sha256=r8PXn00Um2eyKB5e_Kj7fzk4e4LYH-JCzXCpxgJA2vY,4729
|
|
71
71
|
mindsdb/api/http/namespaces/file.py,sha256=u6xYa_moAMb0UXWGkNtErGw9nk-FbloRuLHrLCANjoU,6644
|
|
72
72
|
mindsdb/api/http/namespaces/handlers.py,sha256=zRWZvPOplwSAbKDKeQz93J38TsCQT89-GSlSug6Mtug,7911
|
|
@@ -607,11 +607,11 @@ mindsdb/integrations/handlers/faunadb_handler/tests/__init__.py,sha256=47DEQpj8H
|
|
|
607
607
|
mindsdb/integrations/handlers/faunadb_handler/tests/test_faunadb_handler.py,sha256=JrgVIq6jucZjUCr8FpbC9ByJ3WdZGppZKHdovvFbNiw,1151
|
|
608
608
|
mindsdb/integrations/handlers/file_handler/__about__.py,sha256=UavP5jJ_xWIaJ0oRsEANLulczZ0craaUb3XLfCSBmEc,331
|
|
609
609
|
mindsdb/integrations/handlers/file_handler/__init__.py,sha256=46Hnm3ijRsYw95BkkBxHJK8k4h_2Te0j1W0r3-ptVCg,329
|
|
610
|
-
mindsdb/integrations/handlers/file_handler/file_handler.py,sha256=
|
|
610
|
+
mindsdb/integrations/handlers/file_handler/file_handler.py,sha256=atOboSG8pcMwnHCsxTzT1vtr92LqYgCcMyqt3hjhO2w,7148
|
|
611
611
|
mindsdb/integrations/handlers/file_handler/icon.svg,sha256=hsXEvUzrO7WQMOPC83LYQt-FW0wey9TCj5EwiIJwKwk,565
|
|
612
612
|
mindsdb/integrations/handlers/file_handler/requirements.txt,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
613
613
|
mindsdb/integrations/handlers/file_handler/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
614
|
-
mindsdb/integrations/handlers/file_handler/tests/test_file_handler.py,sha256=
|
|
614
|
+
mindsdb/integrations/handlers/file_handler/tests/test_file_handler.py,sha256=UubwfvgJFe7aSZxpzPGcJ32bvMUE72VnxUJB3RqmSZo,11743
|
|
615
615
|
mindsdb/integrations/handlers/file_handler/tests/data/test.txt,sha256=SUG2Uw_Hmth51gyaCJyeyfAbFssHfsB3qWFcxBX_W1g,570
|
|
616
616
|
mindsdb/integrations/handlers/financial_modeling_prep_handler/__about__.py,sha256=Dz6gPZVoxWJqMgte07pkMKtQq42u7qYtCiaxdHVgANw,392
|
|
617
617
|
mindsdb/integrations/handlers/financial_modeling_prep_handler/__init__.py,sha256=AncBNXt6EECSYDNTlnkxkQ3_dfgEITMKaUUWjV-KSMY,537
|
|
@@ -1033,7 +1033,7 @@ mindsdb/integrations/handlers/ms_one_drive_handler/__init__.py,sha256=vraI6WlfpN
|
|
|
1033
1033
|
mindsdb/integrations/handlers/ms_one_drive_handler/icon.svg,sha256=52dL1SJWSi44Z2hQUMEyyP8DUmhgUtjvsfN3HU2EBes,2430
|
|
1034
1034
|
mindsdb/integrations/handlers/ms_one_drive_handler/ms_graph_api_one_drive_client.py,sha256=q3Qd-9bBYacurH_rV5cYpLUgrI0kvSHGSDoi_OnkeWY,3508
|
|
1035
1035
|
mindsdb/integrations/handlers/ms_one_drive_handler/ms_one_drive_handler.py,sha256=8Xi8rKFWpEsDw5mzqgR7IW4wEbk-TBNqNqOj4350gL4,9333
|
|
1036
|
-
mindsdb/integrations/handlers/ms_one_drive_handler/ms_one_drive_tables.py,sha256=
|
|
1036
|
+
mindsdb/integrations/handlers/ms_one_drive_handler/ms_one_drive_tables.py,sha256=rBD6sEli8XP-cxTogFu2J7ScO_WTw1gM-6gZm2P8lsc,2802
|
|
1037
1037
|
mindsdb/integrations/handlers/ms_teams_handler/__about__.py,sha256=CqqxMr2iXyNXzPfcrMnDpK7u7gC6tmYPkOYR3KBXgQM,357
|
|
1038
1038
|
mindsdb/integrations/handlers/ms_teams_handler/__init__.py,sha256=btEl5YvlZMkMn3NR6eChKtfT31OQY1LBEII1qJ4hf7g,518
|
|
1039
1039
|
mindsdb/integrations/handlers/ms_teams_handler/icon.svg,sha256=pso2BBOFF2aiAw28W9lBPnvnVeNib21kvrqfZnO_C0M,3934
|
|
@@ -1530,7 +1530,7 @@ mindsdb/integrations/handlers/tidb_handler/tests/test_tidb_handler.py,sha256=2jW
|
|
|
1530
1530
|
mindsdb/integrations/handlers/timegpt_handler/__about__.py,sha256=wBHyAM5O7yWVgUKBGJwRM6gpy8FUWzcBxL048e4yjhA,390
|
|
1531
1531
|
mindsdb/integrations/handlers/timegpt_handler/__init__.py,sha256=lMvw91ghkXJj2RuXSi1om4KYc8xbNik3YwXcYA-Hld8,491
|
|
1532
1532
|
mindsdb/integrations/handlers/timegpt_handler/icon.svg,sha256=CfkLqkeZTKSjsQMKTx_KqkCqXvnHRyAAa1DVs-R1qiA,590
|
|
1533
|
-
mindsdb/integrations/handlers/timegpt_handler/requirements.txt,sha256=
|
|
1533
|
+
mindsdb/integrations/handlers/timegpt_handler/requirements.txt,sha256=AfBYvR80CShVm2CPUOonQgDHgcTxApeX5OTKgfkjB-I,14
|
|
1534
1534
|
mindsdb/integrations/handlers/timegpt_handler/timegpt_handler.py,sha256=rdAQhmGP_hqguShfHPNj5r5pBXZEla8A-4ErTQ0_LbY,9868
|
|
1535
1535
|
mindsdb/integrations/handlers/timescaledb_handler/__about__.py,sha256=joUHMPBAiylB7Uc7sipeFVWlWDZ_wqVqyyy_MivkNFI,355
|
|
1536
1536
|
mindsdb/integrations/handlers/timescaledb_handler/__init__.py,sha256=ynMpLqCRlYomZEFEinXmtYYGr0DPIMQZ9HtsytOpe6c,497
|
|
@@ -1714,7 +1714,7 @@ mindsdb/integrations/utilities/datasets/dataset.py,sha256=HjxaMAPuos3HaMsVsAm3q-
|
|
|
1714
1714
|
mindsdb/integrations/utilities/datasets/question_answering/fda_style_qa.csv,sha256=uEOevZwKVjtPScjDVvWfTl4VLvDrhL7D9_9DoRJu7ic,6906
|
|
1715
1715
|
mindsdb/integrations/utilities/datasets/question_answering/squad_v2_val_100_sample.csv,sha256=erj_BGVabfxDFi9TbTXSJ-OiixHRYnDzoJUJcWWeeQY,104035
|
|
1716
1716
|
mindsdb/integrations/utilities/files/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
1717
|
-
mindsdb/integrations/utilities/files/file_reader.py,sha256=
|
|
1717
|
+
mindsdb/integrations/utilities/files/file_reader.py,sha256=MwityIR28k3lrLQeVxOufRNlATHDMihedLtqB8YhuBQ,11219
|
|
1718
1718
|
mindsdb/integrations/utilities/handlers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
1719
1719
|
mindsdb/integrations/utilities/handlers/api_utilities/__init__.py,sha256=zkvVb54zxtn4_NSwqCD-SVQG_5JI9EYRygb6EABQu7g,43
|
|
1720
1720
|
mindsdb/integrations/utilities/handlers/api_utilities/microsoft/__init__.py,sha256=xmcg0gXVY4WUIazM-aCjXXFikFonS6hI05GbdEs5_I8,56
|
|
@@ -1788,7 +1788,7 @@ mindsdb/interfaces/database/log.py,sha256=tewoKWc-xvfbFC9RVaUUAjQGuRY__Lex8fbR_p
|
|
|
1788
1788
|
mindsdb/interfaces/database/projects.py,sha256=0ZjcqpDf437FmRhUk4z8YJR3MaoxtwJH60uPusLbfPU,15077
|
|
1789
1789
|
mindsdb/interfaces/database/views.py,sha256=CthbUly3OgOyFV8a-VRQwhjLh6I1LXbBUzMAcfu8USI,4404
|
|
1790
1790
|
mindsdb/interfaces/file/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
1791
|
-
mindsdb/interfaces/file/file_controller.py,sha256=
|
|
1791
|
+
mindsdb/interfaces/file/file_controller.py,sha256=ebe0hEZhOHbrMhVg84XW33Jl1jUeo2uARKGp_NWBkKY,8626
|
|
1792
1792
|
mindsdb/interfaces/functions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
1793
1793
|
mindsdb/interfaces/functions/controller.py,sha256=8kyWA8TnMsRDyIl2s3JcvdGYeww4_Qmf-jYq_wwOYH4,6057
|
|
1794
1794
|
mindsdb/interfaces/jobs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -1817,7 +1817,7 @@ mindsdb/interfaces/skills/custom/text2sql/__init__.py,sha256=47DEQpj8HBSa-_TImW-
|
|
|
1817
1817
|
mindsdb/interfaces/skills/custom/text2sql/mindsdb_sql_tool.py,sha256=n7r08idG9Qaa0C41HokUf-w72yyACoINOFKGgtNVHLA,1375
|
|
1818
1818
|
mindsdb/interfaces/skills/custom/text2sql/mindsdb_sql_toolkit.py,sha256=08WyJXYJ_hP7JYLvwaSWuJN6Q1PLgIAvh7bcgsV-0XU,7962
|
|
1819
1819
|
mindsdb/interfaces/storage/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
1820
|
-
mindsdb/interfaces/storage/db.py,sha256=
|
|
1820
|
+
mindsdb/interfaces/storage/db.py,sha256=FDp3E1gqRUL9TJtEBii-g9o_Xdsj1ue7L0pJAwkTtsQ,19413
|
|
1821
1821
|
mindsdb/interfaces/storage/fs.py,sha256=4Nyo-h23UtZc2nz_LWyVzboC_e1jlU58aph1_en8MdE,21155
|
|
1822
1822
|
mindsdb/interfaces/storage/json.py,sha256=KdrmXfqVCNZ_anNpfyygcFQeywbdJMCMbaI3HFJic-U,2925
|
|
1823
1823
|
mindsdb/interfaces/storage/model_fs.py,sha256=LQHyIs3wlOEpFHceAjziA7zuQKY3N-8gt9EuSYHO8zI,8267
|
|
@@ -1890,6 +1890,7 @@ mindsdb/migrations/versions/2024-11-19_0f89b523f346_agent_skills_parameters.py,s
|
|
|
1890
1890
|
mindsdb/migrations/versions/2024-11-28_a8a3fac369e7_llm_log_json_in_out.py,sha256=1qQl720lynCbtcGqKIbFM0HCo81xIXqz0Hj3dWkH6UI,3174
|
|
1891
1891
|
mindsdb/migrations/versions/2024-11-29_f6dc924079fa_predictor_training_metadata.py,sha256=nCnp-M3ljIEAO9XTc5mSURp2SX5Q8gE6kyJHp5J5vmo,1322
|
|
1892
1892
|
mindsdb/migrations/versions/2025-01-15_c06c35f7e8e1_project_company.py,sha256=zJcBGdgBMlt9oI-SbB8C74w-jDf3ZMYlEuO2guwrfZw,1896
|
|
1893
|
+
mindsdb/migrations/versions/2025-02-09_4943359e354a_file_metadata.py,sha256=0-QqeVZECfpjKwe86PEwQ9dPgo6Pgmkp_213wTHYiPk,826
|
|
1893
1894
|
mindsdb/migrations/versions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
1894
1895
|
mindsdb/utilities/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
1895
1896
|
mindsdb/utilities/auth.py,sha256=6ycLJgXySghgFdzK6emxdStElzt5aOPafjDCRR_g_q0,2336
|
|
@@ -1929,9 +1930,9 @@ mindsdb/utilities/otel/metric_handlers/__init__.py,sha256=3jGsLt5KkdhqaAUUw8ALxE
|
|
|
1929
1930
|
mindsdb/utilities/profiler/__init__.py,sha256=d4VXl80uSm1IotR-WwbBInPmLmACiK0AzxXGBA40I-0,251
|
|
1930
1931
|
mindsdb/utilities/profiler/profiler.py,sha256=KCUtOupkbM_nCoof9MtiuhUzDGezx4a4NsBX6vGWbPA,3936
|
|
1931
1932
|
mindsdb/utilities/render/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
1932
|
-
mindsdb/utilities/render/sqlalchemy_render.py,sha256
|
|
1933
|
-
MindsDB-25.2.
|
|
1934
|
-
MindsDB-25.2.
|
|
1935
|
-
MindsDB-25.2.
|
|
1936
|
-
MindsDB-25.2.
|
|
1937
|
-
MindsDB-25.2.
|
|
1933
|
+
mindsdb/utilities/render/sqlalchemy_render.py,sha256=Z6bzVfQLqnMTYPJ9X4mZlFdrLmNwxM86yuvO_9TAsq4,30193
|
|
1934
|
+
MindsDB-25.2.3.0.dist-info/LICENSE,sha256=ziqdjujs6WDn-9g3t0SISjHCBc2pLRht3gnRbQoXmIs,5804
|
|
1935
|
+
MindsDB-25.2.3.0.dist-info/METADATA,sha256=lg683Nqs7NiKayjl8EL9yT4iLN-j059ktmL4Q5-eJWk,42861
|
|
1936
|
+
MindsDB-25.2.3.0.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
|
1937
|
+
MindsDB-25.2.3.0.dist-info/top_level.txt,sha256=10wPR96JDf3hM8aMP7Fz0lDlmClEP480zgXISJKr5jE,8
|
|
1938
|
+
MindsDB-25.2.3.0.dist-info/RECORD,,
|
mindsdb/__about__.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
__title__ = 'MindsDB'
|
|
2
2
|
__package_name__ = 'mindsdb'
|
|
3
|
-
__version__ = '25.2.
|
|
3
|
+
__version__ = '25.2.3.0'
|
|
4
4
|
__description__ = "MindsDB's AI SQL Server enables developers to build AI tools that need access to real-time data to perform their tasks"
|
|
5
5
|
__email__ = "jorge@mindsdb.com"
|
|
6
6
|
__author__ = 'MindsDB Inc'
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
from http import HTTPStatus
|
|
2
|
+
import tempfile
|
|
1
3
|
import time
|
|
2
4
|
from typing import Dict
|
|
3
|
-
from
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
import shutil
|
|
4
7
|
from sqlalchemy.exc import NoResultFound
|
|
5
8
|
|
|
6
9
|
from flask import request
|
|
@@ -94,6 +97,71 @@ class DatabasesResource(Resource):
|
|
|
94
97
|
return new_integration, HTTPStatus.CREATED
|
|
95
98
|
|
|
96
99
|
|
|
100
|
+
@ns_conf.route('/status')
|
|
101
|
+
class DatabasesStatusResource(Resource):
|
|
102
|
+
@ns_conf.doc('check_database_connection_status')
|
|
103
|
+
@api_endpoint_metrics('POST', '/databases/status')
|
|
104
|
+
def post(self):
|
|
105
|
+
'''Check the connection parameters for a database'''
|
|
106
|
+
data = {}
|
|
107
|
+
if request.content_type == 'application/json':
|
|
108
|
+
data.update(request.json or {})
|
|
109
|
+
elif request.content_type.startswith('multipart/form-data'):
|
|
110
|
+
data.update(request.form or {})
|
|
111
|
+
|
|
112
|
+
if 'engine' not in data:
|
|
113
|
+
return http_error(
|
|
114
|
+
HTTPStatus.BAD_REQUEST, 'Wrong argument',
|
|
115
|
+
'Missing "engine" field for database'
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
engine = data['engine']
|
|
119
|
+
parameters = data
|
|
120
|
+
del parameters['engine']
|
|
121
|
+
|
|
122
|
+
files = request.files
|
|
123
|
+
temp_dir = None
|
|
124
|
+
if files is not None and len(files) > 0:
|
|
125
|
+
temp_dir = tempfile.mkdtemp(prefix='integration_files_')
|
|
126
|
+
for key, file in files.items():
|
|
127
|
+
temp_dir_path = Path(temp_dir)
|
|
128
|
+
file_name = Path(file.filename)
|
|
129
|
+
file_path = temp_dir_path.joinpath(file_name).resolve()
|
|
130
|
+
if temp_dir_path not in file_path.parents:
|
|
131
|
+
raise Exception(f'Can not save file at path: {file_path}')
|
|
132
|
+
file.save(file_path)
|
|
133
|
+
parameters[key] = str(file_path)
|
|
134
|
+
|
|
135
|
+
session = SessionController()
|
|
136
|
+
|
|
137
|
+
try:
|
|
138
|
+
handler = session.integration_controller.create_tmp_handler("test_connection", engine, parameters)
|
|
139
|
+
status = handler.check_connection()
|
|
140
|
+
except ImportError as import_error:
|
|
141
|
+
status = HandlerStatusResponse(success=False, error_message=str(import_error))
|
|
142
|
+
except Exception as unknown_error:
|
|
143
|
+
status = HandlerStatusResponse(success=False, error_message=str(unknown_error))
|
|
144
|
+
finally:
|
|
145
|
+
if temp_dir is not None:
|
|
146
|
+
shutil.rmtree(temp_dir)
|
|
147
|
+
|
|
148
|
+
if not status.success:
|
|
149
|
+
if hasattr(status, 'redirect_url') and isinstance(status, str):
|
|
150
|
+
return {
|
|
151
|
+
"status": "redirect_required",
|
|
152
|
+
"redirect_url": status.redirect_url,
|
|
153
|
+
"detail": status.error_message
|
|
154
|
+
}, HTTPStatus.OK
|
|
155
|
+
return {
|
|
156
|
+
"status": "connection_error",
|
|
157
|
+
"detail": status.error_message
|
|
158
|
+
}, HTTPStatus.OK
|
|
159
|
+
|
|
160
|
+
return {
|
|
161
|
+
"status": "success",
|
|
162
|
+
}, HTTPStatus.OK
|
|
163
|
+
|
|
164
|
+
|
|
97
165
|
@ns_conf.route('/<database_name>')
|
|
98
166
|
class DatabaseResource(Resource):
|
|
99
167
|
@ns_conf.doc('get_database')
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import shutil
|
|
3
3
|
import tempfile
|
|
4
|
-
from pathlib import Path
|
|
5
4
|
|
|
6
5
|
import pandas as pd
|
|
7
6
|
from mindsdb_sql_parser import parse_sql
|
|
8
|
-
from mindsdb_sql_parser.ast import CreateTable, DropTables, Insert, Select
|
|
7
|
+
from mindsdb_sql_parser.ast import CreateTable, DropTables, Insert, Select, Identifier
|
|
9
8
|
from mindsdb_sql_parser.ast.base import ASTNode
|
|
10
9
|
|
|
11
10
|
from mindsdb.api.executor.utilities.sql import query_df
|
|
@@ -15,8 +14,6 @@ from mindsdb.integrations.libs.response import HandlerResponse as Response
|
|
|
15
14
|
from mindsdb.integrations.libs.response import HandlerStatusResponse as StatusResponse
|
|
16
15
|
from mindsdb.utilities import log
|
|
17
16
|
|
|
18
|
-
from mindsdb.integrations.utilities.files.file_reader import FileReader
|
|
19
|
-
|
|
20
17
|
|
|
21
18
|
logger = log.getLogger(__name__)
|
|
22
19
|
|
|
@@ -63,6 +60,18 @@ class FileHandler(DatabaseHandler):
|
|
|
63
60
|
def check_connection(self) -> StatusResponse:
|
|
64
61
|
return StatusResponse(True)
|
|
65
62
|
|
|
63
|
+
def _get_table_page_names(self, table: Identifier):
|
|
64
|
+
table_name_parts = table.parts
|
|
65
|
+
|
|
66
|
+
# Check if it's a multi-part name (e.g., `file_name.sheet_name`)
|
|
67
|
+
if len(table_name_parts) > 1:
|
|
68
|
+
table_name = table_name_parts[-2]
|
|
69
|
+
page_name = table_name_parts[-1] # Get the sheet name
|
|
70
|
+
else:
|
|
71
|
+
table_name = table_name_parts[-1]
|
|
72
|
+
page_name = None
|
|
73
|
+
return table_name, page_name
|
|
74
|
+
|
|
66
75
|
def query(self, query: ASTNode) -> Response:
|
|
67
76
|
if type(query) is DropTables:
|
|
68
77
|
for table_identifier in query.tables:
|
|
@@ -84,7 +93,7 @@ class FileHandler(DatabaseHandler):
|
|
|
84
93
|
)
|
|
85
94
|
return Response(RESPONSE_TYPE.OK)
|
|
86
95
|
|
|
87
|
-
if
|
|
96
|
+
if isinstance(query, CreateTable):
|
|
88
97
|
# Check if the table already exists or if the table name contains more than one namespace
|
|
89
98
|
existing_files = self.file_controller.get_files_names()
|
|
90
99
|
|
|
@@ -96,13 +105,13 @@ class FileHandler(DatabaseHandler):
|
|
|
96
105
|
|
|
97
106
|
table_name = query.name.parts[-1]
|
|
98
107
|
if table_name in existing_files:
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
108
|
+
if query.is_replace:
|
|
109
|
+
self.file_controller.delete_file(table_name)
|
|
110
|
+
else:
|
|
111
|
+
return Response(
|
|
112
|
+
RESPONSE_TYPE.ERROR,
|
|
113
|
+
error_message=f"Table '{table_name}' already exists",
|
|
114
|
+
)
|
|
106
115
|
|
|
107
116
|
temp_dir_path = tempfile.mkdtemp(prefix="mindsdb_file_")
|
|
108
117
|
|
|
@@ -126,31 +135,19 @@ class FileHandler(DatabaseHandler):
|
|
|
126
135
|
|
|
127
136
|
return Response(RESPONSE_TYPE.OK)
|
|
128
137
|
|
|
129
|
-
elif
|
|
130
|
-
|
|
131
|
-
table_name = table_name_parts[-1]
|
|
132
|
-
|
|
133
|
-
# Check if it's a multi-part name (e.g., `files.file_name.sheet_name`)
|
|
134
|
-
if len(table_name_parts) > 1:
|
|
135
|
-
table_name = table_name_parts[-2]
|
|
136
|
-
sheet_name = table_name_parts[-1] # Get the sheet name
|
|
137
|
-
else:
|
|
138
|
-
sheet_name = None
|
|
139
|
-
file_path = self.file_controller.get_file_path(table_name)
|
|
138
|
+
elif isinstance(query, Select):
|
|
139
|
+
table_name, page_name = self._get_table_page_names(query.from_table)
|
|
140
140
|
|
|
141
|
-
df = self.
|
|
141
|
+
df = self.file_controller.get_file_data(table_name, page_name)
|
|
142
142
|
|
|
143
143
|
# Process the SELECT query
|
|
144
144
|
result_df = query_df(df, query)
|
|
145
145
|
return Response(RESPONSE_TYPE.TABLE, data_frame=result_df)
|
|
146
146
|
|
|
147
|
-
elif
|
|
148
|
-
table_name = query.table
|
|
149
|
-
file_path = self.file_controller.get_file_path(table_name)
|
|
147
|
+
elif isinstance(query, Insert):
|
|
148
|
+
table_name, page_name = self._get_table_page_names(query.table)
|
|
150
149
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
df = file_reader.to_df()
|
|
150
|
+
df = self.file_controller.get_file_data(table_name, page_name)
|
|
154
151
|
|
|
155
152
|
# Create a new dataframe with the values from the query
|
|
156
153
|
new_df = pd.DataFrame(query.values, columns=[col.name for col in query.columns])
|
|
@@ -158,10 +155,7 @@ class FileHandler(DatabaseHandler):
|
|
|
158
155
|
# Concatenate the new dataframe with the existing one
|
|
159
156
|
df = pd.concat([df, new_df], ignore_index=True)
|
|
160
157
|
|
|
161
|
-
|
|
162
|
-
format = Path(file_path).suffix.strip(".").lower()
|
|
163
|
-
write_method = getattr(df, f"to_{format}")
|
|
164
|
-
write_method(file_path, index=False)
|
|
158
|
+
self.file_controller.set_file_data(table_name, df, page_name=page_name)
|
|
165
159
|
|
|
166
160
|
return Response(RESPONSE_TYPE.OK)
|
|
167
161
|
|
|
@@ -175,18 +169,6 @@ class FileHandler(DatabaseHandler):
|
|
|
175
169
|
ast = self.parser(query)
|
|
176
170
|
return self.query(ast)
|
|
177
171
|
|
|
178
|
-
@staticmethod
|
|
179
|
-
def handle_source(file_path, **kwargs):
|
|
180
|
-
file_reader = FileReader(path=file_path)
|
|
181
|
-
|
|
182
|
-
df = file_reader.to_df(**kwargs)
|
|
183
|
-
|
|
184
|
-
header = df.columns.values.tolist()
|
|
185
|
-
|
|
186
|
-
df.columns = [key.strip() for key in header]
|
|
187
|
-
df = df.applymap(clean_cell)
|
|
188
|
-
return df
|
|
189
|
-
|
|
190
172
|
def get_tables(self) -> Response:
|
|
191
173
|
"""
|
|
192
174
|
List all files
|
|
@@ -64,6 +64,12 @@ class MockFileController:
|
|
|
64
64
|
def save_file(self, name, file_path, file_name=None):
|
|
65
65
|
return True
|
|
66
66
|
|
|
67
|
+
def get_file_data(self, name, page_name=None):
|
|
68
|
+
return pandas.DataFrame(test_file_content[1:], columns=test_file_content[0])
|
|
69
|
+
|
|
70
|
+
def set_file_data(self, name, df, page_name=None):
|
|
71
|
+
return True
|
|
72
|
+
|
|
67
73
|
|
|
68
74
|
def curr_dir():
|
|
69
75
|
return os.path.dirname(os.path.realpath(__file__))
|
|
@@ -296,18 +302,9 @@ def test_handle_source(file_path, expected_columns):
|
|
|
296
302
|
|
|
297
303
|
# using different methods to create reader
|
|
298
304
|
for reader in get_reader(file_path):
|
|
299
|
-
df = reader.
|
|
305
|
+
df = reader.get_page_content()
|
|
300
306
|
assert isinstance(df, pandas.DataFrame)
|
|
301
307
|
|
|
302
|
-
if reader.get_format() == 'xlsx':
|
|
303
|
-
|
|
304
|
-
assert df.columns.tolist() == test_excel_sheet_content[0]
|
|
305
|
-
assert len(df) == len(test_excel_sheet_content) - 1
|
|
306
|
-
assert df.values.tolist() == test_excel_sheet_content[1:]
|
|
307
|
-
sheet_name = test_excel_sheet_content[1][0]
|
|
308
|
-
|
|
309
|
-
df = reader.to_df(sheet_name=sheet_name)
|
|
310
|
-
|
|
311
308
|
assert df.columns.tolist() == expected_columns
|
|
312
309
|
|
|
313
310
|
# The pdf and txt files have some different content
|
|
@@ -336,7 +333,7 @@ def test_tsv():
|
|
|
336
333
|
assert reader.get_format() == 'csv'
|
|
337
334
|
assert reader.parameters['delimiter'] == '\t'
|
|
338
335
|
|
|
339
|
-
df = reader.
|
|
336
|
+
df = reader.get_page_content()
|
|
340
337
|
assert len(df.columns) == 2
|
|
341
338
|
|
|
342
339
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
nixtla==0.
|
|
1
|
+
nixtla==0.6.6
|
|
@@ -4,6 +4,7 @@ import csv
|
|
|
4
4
|
from io import BytesIO, StringIO, IOBase
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
import codecs
|
|
7
|
+
from typing import List
|
|
7
8
|
|
|
8
9
|
import filetype
|
|
9
10
|
import pandas as pd
|
|
@@ -65,6 +66,7 @@ def decode(file_obj: IOBase) -> StringIO:
|
|
|
65
66
|
class FormatDetector:
|
|
66
67
|
|
|
67
68
|
supported_formats = ['parquet', 'csv', 'xlsx', 'pdf', 'json', 'txt']
|
|
69
|
+
multipage_formats = ['xlsx']
|
|
68
70
|
|
|
69
71
|
def __init__(
|
|
70
72
|
self,
|
|
@@ -200,16 +202,62 @@ class FormatDetector:
|
|
|
200
202
|
|
|
201
203
|
class FileReader(FormatDetector):
|
|
202
204
|
|
|
203
|
-
def
|
|
205
|
+
def _get_fnc(self):
|
|
204
206
|
format = self.get_format()
|
|
205
|
-
|
|
206
207
|
func = getattr(self, f'read_{format}', None)
|
|
207
208
|
if func is None:
|
|
208
209
|
raise FileDetectError(f'Unsupported format: {format}')
|
|
210
|
+
return func
|
|
211
|
+
|
|
212
|
+
def get_pages(self, **kwargs) -> List[str]:
|
|
213
|
+
"""
|
|
214
|
+
Get list of tables in file
|
|
215
|
+
"""
|
|
216
|
+
format = self.get_format()
|
|
217
|
+
if format not in self.multipage_formats:
|
|
218
|
+
# only one table
|
|
219
|
+
return ['main']
|
|
220
|
+
|
|
221
|
+
func = self._get_fnc()
|
|
222
|
+
self.file_obj.seek(0)
|
|
209
223
|
|
|
224
|
+
return [
|
|
225
|
+
name for name, _ in
|
|
226
|
+
func(self.file_obj, only_names=True, **kwargs)
|
|
227
|
+
]
|
|
228
|
+
|
|
229
|
+
def get_contents(self, **kwargs):
|
|
230
|
+
"""
|
|
231
|
+
Get all info(pages with content) from file as dict: {tablename, content}
|
|
232
|
+
"""
|
|
233
|
+
func = self._get_fnc()
|
|
210
234
|
self.file_obj.seek(0)
|
|
211
|
-
|
|
212
|
-
|
|
235
|
+
|
|
236
|
+
format = self.get_format()
|
|
237
|
+
if format not in self.multipage_formats:
|
|
238
|
+
# only one table
|
|
239
|
+
return {'main': func(self.file_obj, name=self.name, **kwargs)}
|
|
240
|
+
|
|
241
|
+
return {
|
|
242
|
+
name: df
|
|
243
|
+
for name, df in
|
|
244
|
+
func(self.file_obj, **kwargs)
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
def get_page_content(self, page_name: str = None, **kwargs) -> pd.DataFrame:
|
|
248
|
+
"""
|
|
249
|
+
Get content of a single table
|
|
250
|
+
"""
|
|
251
|
+
func = self._get_fnc()
|
|
252
|
+
self.file_obj.seek(0)
|
|
253
|
+
|
|
254
|
+
format = self.get_format()
|
|
255
|
+
if format not in self.multipage_formats:
|
|
256
|
+
# only one table
|
|
257
|
+
return func(self.file_obj, name=self.name, **kwargs)
|
|
258
|
+
|
|
259
|
+
for _, df in func(self.file_obj, name=self.name, page_name=page_name, **kwargs):
|
|
260
|
+
return df
|
|
213
261
|
|
|
214
262
|
@staticmethod
|
|
215
263
|
def _get_csv_dialect(buffer, delimiter=None) -> csv.Dialect:
|
|
@@ -304,14 +352,18 @@ class FileReader(FormatDetector):
|
|
|
304
352
|
return pd.read_parquet(file_obj)
|
|
305
353
|
|
|
306
354
|
@staticmethod
|
|
307
|
-
def read_xlsx(file_obj: BytesIO,
|
|
308
|
-
|
|
309
|
-
file_obj.seek(0)
|
|
355
|
+
def read_xlsx(file_obj: BytesIO, page_name=None, only_names=False, **kwargs):
|
|
310
356
|
with pd.ExcelFile(file_obj) as xls:
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
357
|
+
|
|
358
|
+
if page_name is not None:
|
|
359
|
+
# return specific page
|
|
360
|
+
yield page_name, pd.read_excel(xls, sheet_name=page_name)
|
|
361
|
+
|
|
362
|
+
for page_name in xls.sheet_names:
|
|
363
|
+
|
|
364
|
+
if only_names:
|
|
365
|
+
# extract only pages names
|
|
366
|
+
df = None
|
|
367
|
+
else:
|
|
368
|
+
df = pd.read_excel(xls, sheet_name=page_name)
|
|
369
|
+
yield page_name, df
|