refactor(sql_explorer): Replace Plotly with SVG rendering for plots
Replaces the Plotly-based graph generation in the SQL Explorer with direct SVG rendering within an HTML template, similar to the exercise progress sparklines. - Modifies `routes/sql_explorer.py` endpoints (`plot_query`, `plot_unsaved_query`) to fetch raw data instead of using pandas/Plotly. - Adds `utils.prepare_svg_plot_data` to process raw SQL results, determine plot type (scatter, line, bar, table), normalize data, and prepare it for SVG. - Creates `templates/partials/sql_explorer/svg_plot.html` to render the SVG plot with axes, ticks, labels, and basic tooltips. - Removes the `generate_plot` function's usage for SQL Explorer and the direct dependency on Plotly for this feature.
This commit is contained in:
@@ -20,7 +20,6 @@
|
||||
<script src="/static/js/sweetalert2@11.js" defer></script>
|
||||
<!-- Mermaid -->
|
||||
<script src="/static/js/mermaid.min.js"></script>
|
||||
<script src="/static/js/plotly-2.35.2.min.js" defer></script>
|
||||
<script>
|
||||
// Initialize Mermaid with startOnLoad set to false
|
||||
mermaid.initialize({
|
||||
|
||||
@@ -10,6 +10,23 @@
|
||||
<div class="prose max-w-none">
|
||||
<p>Updates and changes to the site will be documented here, with the most recent changes listed first.</p>
|
||||
|
||||
<!-- New Entry for SQL Explorer SVG Plots -->
|
||||
<hr class="my-6">
|
||||
<h2 class="text-xl font-semibold mb-2">April 15, 2025</h2>
|
||||
<ul class="list-disc pl-5 space-y-1">
|
||||
<li>Replaced Plotly graph generation in SQL Explorer with direct SVG rendering:</li>
|
||||
<ul class="list-disc pl-5 space-y-1">
|
||||
<li>Updated `plot_query` and `plot_unsaved_query` endpoints in `routes/sql_explorer.py` to fetch raw
|
||||
data.</li>
|
||||
<li>Added `prepare_svg_plot_data` function in `utils.py` to process data and determine plot type
|
||||
(scatter, line, bar, or table fallback).</li>
|
||||
<li>Created `templates/partials/sql_explorer/svg_plot.html` template to render SVG plots with axes
|
||||
and basic tooltips.</li>
|
||||
<li>Removes the need for Plotly library for SQL Explorer plots, reducing dependencies and
|
||||
potentially improving load times.</li>
|
||||
</ul>
|
||||
</ul>
|
||||
|
||||
<!-- New Entry for Dismissible Exercise Graph -->
|
||||
<hr class="my-6">
|
||||
<h2 class="text-xl font-semibold mb-2">April 13, 2025</h2>
|
||||
|
||||
125
templates/partials/sql_explorer/svg_plot.html
Normal file
125
templates/partials/sql_explorer/svg_plot.html
Normal file
@@ -0,0 +1,125 @@
|
||||
{# Basic SVG Plot Template for SQL Explorer #}
|
||||
{% set unique_id = range(1000, 9999) | random %} {# Simple unique ID for elements #}
|
||||
|
||||
<div class="sql-plot-container p-4 border rounded bg-white shadow" id="sql-plot-{{ unique_id }}">
|
||||
<h4 class="text-lg font-semibold text-gray-700 text-center mb-2">{{ title }}</h4>
|
||||
|
||||
{% if plot_type == 'table' %}
|
||||
{# Fallback to rendering a table if plot type is not supported or data is unsuitable #}
|
||||
<div class="overflow-x-auto max-h-96"> {# Limit height and allow scroll #}
|
||||
<table class="min-w-full divide-y divide-gray-200 text-sm">
|
||||
<thead class="bg-gray-50">
|
||||
<tr>
|
||||
{% for col in original_columns %}
|
||||
<th scope="col" class="px-4 py-2 text-left font-medium text-gray-500 uppercase tracking-wider">
|
||||
{{ col }}
|
||||
</th>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white divide-y divide-gray-200">
|
||||
{% for row in original_results %}
|
||||
<tr>
|
||||
{% for col in original_columns %}
|
||||
<td class="px-4 py-2 whitespace-nowrap">
|
||||
{{ row[col] }}
|
||||
</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr>
|
||||
<td colspan="{{ original_columns|length }}" class="px-4 py-2 text-center text-gray-500">No data
|
||||
available.</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{% else %}
|
||||
{# SVG Plot Area #}
|
||||
<div class="relative" _="
|
||||
on mouseover from .plot-point-{{ unique_id }}
|
||||
get event.target @data-tooltip
|
||||
if it
|
||||
put it into #tooltip-{{ unique_id }}
|
||||
remove .hidden from #tooltip-{{ unique_id }}
|
||||
end
|
||||
on mouseout from .plot-point-{{ unique_id }}
|
||||
add .hidden to #tooltip-{{ unique_id }}
|
||||
">
|
||||
|
||||
{# Tooltip Element #}
|
||||
<div id="tooltip-{{ unique_id }}"
|
||||
class="absolute top-0 left-0 hidden bg-gray-800 text-white text-xs p-1 rounded shadow-lg z-10 pointer-events-none">
|
||||
Tooltip
|
||||
</div>
|
||||
|
||||
<svg viewBox="0 0 {{ vb_width }} {{ vb_height }}" preserveAspectRatio="xMidYMid meet" class="w-full h-auto">
|
||||
{# Draw Axes #}
|
||||
<g class="axes" stroke="#6b7280" stroke-width="1">
|
||||
{# Y Axis #}
|
||||
<line x1="{{ margin.left }}" y1="{{ margin.top }}" x2="{{ margin.left }}"
|
||||
y2="{{ vb_height - margin.bottom }}"></line>
|
||||
{# X Axis #}
|
||||
<line x1="{{ margin.left }}" y1="{{ vb_height - margin.bottom }}" x2="{{ vb_width - margin.right }}"
|
||||
y2="{{ vb_height - margin.bottom }}"></line>
|
||||
</g>
|
||||
|
||||
{# Draw Ticks and Grid Lines #}
|
||||
<g class="ticks" font-size="10" fill="#6b7280" text-anchor="middle">
|
||||
{# Y Ticks #}
|
||||
{% for tick in y_ticks %}
|
||||
<line x1="{{ margin.left - 5 }}" y1="{{ tick.position }}" x2="{{ vb_width - margin.right }}"
|
||||
y2="{{ tick.position }}" stroke="#e5e7eb" stroke-width="0.5"></line> {# Grid line #}
|
||||
<text x="{{ margin.left - 8 }}" y="{{ tick.position + 3 }}" text-anchor="end">{{ tick.label }}</text>
|
||||
{% endfor %}
|
||||
{# X Ticks #}
|
||||
{% for tick in x_ticks %}
|
||||
<line x1="{{ tick.position }}" y1="{{ margin.top }}" x2="{{ tick.position }}"
|
||||
y2="{{ vb_height - margin.bottom + 5 }}" stroke="#e5e7eb" stroke-width="0.5"></line> {# Grid line #}
|
||||
<text x="{{ tick.position }}" y="{{ vb_height - margin.bottom + 15 }}">{{ tick.label }}</text>
|
||||
{% endfor %}
|
||||
</g>
|
||||
|
||||
{# Draw Axis Labels #}
|
||||
<g class="axis-labels" font-size="12" fill="#374151" text-anchor="middle">
|
||||
{# Y Axis Label #}
|
||||
<text
|
||||
transform="translate({{ margin.left / 2 - 5 }}, {{ (vb_height - margin.bottom + margin.top) / 2 }}) rotate(-90)">{{
|
||||
y_axis_label }}</text>
|
||||
{# X Axis Label #}
|
||||
<text x="{{ (vb_width - margin.right + margin.left) / 2 }}"
|
||||
y="{{ vb_height - margin.bottom / 2 + 10 }}">{{ x_axis_label }}</text>
|
||||
</g>
|
||||
|
||||
{# Plot Data Points/Bars #}
|
||||
{% for plot in plots %}
|
||||
<g class="plot-series-{{ loop.index }}" fill="{{ plot.color }}" stroke="{{ plot.color }}">
|
||||
{% if plot_type == 'scatter' %}
|
||||
{% for p in plot.points %}
|
||||
<circle cx="{{ p.x }}" cy="{{ p.y }}" r="3" class="plot-point-{{ unique_id }}"
|
||||
data-tooltip="{{ p.original | tojson | escape }}" />
|
||||
{% endfor %}
|
||||
{% elif plot_type == 'line' %}
|
||||
<path
|
||||
d="{% for p in plot.points %}{% if loop.first %}M{% else %}L{% endif %}{{ p.x }} {{ p.y }}{% endfor %}"
|
||||
fill="none" stroke-width="1.5" />
|
||||
{% for p in plot.points %}
|
||||
<circle cx="{{ p.x }}" cy="{{ p.y }}" r="2.5" class="plot-point-{{ unique_id }}"
|
||||
data-tooltip="{{ p.original | tojson | escape }}" />
|
||||
{% endfor %}
|
||||
{% elif plot_type == 'bar' %}
|
||||
{% set bar_w = bar_width | default(10) %}
|
||||
{% for p in plot.points %}
|
||||
<rect x="{{ p.x - bar_w / 2 }}" y="{{ p.y }}" width="{{ bar_w }}"
|
||||
height="{{ (vb_height - margin.bottom) - p.y }}" stroke-width="0.5"
|
||||
class="plot-point-{{ unique_id }}" data-tooltip="{{ p.original | tojson | escape }}" />
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</g>
|
||||
{% endfor %}
|
||||
</svg>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
Reference in New Issue
Block a user