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.

@@ -1,4 +1,4 @@
1
- mindsdb/__about__.py,sha256=cXoHk72JCuA2YGQFO8tg_9d1m4nY9k7CitOwdgLfyKY,444
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=WlEBulLFPLtpQXGvnHR9a0r5nHGFhq44OgQPdu8TyxM,16016
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=r2EJh31dGygn6T6uiIDEQ1AStYeoExPOu-BF114kHmc,7638
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=zc4QU4knxYLNVRq-kKKTwaJqquDeisLvw9DDnDzLuBY,11866
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=_Lq02LB_4swYjmxiWX6i-K4sUtpHuI7-Njhzy_LY2zA,2791
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=vvCmrZlyKlVJH4KzFoegYd5NFsP6hjYaMTZo8MEQGlY,14
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=Qb43LJY-Kbe6kyL63OFQcpvCmzBPiwtXOcKxUarUJe0,9794
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=9WgvdmPIHG-obJmYO9xVLKbW6tRZm_WkewSNdDR0yoU,4601
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=L-nXGVVkt4izM2VgORfCitLUg3xVup8nwLi7B9PyKCg,19351
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=-7iLVSXYcgta3p4Ihf2Si-D4aY2qFaD0b0Ea2wuqAHE,29816
1933
- MindsDB-25.2.2.1.dist-info/LICENSE,sha256=ziqdjujs6WDn-9g3t0SISjHCBc2pLRht3gnRbQoXmIs,5804
1934
- MindsDB-25.2.2.1.dist-info/METADATA,sha256=S1Hbsjgvm1aWQcrmBqYcJnawQIxhg2FjZR3MXXoLI08,42853
1935
- MindsDB-25.2.2.1.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
1936
- MindsDB-25.2.2.1.dist-info/top_level.txt,sha256=10wPR96JDf3hM8aMP7Fz0lDlmClEP480zgXISJKr5jE,8
1937
- MindsDB-25.2.2.1.dist-info/RECORD,,
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.2.1'
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 http import HTTPStatus
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 type(query) is CreateTable:
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
- return Response(
100
- RESPONSE_TYPE.ERROR,
101
- error_message=f"Table '{table_name}' already exists",
102
- )
103
-
104
- if query.is_replace:
105
- self.file_controller.delete_file(table_name)
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 type(query) is Select:
130
- table_name_parts = query.from_table.parts
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.handle_source(file_path, sheet_name=sheet_name)
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 type(query) is Insert:
148
- table_name = query.table.parts[-1]
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
- file_reader = FileReader(path=file_path)
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
- # Write the concatenated data to the file based on its format
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.to_df()
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.to_df()
336
+ df = reader.get_page_content()
340
337
  assert len(df.columns) == 2
341
338
 
342
339
 
@@ -87,4 +87,4 @@ class FileTable(APIResource):
87
87
 
88
88
  reader = FileReader(file=BytesIO(file_content), name=table_name)
89
89
 
90
- return reader.to_df()
90
+ return reader.get_page_content()
@@ -1 +1 @@
1
- nixtla==0.5.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 to_df(self, **kwargs) -> pd.DataFrame:
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
- kwargs.update(self.parameters)
212
- return func(self.file_obj, name=self.name, **kwargs)
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, sheet_name=None, **kwargs) -> pd.DataFrame:
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
- if sheet_name is None:
312
- # No sheet specified: Return list of sheets
313
- sheet_list = xls.sheet_names
314
- return pd.DataFrame(sheet_list, columns=["Sheet_Name"])
315
- else:
316
- # Specific sheet requested: Load that sheet
317
- return pd.read_excel(xls, sheet_name=sheet_name)
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