zaturn 0.1.4__tar.gz → 0.1.6__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.
@@ -0,0 +1,6 @@
1
+ #### v0.1.6 (2025-04-25)
2
+ - Added Clickhouse Support
3
+
4
+ #### v0.1.5 (2025-04-22)
5
+ - Changed plotting library to plotly instead of seaborn
6
+ - Fixed labeling issues in visualizations
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: zaturn
3
- Version: 0.1.4
3
+ Version: 0.1.6
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>
@@ -9,11 +9,14 @@ Project-URL: Issues, https://github.com/kdqed/zaturn/issues
9
9
  Requires-Python: >=3.11
10
10
  Description-Content-Type: text/markdown
11
11
  License-File: LICENSE
12
+ Requires-Dist: clickhouse-connect>=0.8.17
12
13
  Requires-Dist: cryptography>=44.0.2
13
14
  Requires-Dist: duckdb>=1.2.1
14
15
  Requires-Dist: fastmcp>=0.4.1
16
+ Requires-Dist: kaleido==0.2.1
15
17
  Requires-Dist: pandas>=2.2.3
16
18
  Requires-Dist: platformdirs>=4.3.7
19
+ Requires-Dist: plotly[express]>=6.0.1
17
20
  Requires-Dist: psycopg2-binary>=2.9.10
18
21
  Requires-Dist: pyarrow>=19.0.1
19
22
  Requires-Dist: pymysql>=1.1.1
@@ -54,7 +57,7 @@ https://github.com/user-attachments/assets/d42dc433-e5ec-4b3e-bef0-5cfc097396ab
54
57
 
55
58
  ### Multiple Data Sources
56
59
  Zaturn can currently connect to the following data sources:
57
- - SQL Databases: PostgreSQL, SQLite, DuckDB, MySQL
60
+ - SQL Databases: PostgreSQL, SQLite, DuckDB, MySQL, ClickHouse
58
61
  - Files: CSV, Parquet
59
62
 
60
63
  Connectors for more data sources are being added.
@@ -103,6 +106,7 @@ OR add a `sources.txt` to the Zaturn config directory:
103
106
  ```
104
107
  postgresql://username:password@host:port/dbname
105
108
  mysql+pymysql://username:password@host:3306/dbname
109
+ clickhouse://username:password@host:port/dbname
106
110
  sqlite:////full/path/to/sample_dbs/northwind.db
107
111
  /full/path/to/sample_dbs/titanic.parquet
108
112
  /full/path/to/sample_dbs/ny_aq.csv
@@ -158,11 +162,20 @@ Analyst:
158
162
  ```
159
163
  - A native notebook interface
160
164
 
161
- ## Support And Feedback
165
+ ## Help And Feedback
162
166
 
