zaturn 0.1.4__tar.gz → 0.1.5__tar.gz
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.
- {zaturn-0.1.4/zaturn.egg-info → zaturn-0.1.5}/PKG-INFO +13 -2
- {zaturn-0.1.4 → zaturn-0.1.5}/README.md +10 -1
- {zaturn-0.1.4 → zaturn-0.1.5}/pyproject.toml +3 -1
- {zaturn-0.1.4 → zaturn-0.1.5}/uv.lock +45 -1
- zaturn-0.1.5/zaturn/visualizations.py +155 -0
- {zaturn-0.1.4 → zaturn-0.1.5/zaturn.egg-info}/PKG-INFO +13 -2
- {zaturn-0.1.4 → zaturn-0.1.5}/zaturn.egg-info/requires.txt +2 -0
- zaturn-0.1.4/zaturn/visualizations.py +0 -203
- {zaturn-0.1.4 → zaturn-0.1.5}/.gitignore +0 -0
- {zaturn-0.1.4 → zaturn-0.1.5}/LICENSE +0 -0
- {zaturn-0.1.4 → zaturn-0.1.5}/brand/logo.png +0 -0
- {zaturn-0.1.4 → zaturn-0.1.5}/brand/logo.svg +0 -0
- {zaturn-0.1.4 → zaturn-0.1.5}/debug_server.py +0 -0
- {zaturn-0.1.4 → zaturn-0.1.5}/setup.cfg +0 -0
- {zaturn-0.1.4 → zaturn-0.1.5}/zaturn/__init__.py +0 -0
- {zaturn-0.1.4 → zaturn-0.1.5}/zaturn/config.py +0 -0
- {zaturn-0.1.4 → zaturn-0.1.5}/zaturn/core.py +0 -0
- {zaturn-0.1.4 → zaturn-0.1.5}/zaturn/example_data/all_pokemon_data.csv +0 -0
- {zaturn-0.1.4 → zaturn-0.1.5}/zaturn/query_utils.py +0 -0
- {zaturn-0.1.4 → zaturn-0.1.5}/zaturn.egg-info/SOURCES.txt +0 -0
- {zaturn-0.1.4 → zaturn-0.1.5}/zaturn.egg-info/dependency_links.txt +0 -0
- {zaturn-0.1.4 → zaturn-0.1.5}/zaturn.egg-info/entry_points.txt +0 -0
- {zaturn-0.1.4 → zaturn-0.1.5}/zaturn.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: zaturn
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.5
|
4
4
|
Summary: AI Data Analysis MCP
|
5
5
|
Author-email: Karthik Devan <krtdvn@gmail.com>
|
6
6
|
Maintainer-email: Karthik Devan <krtdvn@gmail.com>
|
@@ -12,8 +12,10 @@ License-File: LICENSE
|
|
12
12
|
Requires-Dist: cryptography>=44.0.2
|
13
13
|
Requires-Dist: duckdb>=1.2.1
|
14
14
|
Requires-Dist: fastmcp>=0.4.1
|
15
|
+
Requires-Dist: kaleido==0.2.1
|
15
16
|
Requires-Dist: pandas>=2.2.3
|
16
17
|
Requires-Dist: platformdirs>=4.3.7
|
18
|
+
Requires-Dist: plotly[express]>=6.0.1
|
17
19
|
Requires-Dist: psycopg2-binary>=2.9.10
|
18
20
|
Requires-Dist: pyarrow>=19.0.1
|
19
21
|
Requires-Dist: pymysql>=1.1.1
|
@@ -158,11 +160,20 @@ Analyst:
|
|
158
160
|
```
|
159
161
|
- A native notebook interface
|
160
162
|
|
161
|
-
##
|
163
|
+
## Help And Feedback
|
162
164
|
|
163
165
|
[Raise an issue](https://github.com/kdqed/zaturn/issues) or [join the Discord](https://discord.gg/K8mECeVzpQ).
|
164
166
|
|
165
167
|
|
168
|
+
## Support The Project
|
169
|
+
|
170
|
+
If you find Zaturn useful, please support this project by:
|
171
|
+
- Starring the Project
|
172
|
+
- Spreading the word
|
173
|
+
- [Pledging $9/month on Patreon](https://www.patreon.com/kdqed?utm_medium=github&utm_source=join_link&utm_campaign=creatorshare_creator&utm_content=copyLink)
|
174
|
+
|
175
|
+
Your support will enable me to dedicate more of my time to Zaturn.
|
176
|
+
|
166
177
|
## Example Dataset Credits
|
167
178
|
|
168
179
|
The [pokemon dataset compiled by Sarah Taha and PokéAPI](https://www.kaggle.com/datasets/sarahtaha/1025-pokemon) has been included under the [CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/) license for demonstration purposes.
|
@@ -132,11 +132,20 @@ Analyst:
|
|
132
132
|
```
|
133
133
|
- A native notebook interface
|
134
134
|
|
135
|
-
##
|
135
|
+
## Help And Feedback
|
136
136
|
|
137
137
|
[Raise an issue](https://github.com/kdqed/zaturn/issues) or [join the Discord](https://discord.gg/K8mECeVzpQ).
|
138
138
|
|
139
139
|
|
140
|
+
## Support The Project
|
141
|
+
|
142
|
+
If you find Zaturn useful, please support this project by:
|
143
|
+
- Starring the Project
|
144
|
+
- Spreading the word
|
145
|
+
- [Pledging $9/month on Patreon](https://www.patreon.com/kdqed?utm_medium=github&utm_source=join_link&utm_campaign=creatorshare_creator&utm_content=copyLink)
|
146
|
+
|
147
|
+
Your support will enable me to dedicate more of my time to Zaturn.
|
148
|
+
|
140
149
|
## Example Dataset Credits
|
141
150
|
|
142
151
|
The [pokemon dataset compiled by Sarah Taha and PokéAPI](https://www.kaggle.com/datasets/sarahtaha/1025-pokemon) has been included under the [CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/) license for demonstration purposes.
|
@@ -1,6 +1,6 @@
|
|
1
1
|
[project]
|
2
2
|
name = "zaturn"
|
3
|
-
version = "0.1.
|
3
|
+
version = "0.1.5"
|
4
4
|
description = "AI Data Analysis MCP"
|
5
5
|
authors = [
|
6
6
|
{name = "Karthik Devan", email = "krtdvn@gmail.com"},
|
@@ -14,8 +14,10 @@ dependencies = [
|
|
14
14
|
"cryptography>=44.0.2",
|
15
15
|
"duckdb>=1.2.1",
|
16
16
|
"fastmcp>=0.4.1",
|
17
|
+
"kaleido==0.2.1",
|
17
18
|
"pandas>=2.2.3",
|
18
19
|
"platformdirs>=4.3.7",
|
20
|
+
"plotly[express]>=6.0.1",
|
19
21
|
"psycopg2-binary>=2.9.10",
|
20
22
|
"pyarrow>=19.0.1",
|
21
23
|
"pymysql>=1.1.1",
|
@@ -445,6 +445,19 @@ wheels = [
|
|
445
445
|
{ url = "https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9", size = 1572278 },
|
446
446
|
]
|
447
447
|
|
448
|
+
[[package]]
|
449
|
+
name = "kaleido"
|
450
|
+
version = "0.2.1"
|
451
|
+
source = { registry = "https://pypi.org/simple" }
|
452
|
+
wheels = [
|
453
|
+
{ url = "https://files.pythonhosted.org/packages/e0/f7/0ccaa596ec341963adbb4f839774c36d5659e75a0812d946732b927d480e/kaleido-0.2.1-py2.py3-none-macosx_10_11_x86_64.whl", hash = "sha256:ca6f73e7ff00aaebf2843f73f1d3bacde1930ef5041093fe76b83a15785049a7", size = 85153681 },
|
454
|
+
{ url = "https://files.pythonhosted.org/packages/45/8e/4297556be5a07b713bb42dde0f748354de9a6918dee251c0e6bdcda341e7/kaleido-0.2.1-py2.py3-none-macosx_11_0_arm64.whl", hash = "sha256:bb9a5d1f710357d5d432ee240ef6658a6d124c3e610935817b4b42da9c787c05", size = 85808197 },
|
455
|
+
{ url = "https://files.pythonhosted.org/packages/ae/b3/a0f0f4faac229b0011d8c4a7ee6da7c2dca0b6fd08039c95920846f23ca4/kaleido-0.2.1-py2.py3-none-manylinux1_x86_64.whl", hash = "sha256:aa21cf1bf1c78f8fa50a9f7d45e1003c387bd3d6fe0a767cfbbf344b95bdc3a8", size = 79902476 },
|
456
|
+
{ url = "https://files.pythonhosted.org/packages/a1/2b/680662678a57afab1685f0c431c2aba7783ce4344f06ec162074d485d469/kaleido-0.2.1-py2.py3-none-manylinux2014_aarch64.whl", hash = "sha256:845819844c8082c9469d9c17e42621fbf85c2b237ef8a86ec8a8527f98b6512a", size = 83711746 },
|
457
|
+
{ url = "https://files.pythonhosted.org/packages/88/89/4b6f8bb3f9ab036fd4ad1cb2d628ab5c81db32ac9aa0641d7b180073ba43/kaleido-0.2.1-py2.py3-none-win32.whl", hash = "sha256:ecc72635860be616c6b7161807a65c0dbd9b90c6437ac96965831e2e24066552", size = 62312480 },
|
458
|
+
{ url = "https://files.pythonhosted.org/packages/f7/9a/0408b02a4bcb3cf8b338a2b074ac7d1b2099e2b092b42473def22f7b625f/kaleido-0.2.1-py2.py3-none-win_amd64.whl", hash = "sha256:4670985f28913c2d063c5734d125ecc28e40810141bdb0a46f15b76c1d45f23c", size = 65945521 },
|
459
|
+
]
|
460
|
+
|
448
461
|
[[package]]
|
449
462
|
name = "kiwisolver"
|
450
463
|
version = "1.4.8"
|
@@ -594,6 +607,15 @@ wheels = [
|
|
594
607
|
{ url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 },
|
595
608
|
]
|
596
609
|
|
610
|
+
[[package]]
|
611
|
+
name = "narwhals"
|
612
|
+
version = "1.35.0"
|
613
|
+
source = { registry = "https://pypi.org/simple" }
|
614
|
+
sdist = { url = "https://files.pythonhosted.org/packages/ee/6a/a98fa5e9d530a428a0cd79d27f059ed65efd3a07aad61a8c93e323c9c20b/narwhals-1.35.0.tar.gz", hash = "sha256:07477d18487fbc940243b69818a177ed7119b737910a8a254fb67688b48a7c96", size = 265784 }
|
615
|
+
wheels = [
|
616
|
+
{ url = "https://files.pythonhosted.org/packages/80/b3/5781eb874f04cb1e882a7d93cf30abcb00362a3205c5f3708a7434a1a2ac/narwhals-1.35.0-py3-none-any.whl", hash = "sha256:7562af132fa3f8aaaf34dc96d7ec95bdca29d1c795e8fcf14e01edf1d32122bc", size = 325708 },
|
617
|
+
]
|
618
|
+
|
597
619
|
[[package]]
|
598
620
|
name = "numpy"
|
599
621
|
version = "2.2.4"
|
@@ -781,6 +803,24 @@ wheels = [
|
|
781
803
|
{ url = "https://files.pythonhosted.org/packages/6d/45/59578566b3275b8fd9157885918fcd0c4d74162928a5310926887b856a51/platformdirs-4.3.7-py3-none-any.whl", hash = "sha256:a03875334331946f13c549dbd8f4bac7a13a50a895a0eb1e8c6a8ace80d40a94", size = 18499 },
|
782
804
|
]
|
783
805
|
|
806
|
+
[[package]]
|
807
|
+
name = "plotly"
|
808
|
+
version = "6.0.1"
|
809
|
+
source = { registry = "https://pypi.org/simple" }
|
810
|
+
dependencies = [
|
811
|
+
{ name = "narwhals" },
|
812
|
+
{ name = "packaging" },
|
813
|
+
]
|
814
|
+
sdist = { url = "https://files.pythonhosted.org/packages/c7/cc/e41b5f697ae403f0b50e47b7af2e36642a193085f553bf7cc1169362873a/plotly-6.0.1.tar.gz", hash = "sha256:dd8400229872b6e3c964b099be699f8d00c489a974f2cfccfad5e8240873366b", size = 8094643 }
|
815
|
+
wheels = [
|
816
|
+
{ url = "https://files.pythonhosted.org/packages/02/65/ad2bc85f7377f5cfba5d4466d5474423a3fb7f6a97fd807c06f92dd3e721/plotly-6.0.1-py3-none-any.whl", hash = "sha256:4714db20fea57a435692c548a4eb4fae454f7daddf15f8d8ba7e1045681d7768", size = 14805757 },
|
817
|
+
]
|
818
|
+
|
819
|
+
[package.optional-dependencies]
|
820
|
+
express = [
|
821
|
+
{ name = "numpy" },
|
822
|
+
]
|
823
|
+
|
784
824
|
[[package]]
|
785
825
|
name = "pluggy"
|
786
826
|
version = "1.5.0"
|
@@ -1329,14 +1369,16 @@ wheels = [
|
|
1329
1369
|
|
1330
1370
|
[[package]]
|
1331
1371
|
name = "zaturn"
|
1332
|
-
version = "0.1.
|
1372
|
+
version = "0.1.5"
|
1333
1373
|
source = { editable = "." }
|
1334
1374
|
dependencies = [
|
1335
1375
|
{ name = "cryptography" },
|
1336
1376
|
{ name = "duckdb" },
|
1337
1377
|
{ name = "fastmcp" },
|
1378
|
+
{ name = "kaleido" },
|
1338
1379
|
{ name = "pandas" },
|
1339
1380
|
{ name = "platformdirs" },
|
1381
|
+
{ name = "plotly", extra = ["express"] },
|
1340
1382
|
{ name = "psycopg2-binary" },
|
1341
1383
|
{ name = "pyarrow" },
|
1342
1384
|
{ name = "pymysql" },
|
@@ -1352,8 +1394,10 @@ requires-dist = [
|
|
1352
1394
|
{ name = "cryptography", specifier = ">=44.0.2" },
|
1353
1395
|
{ name = "duckdb", specifier = ">=1.2.1" },
|
1354
1396
|
{ name = "fastmcp", specifier = ">=0.4.1" },
|
1397
|
+
{ name = "kaleido", specifier = "==0.2.1" },
|
1355
1398
|
{ name = "pandas", specifier = ">=2.2.3" },
|
1356
1399
|
{ name = "platformdirs", specifier = ">=4.3.7" },
|
1400
|
+
{ name = "plotly", extras = ["express"], specifier = ">=6.0.1" },
|
1357
1401
|
{ name = "psycopg2-binary", specifier = ">=2.9.10" },
|
1358
1402
|
{ name = "pyarrow", specifier = ">=19.0.1" },
|
1359
1403
|
{ name = "pymysql", specifier = ">=1.1.1" },
|
@@ -0,0 +1,155 @@
|
|
1
|
+
from fastmcp import FastMCP, Image
|
2
|
+
import math
|
3
|
+
import os
|
4
|
+
import plotly.express as px
|
5
|
+
import time
|
6
|
+
from typing import Any, Union, Optional
|
7
|
+
from zaturn import config, query_utils
|
8
|
+
|
9
|
+
|
10
|
+
mcp = FastMCP("Zaturn Visualizations")
|
11
|
+
|
12
|
+
|
13
|
+
def _fig_to_image(fig) -> Union[str, Image]:
|
14
|
+
filepath = os.path.join(config.VISUALS_DIR, str(int(time.time())) + '.png')
|
15
|
+
fig.write_image(filepath)
|
16
|
+
if config.RETURN_IMAGES:
|
17
|
+
return Image(path=filepath)
|
18
|
+
else:
|
19
|
+
return filepath
|
20
|
+
|
21
|
+
|
22
|
+
# Relationships
|
23
|
+
|
24
|
+
@mcp.tool()
|
25
|
+
def scatter_plot(
|
26
|
+
query_id: str,
|
27
|
+
x: str,
|
28
|
+
y: str,
|
29
|
+
color: str = None
|
30
|
+
):
|
31
|
+
"""
|
32
|
+
Make a scatter plot with the dataframe obtained from running SQL Query against source
|
33
|
+
If this returns an image, display it. If it returns a file path, mention it.
|
34
|
+
Args:
|
35
|
+
query_id: Previously run query to use for plotting
|
36
|
+
x: Column name from SQL result to use for x-axis
|
37
|
+
y: Column name from SQL result to use for y-axis
|
38
|
+
color: Optional; column name from SQL result to use for coloring the points, with color representing another dimension
|
39
|
+
"""
|
40
|
+
df = query_utils.load_query(query_id)
|
41
|
+
fig = px.scatter(df, x=x, y=y, color=color)
|
42
|
+
fig.update_xaxes(autotickangles=[0, 45, 60, 90])
|
43
|
+
return _fig_to_image(fig)
|
44
|
+
|
45
|
+
|
46
|
+
@mcp.tool()
|
47
|
+
def line_plot(
|
48
|
+
query_id: str,
|
49
|
+
x: str,
|
50
|
+
y: str,
|
51
|
+
color: str = None
|
52
|
+
):
|
53
|
+
"""
|
54
|
+
Make a line plot with the dataframe obtained from running SQL Query against source
|
55
|
+
Args:
|
56
|
+
query_id: Previously run query to use for plotting
|
57
|
+
x: Column name from SQL result to use for x-axis
|
58
|
+
y: Column name from SQL result to use for y-axis
|
59
|
+
color: Optional; column name from SQL result to use for drawing multiple colored lines representing another dimension
|
60
|
+
"""
|
61
|
+
df = query_utils.load_query(query_id)
|
62
|
+
fig = px.line(df, x=x, y=y, color=color)
|
63
|
+
fig.update_xaxes(autotickangles=[0, 45, 60, 90])
|
64
|
+
return _fig_to_image(fig)
|
65
|
+
|
66
|
+
|
67
|
+
# Distributions
|
68
|
+
|
69
|
+
@mcp.tool()
|
70
|
+
def histogram(
|
71
|
+
query_id: str,
|
72
|
+
column: str,
|
73
|
+
color: str = None,
|
74
|
+
nbins: int = None
|
75
|
+
):
|
76
|
+
"""
|
77
|
+
Make a histogram with a column of the dataframe obtained from running SQL Query against source
|
78
|
+
Args:
|
79
|
+
query_id: Previously run query to use for plotting
|
80
|
+
column: Column name from SQL result to use for the histogram
|
81
|
+
color: Optional; column name from SQL result to use for drawing multiple colored histograms representing another dimension
|
82
|
+
nbins: Optional; number of bins
|
83
|
+
"""
|
84
|
+
df = query_utils.load_query(query_id)
|
85
|
+
fig = px.histogram(df, x=column, color=color, nbins=nbins)
|
86
|
+
fig.update_xaxes(autotickangles=[0, 45, 60, 90])
|
87
|
+
return _fig_to_image(fig)
|
88
|
+
|
89
|
+
# Categorical
|
90
|
+
|
91
|
+
@mcp.tool()
|
92
|
+
def strip_plot(
|
93
|
+
query_id: str,
|
94
|
+
x: str,
|
95
|
+
y: str = None,
|
96
|
+
color: str = None
|
97
|
+
):
|
98
|
+
"""
|
99
|
+
Make a strip plot with the dataframe obtained from running SQL Query against source
|
100
|
+
Args:
|
101
|
+
query_id: Previously run query to use for plotting
|
102
|
+
x: Column name from SQL result to use for x axis
|
103
|
+
y: Optional; column name from SQL result to use for y axis
|
104
|
+
color: Optional column name from SQL result to show multiple colored strips representing another dimension
|
105
|
+
"""
|
106
|
+
df = query_utils.load_query(query_id)
|
107
|
+
fig = px.strip(df, x=x, y=y, color=color)
|
108
|
+
fig.update_xaxes(autotickangles=[0, 45, 60, 90])
|
109
|
+
return _fig_to_image(fig)
|
110
|
+
|
111
|
+
|
112
|
+
@mcp.tool()
|
113
|
+
def box_plot(
|
114
|
+
query_id: str,
|
115
|
+
y: str,
|
116
|
+
x: str = None,
|
117
|
+
color: str = None
|
118
|
+
):
|
119
|
+
"""
|
120
|
+
Make a box plot with the dataframe obtained from running SQL Query against source
|
121
|
+
Args:
|
122
|
+
query_id: Previously run query to use for plotting
|
123
|
+
y: Column name from SQL result to use for y axis
|
124
|
+
x: Optional; Column name from SQL result to use for x axis
|
125
|
+
color: Optional column name from SQL result to show multiple colored bars representing another dimension
|
126
|
+
"""
|
127
|
+
df = query_utils.load_query(query_id)
|
128
|
+
fig = px.box(df, x=x, y=y, color=color)
|
129
|
+
fig.update_xaxes(autotickangles=[0, 45, 60, 90])
|
130
|
+
return _fig_to_image(fig)
|
131
|
+
|
132
|
+
|
133
|
+
@mcp.tool()
|
134
|
+
def bar_plot(
|
135
|
+
query_id: str,
|
136
|
+
x: str,
|
137
|
+
y: str = None,
|
138
|
+
color: str = None,
|
139
|
+
orientation: str = 'v'
|
140
|
+
):
|
141
|
+
"""
|
142
|
+
Make a bar plot with the dataframe obtained from running SQL Query against source
|
143
|
+
Args:
|
144
|
+
query_id: Previously run query to use for plotting
|
145
|
+
x: Column name from SQL result to use for x axis
|
146
|
+
y: Optional; column name from SQL result to use for y axis
|
147
|
+
color: Optional column name from SQL result to use as a 3rd dimension by splitting each bar into colored sections
|
148
|
+
orientation: Orientation of the box plot, use 'v' for vertical (default) and 'h' for horizontal. Be mindful of choosing the correct X and Y columns as per orientation
|
149
|
+
"""
|
150
|
+
df = query_utils.load_query(query_id)
|
151
|
+
fig = px.bar(df, x=x, y=y, color=color, orientation=orientation)
|
152
|
+
fig.update_xaxes(autotickangles=[0, 45, 60, 90])
|
153
|
+
return _fig_to_image(fig)
|
154
|
+
|
155
|
+
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: zaturn
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.5
|
4
4
|
Summary: AI Data Analysis MCP
|
5
5
|
Author-email: Karthik Devan <krtdvn@gmail.com>
|
6
6
|
Maintainer-email: Karthik Devan <krtdvn@gmail.com>
|
@@ -12,8 +12,10 @@ License-File: LICENSE
|
|
12
12
|
Requires-Dist: cryptography>=44.0.2
|
13
13
|
Requires-Dist: duckdb>=1.2.1
|
14
14
|
Requires-Dist: fastmcp>=0.4.1
|
15
|
+
Requires-Dist: kaleido==0.2.1
|
15
16
|
Requires-Dist: pandas>=2.2.3
|
16
17
|
Requires-Dist: platformdirs>=4.3.7
|
18
|
+
Requires-Dist: plotly[express]>=6.0.1
|
17
19
|
Requires-Dist: psycopg2-binary>=2.9.10
|
18
20
|
Requires-Dist: pyarrow>=19.0.1
|
19
21
|
Requires-Dist: pymysql>=1.1.1
|
@@ -158,11 +160,20 @@ Analyst:
|
|
158
160
|
```
|
159
161
|
- A native notebook interface
|
160
162
|
|
161
|
-
##
|
163
|
+
## Help And Feedback
|
162
164
|
|
163
165
|
[Raise an issue](https://github.com/kdqed/zaturn/issues) or [join the Discord](https://discord.gg/K8mECeVzpQ).
|
164
166
|
|
165
167
|
|
168
|
+
## Support The Project
|
169
|
+
|
170
|
+
If you find Zaturn useful, please support this project by:
|
171
|
+
- Starring the Project
|
172
|
+
- Spreading the word
|
173
|
+
- [Pledging $9/month on Patreon](https://www.patreon.com/kdqed?utm_medium=github&utm_source=join_link&utm_campaign=creatorshare_creator&utm_content=copyLink)
|
174
|
+
|
175
|
+
Your support will enable me to dedicate more of my time to Zaturn.
|
176
|
+
|
166
177
|
## Example Dataset Credits
|
167
178
|
|
168
179
|
The [pokemon dataset compiled by Sarah Taha and PokéAPI](https://www.kaggle.com/datasets/sarahtaha/1025-pokemon) has been included under the [CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/) license for demonstration purposes.
|
@@ -1,203 +0,0 @@
|
|
1
|
-
from fastmcp import FastMCP, Image
|
2
|
-
import math
|
3
|
-
import matplotlib.pyplot as plt
|
4
|
-
import os
|
5
|
-
import seaborn as sns
|
6
|
-
import time
|
7
|
-
from typing import Any, Union, Optional
|
8
|
-
from zaturn import config, query_utils
|
9
|
-
|
10
|
-
sns.set_theme()
|
11
|
-
sns.set_style('ticks')
|
12
|
-
|
13
|
-
mcp = FastMCP("Zaturn Visualizations")
|
14
|
-
|
15
|
-
|
16
|
-
def _plot_to_image(plot) -> Union[str, Image]:
|
17
|
-
figure = plot.get_figure()
|
18
|
-
filepath = os.path.join(config.VISUALS_DIR, str(int(time.time())) + '.png')
|
19
|
-
figure.savefig(filepath, bbox_inches='tight')
|
20
|
-
plt.clf()
|
21
|
-
if config.RETURN_IMAGES:
|
22
|
-
return Image(path=filepath)
|
23
|
-
else:
|
24
|
-
return filepath
|
25
|
-
|
26
|
-
|
27
|
-
def _fix_x_labels(plot, labels):
|
28
|
-
max_label_length = max(list(labels.map(lambda x: len(str(x)))))
|
29
|
-
|
30
|
-
LABEL_HIDE_FACTOR = 1
|
31
|
-
if len(labels) > 20:
|
32
|
-
LABEL_HIDE_FACTOR = math.ceil(len(labels)/20)
|
33
|
-
|
34
|
-
labels_to_show = list(labels)
|
35
|
-
ticks = list(plot.get_xticks())
|
36
|
-
if LABEL_HIDE_FACTOR > 1:
|
37
|
-
ticks = ticks[::LABEL_HIDE_FACTOR]
|
38
|
-
labels_to_show = labels_to_show[::LABEL_HIDE_FACTOR]
|
39
|
-
|
40
|
-
plot.set_xticks(ticks, labels_to_show)
|
41
|
-
cutoff = 2 # for rotation
|
42
|
-
|
43
|
-
if max_label_length >= 12:
|
44
|
-
cutoff = 3
|
45
|
-
elif max_label_length >= 10:
|
46
|
-
cutoff = 4
|
47
|
-
elif max_label_length >= 8:
|
48
|
-
cutoff = 5
|
49
|
-
elif max_label_length >= 7:
|
50
|
-
cutoff = 5
|
51
|
-
elif max_label_length >= 6:
|
52
|
-
cutoff = 6
|
53
|
-
elif max_label_length >= 5:
|
54
|
-
cutoff = 7
|
55
|
-
elif max_label_length >= 4:
|
56
|
-
cutoff = 9
|
57
|
-
elif max_label_length >= 3:
|
58
|
-
cutoff = 13
|
59
|
-
else:
|
60
|
-
cutoff = 15
|
61
|
-
|
62
|
-
if len(labels)>cutoff:
|
63
|
-
plot.set_xticklabels(plot.get_xticklabels(), rotation=-45, ha='left', va='top')
|
64
|
-
|
65
|
-
return plot
|
66
|
-
|
67
|
-
|
68
|
-
# Relationships
|
69
|
-
|
70
|
-
@mcp.tool()
|
71
|
-
def scatter_plot(
|
72
|
-
query_id: str,
|
73
|
-
x: str,
|
74
|
-
y: str,
|
75
|
-
hue: str = None
|
76
|
-
):
|
77
|
-
"""
|
78
|
-
Make a scatter plot with the dataframe obtained from running SQL Query against source
|
79
|
-
If this returns an image, display it. If it returns a file path, mention it.
|
80
|
-
Args:
|
81
|
-
query_id: Previously run query to use for plotting
|
82
|
-
x: Column name from SQL result to use for x-axis
|
83
|
-
y: Column name from SQL result to use for y-axis
|
84
|
-
hue: Optional String; Column name from SQL result to use for coloring the points
|
85
|
-
"""
|
86
|
-
df = query_utils.load_query(query_id)
|
87
|
-
plot = sns.scatterplot(df, x=x, y=y, hue=hue)
|
88
|
-
plot = _fix_x_labels(plot, df[x])
|
89
|
-
return _plot_to_image(plot)
|
90
|
-
|
91
|
-
|
92
|
-
@mcp.tool()
|
93
|
-
def line_plot(
|
94
|
-
query_id: str,
|
95
|
-
x: str,
|
96
|
-
y: str,
|
97
|
-
hue: str = None
|
98
|
-
):
|
99
|
-
"""
|
100
|
-
Make a line plot with the dataframe obtained from running SQL Query against source
|
101
|
-
Args:
|
102
|
-
query_id: Previously run query to use for plotting
|
103
|
-
x: Column name from SQL result to use for x-axis
|
104
|
-
y: Column name from SQL result to use for y-axis
|
105
|
-
hue: Optional; column name from SQL result to use for drawing multiple colored lines
|
106
|
-
"""
|
107
|
-
df = query_utils.load_query(query_id)
|
108
|
-
plot = sns.lineplot(df, x=x, y=y, hue=hue)
|
109
|
-
plot = _fix_x_labels(plot, df[x])
|
110
|
-
return _plot_to_image(plot)
|
111
|
-
|
112
|
-
|
113
|
-
# Distributions
|
114
|
-
|
115
|
-
@mcp.tool()
|
116
|
-
def histogram(
|
117
|
-
query_id: str,
|
118
|
-
column: str,
|
119
|
-
hue: str = None,
|
120
|
-
bins: int = None
|
121
|
-
):
|
122
|
-
"""
|
123
|
-
Make a histogram with a column of the dataframe obtained from running SQL Query against source
|
124
|
-
Args:
|
125
|
-
query_id: Previously run query to use for plotting
|
126
|
-
column: Column name from SQL result to use for the histogram
|
127
|
-
hue: Optional; column name from SQL result to use for drawing multiple colored histograms
|
128
|
-
bins: Optional; number of bins
|
129
|
-
"""
|
130
|
-
df = query_utils.load_query(query_id)
|
131
|
-
plot = sns.histplot(df, x=column, hue=hue, bins=bins)
|
132
|
-
return _plot_to_image(plot)
|
133
|
-
|
134
|
-
|
135
|
-
# Categorical
|
136
|
-
|
137
|
-
@mcp.tool()
|
138
|
-
def strip_plot(
|
139
|
-
query_id: str,
|
140
|
-
x: str,
|
141
|
-
y: str = None,
|
142
|
-
hue: str = None,
|
143
|
-
legend: bool = False
|
144
|
-
):
|
145
|
-
"""
|
146
|
-
Make a strip plot with the dataframe obtained from running SQL Query against source
|
147
|
-
Args:
|
148
|
-
query_id: Previously run query to use for plotting
|
149
|
-
x: Column name from SQL result to use for x axis
|
150
|
-
y: Optional; column name from SQL result to use for y axis
|
151
|
-
hue: Optional; column name from SQL result to use for coloring the points
|
152
|
-
legend: Whether to draw a legend for the hue
|
153
|
-
"""
|
154
|
-
df = query_utils.load_query(query_id)
|
155
|
-
plot = sns.stripplot(df, x=x, y=y, hue=hue, legend=legend)
|
156
|
-
plot = _fix_x_labels(plot, df[x])
|
157
|
-
return _plot_to_image(plot)
|
158
|
-
|
159
|
-
|
160
|
-
@mcp.tool()
|
161
|
-
def box_plot(
|
162
|
-
query_id: str,
|
163
|
-
x: str,
|
164
|
-
y: str = None,
|
165
|
-
hue: str = None
|
166
|
-
):
|
167
|
-
"""
|
168
|
-
Make a box plot with the dataframe obtained from running SQL Query against source
|
169
|
-
Args:
|
170
|
-
query_id: Previously run query to use for plotting
|
171
|
-
x: Column name from SQL result to use for x axis
|
172
|
-
y: Optional; column name from SQL result to use for y axis
|
173
|
-
hue: Optional column name from SQL result to use for coloring the points
|
174
|
-
"""
|
175
|
-
df = query_utils.load_query(query_id)
|
176
|
-
plot = sns.boxplot(df, x=x, y=y, hue=hue)
|
177
|
-
plot = _fix_x_labels(plot, df[x])
|
178
|
-
return _plot_to_image(plot)
|
179
|
-
|
180
|
-
|
181
|
-
@mcp.tool()
|
182
|
-
def bar_plot(
|
183
|
-
query_id: str,
|
184
|
-
x: str,
|
185
|
-
y: str = None,
|
186
|
-
hue: str = None,
|
187
|
-
orient: str = 'v'
|
188
|
-
):
|
189
|
-
"""
|
190
|
-
Make a bar plot with the dataframe obtained from running SQL Query against source
|
191
|
-
Args:
|
192
|
-
query_id: Previously run query to use for plotting
|
193
|
-
x: Column name from SQL result to use for x axis
|
194
|
-
y: Optional; column name from SQL result to use for y axis
|
195
|
-
hue: Optional column name from SQL result to use for coloring the bars
|
196
|
-
orient: Orientation of the box plot, use 'v' for vertical and 'h' for horizontal
|
197
|
-
"""
|
198
|
-
df = query_utils.load_query(query_id)
|
199
|
-
plot = sns.barplot(df, x=x, y=y, hue=hue, orient=orient)
|
200
|
-
plot = _fix_x_labels(plot, df[x])
|
201
|
-
return _plot_to_image(plot)
|
202
|
-
|
203
|
-
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|