MindsDB 25.3.1.0__py3-none-any.whl → 25.3.2.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/__about__.py +1 -1
- mindsdb/__main__.py +1 -1
- mindsdb/api/executor/sql_query/steps/union_step.py +21 -24
- mindsdb/api/http/gui.py +5 -4
- mindsdb/api/http/initialize.py +19 -19
- mindsdb/integrations/handlers/jira_handler/__init__.py +1 -0
- mindsdb/integrations/handlers/jira_handler/jira_handler.py +22 -80
- mindsdb/integrations/handlers/pgvector_handler/pgvector_handler.py +3 -3
- mindsdb/integrations/handlers/slack_handler/slack_handler.py +2 -1
- mindsdb/integrations/libs/api_handler_generator.py +583 -0
- mindsdb/utilities/config.py +1 -1
- {mindsdb-25.3.1.0.dist-info → mindsdb-25.3.2.0.dist-info}/METADATA +233 -234
- {mindsdb-25.3.1.0.dist-info → mindsdb-25.3.2.0.dist-info}/RECORD +16 -17
- {mindsdb-25.3.1.0.dist-info → mindsdb-25.3.2.0.dist-info}/WHEEL +1 -1
- mindsdb/integrations/handlers/jira_handler/jira_table.py +0 -172
- mindsdb/integrations/handlers/jira_handler/requirements.txt +0 -1
- {mindsdb-25.3.1.0.dist-info → mindsdb-25.3.2.0.dist-info}/LICENSE +0 -0
- {mindsdb-25.3.1.0.dist-info → mindsdb-25.3.2.0.dist-info}/top_level.txt +0 -0
mindsdb/__about__.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
__title__ = 'MindsDB'
|
|
2
2
|
__package_name__ = 'mindsdb'
|
|
3
|
-
__version__ = '25.3.
|
|
3
|
+
__version__ = '25.3.2.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'
|
mindsdb/__main__.py
CHANGED
|
@@ -9,10 +9,10 @@ import secrets
|
|
|
9
9
|
import traceback
|
|
10
10
|
import threading
|
|
11
11
|
from enum import Enum
|
|
12
|
-
from packaging import version
|
|
13
12
|
from dataclasses import dataclass, field
|
|
14
13
|
from typing import Callable, Optional, Tuple, List
|
|
15
14
|
|
|
15
|
+
from packaging import version
|
|
16
16
|
from sqlalchemy.orm.attributes import flag_modified
|
|
17
17
|
|
|
18
18
|
from mindsdb.utilities import log
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import hashlib
|
|
2
|
-
|
|
3
1
|
from mindsdb.api.executor.planner.steps import UnionStep
|
|
4
2
|
|
|
5
3
|
from mindsdb.api.executor.sql_query.result_set import ResultSet
|
|
6
4
|
from mindsdb.api.executor.exceptions import WrongArgumentError
|
|
5
|
+
from mindsdb.api.executor.utilities.sql import query_df_with_type_infer_fallback
|
|
6
|
+
import numpy as np
|
|
7
7
|
|
|
8
8
|
from .base import BaseStepCall
|
|
9
9
|
|
|
@@ -30,27 +30,24 @@ class UnionStepCall(BaseStepCall):
|
|
|
30
30
|
# if type1 != type2:
|
|
31
31
|
# raise ErSqlWrongArguments(f'UNION types mismatch: {type1} != {type2}')
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
records_hashes.append(checksum)
|
|
53
|
-
results.append(list(row))
|
|
54
|
-
data.add_raw_values(results)
|
|
33
|
+
table_a, names = left_result.to_df_cols()
|
|
34
|
+
table_b, _ = right_result.to_df_cols()
|
|
35
|
+
|
|
36
|
+
op = 'UNION ALL'
|
|
37
|
+
if step.unique:
|
|
38
|
+
op = 'UNION'
|
|
39
|
+
query = f"""
|
|
40
|
+
SELECT * FROM table_a
|
|
41
|
+
{op}
|
|
42
|
+
SELECT * FROM table_b
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
resp_df, _description = query_df_with_type_infer_fallback(query, {
|
|
46
|
+
'table_a': table_a,
|
|
47
|
+
'table_b': table_b
|
|
48
|
+
})
|
|
49
|
+
resp_df.replace({np.nan: None}, inplace=True)
|
|
50
|
+
|
|
51
|
+
data = ResultSet().from_df_cols(resp_df, col_names=names)
|
|
55
52
|
|
|
56
53
|
return data
|
mindsdb/api/http/gui.py
CHANGED
|
@@ -5,6 +5,7 @@ from pathlib import Path
|
|
|
5
5
|
from zipfile import ZipFile
|
|
6
6
|
|
|
7
7
|
import requests
|
|
8
|
+
from packaging.version import Version
|
|
8
9
|
|
|
9
10
|
from mindsdb.utilities.config import Config
|
|
10
11
|
from mindsdb.utilities import log
|
|
@@ -65,7 +66,7 @@ def download_gui(destignation, version):
|
|
|
65
66
|
"""
|
|
66
67
|
|
|
67
68
|
|
|
68
|
-
def update_static(
|
|
69
|
+
def update_static(gui_version: Version):
|
|
69
70
|
"""Update Scout files basing on compatible-config.json content.
|
|
70
71
|
Files will be downloaded and updated if new version of GUI > current.
|
|
71
72
|
Current GUI version stored in static/version.txt.
|
|
@@ -74,11 +75,11 @@ def update_static(gui_version_lv):
|
|
|
74
75
|
static_path = Path(config["paths"]["static"])
|
|
75
76
|
|
|
76
77
|
logger.info(
|
|
77
|
-
f"New version of GUI available ({
|
|
78
|
+
f"New version of GUI available ({gui_version.base_version}). Downloading..."
|
|
78
79
|
)
|
|
79
80
|
|
|
80
81
|
temp_dir = tempfile.mkdtemp(prefix="mindsdb_gui_files_")
|
|
81
|
-
success = download_gui(temp_dir,
|
|
82
|
+
success = download_gui(temp_dir, gui_version.base_version)
|
|
82
83
|
if success is False:
|
|
83
84
|
shutil.rmtree(temp_dir)
|
|
84
85
|
return False
|
|
@@ -90,5 +91,5 @@ def update_static(gui_version_lv):
|
|
|
90
91
|
shutil.copytree(temp_dir, str(static_path))
|
|
91
92
|
shutil.rmtree(temp_dir_for_rm)
|
|
92
93
|
|
|
93
|
-
logger.info(f"GUI version updated to {
|
|
94
|
+
logger.info(f"GUI version updated to {gui_version.base_version}")
|
|
94
95
|
return True
|
mindsdb/api/http/initialize.py
CHANGED
|
@@ -6,7 +6,6 @@ import traceback
|
|
|
6
6
|
import webbrowser
|
|
7
7
|
from pathlib import Path
|
|
8
8
|
from http import HTTPStatus
|
|
9
|
-
from distutils.version import LooseVersion
|
|
10
9
|
|
|
11
10
|
import requests
|
|
12
11
|
from flask import Flask, url_for, make_response, request, send_from_directory
|
|
@@ -14,6 +13,7 @@ from flask.json import dumps
|
|
|
14
13
|
from flask_compress import Compress
|
|
15
14
|
from flask_restx import Api
|
|
16
15
|
from werkzeug.exceptions import HTTPException
|
|
16
|
+
from packaging.version import Version, parse as parse_version
|
|
17
17
|
|
|
18
18
|
from mindsdb.__about__ import __version__ as mindsdb_version
|
|
19
19
|
from mindsdb.api.http.gui import update_static
|
|
@@ -72,7 +72,7 @@ def custom_output_json(data, code, headers=None):
|
|
|
72
72
|
return resp
|
|
73
73
|
|
|
74
74
|
|
|
75
|
-
def get_last_compatible_gui_version() ->
|
|
75
|
+
def get_last_compatible_gui_version() -> Version:
|
|
76
76
|
logger.debug("Getting last compatible frontend..")
|
|
77
77
|
try:
|
|
78
78
|
res = requests.get('https://mindsdb-web-builds.s3.amazonaws.com/compatible-config.json', timeout=5)
|
|
@@ -95,7 +95,7 @@ def get_last_compatible_gui_version() -> LooseVersion:
|
|
|
95
95
|
logger.error(f"Cant decode compatible-config.json: {e}")
|
|
96
96
|
return False
|
|
97
97
|
|
|
98
|
-
current_mindsdb_lv =
|
|
98
|
+
current_mindsdb_lv = parse_version(mindsdb_version)
|
|
99
99
|
|
|
100
100
|
try:
|
|
101
101
|
gui_versions = {}
|
|
@@ -103,31 +103,31 @@ def get_last_compatible_gui_version() -> LooseVersion:
|
|
|
103
103
|
max_gui_lv = None
|
|
104
104
|
for el in versions['mindsdb']:
|
|
105
105
|
if el['mindsdb_version'] is None:
|
|
106
|
-
gui_lv =
|
|
106
|
+
gui_lv = parse_version(el['gui_version'])
|
|
107
107
|
else:
|
|
108
|
-
mindsdb_lv =
|
|
109
|
-
gui_lv =
|
|
110
|
-
if mindsdb_lv.
|
|
111
|
-
gui_versions[mindsdb_lv.
|
|
108
|
+
mindsdb_lv = parse_version(el['mindsdb_version'])
|
|
109
|
+
gui_lv = parse_version(el['gui_version'])
|
|
110
|
+
if mindsdb_lv.base_version not in gui_versions or gui_lv > gui_versions[mindsdb_lv.base_version]:
|
|
111
|
+
gui_versions[mindsdb_lv.base_version] = gui_lv
|
|
112
112
|
if max_mindsdb_lv is None or max_mindsdb_lv < mindsdb_lv:
|
|
113
113
|
max_mindsdb_lv = mindsdb_lv
|
|
114
114
|
if max_gui_lv is None or max_gui_lv < gui_lv:
|
|
115
115
|
max_gui_lv = gui_lv
|
|
116
116
|
|
|
117
|
-
all_mindsdb_lv = [
|
|
117
|
+
all_mindsdb_lv = [parse_version(x) for x in gui_versions.keys()]
|
|
118
118
|
all_mindsdb_lv.sort()
|
|
119
119
|
|
|
120
|
-
if current_mindsdb_lv.
|
|
121
|
-
gui_version_lv = gui_versions[current_mindsdb_lv.
|
|
120
|
+
if current_mindsdb_lv.base_version in gui_versions:
|
|
121
|
+
gui_version_lv = gui_versions[current_mindsdb_lv.base_version]
|
|
122
122
|
elif current_mindsdb_lv > all_mindsdb_lv[-1]:
|
|
123
123
|
gui_version_lv = max_gui_lv
|
|
124
124
|
else:
|
|
125
|
-
lower_versions = {key: value for key, value in gui_versions.items() if
|
|
125
|
+
lower_versions = {key: value for key, value in gui_versions.items() if parse_version(key) < current_mindsdb_lv}
|
|
126
126
|
if len(lower_versions) == 0:
|
|
127
|
-
gui_version_lv = gui_versions[all_mindsdb_lv[0].
|
|
127
|
+
gui_version_lv = gui_versions[all_mindsdb_lv[0].base_version]
|
|
128
128
|
else:
|
|
129
|
-
all_lower_versions = [
|
|
130
|
-
gui_version_lv = gui_versions[all_lower_versions[-1].
|
|
129
|
+
all_lower_versions = [parse_version(x) for x in lower_versions.keys()]
|
|
130
|
+
gui_version_lv = gui_versions[all_lower_versions[-1].base_version]
|
|
131
131
|
except Exception as e:
|
|
132
132
|
logger.error(f"Error in compatible-config.json structure: {e}")
|
|
133
133
|
return False
|
|
@@ -136,7 +136,7 @@ def get_last_compatible_gui_version() -> LooseVersion:
|
|
|
136
136
|
return gui_version_lv
|
|
137
137
|
|
|
138
138
|
|
|
139
|
-
def get_current_gui_version() ->
|
|
139
|
+
def get_current_gui_version() -> Version:
|
|
140
140
|
logger.debug("Getting current frontend version..")
|
|
141
141
|
config = Config()
|
|
142
142
|
static_path = Path(config['paths']['static'])
|
|
@@ -148,7 +148,7 @@ def get_current_gui_version() -> LooseVersion:
|
|
|
148
148
|
current_gui_version = f.readline()
|
|
149
149
|
|
|
150
150
|
current_gui_lv = (
|
|
151
|
-
None if current_gui_version is None else
|
|
151
|
+
None if current_gui_version is None else parse_version(current_gui_version)
|
|
152
152
|
)
|
|
153
153
|
logger.debug(f"Current frontend version: {current_gui_lv}.")
|
|
154
154
|
|
|
@@ -163,7 +163,7 @@ def initialize_static():
|
|
|
163
163
|
required_gui_version = config['gui'].get('version')
|
|
164
164
|
|
|
165
165
|
if required_gui_version is not None:
|
|
166
|
-
required_gui_version_lv =
|
|
166
|
+
required_gui_version_lv = parse_version(required_gui_version)
|
|
167
167
|
success = True
|
|
168
168
|
if (
|
|
169
169
|
current_gui_version_lv is None
|
|
@@ -176,7 +176,7 @@ def initialize_static():
|
|
|
176
176
|
return False
|
|
177
177
|
|
|
178
178
|
# ignore versions like '23.9.2.2'
|
|
179
|
-
if current_gui_version_lv is not None and len(current_gui_version_lv.
|
|
179
|
+
if current_gui_version_lv is not None and len(current_gui_version_lv.release) < 3:
|
|
180
180
|
if current_gui_version_lv == last_gui_version_lv:
|
|
181
181
|
return True
|
|
182
182
|
logger.debug("Updating gui..")
|
|
@@ -1,23 +1,15 @@
|
|
|
1
|
-
from mindsdb.integrations.handlers.jira_handler.jira_table import JiraProjectsTable
|
|
2
1
|
from mindsdb.integrations.libs.api_handler import APIHandler
|
|
3
2
|
from mindsdb.integrations.libs.response import (
|
|
4
3
|
HandlerStatusResponse as StatusResponse,
|
|
5
4
|
)
|
|
6
5
|
from mindsdb.utilities import log
|
|
7
|
-
from
|
|
8
|
-
|
|
9
|
-
from atlassian import Jira
|
|
10
|
-
from typing import Optional
|
|
11
|
-
import requests
|
|
6
|
+
from mindsdb.integrations.libs.api_handler_generator import APIResourceGenerator
|
|
12
7
|
|
|
13
8
|
|
|
14
9
|
logger = log.getLogger(__name__)
|
|
15
10
|
|
|
16
|
-
class JiraHandler(APIHandler):
|
|
17
|
-
"""
|
|
18
|
-
This handler handles connection and execution of the Airtable statements.
|
|
19
|
-
"""
|
|
20
11
|
|
|
12
|
+
class JiraHandler(APIHandler):
|
|
21
13
|
|
|
22
14
|
def __init__(self, name=None, **kwargs):
|
|
23
15
|
"""
|
|
@@ -30,64 +22,38 @@ class JiraHandler(APIHandler):
|
|
|
30
22
|
super().__init__(name)
|
|
31
23
|
self.connection_data = kwargs.get("connection_data", {})
|
|
32
24
|
|
|
33
|
-
self.parser = parse_sql
|
|
34
|
-
self.dialect = 'jira'
|
|
35
|
-
self.kwargs = kwargs
|
|
36
25
|
self.connection = None
|
|
37
26
|
self.is_connected = False
|
|
38
27
|
|
|
28
|
+
# todo store parsed data in files
|
|
29
|
+
|
|
30
|
+
self.api_resource_generator = APIResourceGenerator(
|
|
31
|
+
"https://developer.atlassian.com/cloud/jira/platform/swagger-v3.v3.json",
|
|
32
|
+
self.connection_data,
|
|
33
|
+
url_base='/rest/api/3/',
|
|
34
|
+
options={
|
|
35
|
+
'offset_param': ['startAt', 'offset'],
|
|
36
|
+
'total_column': ['totalEntryCount', 'total'],
|
|
37
|
+
'check_connection_table': 'myself'
|
|
38
|
+
}
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
resource_tables = self.api_resource_generator.generate_api_resources(self)
|
|
39
42
|
|
|
40
|
-
|
|
41
|
-
|
|
43
|
+
for table_name, resource in resource_tables.items():
|
|
44
|
+
self._register_table(table_name, resource)
|
|
42
45
|
|
|
43
46
|
def __del__(self):
|
|
44
47
|
if self.is_connected is True:
|
|
45
48
|
self.disconnect()
|
|
46
49
|
|
|
47
|
-
def connect(self)
|
|
50
|
+
def connect(self):
|
|
48
51
|
"""
|
|
49
52
|
Set up the connection required by the handler.
|
|
50
53
|
Returns:
|
|
51
54
|
HandlerStatusResponse
|
|
52
55
|
"""
|
|
53
|
-
|
|
54
|
-
if self.is_connected is True:
|
|
55
|
-
return self.connection
|
|
56
|
-
|
|
57
|
-
s = requests.Session()
|
|
58
|
-
if self.connection_data.get("cloud", False):
|
|
59
|
-
params = {
|
|
60
|
-
"cloud": True,
|
|
61
|
-
"username": self.connection_data['jira_username'],
|
|
62
|
-
"password": self.connection_data['jira_api_token'],
|
|
63
|
-
"url": self.connection_data['jira_url'],
|
|
64
|
-
}
|
|
65
|
-
else:
|
|
66
|
-
params = {
|
|
67
|
-
"cloud": False,
|
|
68
|
-
"url": self.connection_data['jira_url'],
|
|
69
|
-
"session": s
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
s.headers['Authorization'] = f"Bearer {self.connection_data['jira_api_token']}"
|
|
73
|
-
|
|
74
|
-
self.connection = Jira(**params)
|
|
75
|
-
self.is_connected = True
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
return self.connection
|
|
79
|
-
|
|
80
|
-
def disconnect(self):
|
|
81
|
-
"""
|
|
82
|
-
Close any existing connections.
|
|
83
|
-
"""
|
|
84
|
-
|
|
85
|
-
if self.is_connected is False:
|
|
86
|
-
return
|
|
87
|
-
|
|
88
|
-
self.connection.close()
|
|
89
|
-
self.is_connected = False
|
|
90
|
-
return self.is_connected
|
|
56
|
+
return
|
|
91
57
|
|
|
92
58
|
def check_connection(self) -> StatusResponse:
|
|
93
59
|
"""
|
|
@@ -97,37 +63,13 @@ class JiraHandler(APIHandler):
|
|
|
97
63
|
"""
|
|
98
64
|
|
|
99
65
|
response = StatusResponse(False)
|
|
100
|
-
|
|
101
|
-
|
|
66
|
+
|
|
102
67
|
try:
|
|
103
|
-
self.
|
|
68
|
+
self.api_resource_generator.check_connection()
|
|
104
69
|
response.success = True
|
|
105
70
|
except Exception as e:
|
|
106
71
|
logger.error(f"Error connecting to Jira API: {e}!")
|
|
107
72
|
response.error_message = e
|
|
108
73
|
|
|
109
74
|
self.is_connected = response.success
|
|
110
|
-
|
|
111
75
|
return response
|
|
112
|
-
|
|
113
|
-
def native_query(self, query: str) -> StatusResponse:
|
|
114
|
-
"""Receive and process a raw query.
|
|
115
|
-
Parameters
|
|
116
|
-
----------
|
|
117
|
-
query : str
|
|
118
|
-
query in a native format
|
|
119
|
-
Returns
|
|
120
|
-
-------
|
|
121
|
-
StatusResponse
|
|
122
|
-
Request status
|
|
123
|
-
"""
|
|
124
|
-
ast = parse_sql(query)
|
|
125
|
-
return self.query(ast)
|
|
126
|
-
|
|
127
|
-
def construct_jql(self):
|
|
128
|
-
"""construct jql & returns it to JiraProjectsTable class
|
|
129
|
-
Returns
|
|
130
|
-
-------
|
|
131
|
-
Str
|
|
132
|
-
"""
|
|
133
|
-
return 'project = ' + str(self.connection_data['project'])
|
|
@@ -27,7 +27,7 @@ logger = log.getLogger(__name__)
|
|
|
27
27
|
|
|
28
28
|
|
|
29
29
|
# todo Issue #7316 add support for different indexes and search algorithms e.g. cosine similarity or L2 norm
|
|
30
|
-
class PgVectorHandler(
|
|
30
|
+
class PgVectorHandler(PostgresHandler, VectorStoreHandler):
|
|
31
31
|
"""This handler handles connection and execution of the PostgreSQL with pgvector extension statements."""
|
|
32
32
|
|
|
33
33
|
name = "pgvector"
|
|
@@ -64,11 +64,11 @@ class PgVectorHandler(VectorStoreHandler, PostgresHandler):
|
|
|
64
64
|
return Response(RESPONSE_TYPE.OK)
|
|
65
65
|
return super().get_tables()
|
|
66
66
|
|
|
67
|
-
def native_query(self, query) -> Response:
|
|
67
|
+
def native_query(self, query, params=None) -> Response:
|
|
68
68
|
# Prevent execute native queries
|
|
69
69
|
if self._is_shared_db:
|
|
70
70
|
return Response(RESPONSE_TYPE.OK)
|
|
71
|
-
return super().native_query(query)
|
|
71
|
+
return super().native_query(query, params=params)
|
|
72
72
|
|
|
73
73
|
def raw_query(self, query, params=None) -> Response:
|
|
74
74
|
resp = super().native_query(query, params)
|
|
@@ -267,7 +267,8 @@ class SlackHandler(APIChatHandler):
|
|
|
267
267
|
user_info = web_connection.auth_test().data
|
|
268
268
|
return user_info['bot_id']
|
|
269
269
|
|
|
270
|
-
def subscribe(self, stop_event: threading.Event, callback: Callable, table_name: Text
|
|
270
|
+
def subscribe(self, stop_event: threading.Event, callback: Callable, table_name: Text = 'messages',
|
|
271
|
+
columns: List = None, **kwargs: Any) -> None:
|
|
271
272
|
"""
|
|
272
273
|
Subscribes to the Slack API using the Socket Mode for real-time responses to messages.
|
|
273
274
|
|