@quarri/claude-data-tools 1.2.7 → 1.3.0
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.
- package/package.json +1 -1
- package/skills/quarri-chart/SKILL.md +409 -234
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Generate interactive Plotly charts
|
|
2
|
+
description: Generate interactive Plotly charts as HTML files
|
|
3
3
|
globs:
|
|
4
4
|
alwaysApply: false
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# /quarri-chart - Interactive Chart Generation
|
|
8
8
|
|
|
9
|
-
Generate data visualizations as interactive **Plotly.js**
|
|
9
|
+
Generate data visualizations as interactive **Plotly.js** HTML files that open in the browser.
|
|
10
10
|
|
|
11
|
-
**IMPORTANT**: Always
|
|
11
|
+
**IMPORTANT**: Always generate HTML files with Plotly.js from CDN. Do NOT generate React components, inline JSON, or any other format.
|
|
12
12
|
|
|
13
13
|
## When to Use
|
|
14
14
|
|
|
@@ -33,7 +33,7 @@ Use `/quarri-chart` when users want visualizations:
|
|
|
33
33
|
|
|
34
34
|
```sql
|
|
35
35
|
-- BAD: Returns potentially thousands of products
|
|
36
|
-
SELECT product_name, SUM(sales) FROM
|
|
36
|
+
SELECT product_name, SUM(sales) FROM quarri.schema GROUP BY product_name
|
|
37
37
|
|
|
38
38
|
-- GOOD: Top 10 + Other
|
|
39
39
|
WITH ranked AS (
|
|
@@ -54,7 +54,7 @@ ORDER BY total_sales DESC
|
|
|
54
54
|
|
|
55
55
|
```sql
|
|
56
56
|
-- BAD: Daily data for 5 years = 1800+ points
|
|
57
|
-
SELECT order_date, SUM(sales) FROM
|
|
57
|
+
SELECT order_date, SUM(sales) FROM quarri.schema GROUP BY order_date
|
|
58
58
|
|
|
59
59
|
-- GOOD: Aggregate to months
|
|
60
60
|
SELECT DATE_TRUNC('month', order_date) as month, SUM(sales) as total_sales
|
|
@@ -66,6 +66,9 @@ ORDER BY month
|
|
|
66
66
|
## Primary Workflow
|
|
67
67
|
|
|
68
68
|
### Step 1: Query the data (with appropriate aggregation)
|
|
69
|
+
|
|
70
|
+
Use `quarri_execute_sql` to get the data:
|
|
71
|
+
|
|
69
72
|
```sql
|
|
70
73
|
SELECT category, SUM(sales) as total_sales
|
|
71
74
|
FROM quarri.schema
|
|
@@ -74,31 +77,386 @@ ORDER BY total_sales DESC
|
|
|
74
77
|
LIMIT 20
|
|
75
78
|
```
|
|
76
79
|
|
|
77
|
-
### Step 2:
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
80
|
+
### Step 2: Generate HTML file with Plotly
|
|
81
|
+
|
|
82
|
+
Write an HTML file that:
|
|
83
|
+
1. Loads Plotly.js from CDN
|
|
84
|
+
2. Embeds the query results as a JavaScript data array
|
|
85
|
+
3. Builds Plotly traces from the data
|
|
86
|
+
4. Renders the chart
|
|
87
|
+
|
|
88
|
+
### Step 3: Open in browser
|
|
89
|
+
|
|
90
|
+
Use `open <filepath>` (macOS) or `xdg-open <filepath>` (Linux) to display the chart.
|
|
91
|
+
|
|
92
|
+
## HTML Template
|
|
93
|
+
|
|
94
|
+
Use this template structure for all charts:
|
|
95
|
+
|
|
96
|
+
```html
|
|
97
|
+
<!DOCTYPE html>
|
|
98
|
+
<html lang="en">
|
|
99
|
+
<head>
|
|
100
|
+
<meta charset="UTF-8">
|
|
101
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
102
|
+
<title>CHART_TITLE</title>
|
|
103
|
+
<script src="https://cdn.plot.ly/plotly-2.27.0.min.js"></script>
|
|
104
|
+
<style>
|
|
105
|
+
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; margin: 0; padding: 20px; background: #f8fafc; }
|
|
106
|
+
#chart { width: 100%; height: 600px; background: white; border-radius: 8px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
|
|
107
|
+
h1 { color: #1e293b; font-size: 1.5rem; margin-bottom: 16px; }
|
|
108
|
+
</style>
|
|
109
|
+
</head>
|
|
110
|
+
<body>
|
|
111
|
+
<h1>CHART_TITLE</h1>
|
|
112
|
+
<div id="chart"></div>
|
|
113
|
+
<script>
|
|
114
|
+
// Data from query results
|
|
115
|
+
const data = DATA_ARRAY;
|
|
116
|
+
|
|
117
|
+
// Build Plotly traces
|
|
118
|
+
const traces = [TRACE_CONFIG];
|
|
119
|
+
|
|
120
|
+
// Layout configuration
|
|
121
|
+
const layout = LAYOUT_CONFIG;
|
|
122
|
+
|
|
123
|
+
// Render chart
|
|
124
|
+
Plotly.newPlot('chart', traces, layout, { responsive: true });
|
|
125
|
+
</script>
|
|
126
|
+
</body>
|
|
127
|
+
</html>
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Complete Examples
|
|
131
|
+
|
|
132
|
+
### Bar Chart Example
|
|
133
|
+
|
|
134
|
+
**Query:**
|
|
135
|
+
```sql
|
|
136
|
+
SELECT category, SUM(sales) as total_sales
|
|
137
|
+
FROM quarri.schema
|
|
138
|
+
GROUP BY category
|
|
139
|
+
ORDER BY total_sales DESC
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
**HTML file to write:**
|
|
143
|
+
```html
|
|
144
|
+
<!DOCTYPE html>
|
|
145
|
+
<html lang="en">
|
|
146
|
+
<head>
|
|
147
|
+
<meta charset="UTF-8">
|
|
148
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
149
|
+
<title>Sales by Category</title>
|
|
150
|
+
<script src="https://cdn.plot.ly/plotly-2.27.0.min.js"></script>
|
|
151
|
+
<style>
|
|
152
|
+
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; margin: 0; padding: 20px; background: #f8fafc; }
|
|
153
|
+
#chart { width: 100%; height: 600px; background: white; border-radius: 8px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
|
|
154
|
+
h1 { color: #1e293b; font-size: 1.5rem; margin-bottom: 16px; }
|
|
155
|
+
</style>
|
|
156
|
+
</head>
|
|
157
|
+
<body>
|
|
158
|
+
<h1>Sales by Category</h1>
|
|
159
|
+
<div id="chart"></div>
|
|
160
|
+
<script>
|
|
161
|
+
const data = [
|
|
162
|
+
{ category: "Technology", total_sales: 836154.03 },
|
|
163
|
+
{ category: "Furniture", total_sales: 741999.80 },
|
|
164
|
+
{ category: "Office Supplies", total_sales: 719047.03 }
|
|
165
|
+
];
|
|
166
|
+
|
|
167
|
+
const traces = [{
|
|
168
|
+
x: data.map(d => d.category),
|
|
169
|
+
y: data.map(d => d.total_sales),
|
|
170
|
+
type: 'bar',
|
|
171
|
+
marker: { color: '#4F46E5' },
|
|
172
|
+
text: data.map(d => '$' + (d.total_sales / 1000).toFixed(0) + 'K'),
|
|
173
|
+
textposition: 'auto'
|
|
174
|
+
}];
|
|
175
|
+
|
|
176
|
+
const layout = {
|
|
177
|
+
xaxis: { title: 'Category' },
|
|
178
|
+
yaxis: { title: 'Sales ($)', tickformat: '$,.0f' },
|
|
179
|
+
margin: { t: 20, r: 20, b: 60, l: 80 }
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
Plotly.newPlot('chart', traces, layout, { responsive: true });
|
|
183
|
+
</script>
|
|
184
|
+
</body>
|
|
185
|
+
</html>
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Line Chart Example (Time Series)
|
|
189
|
+
|
|
190
|
+
**Query:**
|
|
191
|
+
```sql
|
|
192
|
+
SELECT DATE_TRUNC('month', order_date) as month, SUM(sales) as total_sales
|
|
193
|
+
FROM quarri.schema
|
|
194
|
+
GROUP BY DATE_TRUNC('month', order_date)
|
|
195
|
+
ORDER BY month
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
**HTML file to write:**
|
|
199
|
+
```html
|
|
200
|
+
<!DOCTYPE html>
|
|
201
|
+
<html lang="en">
|
|
202
|
+
<head>
|
|
203
|
+
<meta charset="UTF-8">
|
|
204
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
205
|
+
<title>Monthly Sales Trend</title>
|
|
206
|
+
<script src="https://cdn.plot.ly/plotly-2.27.0.min.js"></script>
|
|
207
|
+
<style>
|
|
208
|
+
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; margin: 0; padding: 20px; background: #f8fafc; }
|
|
209
|
+
#chart { width: 100%; height: 600px; background: white; border-radius: 8px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
|
|
210
|
+
h1 { color: #1e293b; font-size: 1.5rem; margin-bottom: 16px; }
|
|
211
|
+
</style>
|
|
212
|
+
</head>
|
|
213
|
+
<body>
|
|
214
|
+
<h1>Monthly Sales Trend</h1>
|
|
215
|
+
<div id="chart"></div>
|
|
216
|
+
<script>
|
|
217
|
+
const data = [
|
|
218
|
+
{ month: "2021-01-01", total_sales: 94925.57 },
|
|
219
|
+
{ month: "2021-02-01", total_sales: 59751.25 },
|
|
220
|
+
{ month: "2021-03-01", total_sales: 115973.79 }
|
|
221
|
+
// ... more data points
|
|
222
|
+
];
|
|
223
|
+
|
|
224
|
+
const traces = [{
|
|
225
|
+
x: data.map(d => d.month),
|
|
226
|
+
y: data.map(d => d.total_sales),
|
|
227
|
+
type: 'scatter',
|
|
228
|
+
mode: 'lines+markers',
|
|
229
|
+
line: { color: '#4F46E5', width: 3 },
|
|
230
|
+
marker: { size: 8 }
|
|
231
|
+
}];
|
|
232
|
+
|
|
233
|
+
const layout = {
|
|
234
|
+
xaxis: { title: 'Month' },
|
|
235
|
+
yaxis: { title: 'Sales ($)', tickformat: '$,.0f' },
|
|
236
|
+
margin: { t: 20, r: 20, b: 60, l: 80 }
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
Plotly.newPlot('chart', traces, layout, { responsive: true });
|
|
240
|
+
</script>
|
|
241
|
+
</body>
|
|
242
|
+
</html>
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### Multi-Series Line Chart
|
|
246
|
+
|
|
247
|
+
**Query:**
|
|
248
|
+
```sql
|
|
249
|
+
SELECT DATE_TRUNC('month', order_date) as month, category, SUM(sales) as total_sales
|
|
250
|
+
FROM quarri.schema
|
|
251
|
+
GROUP BY DATE_TRUNC('month', order_date), category
|
|
252
|
+
ORDER BY month, category
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
**HTML file to write:**
|
|
256
|
+
```html
|
|
257
|
+
<!DOCTYPE html>
|
|
258
|
+
<html lang="en">
|
|
259
|
+
<head>
|
|
260
|
+
<meta charset="UTF-8">
|
|
261
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
262
|
+
<title>Sales Trend by Category</title>
|
|
263
|
+
<script src="https://cdn.plot.ly/plotly-2.27.0.min.js"></script>
|
|
264
|
+
<style>
|
|
265
|
+
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; margin: 0; padding: 20px; background: #f8fafc; }
|
|
266
|
+
#chart { width: 100%; height: 600px; background: white; border-radius: 8px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
|
|
267
|
+
h1 { color: #1e293b; font-size: 1.5rem; margin-bottom: 16px; }
|
|
268
|
+
</style>
|
|
269
|
+
</head>
|
|
270
|
+
<body>
|
|
271
|
+
<h1>Sales Trend by Category</h1>
|
|
272
|
+
<div id="chart"></div>
|
|
273
|
+
<script>
|
|
274
|
+
const data = [
|
|
275
|
+
{ month: "2021-01-01", category: "Furniture", total_sales: 28262.03 },
|
|
276
|
+
{ month: "2021-01-01", category: "Office Supplies", total_sales: 21879.28 },
|
|
277
|
+
{ month: "2021-01-01", category: "Technology", total_sales: 44784.26 }
|
|
278
|
+
// ... more data points
|
|
279
|
+
];
|
|
280
|
+
|
|
281
|
+
// Group by category
|
|
282
|
+
const categories = [...new Set(data.map(d => d.category))];
|
|
283
|
+
const colors = ['#4F46E5', '#10B981', '#F59E0B', '#EF4444', '#8B5CF6'];
|
|
284
|
+
|
|
285
|
+
const traces = categories.map((cat, i) => {
|
|
286
|
+
const catData = data.filter(d => d.category === cat);
|
|
287
|
+
return {
|
|
288
|
+
x: catData.map(d => d.month),
|
|
289
|
+
y: catData.map(d => d.total_sales),
|
|
290
|
+
name: cat,
|
|
291
|
+
type: 'scatter',
|
|
292
|
+
mode: 'lines+markers',
|
|
293
|
+
line: { color: colors[i % colors.length], width: 2 }
|
|
294
|
+
};
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
const layout = {
|
|
298
|
+
xaxis: { title: 'Month' },
|
|
299
|
+
yaxis: { title: 'Sales ($)', tickformat: '$,.0f' },
|
|
300
|
+
margin: { t: 20, r: 20, b: 60, l: 80 },
|
|
301
|
+
legend: { orientation: 'h', y: -0.15 }
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
Plotly.newPlot('chart', traces, layout, { responsive: true });
|
|
305
|
+
</script>
|
|
306
|
+
</body>
|
|
307
|
+
</html>
|
|
96
308
|
```
|
|
97
309
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
310
|
+
### Horizontal Bar Chart (Many Categories)
|
|
311
|
+
|
|
312
|
+
**HTML file to write:**
|
|
313
|
+
```html
|
|
314
|
+
<!DOCTYPE html>
|
|
315
|
+
<html lang="en">
|
|
316
|
+
<head>
|
|
317
|
+
<meta charset="UTF-8">
|
|
318
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
319
|
+
<title>Top 10 Products by Sales</title>
|
|
320
|
+
<script src="https://cdn.plot.ly/plotly-2.27.0.min.js"></script>
|
|
321
|
+
<style>
|
|
322
|
+
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; margin: 0; padding: 20px; background: #f8fafc; }
|
|
323
|
+
#chart { width: 100%; height: 600px; background: white; border-radius: 8px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
|
|
324
|
+
h1 { color: #1e293b; font-size: 1.5rem; margin-bottom: 16px; }
|
|
325
|
+
</style>
|
|
326
|
+
</head>
|
|
327
|
+
<body>
|
|
328
|
+
<h1>Top 10 Products by Sales</h1>
|
|
329
|
+
<div id="chart"></div>
|
|
330
|
+
<script>
|
|
331
|
+
const data = [
|
|
332
|
+
{ product_name: "Canon imageCLASS 2200", total_sales: 61599.82 },
|
|
333
|
+
{ product_name: "Fellowes PB500", total_sales: 27453.38 }
|
|
334
|
+
// ... more products
|
|
335
|
+
];
|
|
336
|
+
|
|
337
|
+
// Reverse for horizontal bar (top item at top)
|
|
338
|
+
const sorted = [...data].reverse();
|
|
339
|
+
|
|
340
|
+
const traces = [{
|
|
341
|
+
y: sorted.map(d => d.product_name),
|
|
342
|
+
x: sorted.map(d => d.total_sales),
|
|
343
|
+
type: 'bar',
|
|
344
|
+
orientation: 'h',
|
|
345
|
+
marker: { color: '#4F46E5' }
|
|
346
|
+
}];
|
|
347
|
+
|
|
348
|
+
const layout = {
|
|
349
|
+
xaxis: { title: 'Sales ($)', tickformat: '$,.0f' },
|
|
350
|
+
margin: { t: 20, r: 20, b: 60, l: 200 }
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
Plotly.newPlot('chart', traces, layout, { responsive: true });
|
|
354
|
+
</script>
|
|
355
|
+
</body>
|
|
356
|
+
</html>
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
### Pie/Donut Chart
|
|
360
|
+
|
|
361
|
+
**HTML file to write:**
|
|
362
|
+
```html
|
|
363
|
+
<!DOCTYPE html>
|
|
364
|
+
<html lang="en">
|
|
365
|
+
<head>
|
|
366
|
+
<meta charset="UTF-8">
|
|
367
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
368
|
+
<title>Sales Distribution</title>
|
|
369
|
+
<script src="https://cdn.plot.ly/plotly-2.27.0.min.js"></script>
|
|
370
|
+
<style>
|
|
371
|
+
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; margin: 0; padding: 20px; background: #f8fafc; }
|
|
372
|
+
#chart { width: 100%; height: 600px; background: white; border-radius: 8px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
|
|
373
|
+
h1 { color: #1e293b; font-size: 1.5rem; margin-bottom: 16px; }
|
|
374
|
+
</style>
|
|
375
|
+
</head>
|
|
376
|
+
<body>
|
|
377
|
+
<h1>Sales Distribution by Category</h1>
|
|
378
|
+
<div id="chart"></div>
|
|
379
|
+
<script>
|
|
380
|
+
const data = [
|
|
381
|
+
{ category: "Technology", total_sales: 836154.03 },
|
|
382
|
+
{ category: "Furniture", total_sales: 741999.80 },
|
|
383
|
+
{ category: "Office Supplies", total_sales: 719047.03 }
|
|
384
|
+
];
|
|
385
|
+
|
|
386
|
+
const traces = [{
|
|
387
|
+
labels: data.map(d => d.category),
|
|
388
|
+
values: data.map(d => d.total_sales),
|
|
389
|
+
type: 'pie',
|
|
390
|
+
hole: 0.4,
|
|
391
|
+
marker: { colors: ['#4F46E5', '#10B981', '#F59E0B'] },
|
|
392
|
+
textinfo: 'label+percent'
|
|
393
|
+
}];
|
|
394
|
+
|
|
395
|
+
const layout = {
|
|
396
|
+
margin: { t: 20, r: 20, b: 20, l: 20 },
|
|
397
|
+
showlegend: true
|
|
398
|
+
};
|
|
399
|
+
|
|
400
|
+
Plotly.newPlot('chart', traces, layout, { responsive: true });
|
|
401
|
+
</script>
|
|
402
|
+
</body>
|
|
403
|
+
</html>
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
### Grouped Bar Chart
|
|
407
|
+
|
|
408
|
+
**HTML file to write:**
|
|
409
|
+
```html
|
|
410
|
+
<!DOCTYPE html>
|
|
411
|
+
<html lang="en">
|
|
412
|
+
<head>
|
|
413
|
+
<meta charset="UTF-8">
|
|
414
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
415
|
+
<title>Year over Year Comparison</title>
|
|
416
|
+
<script src="https://cdn.plot.ly/plotly-2.27.0.min.js"></script>
|
|
417
|
+
<style>
|
|
418
|
+
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; margin: 0; padding: 20px; background: #f8fafc; }
|
|
419
|
+
#chart { width: 100%; height: 600px; background: white; border-radius: 8px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
|
|
420
|
+
h1 { color: #1e293b; font-size: 1.5rem; margin-bottom: 16px; }
|
|
421
|
+
</style>
|
|
422
|
+
</head>
|
|
423
|
+
<body>
|
|
424
|
+
<h1>Year over Year Comparison</h1>
|
|
425
|
+
<div id="chart"></div>
|
|
426
|
+
<script>
|
|
427
|
+
const data = [
|
|
428
|
+
{ year: 2023, quarter: "Q1", sales: 120000 },
|
|
429
|
+
{ year: 2023, quarter: "Q2", sales: 150000 },
|
|
430
|
+
{ year: 2024, quarter: "Q1", sales: 140000 },
|
|
431
|
+
{ year: 2024, quarter: "Q2", sales: 165000 }
|
|
432
|
+
];
|
|
433
|
+
|
|
434
|
+
const years = [...new Set(data.map(d => d.year))];
|
|
435
|
+
const colors = ['#4F46E5', '#10B981'];
|
|
436
|
+
|
|
437
|
+
const traces = years.map((year, i) => {
|
|
438
|
+
const yearData = data.filter(d => d.year === year);
|
|
439
|
+
return {
|
|
440
|
+
x: yearData.map(d => d.quarter),
|
|
441
|
+
y: yearData.map(d => d.sales),
|
|
442
|
+
name: String(year),
|
|
443
|
+
type: 'bar',
|
|
444
|
+
marker: { color: colors[i] }
|
|
445
|
+
};
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
const layout = {
|
|
449
|
+
barmode: 'group',
|
|
450
|
+
xaxis: { title: 'Quarter' },
|
|
451
|
+
yaxis: { title: 'Sales ($)', tickformat: '$,.0f' },
|
|
452
|
+
margin: { t: 20, r: 20, b: 60, l: 80 }
|
|
453
|
+
};
|
|
454
|
+
|
|
455
|
+
Plotly.newPlot('chart', traces, layout, { responsive: true });
|
|
456
|
+
</script>
|
|
457
|
+
</body>
|
|
458
|
+
</html>
|
|
459
|
+
```
|
|
102
460
|
|
|
103
461
|
## Chart Type Selection
|
|
104
462
|
|
|
@@ -129,158 +487,6 @@ START: What is the primary analysis goal?
|
|
|
129
487
|
└─→ Two numeric → SCATTER PLOT
|
|
130
488
|
```
|
|
131
489
|
|
|
132
|
-
## Plotly Templates
|
|
133
|
-
|
|
134
|
-
### Bar Chart
|
|
135
|
-
```javascript
|
|
136
|
-
{
|
|
137
|
-
"data": [{
|
|
138
|
-
"x": ["Category A", "Category B", "Category C"],
|
|
139
|
-
"y": [450000, 380000, 290000],
|
|
140
|
-
"type": "bar",
|
|
141
|
-
"marker": {
|
|
142
|
-
"color": ["#4F46E5", "#7C3AED", "#A78BFA"]
|
|
143
|
-
},
|
|
144
|
-
"text": ["$450K", "$380K", "$290K"],
|
|
145
|
-
"textposition": "auto"
|
|
146
|
-
}],
|
|
147
|
-
"layout": {
|
|
148
|
-
"title": "Sales by Category",
|
|
149
|
-
"xaxis": { "title": "Category" },
|
|
150
|
-
"yaxis": { "title": "Sales ($)", "tickformat": "$,.0f" }
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
### Horizontal Bar (many categories)
|
|
156
|
-
```javascript
|
|
157
|
-
{
|
|
158
|
-
"data": [{
|
|
159
|
-
"y": ["Product A", "Product B", "Product C", "Product D", "Product E"],
|
|
160
|
-
"x": [85000, 72000, 65000, 58000, 45000],
|
|
161
|
-
"type": "bar",
|
|
162
|
-
"orientation": "h",
|
|
163
|
-
"marker": { "color": "#4F46E5" }
|
|
164
|
-
}],
|
|
165
|
-
"layout": {
|
|
166
|
-
"title": "Top Products by Revenue",
|
|
167
|
-
"xaxis": { "title": "Revenue ($)", "tickformat": "$,.0f" },
|
|
168
|
-
"margin": { "l": 120 }
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
### Line Chart (time series)
|
|
174
|
-
```javascript
|
|
175
|
-
{
|
|
176
|
-
"data": [{
|
|
177
|
-
"x": ["2024-01", "2024-02", "2024-03", "2024-04", "2024-05", "2024-06"],
|
|
178
|
-
"y": [120000, 135000, 128000, 145000, 160000, 175000],
|
|
179
|
-
"type": "scatter",
|
|
180
|
-
"mode": "lines+markers",
|
|
181
|
-
"line": { "color": "#4F46E5", "width": 3 },
|
|
182
|
-
"marker": { "size": 8 }
|
|
183
|
-
}],
|
|
184
|
-
"layout": {
|
|
185
|
-
"title": "Monthly Revenue Trend",
|
|
186
|
-
"xaxis": { "title": "Month" },
|
|
187
|
-
"yaxis": { "title": "Revenue ($)", "tickformat": "$,.0f" }
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
### Multi-Line Chart
|
|
193
|
-
```javascript
|
|
194
|
-
{
|
|
195
|
-
"data": [
|
|
196
|
-
{
|
|
197
|
-
"x": ["Jan", "Feb", "Mar", "Apr"],
|
|
198
|
-
"y": [100, 120, 115, 140],
|
|
199
|
-
"name": "Product A",
|
|
200
|
-
"type": "scatter",
|
|
201
|
-
"mode": "lines+markers"
|
|
202
|
-
},
|
|
203
|
-
{
|
|
204
|
-
"x": ["Jan", "Feb", "Mar", "Apr"],
|
|
205
|
-
"y": [80, 95, 110, 120],
|
|
206
|
-
"name": "Product B",
|
|
207
|
-
"type": "scatter",
|
|
208
|
-
"mode": "lines+markers"
|
|
209
|
-
}
|
|
210
|
-
],
|
|
211
|
-
"layout": {
|
|
212
|
-
"title": "Product Comparison",
|
|
213
|
-
"xaxis": { "title": "Month" },
|
|
214
|
-
"yaxis": { "title": "Sales" }
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
```
|
|
218
|
-
|
|
219
|
-
### Pie/Donut Chart
|
|
220
|
-
```javascript
|
|
221
|
-
{
|
|
222
|
-
"data": [{
|
|
223
|
-
"labels": ["Technology", "Furniture", "Office Supplies"],
|
|
224
|
-
"values": [5471124, 4730801, 4144724],
|
|
225
|
-
"type": "pie",
|
|
226
|
-
"hole": 0.4,
|
|
227
|
-
"marker": {
|
|
228
|
-
"colors": ["#4F46E5", "#10B981", "#F59E0B"]
|
|
229
|
-
},
|
|
230
|
-
"textinfo": "label+percent"
|
|
231
|
-
}],
|
|
232
|
-
"layout": {
|
|
233
|
-
"title": "Sales Distribution by Category"
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
```
|
|
237
|
-
|
|
238
|
-
### Scatter Plot
|
|
239
|
-
```javascript
|
|
240
|
-
{
|
|
241
|
-
"data": [{
|
|
242
|
-
"x": [/* x values */],
|
|
243
|
-
"y": [/* y values */],
|
|
244
|
-
"mode": "markers",
|
|
245
|
-
"type": "scatter",
|
|
246
|
-
"marker": {
|
|
247
|
-
"size": 10,
|
|
248
|
-
"color": "#4F46E5",
|
|
249
|
-
"opacity": 0.7
|
|
250
|
-
}
|
|
251
|
-
}],
|
|
252
|
-
"layout": {
|
|
253
|
-
"title": "Correlation Analysis",
|
|
254
|
-
"xaxis": { "title": "Variable X" },
|
|
255
|
-
"yaxis": { "title": "Variable Y" }
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
```
|
|
259
|
-
|
|
260
|
-
### Grouped Bar Chart
|
|
261
|
-
```javascript
|
|
262
|
-
{
|
|
263
|
-
"data": [
|
|
264
|
-
{
|
|
265
|
-
"x": ["Q1", "Q2", "Q3", "Q4"],
|
|
266
|
-
"y": [120, 150, 180, 200],
|
|
267
|
-
"name": "2023",
|
|
268
|
-
"type": "bar"
|
|
269
|
-
},
|
|
270
|
-
{
|
|
271
|
-
"x": ["Q1", "Q2", "Q3", "Q4"],
|
|
272
|
-
"y": [140, 165, 195, 220],
|
|
273
|
-
"name": "2024",
|
|
274
|
-
"type": "bar"
|
|
275
|
-
}
|
|
276
|
-
],
|
|
277
|
-
"layout": {
|
|
278
|
-
"title": "Year over Year Comparison",
|
|
279
|
-
"barmode": "group"
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
```
|
|
283
|
-
|
|
284
490
|
## Color Palettes
|
|
285
491
|
|
|
286
492
|
```javascript
|
|
@@ -313,41 +519,36 @@ tickformat: '.1%' // 45.2%
|
|
|
313
519
|
tickformat: '~s' // 1.2M, 3.4K
|
|
314
520
|
```
|
|
315
521
|
|
|
316
|
-
##
|
|
522
|
+
## File Output
|
|
317
523
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
"title": "Chart Title",
|
|
324
|
-
"plotly": {
|
|
325
|
-
"data": [/* Plotly data traces */],
|
|
326
|
-
"layout": {/* Plotly layout config */}
|
|
327
|
-
}
|
|
328
|
-
}
|
|
524
|
+
**Always write to a descriptive filename:**
|
|
525
|
+
```
|
|
526
|
+
/tmp/quarri-chart-sales-by-category.html
|
|
527
|
+
/tmp/quarri-chart-monthly-trend.html
|
|
528
|
+
/tmp/quarri-chart-top-products.html
|
|
329
529
|
```
|
|
330
530
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
531
|
+
**Then open in browser:**
|
|
532
|
+
```bash
|
|
533
|
+
# macOS
|
|
534
|
+
open /tmp/quarri-chart-sales-by-category.html
|
|
334
535
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
```
|
|
338
|
-
https://quickchart.io/chart?c={type:'bar',data:{labels:['A','B','C'],datasets:[{data:[10,20,15]}]}}
|
|
536
|
+
# Linux
|
|
537
|
+
xdg-open /tmp/quarri-chart-sales-by-category.html
|
|
339
538
|
```
|
|
340
539
|
|
|
341
|
-
|
|
342
|
-
When user is in a terminal-only environment:
|
|
343
|
-
```
|
|
344
|
-
Sales by Category
|
|
540
|
+
## Validation Checklist
|
|
345
541
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
542
|
+
Before generating a chart, verify:
|
|
543
|
+
|
|
544
|
+
1. **Format**: Generating an HTML file with Plotly.js from CDN
|
|
545
|
+
2. **Data embedded**: Query results are in a `const data = [...]` JavaScript array
|
|
546
|
+
3. **Data points**: ≤ 500 points total (aggregate or sample if more)
|
|
547
|
+
4. **Categories**: ≤ 20 categories (use Top N + Other if more)
|
|
548
|
+
5. **Time granularity**: Appropriate for date range (don't show daily for multi-year)
|
|
549
|
+
6. **Aggregation**: Never chart raw transactions - always GROUP BY
|
|
550
|
+
7. **File written**: HTML file saved to /tmp/quarri-chart-*.html
|
|
551
|
+
8. **Browser opened**: File opened with `open` or `xdg-open`
|
|
351
552
|
|
|
352
553
|
## Integration
|
|
353
554
|
|
|
@@ -355,29 +556,3 @@ Charts work best when combined with:
|
|
|
355
556
|
- `/quarri-query`: Get data first, then visualize
|
|
356
557
|
- `/quarri-analyze`: Called as part of full analysis pipeline
|
|
357
558
|
- `/quarri-insights`: Visual support for statistical findings
|
|
358
|
-
|
|
359
|
-
## Validation Checklist
|
|
360
|
-
|
|
361
|
-
Before generating a chart, verify:
|
|
362
|
-
|
|
363
|
-
1. **Library**: Using Plotly.js format (NOT React, Recharts, Chart.js, D3, or others)
|
|
364
|
-
2. **Data points**: ≤ 500 points total (aggregate or sample if more)
|
|
365
|
-
3. **Categories**: ≤ 20 categories (use Top N + Other if more)
|
|
366
|
-
4. **Time granularity**: Appropriate for date range (don't show daily for multi-year)
|
|
367
|
-
5. **Aggregation**: Never chart raw transactions - always GROUP BY
|
|
368
|
-
|
|
369
|
-
**Output format must be:**
|
|
370
|
-
```json
|
|
371
|
-
{
|
|
372
|
-
"data": [{ /* Plotly trace */ }],
|
|
373
|
-
"layout": { /* Plotly layout */ }
|
|
374
|
-
}
|
|
375
|
-
```
|
|
376
|
-
|
|
377
|
-
**NOT:**
|
|
378
|
-
```jsx
|
|
379
|
-
// WRONG - Do not generate React components
|
|
380
|
-
<BarChart data={data}>
|
|
381
|
-
<Bar dataKey="value" />
|
|
382
|
-
</BarChart>
|
|
383
|
-
```
|