Add chart loading management
This commit is contained in:
parent
3135dbe378
commit
5d2546ad60
3 changed files with 97 additions and 38 deletions
|
@ -58,14 +58,17 @@ def _save_json(path: Path, data: List[Dict]) -> None:
|
|||
|
||||
|
||||
def _copy_icons() -> None:
|
||||
"""Copy vendored icons to the output directory."""
|
||||
"""Copy vendored icons and scripts to the output directory."""
|
||||
src_dir = Path("static/icons")
|
||||
dst_dir = OUTPUT_DIR / "icons"
|
||||
if not src_dir.is_dir():
|
||||
return
|
||||
dst_dir.mkdir(parents=True, exist_ok=True)
|
||||
for icon in src_dir.glob("*.svg"):
|
||||
shutil.copy(icon, dst_dir / icon.name)
|
||||
if src_dir.is_dir():
|
||||
dst_dir.mkdir(parents=True, exist_ok=True)
|
||||
for icon in src_dir.glob("*.svg"):
|
||||
shutil.copy(icon, dst_dir / icon.name)
|
||||
|
||||
js_src = Path("static/chartManager.js")
|
||||
if js_src.is_file():
|
||||
shutil.copy(js_src, OUTPUT_DIR / js_src.name)
|
||||
|
||||
|
||||
def _render_snippet(report: Dict, out_dir: Path) -> None:
|
||||
|
|
49
static/chartManager.js
Normal file
49
static/chartManager.js
Normal file
|
@ -0,0 +1,49 @@
|
|||
export let currentLoad = null;
|
||||
const loadInfo = new Map();
|
||||
|
||||
export function newLoad(container) {
|
||||
if (currentLoad) {
|
||||
abortLoad(currentLoad);
|
||||
}
|
||||
reset(container);
|
||||
const controller = new AbortController();
|
||||
const token = { controller, charts: new Map() };
|
||||
loadInfo.set(token, token);
|
||||
currentLoad = token;
|
||||
return token;
|
||||
}
|
||||
|
||||
export function abortLoad(token) {
|
||||
const info = loadInfo.get(token);
|
||||
if (!info) return;
|
||||
info.controller.abort();
|
||||
info.charts.forEach(chart => {
|
||||
try {
|
||||
chart.destroy();
|
||||
} catch (e) {}
|
||||
});
|
||||
loadInfo.delete(token);
|
||||
if (currentLoad === token) {
|
||||
currentLoad = null;
|
||||
}
|
||||
}
|
||||
|
||||
export function registerChart(token, id, chart) {
|
||||
const info = loadInfo.get(token);
|
||||
if (info) {
|
||||
info.charts.set(id, chart);
|
||||
} else {
|
||||
chart.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
export function reset(container) {
|
||||
if (!container) return;
|
||||
container.querySelectorAll('canvas').forEach(c => {
|
||||
const chart = Chart.getChart(c);
|
||||
if (chart) {
|
||||
chart.destroy();
|
||||
}
|
||||
});
|
||||
container.innerHTML = '';
|
||||
}
|
|
@ -72,7 +72,14 @@
|
|||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/jquery@3.7.0/dist/jquery.min.js"></script>
|
||||
<script src="https://cdn.datatables.net/1.13.4/js/jquery.dataTables.min.js"></script>
|
||||
<script>
|
||||
<script type="module">
|
||||
import {
|
||||
newLoad,
|
||||
abortLoad,
|
||||
registerChart,
|
||||
reset,
|
||||
currentLoad,
|
||||
} from './chartManager.js';
|
||||
const intervalSelect = document.getElementById('interval-select');
|
||||
const domainSelect = document.getElementById('domain-select');
|
||||
const intervalControl = document.getElementById('interval-control');
|
||||
|
@ -105,20 +112,22 @@
|
|||
let currentDomain = domainSelect.value;
|
||||
let currentTab = 'overview';
|
||||
|
||||
function initReport(rep, base) {
|
||||
fetch(base + '/' + rep.json)
|
||||
function initReport(token, rep, base) {
|
||||
fetch(base + '/' + rep.json, { signal: token.controller.signal })
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
if (token !== currentLoad) return;
|
||||
const bucketField = rep.bucket || 'bucket';
|
||||
if (rep.chart === 'table') {
|
||||
const rows = data.map(x => [x[bucketField], x.value]);
|
||||
new DataTable('#table-' + rep.name, {
|
||||
const table = new DataTable('#table-' + rep.name, {
|
||||
data: rows,
|
||||
columns: [
|
||||
{ title: rep.bucket_label || 'Bucket' },
|
||||
{ title: 'Value' }
|
||||
]
|
||||
});
|
||||
registerChart(token, rep.name, table);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -146,7 +155,7 @@
|
|||
dataset.backgroundColor = 'rgba(54, 162, 235, 0.5)';
|
||||
dataset.borderColor = 'rgba(54, 162, 235, 1)';
|
||||
}
|
||||
new Chart(document.getElementById('chart-' + rep.name), {
|
||||
const chart = new Chart(document.getElementById('chart-' + rep.name), {
|
||||
type: chartType,
|
||||
data: {
|
||||
labels: labels,
|
||||
|
@ -154,6 +163,7 @@
|
|||
},
|
||||
options: options
|
||||
});
|
||||
registerChart(token, rep.name, chart);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -171,18 +181,7 @@
|
|||
});
|
||||
}
|
||||
|
||||
function destroyCharts(container) {
|
||||
container.querySelectorAll('canvas').forEach(c => {
|
||||
const chart = Chart.getChart(c);
|
||||
if (chart) {
|
||||
chart.destroy();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function destroyAllCharts() {
|
||||
Object.values(containers).forEach(destroyCharts);
|
||||
}
|
||||
// Reset helpers managed by chartManager
|
||||
|
||||
function loadReports() {
|
||||
let path;
|
||||
|
@ -196,27 +195,29 @@
|
|||
} else {
|
||||
container = containers.domain;
|
||||
if (!currentDomain) {
|
||||
destroyCharts(container);
|
||||
reset(container);
|
||||
container.innerHTML = '<p>Select a domain</p>';
|
||||
return;
|
||||
}
|
||||
path = 'domains/' + encodeURIComponent(currentDomain) + '/' + currentInterval;
|
||||
}
|
||||
|
||||
fetch(path + '/reports.json')
|
||||
.then(r => r.json())
|
||||
.then(reports => {
|
||||
destroyCharts(container);
|
||||
container.innerHTML = '';
|
||||
reports.forEach(rep => {
|
||||
fetch(path + '/' + rep.html)
|
||||
.then(r => r.text())
|
||||
.then(html => {
|
||||
container.insertAdjacentHTML('beforeend', html);
|
||||
initReport(rep, path);
|
||||
});
|
||||
});
|
||||
const token = newLoad(container);
|
||||
|
||||
fetch(path + '/reports.json', { signal: token.controller.signal })
|
||||
.then(r => r.json())
|
||||
.then(reports => {
|
||||
if (token !== currentLoad) return;
|
||||
reports.forEach(rep => {
|
||||
fetch(path + '/' + rep.html, { signal: token.controller.signal })
|
||||
.then(r => r.text())
|
||||
.then(html => {
|
||||
if (token !== currentLoad) return;
|
||||
container.insertAdjacentHTML('beforeend', html);
|
||||
initReport(token, rep, path);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function loadAnalysis() {
|
||||
|
@ -300,7 +301,8 @@
|
|||
}
|
||||
|
||||
function switchTab(name) {
|
||||
destroyAllCharts();
|
||||
abortLoad(currentLoad);
|
||||
Object.values(containers).forEach(reset);
|
||||
currentTab = name;
|
||||
tabs.forEach(tab => {
|
||||
tab.classList.toggle('is-active', tab.dataset.tab === name);
|
||||
|
@ -322,11 +324,16 @@
|
|||
|
||||
intervalSelect.addEventListener('change', () => {
|
||||
currentInterval = intervalSelect.value;
|
||||
abortLoad(currentLoad);
|
||||
reset(containers.all);
|
||||
reset(containers.domain);
|
||||
loadReports();
|
||||
});
|
||||
|
||||
domainSelect.addEventListener('change', () => {
|
||||
currentDomain = domainSelect.value;
|
||||
abortLoad(currentLoad);
|
||||
reset(containers.domain);
|
||||
loadReports();
|
||||
});
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue