diff --git a/dashboard/app/Http/Controllers/Api/DashboardController.php b/dashboard/app/Http/Controllers/Api/DashboardController.php index c963d4d..4f40631 100644 --- a/dashboard/app/Http/Controllers/Api/DashboardController.php +++ b/dashboard/app/Http/Controllers/Api/DashboardController.php @@ -184,7 +184,6 @@ class DashboardController extends Controller public function covidLineageRelativeOverTime(Request $request) { $range = $this->getEpiRange($request); - if (!$range) { return response()->json(['error' => 'Missing epiweek range'], 400); } @@ -202,7 +201,6 @@ class DashboardController extends Controller public function influenzaRelativeOverTime(Request $request) { $range = $this->getEpiRange($request); - if (!$range) { return response()->json(['error' => 'Missing epiweek range'], 400); } @@ -218,6 +216,24 @@ class DashboardController extends Controller } + public function influenzaRelativeOverTimeSequencing(Request $request) + { + $range = $this->getEpiRange($request); + if (!$range) { + return response()->json(['error' => 'Missing epiweek range'], 400); + } + + $data = $this->service->influenzaRelativeOverTimeSequencing( + $range['startYear'], + $range['startWeek'], + $range['endYear'], + $range['endWeek'] + ); + + return response()->json($data); + } + + diff --git a/dashboard/app/Services/DashboardService.php b/dashboard/app/Services/DashboardService.php index 9e8c013..036e851 100644 --- a/dashboard/app/Services/DashboardService.php +++ b/dashboard/app/Services/DashboardService.php @@ -1020,7 +1020,7 @@ class DashboardService }) ->selectRaw(" - surveillance_cases.week_data as period, + concat(surveillance_cases.year_data,'-',surveillance_cases.week_data) as period, subtype, COUNT(DISTINCT surveillance_cases.lab_code) as total ") @@ -1029,4 +1029,34 @@ class DashboardService ->orderBy('period') ->get(); } + + + public function influenzaRelativeOverTimeSequencing($startYear, $startWeek, $endYear, $endWeek) + { + return SurveillanceCase::join('case_lab_results', function ($join) { + $join->on('surveillance_cases.lab_code', '=', 'case_lab_results.lab_code') + ->on('surveillance_cases.surveillance_id', '=', 'case_lab_results.surveillance_id'); + }) + ->where(function ($q) use ($startYear, $startWeek, $endYear, $endWeek) { + $q->whereRaw( + "(surveillance_cases.year_data * 100 + surveillance_cases.week_data) BETWEEN ? AND ?", + [ + $startYear * 100 + $startWeek, + $endYear * 100 + $endWeek + ] + ); + }) + ->whereRaw('case_lab_results.is_positive = 1 and surveillance_cases.surveillance_id in(6) and case_lab_results.indicator="Influenza"') + ->selectRaw(" + case_lab_results.pathogen_name as lineage, + concat(surveillance_cases.year_data,'-',surveillance_cases.week_data) as week, + COUNT(DISTINCT surveillance_cases.lab_code) as total + ") + ->groupBy( + 'case_lab_results.pathogen_name', + 'week' + ) + ->get(); + } + } diff --git a/dashboard/app/Services/DataRetrievalService.php b/dashboard/app/Services/DataRetrievalService.php index b696d37..733c82f 100644 --- a/dashboard/app/Services/DataRetrievalService.php +++ b/dashboard/app/Services/DataRetrievalService.php @@ -2,27 +2,80 @@ namespace App\Services; -use Carbon\Carbon; -use App\Models\Surveillance; -use App\Models\SurveillanceCase; -use App\Models\CaseLabResult; +use Illuminate\Http\Client\RequestException; use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Log; class DataRetrievalService { + protected $apiBaseUrl; + protected $apiUsername; + protected $apiPassword; + + public function __construct() + { + $this->apiBaseUrl = config('services.nphl_api.url'); + $this->apiUsername = config('services.nphl_api.username'); + $this->apiPassword = config('services.nphl_api.password'); + } + + public function get($endpoint) + { + try { + + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $this->apiBaseUrl . $endpoint); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_ENCODING, ''); + curl_setopt($ch, CURLOPT_MAXREDIRS, 10); + curl_setopt($ch, CURLOPT_TIMEOUT, 0); + curl_setopt($ch, CURL_HTTP_VERSION_1_1, 0); + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "GET"); + curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); + curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json')); + curl_setopt($ch, CURLOPT_USERPWD, $this->apiUsername . ":" . $this->apiPassword); + $resp = curl_exec($ch); + $b=""; + if($e = curl_error($ch)){ + $b= $e; + }else{ + $b= $resp; + } + curl_close($ch); + return $b; + + } catch (RequestException $e) { + return [ + 'error' => true, + 'message' => $e->getMessage(), + 'status' => optional($e->response)->status() + ]; + } + } + + public function getSurveillanceData() { try{ - $toDate = now(); - $this->getSARICases(now()->subDays(config('app.lookback_days.SARI'))->toDateString(), $toDate); // done - $this->getILICases(now()->subDays(config('app.lookback_days.ILI'))->toDateString(), $toDate); // done - $this->getLBMCases(now()->subDays(config('app.lookback_days.LBM'))->toDateString(), $toDate); // done - $this->getAFICases(now()->subDays(config('app.lookback_days.AFI'))->toDateString(), $toDate); // done - $this->getNSDCases(now()->subDays(config('app.lookback_days.NDS'))->toDateString(), $toDate); - $this->getSEQCases(now()->subDays(config('app.lookback_days.SEQ'))->toDateString(), $toDate); // done - Log::channel('jobs')->info($toDate->toDateString(). ' Service Reload Data Successfully Ran'); + $lookbackDays = config('app.lookback_days'); + $surveillances = DB::connection('mysql')->select("select * from surveillances"); + foreach ($surveillances as $surveillance){ + $surveillance_data = $this->get('api/labsurveil.php?surveillance_id='.$surveillance->id.'&start_date='.now()->subDays($lookbackDays[$surveillance->code])->toDateString()); + $data = json_decode(preg_replace('/^\xEF\xBB\xBF/', '', $surveillance_data)); + @$this->insert_surveillance_cases((array)$data->laboratory_cases); + @$this->insert_surveillance_case_lab_results((array)$data->laboratory_results); + + } + + //$this->getSARICases(now()->subDays(config('app.lookback_days.SARI'))->toDateString(), $toDate); // done +// $this->getILICases(now()->subDays(config('app.lookback_days.ILI'))->toDateString(), $toDate); // done +// $this->getLBMCases(now()->subDays(config('app.lookback_days.LBM'))->toDateString(), $toDate); // done +// $this->getAFICases(now()->subDays(config('app.lookback_days.AFI'))->toDateString(), $toDate); // done +// $this->getNSDCases(now()->subDays(config('app.lookback_days.NDS'))->toDateString(), $toDate); +// $this->getSEQCases(now()->subDays(config('app.lookback_days.SEQ'))->toDateString(), $toDate); // done + Log::channel('jobs')->info(now()->toDateString(). ' Service Reload Data Successfully Ran'); return true; } catch (\Exception $e){ @@ -35,6 +88,7 @@ class DataRetrievalService public function getSARICases($dateFrom, $dateTo=NULL) { + $cond = ""; $cond .= " and patient.patdate >= '".date('Y-m-d', strtotime($dateFrom))."' "; $cond .= (!empty($dateTo)) ? " and patient.patdate <='".date('Y-m-d', strtotime($dateTo))."' ":""; diff --git a/dashboard/config/services.php b/dashboard/config/services.php index 6a90eb8..c093a4f 100644 --- a/dashboard/config/services.php +++ b/dashboard/config/services.php @@ -35,4 +35,10 @@ return [ ], ], + 'nphl_api' => [ + 'url' => env('NPHL_API_URL'), + 'username' => env('NPHL_API_USERNAME'), + 'password' => env('NPHL_API_PASSWORD'), + ], + ]; diff --git a/dashboard/public/js/dashboard/charts.js b/dashboard/public/js/dashboard/charts.js index bdda7d7..ed85032 100644 --- a/dashboard/public/js/dashboard/charts.js +++ b/dashboard/public/js/dashboard/charts.js @@ -8,7 +8,7 @@ Chart.register({ (ds.data || []).some(v => Number(v) > 0) ); - chart.$noData = !hasData; + chart.$noData = !hasData; if (hasData) return; @@ -94,7 +94,7 @@ function buildStackedChart(canvasId, labels, datasets) { bottom: 30 } }, - + plugins: { legend: { @@ -159,9 +159,9 @@ function buildChart(id, type, labels, data) { layout: { padding: 30 }, - - indexAxis: isHorizontal ? 'y' : 'x', + + indexAxis: isHorizontal ? 'y' : 'x', plugins: { legend: { @@ -189,7 +189,7 @@ function buildChart(id, type, labels, data) { }, datalabels: { color: "#282626", - offset: 6, + offset: 6, clip: false, display: (ctx) => { const chart = ctx.chart; @@ -257,7 +257,7 @@ function buildChart(id, type, labels, data) { arc: { borderWidth: (ctx) => ctx.chart.$noData ? 0 : 1 } - + }; } @@ -306,7 +306,7 @@ function buildMixedTrendChart(canvasId, labels, samples, fluRate, covidRate) { }, - + { type: 'line', label: 'COVID-19 %', @@ -318,7 +318,7 @@ function buildMixedTrendChart(canvasId, labels, samples, fluRate, covidRate) { pointStyle: 'line', }, - + { type: 'bar', label: 'Total Cases', @@ -327,7 +327,7 @@ function buildMixedTrendChart(canvasId, labels, samples, fluRate, covidRate) { borderRadius: 2, barPercentage: 0.8, categoryPercentage: 0.7, - yAxisID: 'y', + yAxisID: 'y', } ] @@ -338,7 +338,6 @@ function buildMixedTrendChart(canvasId, labels, samples, fluRate, covidRate) { options: { responsive: true, maintainAspectRatio: false, - plugins: { legend: { position: 'bottom', @@ -351,6 +350,7 @@ function buildMixedTrendChart(canvasId, labels, samples, fluRate, covidRate) { align: "top", anchor: "end", color: "#555", + display: false, font: { size: 10 }, @@ -387,7 +387,7 @@ function buildMixedTrendChart(canvasId, labels, samples, fluRate, covidRate) { display: true, text: '% Positivity' }, - + } } } diff --git a/dashboard/public/js/dashboard/filter.js b/dashboard/public/js/dashboard/filter.js index f6e9ddd..3e83bf2 100644 --- a/dashboard/public/js/dashboard/filter.js +++ b/dashboard/public/js/dashboard/filter.js @@ -75,8 +75,8 @@ class DashboardFilter { const year = new Date().getFullYear(); - for (let y = year-20; y <= year; y++) { + for (let y = year; y >= year-20; y--) { this.startYear.innerHTML += ``; this.endYear.innerHTML += ``; @@ -128,4 +128,4 @@ class DashboardFilter { } -} \ No newline at end of file +} diff --git a/dashboard/public/js/overview.js b/dashboard/public/js/overview.js index 88f88a7..88a7db1 100644 --- a/dashboard/public/js/overview.js +++ b/dashboard/public/js/overview.js @@ -931,13 +931,13 @@ function addPositivityLegend() {
Influenza Subtypes
-
A/H1N1pdm
+
A/H1N1pdm
A/H3N2
A/H9N2
A/H5N1
A/Unsubtypable
B/Yam
-
B/Vic
+
B/Vic
B/Unsubtypable
`; @@ -1036,10 +1036,10 @@ function loadProvinceMap(startYear, startWeek, endYear, endWeek) { function getColorByPathogen(name) { const colors = { - "A/H1N1pdm": "#2563eb", + "A/H1N1pdm": "#ff0200", "A/H3N2": "#11de9d", "B/Yam": "#9333ea", - "B/Vic" : "#1021b9", + "B/Vic" : "#086037", "A/H9N2": "#b91081", "A/H5N1": "#ad850d", "A/Unsubtypable": "#047393", diff --git a/dashboard/public/js/sequencing.js b/dashboard/public/js/sequencing.js index 6cfa5c8..daac952 100644 --- a/dashboard/public/js/sequencing.js +++ b/dashboard/public/js/sequencing.js @@ -1,20 +1,33 @@ let sequencingChart; +let covidLineageFrequencyChart; +let influenzaSubtypeFrequencyChart; + document.addEventListener("DOMContentLoaded", () => { - const canvas = document.getElementById('sequencingChart'); - if (!canvas) return; + //const canvas = document.getElementById('sequencingChart'); + //if (!canvas) return; new DashboardFilter((startYear, startWeek, endYear, endWeek) => { fetch(`/api/dashboard/sequencing?surveillance_id=${window.SURVEILLANCE_ID}&start_year=${startYear}&start_week=${startWeek}&end_year=${endYear}&end_week=${endWeek}`) .then(res => res.json()) .then(data => { - renderSequencingChart(data.trend || []); + //renderSequencingChart(data.trend || []); renderSequencingCountChart(data.trend || []); renderSequencingPieChart(data.distribution || []); renderSequencingTotalChart(data.trend || []); }); + loadCovidLineageFrequency('week', startYear, startWeek, endYear, endWeek) + loadInfluenzaSubtypeFrequency('week', startYear, startWeek, endYear, endWeek) + + const elements = document.querySelectorAll(".report-period"); + elements.forEach(el => { + el.textContent = 'Week ' + startWeek + ' of '+startYear+' to ' + 'Week ' + endWeek + ' of ' + endYear + }); + }); + + }); function renderSequencingCountChart(rows) { @@ -39,7 +52,7 @@ function renderSequencingCountChart(rows) { const found = rows.find(r => r.period === w && r.subtype === sub); return found ? found.total : 0; }), - backgroundColor: colors[i % colors.length] + backgroundColor: hexToRGBA(colors[i % colors.length], 0.3)// colors[i % colors.length] })); new Chart(ctx, { @@ -59,6 +72,9 @@ function renderSequencingCountChart(rows) { display: true, position: 'bottom', align: 'center' + }, + datalabels: { + display: true } } @@ -123,9 +139,13 @@ function renderSequencingTotalChart(rows) { options: { maintainAspectRatio: false, plugins: { - legend: { display: false } + legend: { display: false }, + datalabels: { + display: false + } }, + } }); } @@ -176,6 +196,8 @@ function renderSequencingChart(rows) { } }); + console.log('aggregated', aggregated) + const cleanRows = Object.values(aggregated); const weeks = [...new Set(cleanRows.map(r => r.period))]; @@ -235,4 +257,310 @@ function renderSequencingChart(rows) { } } }); -} \ No newline at end of file +} + + + +function hexToRGBA(hex, alpha) { + const r = parseInt(hex.slice(1, 3), 16); + const g = parseInt(hex.slice(3, 5), 16); + const b = parseInt(hex.slice(5, 7), 16); + return `rgba(${r}, ${g}, ${b}, ${alpha})`; +} + +function loadCovidLineageFrequency(periodType, startYear, startWeek, endYear, endWeek) { + + fetch(`/api/dashboard/covid-lineage-frequency?period_type=${periodType}&start_year=${startYear}&start_week=${startWeek}&end_year=${endYear}&end_week=${endWeek}`) + .then(res => res.json()) + .then(data => { + + // Extract unique weeks (X-axis) + const weeks = [...new Set(data.map(item => item.week))].sort(); + + // Extract unique lineages + const lineages = [...new Set(data.map(item => item.lineage))]; + + // Color palette + const colors = [ + '#84cc16','#22c55e','#06b6d4','#3b82f6', + '#6366f1','#a855f7','#ec4899','#ef4444', + '#f97316','#eab308' + ]; + + // Build datasets + const datasets = lineages.map((lineage, index) => { + const lineageData = weeks.map(week => { + const found = data.find( + item => item.week === week && item.lineage === lineage + ); + return found ? found.total : 0; + }); + + return { + label: lineage, + data: lineageData, + fill: true, // area fill + tension: 0.4, // smooth curve + borderColor: 'transparent', // hide the line + borderWidth: 0, + pointRadius: 0, // hide points + backgroundColor: hexToRGBA(colors[index % colors.length], 0.3), + stack: 'total' + }; + }); + + // Destroy previous chart if exists + if (covidLineageFrequencyChart) covidLineageFrequencyChart.destroy(); + + const ctx = document.getElementById('covidLineageFrequency').getContext('2d'); + + covidLineageFrequencyChart = new Chart(ctx, { + type: 'line', + data: { + labels: weeks, + datasets: datasets + }, + options: { + responsive: true, + maintainAspectRatio: false, + interaction: { + mode: 'index', + intersect: false + }, + plugins: { + legend: { + display: false // hide default legend + }, + tooltip: { + mode: 'index', + intersect: false + }, + datalabels: { + display: false // hide labels + } + }, + scales: { + x: { + stacked: true, + title: { + display: true, + text: 'Week' + }, + grid:{ + display: false + } + }, + y: { + stacked: true, + beginAtZero: true, + title: { + display: true, + text: 'Relative Frequency' + }, + } + } + } + }); + + // ------------------------- + // Custom right-side scrollable legend + // ------------------------- + const legendContainer = document.getElementById('legendContainer'); + legendContainer.innerHTML = ''; // clear old legend + + datasets.forEach((dataset, index) => { + const item = document.createElement('div'); + item.style.display = 'flex'; + item.style.alignItems = 'center'; + item.style.marginBottom = '4px'; + item.style.fontSize = '11px'; + item.style.cursor = 'pointer'; + item.innerHTML = ` + + ${dataset.label} + `; + + item.addEventListener('click', () => { + const meta = covidLineageFrequencyChart.getDatasetMeta(index); + + // If the clicked dataset is already the only visible one, show all + const allHidden = datasets.every((d, i) => covidLineageFrequencyChart.getDatasetMeta(i).hidden || i === index); + if (!allHidden) { + // Hide all datasets + datasets.forEach((d, i) => { + covidLineageFrequencyChart.getDatasetMeta(i).hidden = true; + }); + // Show only clicked + meta.hidden = false; + } else { + // Show all datasets + datasets.forEach((d, i) => { + covidLineageFrequencyChart.getDatasetMeta(i).hidden = false; + }); + } + + covidLineageFrequencyChart.update(); + + // Update legend opacity + Array.from(legendContainer.children).forEach((child, i) => { + const metaItem = covidLineageFrequencyChart.getDatasetMeta(i); + child.style.opacity = metaItem.hidden ? 0.5 : 1; + }); + }); + + legendContainer.appendChild(item); + }); + + // Scrollable CSS (in case legend is long) + legendContainer.style.maxHeight = '375px'; + legendContainer.style.overflowY = 'auto'; + legendContainer.style.padding = '8px'; + legendContainer.style.borderRadius = '0px'; + }); +} + +function loadInfluenzaSubtypeFrequency(periodType, startYear, startWeek, endYear, endWeek) { + + fetch(`/api/dashboard/influenza-relative-frequency-sequencing?period_type=${periodType}&start_year=${startYear}&start_week=${startWeek}&end_year=${endYear}&end_week=${endWeek}`) + .then(res => res.json()) + .then(data => { + + // Extract unique weeks (X-axis) + const weeks = [...new Set(data.map(item => item.week))].sort(); + + const colors = [ + '#84cc16','#22c55e','#06b6d4','#3b82f6', + '#6366f1','#a855f7','#ec4899','#ef4444', + '#f97316','#eab308' + ]; + + // Extract unique lineages + const lineages = [...new Set(data.map(item => item.lineage))]; + // Build datasets + const datasets = lineages.map((lineage, index) => { + const lineageData = weeks.map(week => { + const found = data.find( + item => item.week === week && item.lineage === lineage + ); + return found ? found.total : 0; + }); + + return { + label: lineage, + data: lineageData, + fill: true, // area fill + tension: 0.4, // smooth curve + borderColor: 'transparent', // hide the line + borderWidth: 0, + pointRadius: 0, // hide points + backgroundColor: hexToRGBA(colors[(index*2) % colors.length], 0.8), + stack: 'total' + }; + }); + + // Destroy previous chart if exists + if (influenzaSubtypeFrequencyChart) influenzaSubtypeFrequencyChart.destroy(); + + const ctx = document.getElementById('influenzaSubtypeFrequency').getContext('2d'); + + influenzaSubtypeFrequencyChart = new Chart(ctx, { + type: 'line', + data: { + labels: weeks, + datasets: datasets + }, + options: { + responsive: true, + maintainAspectRatio: false, + interaction: { + mode: 'index', + intersect: false + }, + plugins: { + legend: { + display: false // hide default legend + }, + tooltip: { + mode: 'index', + intersect: false + }, + datalabels: { + display: false // hide labels + } + }, + scales: { + x: { + stacked: true, + title: { + display: true, + text: 'Week' + }, + grid:{ + display: false + } + }, + y: { + stacked: true, + beginAtZero: true, + title: { + display: true, + text: 'Relative Frequency' + }, + } + } + } + }); + + const legendContainer = document.getElementById('legendContainerInfluenzaSubtypeFrequency'); + legendContainer.innerHTML = ''; // clear old legend + + datasets.forEach((dataset, index) => { + const item = document.createElement('div'); + item.style.display = 'flex'; + item.style.alignItems = 'center'; + item.style.marginBottom = '4px'; + item.style.fontSize = '11px'; + item.style.cursor = 'pointer'; + item.innerHTML = ` + + ${dataset.label} + `; + + item.addEventListener('click', () => { + const meta = influenzaSubtypeFrequencyChart.getDatasetMeta(index); + + // If the clicked dataset is already the only visible one, show all + const allHidden = datasets.every((d, i) => influenzaSubtypeFrequencyChart.getDatasetMeta(i).hidden || i === index); + if (!allHidden) { + // Hide all datasets + datasets.forEach((d, i) => { + influenzaSubtypeFrequencyChart.getDatasetMeta(i).hidden = true; + }); + // Show only clicked + meta.hidden = false; + } else { + // Show all datasets + datasets.forEach((d, i) => { + influenzaSubtypeFrequencyChart.getDatasetMeta(i).hidden = false; + }); + } + + influenzaSubtypeFrequencyChart.update(); + + // Update legend opacity + Array.from(legendContainer.children).forEach((child, i) => { + const metaItem = influenzaSubtypeFrequencyChart.getDatasetMeta(i); + child.style.opacity = metaItem.hidden ? 0.5 : 1; + }); + }); + + legendContainer.appendChild(item); + }); + + // Scrollable CSS (in case legend is long) + legendContainer.style.maxHeight = '375px'; + legendContainer.style.overflowY = 'auto'; + legendContainer.style.padding = '8px'; + legendContainer.style.borderRadius = '0px'; + }); +} diff --git a/dashboard/resources/views/dashboard/overview.blade.php b/dashboard/resources/views/dashboard/overview.blade.php index 92f6985..700b758 100644 --- a/dashboard/resources/views/dashboard/overview.blade.php +++ b/dashboard/resources/views/dashboard/overview.blade.php @@ -47,7 +47,6 @@
-
Epidemic Trend

(based on selected epiweek range) @@ -108,11 +107,11 @@

(based on selected epiweek range)

- +
(based on selected epiweek range)

- +
-
- +
+ +
+
+
+
Total Sequenced Samples Over Time
+

+ (based on selected epiweek range) +

+ +
+
+ +
+
+
+
Influenza Subtypes Relative Frequencies Over Time
+

+ (based on selected epiweek range) +

+ +
+
+
+
+
-
+
-
-
Lineage Counts Over Time
- +
+
SARS-CoV-2 Lineage/Sublineage Relative Frequencies Over Time
+

+ (based on selected epiweek range) +

+{{-- --}} + +
+{{--
--}} +{{--
--}} +{{--
--}} +{{-- --}} +{{-- --}} +{{--
--}} +{{--
--}} +{{--
--}} + -
-
-
-
Total Sequenced Samples Over Time
- -
-
-
+
@@ -62,8 +105,8 @@ @section('scripts') -@endsection \ No newline at end of file +@endsection diff --git a/dashboard/resources/views/layouts/app.blade.php b/dashboard/resources/views/layouts/app.blade.php index 8e621e9..163044f 100644 --- a/dashboard/resources/views/layouts/app.blade.php +++ b/dashboard/resources/views/layouts/app.blade.php @@ -46,6 +46,9 @@ top: 0; z-index: 1000; } + .btn{ + border-radius: 0 !important; + } .btn-theme-outline { background-color: #fff; @@ -263,4 +266,4 @@ - \ No newline at end of file + diff --git a/dashboard/routes/api.php b/dashboard/routes/api.php index fafcf38..0210d77 100644 --- a/dashboard/routes/api.php +++ b/dashboard/routes/api.php @@ -11,6 +11,7 @@ Route::get('/dashboard/influenza-subtype-distribution', [DashboardController::cl Route::get('/dashboard/covid-distributed-by-age-group', [DashboardController::class, 'covidDistributedByAgeGroup']); Route::get('/dashboard/covid-lineage-frequency', [DashboardController::class, 'covidLineageRelativeOverTime']); Route::get('/dashboard/influenza-relative-frequency', [DashboardController::class, 'influenzaRelativeOverTime']); +Route::get('/dashboard/influenza-relative-frequency-sequencing', [DashboardController::class, 'influenzaRelativeOverTimeSequencing']); Route::get('/dashboard/sentinel-map', [DashboardController::class, 'sentinelMap']); Route::get('/dashboard/reload', [DashboardController::class, 'fetchSourceData']); Route::get('/dashboard/sequencing', [DashboardController::class, 'sequencing']);