finalize export function
This commit is contained in:
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user