163
167
  [Raise an issue](https://github.com/kdqed/zaturn/issues) or [join the Discord](https://discord.gg/K8mECeVzpQ).
164
168
 
165
169
 
170
+ ## Support The Project
171
+
172
+ If you find Zaturn useful, please support this project by:
173
+ - Starring the Project
174
+ - Spreading the word
175
+ - [Pledging $9/month on Patreon](https://www.patreon.com/kdqed?utm_medium=github&utm_source=join_link&utm_campaign=creatorshare_creator&utm_content=copyLink)
176
+
177
+ Your support will enable me to dedicate more of my time to Zaturn.
178
+
166
179
  ## Example Dataset Credits
167
180
 
168
181
  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.
@@ -28,7 +28,7 @@ https://github.com/user-attachments/assets/d42dc433-e5ec-4b3e-bef0-5cfc097396ab
28
28
 
29
29
  ### Multiple Data Sources
30
30
  Zaturn can currently connect to the following data sources:
31
- - SQL Databases: PostgreSQL, SQLite, DuckDB, MySQL
31
+ - SQL Databases: PostgreSQL, SQLite, DuckDB, MySQL, ClickHouse
32
32
  - Files: CSV, Parquet
33
33
 
34
34
  Connectors for more data sources are being added.
@@ -77,6 +77,7 @@ OR add a `sources.txt` to the Zaturn config directory:
77
77
  ```
78
78
  postgresql://username:password@host:port/dbname
79
79
  mysql+pymysql://username:password@host:3306/dbname
80
+ clickhouse://username:password@host:port/dbname
80
81
  sqlite:////full/path/to/sample_dbs/northwind.db
81
82
  /full/path/to/sample_dbs/titanic.parquet
82
83
  /full/path/to/sample_dbs/ny_aq.csv
@@ -132,11 +133,20 @@ Analyst:
132
133
  ```
133
134
  - A native notebook interface
134
135
 
135
- ## Support And Feedback
136
+ ## Help And Feedback
136
137
 
137
138
  [Raise an issue](https://github.com/kdqed/zaturn/issues) or [join the Discord](https://discord.gg/K8mECeVzpQ).
138
139
 
139
140
 
141
+ ## Support The Project
142
+
143
+ If you find Zaturn useful, please support this project by:
144
+ - Starring the Project
145
+ - Spreading the word
146
+ - [Pledging $9/month on Patreon](https://www.patreon.com/kdqed?utm_medium=github&utm_source=join_link&utm_campaign=creatorshare_creator&utm_content=copyLink)
147
+
148
+ Your support will enable me to dedicate more of my time to Zaturn.
149
+
140
150
  ## Example Dataset Credits
141
151
 
142
152
  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.4"
3
+ version = "0.1.6"
4
4
  description = "AI Data Analysis MCP"
5
5
  authors = [
6
6
  {name = "Karthik Devan", email = "krtdvn@gmail.com"},
@@ -11,11 +11,14 @@ maintainers = [
11
11
  readme = "README.md"
12
12
  requires-python = ">=3.11"
13
13
  dependencies = [
14
+ "clickhouse-connect>=0.8.17",
14
15
  "cryptography>=44.0.2",
15
16
  "duckdb>=1.2.1",
16
17
  "fastmcp>=0.4.1",
18
+ "kaleido==0.2.1",
17
19
  "pandas>=2.2.3",
18
20
  "platformdirs>=4.3.7",
21
+ "plotly[express]>=6.0.1",
19
22
  "psycopg2-binary>=2.9.10",
20
23
  "pyarrow>=19.0.1",
21
24
  "pymysql>=1.1.1",
@@ -95,6 +95,51 @@ wheels = [
95
95
  { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188 },
96
96
  ]
97
97
 
98
+ [[package]]
99
+ name = "clickhouse-connect"
100
+ version = "0.8.17"
101
+ source = { registry = "https://pypi.org/simple" }
102
+ dependencies = [
103
+ { name = "certifi" },
104
+ { name = "lz4" },
105
+ { name = "pytz" },
106
+ { name = "urllib3" },
107
+ { name = "zstandard" },
108
+ ]
109
+ sdist = { url = "https://files.pythonhosted.org/packages/c2/c2/e46db00486e02007551ff9ba8880ebee9f6d564b26b9323bc9f83f961849/clickhouse_connect-0.8.17.tar.gz", hash = "sha256:16405a37f8229a83956fbc372598d03b876537ee3acf2a5ad2f660336879b3fa", size = 91247 }
110
+ wheels = [
111
+ { url = "https://files.pythonhosted.org/packages/cb/31/2077ded60a46f7a4ac16ab23b73be1e817975e45dcfef4d8c1803f4e84cc/clickhouse_connect-0.8.17-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d292a4c73ec863115875b91533003427ef4cdd4931e899064488ce2c75d22d4e", size = 258605 },
112
+ { url = "https://files.pythonhosted.org/packages/6d/b6/744f7f9664d9611b5835cc9b8c5d27ae2c6e826bd974712dccd8be4c31ec/clickhouse_connect-0.8.17-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:45f3d55bf17ff69f63ca0150cdd77436d35f5cf85e672056798befe06651b78c", size = 251649 },
113
+ { url = "https://files.pythonhosted.org/packages/63/53/b03dfd20fd8fa090c4d6d70f96b2e2e44574408de2561d6b68fbb6dd371e/clickhouse_connect-0.8.17-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ecc8539cdbcde71394d242665329ad3d32477a9a8d21449876f4a841def0a2d", size = 1061626 },
114
+ { url = "https://files.pythonhosted.org/packages/33/3f/c1f80477ab5b90d8e50480410d1f384ca1fdbf800c6e5b72a029699cffb6/clickhouse_connect-0.8.17-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab35c6eec3758c65d61fe6f96bd13e242f7a7cd27dcb6fbe527db0285011f400", size = 1072472 },
115
+ { url = "https://files.pythonhosted.org/packages/22/08/076fa97e065ac5b5bc8d50c5ea24f9ac852c5b6d2f60ee2f54b57eb230b2/clickhouse_connect-0.8.17-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:db2227650d3cf34f3b1bafc207ec113697e58b7b08f0947014e5d66f2687d702", size = 1034338 },
116
+ { url = "https://files.pythonhosted.org/packages/f2/01/5218d78c97683fd4789030287fcc7f239003e93bad71634c5c58b3535afc/clickhouse_connect-0.8.17-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6c25c76a07ab2d4188b9923d76a61bf6f6e7a1a9e702ec1124cff4478e4bc3b7", size = 1066617 },
117
+ { url = "https://files.pythonhosted.org/packages/0d/bb/3916a0a675245598d52ea4f72eb2ba2e1f79cc45aee632a48fd757c18a19/clickhouse_connect-0.8.17-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f257922b32949f18013bb52e71f50f2eb4667458d73d5782861b46e22081221f", size = 1072619 },
118
+ { url = "https://files.pythonhosted.org/packages/78/8c/5feb108128ca33b687bb03bcc8ff52be35d26cbba291af9504f6aed55fd6/clickhouse_connect-0.8.17-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8b59c22ed333f20d547aaf9b1dbd5fd475b2861da8acbb59633503b41b51fdf2", size = 1103629 },
119
+ { url = "https://files.pythonhosted.org/packages/58/c1/ce42d2381c29cd2b6a08ae7810639de95bc53051240ca87ab1c59f3da9ab/clickhouse_connect-0.8.17-cp311-cp311-win32.whl", hash = "sha256:6613e1b863535a94b438b946e0dc4619fe83d8a021d99f947d88eee95568838e", size = 229555 },
120
+ { url = "https://files.pythonhosted.org/packages/4a/dd/050a2c10856ef08dce5c188c26c750881b6a2a7dd5be829eaab334cb0fb0/clickhouse_connect-0.8.17-cp311-cp311-win_amd64.whl", hash = "sha256:d6879fa613128229a397ea5f31612bc1731dad72759a7b566ee684c600e61c90", size = 247119 },
121
+ { url = "https://files.pythonhosted.org/packages/3d/18/da233967602118f985088ae29ed7fa8b24a6893178ccce35ff6b09261606/clickhouse_connect-0.8.17-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ca0a9d39af57c269f2c26a3142ca31812bde82a5a176d89c2ee1b9500d1e471d", size = 262090 },
122
+ { url = "https://files.pythonhosted.org/packages/61/cf/9a2efb2cc72bb2312bc3ca5464239b31388c537aef60278cbfbf8a0134ff/clickhouse_connect-0.8.17-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cac30623ed9ca51c3619751b23567901e8a85025e8c4fde11dc64292f68a3dee", size = 253789 },
123
+ { url = "https://files.pythonhosted.org/packages/f7/75/7b083fcbc9e5b4e92a56c08a5eb943412d1a6e5c01b137bd62e06d56525d/clickhouse_connect-0.8.17-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e0f7f3dde0c9ffae210a402574bb3ea30a9fef1b6d1fbc81d95e67a8ac7799d", size = 1058039 },
124
+ { url = "https://files.pythonhosted.org/packages/1a/58/339bd60c3d0a16eb793badd6a67bfdcbc13c10b0232ea7e6feae2390ba10/clickhouse_connect-0.8.17-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a85d11ff8d115a707ee91c86b96ef1d8e92e2c34ab3f3ce41241ec0ebae78d5", size = 1076405 },
125
+ { url = "https://files.pythonhosted.org/packages/dd/e8/26dac5044f197f985c52263861f6771c39449e642fcad83374dbce1c70b0/clickhouse_connect-0.8.17-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32cfe3a81b8b3d58af62948c0cdfd3fe0aaab6e35299ddfe6a556c9c3c00c37d", size = 1031665 },
126
+ { url = "https://files.pythonhosted.org/packages/81/39/dd7977f111e77f16a4d7e7249104ef9a4a7c86c0569a3b792352c560a15e/clickhouse_connect-0.8.17-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ba1e360fa7a28f6dcfa584c82e0a03db26a71e91e14c115a2d70603eea43cbac", size = 1057311 },
127
+ { url = "https://files.pythonhosted.org/packages/3d/68/a46278e295658cc508ffc33b008491a8e14d85ebd60873167aaf0f8c9cc1/clickhouse_connect-0.8.17-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4ea849c2dcb1712fd209d8b58fe3fcf016b65b30ff6aadae444194f470bbd800", size = 1073995 },
128
+ { url = "https://files.pythonhosted.org/packages/12/9e/095de494d63ebf0f90369b4f3ef9c50de0488a0e2771e84aa767ee99f743/clickhouse_connect-0.8.17-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d0a34eb004e0dcbc1583d4fc31918e665794cdcc242150f1e699c75a7aa46b7d", size = 1099316 },
129
+ { url = "https://files.pythonhosted.org/packages/87/35/3c1335dab9184985b5e73b6f31dda60ba56ab7d05b6c43bf89339eda031b/clickhouse_connect-0.8.17-cp312-cp312-win32.whl", hash = "sha256:b77f5c8aca4d7ed87649afbda1eea62cd16406c74029e85f28512b24c2165106", size = 229349 },
130
+ { url = "https://files.pythonhosted.org/packages/10/3c/e507a6bd392c6618c90deb634a7447a9e11af41dc66e9f141c117d8de232/clickhouse_connect-0.8.17-cp312-cp312-win_amd64.whl", hash = "sha256:01903b8989afc3cf60f959695d56e38c20033a3dbb88aae42af857eb16e028b0", size = 247420 },
131
+ { url = "https://files.pythonhosted.org/packages/83/17/918d5c2396426502ad4ed84b314f3699b8d0834f9741823ba59362aa8205/clickhouse_connect-0.8.17-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f0772cc3de3df5a460c2a5a4ef5b3737da7bfbc93b91e88000cf712f5a7ab5f9", size = 259327 },
132
+ { url = "https://files.pythonhosted.org/packages/43/ce/fb6374bab859d145c403d49cb270998c2fa92f8eac1291fb2dad963e27d6/clickhouse_connect-0.8.17-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dffcb1767c684eac1ae9f2fb5c40cc98e23bd3b57f9fb090cd31f4fd2add898b", size = 250996 },
133
+ { url = "https://files.pythonhosted.org/packages/67/e4/b749f0b402781140702775e2b9d7674679f104b8701034252d51f1fc5194/clickhouse_connect-0.8.17-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b1bd03750e69b2f278fff6e041a5d8b059656735e4d8c60ee82d647645481f1", size = 1041001 },
134
+ { url = "https://files.pythonhosted.org/packages/9f/ec/84745d52d9e15449234debee0b90789113bd057347f87cee582ce59100d0/clickhouse_connect-0.8.17-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:030395cbd6e64e467f004fe481346849e482d38a87004103df7a806835851171", size = 1059424 },
135
+ { url = "https://files.pythonhosted.org/packages/71/d2/8e55c77aad1882ac210a8ec0924bd4bc771a03f390991d4768f42992ea16/clickhouse_connect-0.8.17-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e44328e12997d361e02d1f8208e1f151133d372f8ef85a524fc622385e69096", size = 1015033 },
136
+ { url = "https://files.pythonhosted.org/packages/6c/ae/b37a84923675437e88d8974bf15e75368a781767fd556861205dbd9d2a43/clickhouse_connect-0.8.17-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:44a00808be2c72754e0c0c76005a9ecdb9ef08cb5490f82c45224455ccb47b1c", size = 1042049 },
137
+ { url = "https://files.pythonhosted.org/packages/0c/46/214b8bd87b6a56c2d7bf31edc48c5ea2ffa0b1485ced97f198befd1d232e/clickhouse_connect-0.8.17-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4ab8ea92a2b48dd09f26a491989628244a29231d56731aa53e29b10a8fcabe7e", size = 1057463 },
138
+ { url = "https://files.pythonhosted.org/packages/fc/e1/551af37d606b4ae39ddc254bc1348787dc02d1af53748f9a4d522653e61c/clickhouse_connect-0.8.17-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a6b2111c0b5cecef6f63f57af4e882fe7289918d5f61fd16068b721878e8610f", size = 1084897 },
139
+ { url = "https://files.pythonhosted.org/packages/88/e7/b5059eb293e4d1968385854471fe50c7f1196085bc99e9a439a8798fa367/clickhouse_connect-0.8.17-cp313-cp313-win32.whl", hash = "sha256:8430adebbbb401a80357a81c55c8cf42ff1c6f272c12130c22046c0f4225c399", size = 228640 },
140
+ { url = "https://files.pythonhosted.org/packages/b7/8f/a9be019232982608cf9619a746d46215951d62a4aecbdc3241219c523251/clickhouse_connect-0.8.17-cp313-cp313-win_amd64.whl", hash = "sha256:097209544de81c8c4a5e659c50089066bd92ebfb4a0c96cc7853f7c98bbf7487", size = 246393 },
141
+ ]
142
+
98
143
  [[package]]
99
144
  name = "colorama"
100
145
  version = "0.4.6"
@@ -445,6 +490,19 @@ wheels = [
445
490
  { url = "https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9", size = 1572278 },
446
491
  ]
447
492
 
493
+ [[package]]
494
+ name = "kaleido"
495
+ version = "0.2.1"
496
+ source = { registry = "https://pypi.org/simple" }
497
+ wheels = [
498
+ { 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 },
499
+ { 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 },
500
+ { 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 },
501
+ { url = "https://files.pythonhosted.org/packages/a1/2b/680662678a57afab1685f0c431c2aba7783ce4344f06ec162074d485d469/kaleido-0.2.1-py2.py3-none-manylinux2014_aarch64.whl", hash = "sha256:845819844c8082c9469d9c17e42621fbf85c2b237ef8a86ec8a8527f98b6512a", size = 83711746 },
502
+ { url = "https://files.pythonhosted.org/packages/88/89/4b6f8bb3f9ab036fd4ad1cb2d628ab5c81db32ac9aa0641d7b180073ba43/kaleido-0.2.1-py2.py3-none-win32.whl", hash = "sha256:ecc72635860be616c6b7161807a65c0dbd9b90c6437ac96965831e2e24066552", size = 62312480 },
503
+ { url = "https://files.pythonhosted.org/packages/f7/9a/0408b02a4bcb3cf8b338a2b074ac7d1b2099e2b092b42473def22f7b625f/kaleido-0.2.1-py2.py3-none-win_amd64.whl", hash = "sha256:4670985f28913c2d063c5734d125ecc28e40810141bdb0a46f15b76c1d45f23c", size = 65945521 },
504
+ ]
505
+
448
506
  [[package]]
449
507
  name = "kiwisolver"
450
508
  version = "1.4.8"
@@ -511,6 +569,38 @@ wheels = [
511
569
  { url = "https://files.pythonhosted.org/packages/4c/fa/be89a49c640930180657482a74970cdcf6f7072c8d2471e1babe17a222dc/kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:be4816dc51c8a471749d664161b434912eee82f2ea66bd7628bd14583a833e85", size = 2349213 },
512
570
  ]
513
571
 
572
+ [[package]]
573
+ name = "lz4"
574
+ version = "4.4.4"
575
+ source = { registry = "https://pypi.org/simple" }
576
+ sdist = { url = "https://files.pythonhosted.org/packages/c6/5a/945f5086326d569f14c84ac6f7fcc3229f0b9b1e8cc536b951fd53dfb9e1/lz4-4.4.4.tar.gz", hash = "sha256:070fd0627ec4393011251a094e08ed9fdcc78cb4e7ab28f507638eee4e39abda", size = 171884 }
577
+ wheels = [
578
+ { url = "https://files.pythonhosted.org/packages/28/e8/63843dc5ecb1529eb38e1761ceed04a0ad52a9ad8929ab8b7930ea2e4976/lz4-4.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ddfc7194cd206496c445e9e5b0c47f970ce982c725c87bd22de028884125b68f", size = 220898 },
579
+ { url = "https://files.pythonhosted.org/packages/e4/94/c53de5f07c7dc11cf459aab2a1d754f5df5f693bfacbbe1e4914bfd02f1e/lz4-4.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:714f9298c86f8e7278f1c6af23e509044782fa8220eb0260f8f8f1632f820550", size = 189685 },
580
+ { url = "https://files.pythonhosted.org/packages/fe/59/c22d516dd0352f2a3415d1f665ccef2f3e74ecec3ca6a8f061a38f97d50d/lz4-4.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a8474c91de47733856c6686df3c4aca33753741da7e757979369c2c0d32918ba", size = 1239225 },
581
+ { url = "https://files.pythonhosted.org/packages/81/af/665685072e71f3f0e626221b7922867ec249cd8376aca761078c8f11f5da/lz4-4.4.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80dd27d7d680ea02c261c226acf1d41de2fd77af4fb2da62b278a9376e380de0", size = 1265881 },
582
+ { url = "https://files.pythonhosted.org/packages/90/04/b4557ae381d3aa451388a29755cc410066f5e2f78c847f66f154f4520a68/lz4-4.4.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9b7d6dddfd01b49aedb940fdcaf32f41dc58c926ba35f4e31866aeec2f32f4f4", size = 1185593 },
583
+ { url = "https://files.pythonhosted.org/packages/7b/e4/03636979f4e8bf92c557f998ca98ee4e6ef92e92eaf0ed6d3c7f2524e790/lz4-4.4.4-cp311-cp311-win32.whl", hash = "sha256:4134b9fd70ac41954c080b772816bb1afe0c8354ee993015a83430031d686a4c", size = 88259 },
584
+ { url = "https://files.pythonhosted.org/packages/07/f0/9efe53b4945441a5d2790d455134843ad86739855b7e6199977bf6dc8898/lz4-4.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:f5024d3ca2383470f7c4ef4d0ed8eabad0b22b23eeefde1c192cf1a38d5e9f78", size = 99916 },
585
+ { url = "https://files.pythonhosted.org/packages/87/c8/1675527549ee174b9e1db089f7ddfbb962a97314657269b1e0344a5eaf56/lz4-4.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:6ea715bb3357ea1665f77874cf8f55385ff112553db06f3742d3cdcec08633f7", size = 89741 },
586
+ { url = "https://files.pythonhosted.org/packages/f7/2d/5523b4fabe11cd98f040f715728d1932eb7e696bfe94391872a823332b94/lz4-4.4.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:23ae267494fdd80f0d2a131beff890cf857f1b812ee72dbb96c3204aab725553", size = 220669 },
587
+ { url = "https://files.pythonhosted.org/packages/91/06/1a5bbcacbfb48d8ee5b6eb3fca6aa84143a81d92946bdb5cd6b005f1863e/lz4-4.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fff9f3a1ed63d45cb6514bfb8293005dc4141341ce3500abdfeb76124c0b9b2e", size = 189661 },
588
+ { url = "https://files.pythonhosted.org/packages/fa/08/39eb7ac907f73e11a69a11576a75a9e36406b3241c0ba41453a7eb842abb/lz4-4.4.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ea7f07329f85a8eda4d8cf937b87f27f0ac392c6400f18bea2c667c8b7f8ecc", size = 1238775 },
589
+ { url = "https://files.pythonhosted.org/packages/e9/26/05840fbd4233e8d23e88411a066ab19f1e9de332edddb8df2b6a95c7fddc/lz4-4.4.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ccab8f7f7b82f9fa9fc3b0ba584d353bd5aa818d5821d77d5b9447faad2aaad", size = 1265143 },
590
+ { url = "https://files.pythonhosted.org/packages/b7/5d/5f2db18c298a419932f3ab2023deb689863cf8fd7ed875b1c43492479af2/lz4-4.4.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e43e9d48b2daf80e486213128b0763deed35bbb7a59b66d1681e205e1702d735", size = 1185032 },
591
+ { url = "https://files.pythonhosted.org/packages/c4/e6/736ab5f128694b0f6aac58343bcf37163437ac95997276cd0be3ea4c3342/lz4-4.4.4-cp312-cp312-win32.whl", hash = "sha256:33e01e18e4561b0381b2c33d58e77ceee850a5067f0ece945064cbaac2176962", size = 88284 },
592
+ { url = "https://files.pythonhosted.org/packages/40/b8/243430cb62319175070e06e3a94c4c7bd186a812e474e22148ae1290d47d/lz4-4.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:d21d1a2892a2dcc193163dd13eaadabb2c1b803807a5117d8f8588b22eaf9f12", size = 99918 },
593
+ { url = "https://files.pythonhosted.org/packages/6c/e1/0686c91738f3e6c2e1a243e0fdd4371667c4d2e5009b0a3605806c2aa020/lz4-4.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:2f4f2965c98ab254feddf6b5072854a6935adab7bc81412ec4fe238f07b85f62", size = 89736 },
594
+ { url = "https://files.pythonhosted.org/packages/3b/3c/d1d1b926d3688263893461e7c47ed7382a969a0976fc121fc678ec325fc6/lz4-4.4.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ed6eb9f8deaf25ee4f6fad9625d0955183fdc90c52b6f79a76b7f209af1b6e54", size = 220678 },
595
+ { url = "https://files.pythonhosted.org/packages/26/89/8783d98deb058800dabe07e6cdc90f5a2a8502a9bad8c5343c641120ace2/lz4-4.4.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:18ae4fe3bafb344dbd09f976d45cbf49c05c34416f2462828f9572c1fa6d5af7", size = 189670 },
596
+ { url = "https://files.pythonhosted.org/packages/22/ab/a491ace69a83a8914a49f7391e92ca0698f11b28d5ce7b2ececa2be28e9a/lz4-4.4.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57fd20c5fc1a49d1bbd170836fccf9a338847e73664f8e313dce6ac91b8c1e02", size = 1238746 },
597
+ { url = "https://files.pythonhosted.org/packages/97/12/a1f2f4fdc6b7159c0d12249456f9fe454665b6126e98dbee9f2bd3cf735c/lz4-4.4.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9cb387c33f014dae4db8cb4ba789c8d2a0a6d045ddff6be13f6c8d9def1d2a6", size = 1265119 },
598
+ { url = "https://files.pythonhosted.org/packages/50/6e/e22e50f5207649db6ea83cd31b79049118305be67e96bec60becf317afc6/lz4-4.4.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d0be9f68240231e1e44118a4ebfecd8a5d4184f0bdf5c591c98dd6ade9720afd", size = 1184954 },
599
+ { url = "https://files.pythonhosted.org/packages/4c/c4/2a458039645fcc6324ece731d4d1361c5daf960b553d1fcb4261ba07d51c/lz4-4.4.4-cp313-cp313-win32.whl", hash = "sha256:e9ec5d45ea43684f87c316542af061ef5febc6a6b322928f059ce1fb289c298a", size = 88289 },
600
+ { url = "https://files.pythonhosted.org/packages/00/96/b8e24ea7537ab418074c226279acfcaa470e1ea8271003e24909b6db942b/lz4-4.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:a760a175b46325b2bb33b1f2bbfb8aa21b48e1b9653e29c10b6834f9bb44ead4", size = 99925 },
601
+ { url = "https://files.pythonhosted.org/packages/a5/a5/f9838fe6aa132cfd22733ed2729d0592259fff074cefb80f19aa0607367b/lz4-4.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:f4c21648d81e0dda38b4720dccc9006ae33b0e9e7ffe88af6bf7d4ec124e2fba", size = 89743 },
602
+ ]
603
+
514
604
  [[package]]
515
605
  name = "markdown-it-py"
516
606
  version = "3.0.0"
@@ -594,6 +684,15 @@ wheels = [
594
684
  { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 },
595
685
  ]
596
686
 
687
+ [[package]]
688
+ name = "narwhals"
689
+ version = "1.35.0"
690
+ source = { registry = "https://pypi.org/simple" }
691
+ sdist = { url = "https://files.pythonhosted.org/packages/ee/6a/a98fa5e9d530a428a0cd79d27f059ed65efd3a07aad61a8c93e323c9c20b/narwhals-1.35.0.tar.gz", hash = "sha256:07477d18487fbc940243b69818a177ed7119b737910a8a254fb67688b48a7c96", size = 265784 }
692
+ wheels = [
693
+ { url = "https://files.pythonhosted.org/packages/80/b3/5781eb874f04cb1e882a7d93cf30abcb00362a3205c5f3708a7434a1a2ac/narwhals-1.35.0-py3-none-any.whl", hash = "sha256:7562af132fa3f8aaaf34dc96d7ec95bdca29d1c795e8fcf14e01edf1d32122bc", size = 325708 },
694
+ ]
695
+
597
696
  [[package]]
598
697
  name = "numpy"
599
698
  version = "2.2.4"
@@ -781,6 +880,24 @@ wheels = [
781
880
  { url = "https://files.pythonhosted.org/packages/6d/45/59578566b3275b8fd9157885918fcd0c4d74162928a5310926887b856a51/platformdirs-4.3.7-py3-none-any.whl", hash = "sha256:a03875334331946f13c549dbd8f4bac7a13a50a895a0eb1e8c6a8ace80d40a94", size = 18499 },
782
881
  ]
783
882
 
883
+ [[package]]
884
+ name = "plotly"
885
+ version = "6.0.1"
886
+ source = { registry = "https://pypi.org/simple" }
887
+ dependencies = [
888
+ { name = "narwhals" },
889
+ { name = "packaging" },
890
+ ]
891
+ sdist = { url = "https://files.pythonhosted.org/packages/c7/cc/e41b5f697ae403f0b50e47b7af2e36642a193085f553bf7cc1169362873a/plotly-6.0.1.tar.gz", hash = "sha256:dd8400229872b6e3c964b099be699f8d00c489a974f2cfccfad5e8240873366b", size = 8094643 }
892
+ wheels = [
893
+ { url = "https://files.pythonhosted.org/packages/02/65/ad2bc85f7377f5cfba5d4466d5474423a3fb7f6a97fd807c06f92dd3e721/plotly-6.0.1-py3-none-any.whl", hash = "sha256:4714db20fea57a435692c548a4eb4fae454f7daddf15f8d8ba7e1045681d7768", size = 14805757 },
894
+ ]
895
+
896
+ [package.optional-dependencies]
897
+ express = [
898
+ { name = "numpy" },
899
+ ]
900
+
784
901
  [[package]]
785
902
  name = "pluggy"
786
903
  version = "1.5.0"
@@ -1272,6 +1389,15 @@ wheels = [
1272
1389
  { url = "https://files.pythonhosted.org/packages/d7/72/6cb6728e2738c05bbe9bd522d6fc79f86b9a28402f38663e85a28fddd4a0/ujson-5.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:4573fd1695932d4f619928fd09d5d03d917274381649ade4328091ceca175539", size = 42212 },
1273
1390
  ]
1274
1391
 
1392
+ [[package]]
1393
+ name = "urllib3"
1394
+ version = "2.4.0"
1395
+ source = { registry = "https://pypi.org/simple" }
1396
+ sdist = { url = "https://files.pythonhosted.org/packages/8a/78/16493d9c386d8e60e442a35feac5e00f0913c0f4b7c217c11e8ec2ff53e0/urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466", size = 390672 }
1397
+ wheels = [
1398
+ { url = "https://files.pythonhosted.org/packages/6b/11/cc635220681e93a0183390e26485430ca2c7b5f9d33b15c74c2861cb8091/urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813", size = 128680 },
1399
+ ]
1400
+
1275
1401
  [[package]]
1276
1402
  name = "uvicorn"
1277
1403
  version = "0.34.0"
@@ -1329,14 +1455,17 @@ wheels = [
1329
1455
 
1330
1456
  [[package]]
1331
1457
  name = "zaturn"
1332
- version = "0.1.3"
1458
+ version = "0.1.5"
1333
1459
  source = { editable = "." }
1334
1460
  dependencies = [
1461
+ { name = "clickhouse-connect" },
1335
1462
  { name = "cryptography" },
1336
1463
  { name = "duckdb" },
1337
1464
  { name = "fastmcp" },
1465
+ { name = "kaleido" },
1338
1466
  { name = "pandas" },
1339
1467
  { name = "platformdirs" },
1468
+ { name = "plotly", extra = ["express"] },
1340
1469
  { name = "psycopg2-binary" },
1341
1470
  { name = "pyarrow" },
1342
1471
  { name = "pymysql" },
@@ -1349,11 +1478,14 @@ dependencies = [
1349
1478
 
1350
1479
  [package.metadata]
1351
1480
  requires-dist = [
1481
+ { name = "clickhouse-connect", specifier = ">=0.8.17" },
1352
1482
  { name = "cryptography", specifier = ">=44.0.2" },
1353
1483
  { name = "duckdb", specifier = ">=1.2.1" },
1354
1484
  { name = "fastmcp", specifier = ">=0.4.1" },
1485
+ { name = "kaleido", specifier = "==0.2.1" },
1355
1486
  { name = "pandas", specifier = ">=2.2.3" },
1356
1487
  { name = "platformdirs", specifier = ">=4.3.7" },
1488
+ { name = "plotly", extras = ["express"], specifier = ">=6.0.1" },
1357
1489
  { name = "psycopg2-binary", specifier = ">=2.9.10" },
1358
1490
  { name = "pyarrow", specifier = ">=19.0.1" },
1359
1491
  { name = "pymysql", specifier = ">=1.1.1" },
@@ -1375,3 +1507,62 @@ sdist = { url = "https://files.pythonhosted.org/packages/3f/50/bad581df71744867e
1375
1507
  wheels = [
1376
1508
  { url = "https://files.pythonhosted.org/packages/b7/1a/7e4798e9339adc931158c9d69ecc34f5e6791489d469f5e50ec15e35f458/zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931", size = 9630 },
1377
1509
  ]
1510
+
1511
+ [[package]]
1512
+ name = "zstandard"
1513
+ version = "0.23.0"
1514
+ source = { registry = "https://pypi.org/simple" }
1515
+ dependencies = [
1516
+ { name = "cffi", marker = "platform_python_implementation == 'PyPy'" },
1517
+ ]
1518
+ sdist = { url = "https://files.pythonhosted.org/packages/ed/f6/2ac0287b442160a89d726b17a9184a4c615bb5237db763791a7fd16d9df1/zstandard-0.23.0.tar.gz", hash = "sha256:b2d8c62d08e7255f68f7a740bae85b3c9b8e5466baa9cbf7f57f1cde0ac6bc09", size = 681701 }
1519
+ wheels = [
1520
+ { url = "https://files.pythonhosted.org/packages/9e/40/f67e7d2c25a0e2dc1744dd781110b0b60306657f8696cafb7ad7579469bd/zstandard-0.23.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:34895a41273ad33347b2fc70e1bff4240556de3c46c6ea430a7ed91f9042aa4e", size = 788699 },
1521
+ { url = "https://files.pythonhosted.org/packages/e8/46/66d5b55f4d737dd6ab75851b224abf0afe5774976fe511a54d2eb9063a41/zstandard-0.23.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:77ea385f7dd5b5676d7fd943292ffa18fbf5c72ba98f7d09fc1fb9e819b34c23", size = 633681 },
1522
+ { url = "https://files.pythonhosted.org/packages/63/b6/677e65c095d8e12b66b8f862b069bcf1f1d781b9c9c6f12eb55000d57583/zstandard-0.23.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:983b6efd649723474f29ed42e1467f90a35a74793437d0bc64a5bf482bedfa0a", size = 4944328 },
1523
+ { url = "https://files.pythonhosted.org/packages/59/cc/e76acb4c42afa05a9d20827116d1f9287e9c32b7ad58cc3af0721ce2b481/zstandard-0.23.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80a539906390591dd39ebb8d773771dc4db82ace6372c4d41e2d293f8e32b8db", size = 5311955 },
1524
+ { url = "https://files.pythonhosted.org/packages/78/e4/644b8075f18fc7f632130c32e8f36f6dc1b93065bf2dd87f03223b187f26/zstandard-0.23.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:445e4cb5048b04e90ce96a79b4b63140e3f4ab5f662321975679b5f6360b90e2", size = 5344944 },
1525
+ { url = "https://files.pythonhosted.org/packages/76/3f/dbafccf19cfeca25bbabf6f2dd81796b7218f768ec400f043edc767015a6/zstandard-0.23.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd30d9c67d13d891f2360b2a120186729c111238ac63b43dbd37a5a40670b8ca", size = 5442927 },
1526
+ { url = "https://files.pythonhosted.org/packages/0c/c3/d24a01a19b6733b9f218e94d1a87c477d523237e07f94899e1c10f6fd06c/zstandard-0.23.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d20fd853fbb5807c8e84c136c278827b6167ded66c72ec6f9a14b863d809211c", size = 4864910 },
1527
+ { url = "https://files.pythonhosted.org/packages/1c/a9/cf8f78ead4597264f7618d0875be01f9bc23c9d1d11afb6d225b867cb423/zstandard-0.23.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ed1708dbf4d2e3a1c5c69110ba2b4eb6678262028afd6c6fbcc5a8dac9cda68e", size = 4935544 },
1528
+ { url = "https://files.pythonhosted.org/packages/2c/96/8af1e3731b67965fb995a940c04a2c20997a7b3b14826b9d1301cf160879/zstandard-0.23.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:be9b5b8659dff1f913039c2feee1aca499cfbc19e98fa12bc85e037c17ec6ca5", size = 5467094 },
1529
+ { url = "https://files.pythonhosted.org/packages/ff/57/43ea9df642c636cb79f88a13ab07d92d88d3bfe3e550b55a25a07a26d878/zstandard-0.23.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:65308f4b4890aa12d9b6ad9f2844b7ee42c7f7a4fd3390425b242ffc57498f48", size = 4860440 },
1530
+ { url = "https://files.pythonhosted.org/packages/46/37/edb78f33c7f44f806525f27baa300341918fd4c4af9472fbc2c3094be2e8/zstandard-0.23.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:98da17ce9cbf3bfe4617e836d561e433f871129e3a7ac16d6ef4c680f13a839c", size = 4700091 },
1531
+ { url = "https://files.pythonhosted.org/packages/c1/f1/454ac3962671a754f3cb49242472df5c2cced4eb959ae203a377b45b1a3c/zstandard-0.23.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:8ed7d27cb56b3e058d3cf684d7200703bcae623e1dcc06ed1e18ecda39fee003", size = 5208682 },
1532
+ { url = "https://files.pythonhosted.org/packages/85/b2/1734b0fff1634390b1b887202d557d2dd542de84a4c155c258cf75da4773/zstandard-0.23.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:b69bb4f51daf461b15e7b3db033160937d3ff88303a7bc808c67bbc1eaf98c78", size = 5669707 },
1533
+ { url = "https://files.pythonhosted.org/packages/52/5a/87d6971f0997c4b9b09c495bf92189fb63de86a83cadc4977dc19735f652/zstandard-0.23.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:034b88913ecc1b097f528e42b539453fa82c3557e414b3de9d5632c80439a473", size = 5201792 },
1534
+ { url = "https://files.pythonhosted.org/packages/79/02/6f6a42cc84459d399bd1a4e1adfc78d4dfe45e56d05b072008d10040e13b/zstandard-0.23.0-cp311-cp311-win32.whl", hash = "sha256:f2d4380bf5f62daabd7b751ea2339c1a21d1c9463f1feb7fc2bdcea2c29c3160", size = 430586 },
1535
+ { url = "https://files.pythonhosted.org/packages/be/a2/4272175d47c623ff78196f3c10e9dc7045c1b9caf3735bf041e65271eca4/zstandard-0.23.0-cp311-cp311-win_amd64.whl", hash = "sha256:62136da96a973bd2557f06ddd4e8e807f9e13cbb0bfb9cc06cfe6d98ea90dfe0", size = 495420 },
1536
+ { url = "https://files.pythonhosted.org/packages/7b/83/f23338c963bd9de687d47bf32efe9fd30164e722ba27fb59df33e6b1719b/zstandard-0.23.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b4567955a6bc1b20e9c31612e615af6b53733491aeaa19a6b3b37f3b65477094", size = 788713 },
1537
+ { url = "https://files.pythonhosted.org/packages/5b/b3/1a028f6750fd9227ee0b937a278a434ab7f7fdc3066c3173f64366fe2466/zstandard-0.23.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e172f57cd78c20f13a3415cc8dfe24bf388614324d25539146594c16d78fcc8", size = 633459 },
1538
+ { url = "https://files.pythonhosted.org/packages/26/af/36d89aae0c1f95a0a98e50711bc5d92c144939efc1f81a2fcd3e78d7f4c1/zstandard-0.23.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b0e166f698c5a3e914947388c162be2583e0c638a4703fc6a543e23a88dea3c1", size = 4945707 },
1539
+ { url = "https://files.pythonhosted.org/packages/cd/2e/2051f5c772f4dfc0aae3741d5fc72c3dcfe3aaeb461cc231668a4db1ce14/zstandard-0.23.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12a289832e520c6bd4dcaad68e944b86da3bad0d339ef7989fb7e88f92e96072", size = 5306545 },
1540
+ { url = "https://files.pythonhosted.org/packages/0a/9e/a11c97b087f89cab030fa71206963090d2fecd8eb83e67bb8f3ffb84c024/zstandard-0.23.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d50d31bfedd53a928fed6707b15a8dbeef011bb6366297cc435accc888b27c20", size = 5337533 },
1541
+ { url = "https://files.pythonhosted.org/packages/fc/79/edeb217c57fe1bf16d890aa91a1c2c96b28c07b46afed54a5dcf310c3f6f/zstandard-0.23.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72c68dda124a1a138340fb62fa21b9bf4848437d9ca60bd35db36f2d3345f373", size = 5436510 },
1542
+ { url = "https://files.pythonhosted.org/packages/81/4f/c21383d97cb7a422ddf1ae824b53ce4b51063d0eeb2afa757eb40804a8ef/zstandard-0.23.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:53dd9d5e3d29f95acd5de6802e909ada8d8d8cfa37a3ac64836f3bc4bc5512db", size = 4859973 },
1543
+ { url = "https://files.pythonhosted.org/packages/ab/15/08d22e87753304405ccac8be2493a495f529edd81d39a0870621462276ef/zstandard-0.23.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:6a41c120c3dbc0d81a8e8adc73312d668cd34acd7725f036992b1b72d22c1772", size = 4936968 },
1544
+ { url = "https://files.pythonhosted.org/packages/eb/fa/f3670a597949fe7dcf38119a39f7da49a8a84a6f0b1a2e46b2f71a0ab83f/zstandard-0.23.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:40b33d93c6eddf02d2c19f5773196068d875c41ca25730e8288e9b672897c105", size = 5467179 },
1545
+ { url = "https://files.pythonhosted.org/packages/4e/a9/dad2ab22020211e380adc477a1dbf9f109b1f8d94c614944843e20dc2a99/zstandard-0.23.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9206649ec587e6b02bd124fb7799b86cddec350f6f6c14bc82a2b70183e708ba", size = 4848577 },
1546
+ { url = "https://files.pythonhosted.org/packages/08/03/dd28b4484b0770f1e23478413e01bee476ae8227bbc81561f9c329e12564/zstandard-0.23.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:76e79bc28a65f467e0409098fa2c4376931fd3207fbeb6b956c7c476d53746dd", size = 4693899 },
1547
+ { url = "https://files.pythonhosted.org/packages/2b/64/3da7497eb635d025841e958bcd66a86117ae320c3b14b0ae86e9e8627518/zstandard-0.23.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:66b689c107857eceabf2cf3d3fc699c3c0fe8ccd18df2219d978c0283e4c508a", size = 5199964 },
1548
+ { url = "https://files.pythonhosted.org/packages/43/a4/d82decbab158a0e8a6ebb7fc98bc4d903266bce85b6e9aaedea1d288338c/zstandard-0.23.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9c236e635582742fee16603042553d276cca506e824fa2e6489db04039521e90", size = 5655398 },
1549
+ { url = "https://files.pythonhosted.org/packages/f2/61/ac78a1263bc83a5cf29e7458b77a568eda5a8f81980691bbc6eb6a0d45cc/zstandard-0.23.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a8fffdbd9d1408006baaf02f1068d7dd1f016c6bcb7538682622c556e7b68e35", size = 5191313 },
1550
+ { url = "https://files.pythonhosted.org/packages/e7/54/967c478314e16af5baf849b6ee9d6ea724ae5b100eb506011f045d3d4e16/zstandard-0.23.0-cp312-cp312-win32.whl", hash = "sha256:dc1d33abb8a0d754ea4763bad944fd965d3d95b5baef6b121c0c9013eaf1907d", size = 430877 },
1551
+ { url = "https://files.pythonhosted.org/packages/75/37/872d74bd7739639c4553bf94c84af7d54d8211b626b352bc57f0fd8d1e3f/zstandard-0.23.0-cp312-cp312-win_amd64.whl", hash = "sha256:64585e1dba664dc67c7cdabd56c1e5685233fbb1fc1966cfba2a340ec0dfff7b", size = 495595 },
1552
+ { url = "https://files.pythonhosted.org/packages/80/f1/8386f3f7c10261fe85fbc2c012fdb3d4db793b921c9abcc995d8da1b7a80/zstandard-0.23.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:576856e8594e6649aee06ddbfc738fec6a834f7c85bf7cadd1c53d4a58186ef9", size = 788975 },
1553
+ { url = "https://files.pythonhosted.org/packages/16/e8/cbf01077550b3e5dc86089035ff8f6fbbb312bc0983757c2d1117ebba242/zstandard-0.23.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:38302b78a850ff82656beaddeb0bb989a0322a8bbb1bf1ab10c17506681d772a", size = 633448 },
1554
+ { url = "https://files.pythonhosted.org/packages/06/27/4a1b4c267c29a464a161aeb2589aff212b4db653a1d96bffe3598f3f0d22/zstandard-0.23.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2240ddc86b74966c34554c49d00eaafa8200a18d3a5b6ffbf7da63b11d74ee2", size = 4945269 },
1555
+ { url = "https://files.pythonhosted.org/packages/7c/64/d99261cc57afd9ae65b707e38045ed8269fbdae73544fd2e4a4d50d0ed83/zstandard-0.23.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2ef230a8fd217a2015bc91b74f6b3b7d6522ba48be29ad4ea0ca3a3775bf7dd5", size = 5306228 },
1556
+ { url = "https://files.pythonhosted.org/packages/7a/cf/27b74c6f22541f0263016a0fd6369b1b7818941de639215c84e4e94b2a1c/zstandard-0.23.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:774d45b1fac1461f48698a9d4b5fa19a69d47ece02fa469825b442263f04021f", size = 5336891 },
1557
+ { url = "https://files.pythonhosted.org/packages/fa/18/89ac62eac46b69948bf35fcd90d37103f38722968e2981f752d69081ec4d/zstandard-0.23.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f77fa49079891a4aab203d0b1744acc85577ed16d767b52fc089d83faf8d8ed", size = 5436310 },
1558
+ { url = "https://files.pythonhosted.org/packages/a8/a8/5ca5328ee568a873f5118d5b5f70d1f36c6387716efe2e369010289a5738/zstandard-0.23.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ac184f87ff521f4840e6ea0b10c0ec90c6b1dcd0bad2f1e4a9a1b4fa177982ea", size = 4859912 },
1559
+ { url = "https://files.pythonhosted.org/packages/ea/ca/3781059c95fd0868658b1cf0440edd832b942f84ae60685d0cfdb808bca1/zstandard-0.23.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c363b53e257246a954ebc7c488304b5592b9c53fbe74d03bc1c64dda153fb847", size = 4936946 },
1560
+ { url = "https://files.pythonhosted.org/packages/ce/11/41a58986f809532742c2b832c53b74ba0e0a5dae7e8ab4642bf5876f35de/zstandard-0.23.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:e7792606d606c8df5277c32ccb58f29b9b8603bf83b48639b7aedf6df4fe8171", size = 5466994 },
1561
+ { url = "https://files.pythonhosted.org/packages/83/e3/97d84fe95edd38d7053af05159465d298c8b20cebe9ccb3d26783faa9094/zstandard-0.23.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a0817825b900fcd43ac5d05b8b3079937073d2b1ff9cf89427590718b70dd840", size = 4848681 },
1562
+ { url = "https://files.pythonhosted.org/packages/6e/99/cb1e63e931de15c88af26085e3f2d9af9ce53ccafac73b6e48418fd5a6e6/zstandard-0.23.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:9da6bc32faac9a293ddfdcb9108d4b20416219461e4ec64dfea8383cac186690", size = 4694239 },
1563
+ { url = "https://files.pythonhosted.org/packages/ab/50/b1e703016eebbc6501fc92f34db7b1c68e54e567ef39e6e59cf5fb6f2ec0/zstandard-0.23.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:fd7699e8fd9969f455ef2926221e0233f81a2542921471382e77a9e2f2b57f4b", size = 5200149 },
1564
+ { url = "https://files.pythonhosted.org/packages/aa/e0/932388630aaba70197c78bdb10cce2c91fae01a7e553b76ce85471aec690/zstandard-0.23.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:d477ed829077cd945b01fc3115edd132c47e6540ddcd96ca169facff28173057", size = 5655392 },
1565
+ { url = "https://files.pythonhosted.org/packages/02/90/2633473864f67a15526324b007a9f96c96f56d5f32ef2a56cc12f9548723/zstandard-0.23.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa6ce8b52c5987b3e34d5674b0ab529a4602b632ebab0a93b07bfb4dfc8f8a33", size = 5191299 },
1566
+ { url = "https://files.pythonhosted.org/packages/b0/4c/315ca5c32da7e2dc3455f3b2caee5c8c2246074a61aac6ec3378a97b7136/zstandard-0.23.0-cp313-cp313-win32.whl", hash = "sha256:a9b07268d0c3ca5c170a385a0ab9fb7fdd9f5fd866be004c4ea39e44edce47dd", size = 430862 },
1567
+ { url = "https://files.pythonhosted.org/packages/a2/bf/c6aaba098e2d04781e8f4f7c0ba3c7aa73d00e4c436bcc0cf059a66691d1/zstandard-0.23.0-cp313-cp313-win_amd64.whl", hash = "sha256:f3513916e8c645d0610815c257cbfd3242adfd5c4cfa78be514e5a3ebb42a41b", size = 495578 },
1568
+ ]
@@ -62,6 +62,9 @@ for s in source_list:
62
62
  elif source.startswith("mysql+pymysql://"):
63
63
  source_type = 'mysql'
64
64
  source_name = source.split('/')[-1].split('?')[0]
65
+ elif source.startswith('clickhouse://'):
66
+ source_type = 'clickhouse'
67
+ source_name = source.split('/')[-1].split('?')[0]
65
68
  elif source.endswith(".duckdb"):
66
69
  source_type = "duckdb"
67
70
  source_name = source.split('/')[-1].split('.')[0]
@@ -62,7 +62,7 @@ def _list_tables(source_id: str):
62
62
  if col.startswith("Tables_in_"):
63
63
  return result[col].to_list()
64
64
 
65
- case "duckdb" | "csv" | "parquet":
65
+ case "duckdb" | "csv" | "parquet" | "clickhouse":
66
66
  result = query_utils.execute_query(source, "SHOW TABLES")
67
67
  return result['name'].to_list()
68
68
 
@@ -96,7 +96,7 @@ def describe_table(source_id: str, table_name: str) -> str:
96
96
  )
97
97
  return result.to_markdown(index=False)
98
98
 
99
- case "mysql" | "duckdb" | "csv" | "parquet":
99
+ case "mysql" | "duckdb" | "csv" | "parquet" | "clickhouse":
100
100
  result = query_utils.execute_query(source,
101
101
  f"DESCRIBE {table_name};"
102
102
  )
@@ -1,3 +1,4 @@
1
+ import clickhouse_connect
1
2
  import duckdb
2
3
  import numpy as np
3
4
  import os
@@ -46,6 +47,11 @@ def execute_query(source: dict, query: str):
46
47
  result = conn.execute(sqlalchemy.text(query))
47
48
  return pd.DataFrame(result)
48
49
 
50
+ case "clickhouse":
51
+ client = clickhouse_connect.get_client(dsn=url)
52
+ client.query('SET readonly=1;')
53
+ return client.query_df(query, use_extended_dtypes=False)
54
+
49
55
  case "duckdb":
50
56
  conn = duckdb.connect(url, read_only=True)
51
57
  return conn.execute(query).df()
@@ -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.4
3
+ Version: 0.1.6
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>
@@ -9,11 +9,14 @@ Project-URL: Issues, https://github.com/kdqed/zaturn/issues
9
9
  Requires-Python: >=3.11
10
10
  Description-Content-Type: text/markdown
11
11
  License-File: LICENSE
12
+ Requires-Dist: clickhouse-connect>=0.8.17
12
13
  Requires-Dist: cryptography>=44.0.2
13
14
  Requires-Dist: duckdb>=1.2.1
14
15
  Requires-Dist: fastmcp>=0.4.1
16
+ Requires-Dist: kaleido==0.2.1
15
17
  Requires-Dist: pandas>=2.2.3
16
18
  Requires-Dist: platformdirs>=4.3.7
19
+ Requires-Dist: plotly[express]>=6.0.1
17
20
  Requires-Dist: psycopg2-binary>=2.9.10
18
21
  Requires-Dist: pyarrow>=19.0.1
19
22
  Requires-Dist: pymysql>=1.1.1
@@ -54,7 +57,7 @@ https://github.com/user-attachments/assets/d42dc433-e5ec-4b3e-bef0-5cfc097396ab
54
57
 
55
58
  ### Multiple Data Sources
56
59
  Zaturn can currently connect to the following data sources:
57
- - SQL Databases: PostgreSQL, SQLite, DuckDB, MySQL
60
+ - SQL Databases: PostgreSQL, SQLite, DuckDB, MySQL, ClickHouse
58
61
  - Files: CSV, Parquet
59
62
 
60
63
  Connectors for more data sources are being added.
@@ -103,6 +106,7 @@ OR add a `sources.txt` to the Zaturn config directory:
103
106
  ```
104
107
  postgresql://username:password@host:port/dbname
105
108
  mysql+pymysql://username:password@host:3306/dbname
109
+ clickhouse://username:password@host:port/dbname
106
110
  sqlite:////full/path/to/sample_dbs/northwind.db
107
111
  /full/path/to/sample_dbs/titanic.parquet
108
112
  /full/path/to/sample_dbs/ny_aq.csv
@@ -158,11 +162,20 @@ Analyst:
158
162
  ```
159
163
  - A native notebook interface
160
164
 
161
- ## Support And Feedback
165
+ ## Help And Feedback
162
166
 
163
167
  [Raise an issue](https://github.com/kdqed/zaturn/issues) or [join the Discord](https://discord.gg/K8mECeVzpQ).
164
168
 
165
169
 
170
+ ## Support The Project
171
+
172
+ If you find Zaturn useful, please support this project by:
173
+ - Starring the Project
174
+ - Spreading the word
175
+ - [Pledging $9/month on Patreon](https://www.patreon.com/kdqed?utm_medium=github&utm_source=join_link&utm_campaign=creatorshare_creator&utm_content=copyLink)
176
+
177
+ Your support will enable me to dedicate more of my time to Zaturn.
178
+
166
179
  ## Example Dataset Credits
167
180
 
168
181
  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,8 +1,9 @@
1
1
  .gitignore
2
+ CHANGELOG.md
2
3
  LICENSE
3
4
  README.md
4
- debug_server.py
5
5
  pyproject.toml
6
+ run_sse_server.py
6
7
  uv.lock
7
8
  brand/logo.png
8
9
  brand/logo.svg
@@ -1,8 +1,11 @@
1
+ clickhouse-connect>=0.8.17
1
2
  cryptography>=44.0.2
2
3
  duckdb>=1.2.1
3
4
  fastmcp>=0.4.1
5
+ kaleido==0.2.1
4
6
  pandas>=2.2.3
5
7
  platformdirs>=4.3.7
8
+ plotly[express]>=6.0.1
6
9
  psycopg2-binary>=2.9.10
7
10
  pyarrow>=19.0.1
8
11
  pymysql>=1.1.1
@@ -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