finalize export function

This commit is contained in:
2026-04-29 08:58:06 +07:00
parent d801964f59
commit cdd8251b17
7 changed files with 748 additions and 563 deletions

View File

@@ -1,12 +1,16 @@
const standardPrograms = ['SARI', 'ILI', 'LBM', 'AFI'];
const standardPrograms = ['SARI', 'ILI', 'LBM', 'AFI', 'NDS'];
const programCode = (window.PROGRAM_CODE || '').trim().toUpperCase();
let map;
let provinceLayer;
document.addEventListener("DOMContentLoaded", () => {
if (!standardPrograms.includes(programCode)) return;
new DashboardFilter((startYear, startWeek, endYear, endWeek) => {
const elements = document.querySelectorAll(".report-period");
elements.forEach(el => {
el.textContent = 'Week ' + startWeek + ' of ' + startYear + ' to ' + 'Week ' + endWeek + ' of ' + endYear
});
fetch(`/api/dashboard/program?surveillance_id=${window.SURVEILLANCE_ID}&start_year=${startYear}&start_week=${startWeek}&end_year=${endYear}&end_week=${endWeek}`)
.then(res => res.json())
@@ -15,6 +19,7 @@ document.addEventListener("DOMContentLoaded", () => {
});
});
function normalizeProvince(name, validSet) {
if (!name || !validSet) return null;
@@ -46,6 +51,7 @@ function normalizeProvince(name, validSet) {
return match || null;
}
function renderAFIDashboard(data) {
const pathogenRows = (data.pathogen_distribution || [])
.sort((a, b) => b.total - a.total);
@@ -54,16 +60,22 @@ function renderAFIDashboard(data) {
'#8b5cf6', '#14b8a6', '#f97316', '#84cc16', '#e11dba', '#f6f63b'
];
const rows = data.afi_trend || [];
const pcr = rows.filter(r => r.test_type === 'PCR');
const serum = rows.filter(r => r.test_type === 'Serum');
renderAFITrend(pcr, 'trendChart', colors);
//renderAFITrend(serum, 'pathogenChart', colors);
renderSummary(data.summary);
renderAFITrend(data.afi_trend);
renderProvinceHeatmap(data.province_distribution);
renderPathogenChart(data.pathogen_distribution);
renderDemographics(data);
renderPathogenChart(data.pathogen_distribution || []);
renderSentinel(data.sentinel_sites || []);
renderSubtypeChart(data.subtype_distribution || []);
charts['pathogenChart'].data.datasets[0].backgroundColor = colors;
charts['pathogenChart'].update();
charts['ageChart'].data.datasets[0].backgroundColor = colors;
charts['ageChart'].update();
charts['sexChart'].data.datasets[0].backgroundColor = colors;
@@ -72,9 +84,12 @@ function renderAFIDashboard(data) {
charts['sentinelChart'].update();
charts['subtypeChart'].data.datasets[0].backgroundColor = colors;
charts['subtypeChart'].update();
charts['pathogenChart'].data.datasets[0].backgroundColor = colors;
charts['pathogenChart'].update();
}
function renderProvinceHeatmap(rows) {
window.latestProvinceData = rows;
if (map) map.remove();
map = L.map('provinceMap').setView([12.7, 104.9], 7);
@@ -92,7 +107,7 @@ function renderProvinceHeatmap(rows) {
const validProvinces = new Set(
geo.features.map(f => f.properties.ADM1_EN)
);
window.validProvinces = validProvinces;
const totals = {};
rows.forEach(r => {
@@ -116,7 +131,7 @@ function renderProvinceHeatmap(rows) {
return "#f3f4f600";
}
provinceLayer = L.geoJSON(geo, {
window.provinceLayer = L.geoJSON(geo, {
style: feature => {
const province = feature.properties.ADM1_EN;
@@ -124,9 +139,9 @@ function renderProvinceHeatmap(rows) {
return {
color: "#444",
weight: 1,
weight: 1.5,
fillColor: getColor(value),
fillOpacity: 0.7
fillOpacity: 0.8
};
},
onEachFeature: (feature, layer) => {
@@ -233,12 +248,27 @@ function renderProgramTrend(rows) {
const fluRate = rows.map(r => r.influenza_rate || 0);
const covidRate = rows.map(r => r.covid_rate || 0);
const lines = [
{ label: 'Influenza %', data: fluRate, color: '#fa2929' },
{ label: 'COVID-19 %', data: covidRate, color: '#1976D2' }
];
// ✅ ONLY NDS gets EV + Mpox
if (programCode === 'NDS') {
const evRate = rows.map(r => r.ev_rate || 0);
const mpoxRate = rows.map(r => r.mpox_rate || 0);
lines.push(
{ label: 'EV %', data: evRate, color: '#f59e0b' },
{ label: 'Mpox %', data: mpoxRate, color: '#8b5cf6' }
);
}
buildMixedTrendChart(
'trendChart',
labels,
samples,
fluRate,
covidRate
lines
);
}
function renderSummary(summary) {
@@ -365,74 +395,43 @@ function renderDashboard(data) {
//AFI
function renderAFITrend(rows) {
function renderAFITrend(rows, canvasId, colors) {
if (!rows.length) {
buildStackedChart('trendChart', [], []);
if (!rows || !rows.length) {
buildStackedChart(canvasId, [], []);
return;
}
const { labels, datasets } = transformAFIData(rows);
const cleanRows = rows.filter(r => r.pathogen);
buildStackedChart('trendChart', labels, datasets);
}
function transformAFIData(rows) {
const keyFn = r => `${r.year}-${r.period}`;
const grouped = {};
const pathogensSet = new Set();
rows.forEach(r => {
const key = `${r.year}-W${r.period}`;
if (!grouped[key]) {
grouped[key] = {};
}
grouped[key][r.pathogen] = r.total;
pathogensSet.add(r.pathogen);
const map = {};
cleanRows.forEach(r => {
const key = keyFn(r);
if (!map[key]) map[key] = {};
map[key][r.pathogen] = Number(r.total_tests || r.total || 0);
});
const labels = Object.keys(grouped).sort((a, b) => {
const [yA, wA] = a.split('-W').map(Number);
const [yB, wB] = b.split('-W').map(Number);
return yA === yB ? wA - wB : yA - yB;
const keys = Object.keys(map).sort((a, b) => {
const [y1, w1] = a.split('-').map(Number);
const [y2, w2] = b.split('-').map(Number);
return y1 !== y2 ? y1 - y2 : w1 - w2;
});
const pathogenTotals = {};
const labels = keys.map(k => `W${k.split('-')[1]}`);
rows.forEach(r => {
pathogenTotals[r.pathogen] = (pathogenTotals[r.pathogen] || 0) + r.total;
});
const pathogens = [...new Set(cleanRows.map(r => r.pathogen))];
const pathogens = Object.keys(pathogenTotals)
.sort((a, b) => pathogenTotals[b] - pathogenTotals[a]);
const datasets = pathogens.map(p => ({
const datasets = pathogens.map((p, i) => ({
label: p,
data: labels.map(l => grouped[l][p] || 0),
backgroundColor: getColorForPathogen(p)
data: keys.map(k => map[k][p] || 0),
backgroundColor: colors[i % colors.length]
}));
return { labels: labels.map(l => l.split('-')[1]), datasets };
buildStackedChart(canvasId, labels, datasets);
}
function getColorForPathogen(name) {
const colors = {
Dengue: '#2563eb',
Chikungunya: '#10b981',
Zika: '#f59e0b',
Leptospira: '#ef4444',
Rickettsia: '#8b5cf6',
Salmonella: '#f97316',
Plasmodium: '#14b8a6',
Influenza: '#84cc16'
};
if (colors[name]) return colors[name];
return `hsl(${Math.floor(Math.random() * 360)}, 70%, 60%)`;
}
function renderPathogenChart(rows) {
buildChart(
'pathogenChart',