refactor remove dead correlation feature and placeholder page

The correlation scatter chart was never wired into any page: its JS module was never imported and its template partial never included. Remove the orphaned frontend along with the now-unused backend that only survived via its own tests. Also drop the stock Mojolicious placeholder index.html. - Delete public/js/dashboard/correlation.js (never imported) - Delete templates/dashboard/correlation-card.html.ep (never included) - Delete public/index.html (default Mojolicious welcome page) - Remove fetchCorrelation() from api.js - Remove /api/correlation route from Dashboard.pm - Remove correlation action from Controller/Reading.pm - Remove correlation_rollups from Model/Reading.pm - Remove correlation subtests from t/08-insights.t

Commit
f350a0ceba015834cdabf1fd2e3ed310a9788ff0
Author
gpt-5.6-terra medium <kiro-ai@amazon.com>
Author date
Committer
gpt-5.6-terra medium <kiro-ai@amazon.com>
Committer date
Changed files
roles/dashboard/lib/FAPG/DAQ/Dashboard.pm
index fd62f1a7..83ae04f7 100644..100644
@@ -152,7 +152,6 @@
152 152
153 153 $r->get('/api/readings/:probe/series')->to('Reading#series');
154 154 $r->get('/api/readings/:probe')->to('Reading#list');
155 Removed: $r->get('/api/correlation/:probe_x/:probe_y')->to('Reading#correlation');
156 155 $r->get('/api/status')->to('Reading#status_list');
157 156 $r->get('/api/status/:probe')->to('Reading#status');
158 157
roles/dashboard/lib/FAPG/DAQ/Dashboard/Controller/Reading.pm
index c27dcabe..1631d438 100644..100644
@@ -147,54 +147,4 @@
147 147 return utc_timestamp( time - $STATUS_REACHABLE_SECONDS );
148 148 }
149 149
150 Removed: sub correlation ($self) {
151 Removed: my $probe_x = $self->param('probe_x') // '';
152 Removed: my $probe_y = $self->param('probe_y') // '';
153 Removed:
154 Removed: return $self->render(
155 Removed: status => 404,
156 Removed: json => { error => "Unknown probe type: $probe_x", },
157 Removed: ) unless $self->probe_by_key($probe_x);
158 Removed:
159 Removed: return $self->render(
160 Removed: status => 404,
161 Removed: json => { error => "Unknown probe type: $probe_y", },
162 Removed: ) unless $self->probe_by_key($probe_y);
163 Removed:
164 Removed: my $timeframe = $self->param('timeframe') // 'day';
165 Removed: my $range = $self->param('range') // 1;
166 Removed:
167 Removed: return $self->render(
168 Removed: status => 400,
169 Removed: json => { error => 'Range must be an integer from 1 to 24', },
170 Removed: ) if $range !~ /\A\d+\z/ || $range < 1 || $range > $MAX_SERIES_RANGE;
171 Removed:
172 Removed: my $window = series_window( $timeframe, $range );
173 Removed:
174 Removed: return $self->render(
175 Removed: status => 400,
176 Removed: json => { error => "Unknown timeframe: $timeframe", },
177 Removed: ) unless defined $window;
178 Removed:
179 Removed: my $rollup_seconds
180 Removed: = $window->{bucket_stride} >= 3_600 ? 3_600 : $MINUTE_SECONDS;
181 Removed:
182 Removed: my $rows
183 Removed: = $self->reading->correlation_rollups( $probe_x, $probe_y,
184 Removed: $rollup_seconds, $window->{start_epoch},
185 Removed: $window->{end_epoch}, );
186 Removed:
187 Removed: my @points = map { { x => 0 + $_->{x}, y => 0 + $_->{y} } } @$rows;
188 Removed:
189 Removed: $self->render(
190 Removed: json => {
191 Removed: probe_x => $probe_x,
192 Removed: probe_y => $probe_y,
193 Removed: timeframe => $timeframe,
194 Removed: range => 0 + $range,
195 Removed: points => \@points,
196 Removed: },
197 Removed: );
198 Removed: }
199 Removed:
200 150 1;
roles/dashboard/lib/FAPG/DAQ/Dashboard/Model/Reading.pm
index 7858936e..e41174e8 100644..100644
@@ -57,34 +57,6 @@
57 57 )->hashes->to_array;
58 58 }
59 59
60 Removed: sub correlation_rollups ( $self, $probe_x, $probe_y, $bucket_seconds,
61 Removed: $start_epoch, $end_epoch )
62 Removed: {
63 Removed: return $self->sqlite->db->query(
64 Removed: q{
65 Removed: SELECT
66 Removed: a.bucket_epoch,
67 Removed: a.value_total / a.sample_count AS x,
68 Removed: b.value_total / b.sample_count AS y
69 Removed: FROM reading_rollups a
70 Removed: JOIN reading_rollups b
71 Removed: ON b.probe = ?
72 Removed: AND b.bucket_seconds = a.bucket_seconds
73 Removed: AND b.bucket_epoch = a.bucket_epoch
74 Removed: WHERE a.probe = ?
75 Removed: AND a.bucket_seconds = ?
76 Removed: AND a.bucket_epoch >= ?
77 Removed: AND a.bucket_epoch < ?
78 Removed: ORDER BY a.bucket_epoch
79 Removed: },
80 Removed: $probe_y,
81 Removed: $probe_x,
82 Removed: $bucket_seconds,
83 Removed: $start_epoch,
84 Removed: $end_epoch,
85 Removed: )->hashes->to_array;
86 Removed: }
87 Removed:
88 60 sub latest_status ( $self, $probe ) {
89 61 return if !$self->_table_exists('node_status');
90 62 return $self->sqlite->db->query(
roles/dashboard/public/index.html
index e74bb5f0..00000000 100644..000000
@@ -1,11 +0,0 @@
1 Removed: <!DOCTYPE html>
2 Removed: <html>
3 Removed: <head>
4 Removed: <title>Welcome to the Mojolicious real-time web framework!</title>
5 Removed: </head>
6 Removed: <body>
7 Removed: <h2>Welcome to the Mojolicious real-time web framework!</h2>
8 Removed: This is the static document "public/index.html",
9 Removed: <a href="/">click here</a> to get back to the start.
10 Removed: </body>
11 Removed: </html>
roles/dashboard/public/js/dashboard/api.js
index c162bb23..bb02dec8 100644..100644
@@ -42,13 +42,6 @@
42 42 };
43 43 }
44 44
45 Removed: export async function fetchCorrelation(probeX, probeY, timeframe, range, signal = null) {
46 Removed: const params = new URLSearchParams({ timeframe, range: String(range) });
47 Removed: const options = signal ? { signal } : {};
48 Removed: const payload = await fetchJson(`/api/correlation/${probeX}/${probeY}?${params}`, options);
49 Removed: return payload.points || [];
50 Removed: }
51 Removed:
52 45 export async function fetchWeatherHourly(hours, signal = null) {
53 46 const params = new URLSearchParams({ hours: String(hours) });
54 47 const options = signal ? { signal } : {};
roles/dashboard/public/js/dashboard/correlation.js
index 3e7e4678..00000000 100644..000000
@@ -1,77 +0,0 @@
1 Removed: import { fetchCorrelation } from "./api.js";
2 Removed: import { PRIMARY_CHART_COLOR } from "./constants.js";
3 Removed:
4 Removed: let chart = null;
5 Removed: let activeRequest = null;
6 Removed:
7 Removed: function createScatterChart(canvas, points, labelX, labelY, unitX, unitY) {
8 Removed: return new Chart(canvas, {
9 Removed: type: "scatter",
10 Removed: data: {
11 Removed: datasets: [{
12 Removed: label: `${labelX} vs ${labelY}`,
13 Removed: data: points,
14 Removed: backgroundColor: PRIMARY_CHART_COLOR,
15 Removed: borderColor: PRIMARY_CHART_COLOR,
16 Removed: pointRadius: 3,
17 Removed: pointHoverRadius: 5
18 Removed: }]
19 Removed: },
20 Removed: options: {
21 Removed: responsive: true,
22 Removed: maintainAspectRatio: false,
23 Removed: animation: false,
24 Removed: scales: {
25 Removed: x: {
26 Removed: title: { display: true, text: `${labelX} (${unitX})` }
27 Removed: },
28 Removed: y: {
29 Removed: title: { display: true, text: `${labelY} (${unitY})` }
30 Removed: }
31 Removed: },
32 Removed: plugins: {
33 Removed: tooltip: {
34 Removed: callbacks: {
35 Removed: label: context => {
36 Removed: const point = context.raw;
37 Removed: return `${labelX}: ${point.x.toFixed(2)} ${unitX} ${labelY}: ${point.y.toFixed(2)} ${unitY}`;
38 Removed: }
39 Removed: }
40 Removed: }
41 Removed: }
42 Removed: }
43 Removed: });
44 Removed: }
45 Removed:
46 Removed: export function createCorrelationChart(timeframe, probeX, probeY, config) {
47 Removed: const canvas = document.getElementById("chart-correlation");
48 Removed: if (!canvas) return { reload: () => {} };
49 Removed:
50 Removed: async function load() {
51 Removed: if (activeRequest) activeRequest.abort();
52 Removed:
53 Removed: const controller = new AbortController();
54 Removed: activeRequest = controller;
55 Removed:
56 Removed: try {
57 Removed: const points = await fetchCorrelation(
58 Removed: probeX, probeY, timeframe.selected(), timeframe.range(), controller.signal
59 Removed: );
60 Removed:
61 Removed: if (!chart) {
62 Removed: chart = createScatterChart(canvas, points, config.labelX, config.labelY, config.unitX, config.unitY);
63 Removed: } else {
64 Removed: chart.data.datasets[0].data = points;
65 Removed: chart.update();
66 Removed: }
67 Removed: } catch (error) {
68 Removed: if (error.name === "AbortError") return;
69 Removed: } finally {
70 Removed: if (activeRequest === controller) activeRequest = null;
71 Removed: }
72 Removed: }
73 Removed:
74 Removed: load();
75 Removed:
76 Removed: return { reload: load };
77 Removed: }
roles/dashboard/t/08-insights.t
index 2df83e78..a724dfa2 100644..100644
@@ -92,26 +92,6 @@
92 92 ->json_is( '/hours' => 48 );
93 93 };
94 94
95 Removed: subtest 'correlation API returns points' => sub {
96 Removed: $t->get_ok('/api/correlation/ph/do?timeframe=year&range=1')
97 Removed: ->status_is(200)
98 Removed: ->json_has('/points')
99 Removed: ->json_is( '/points/0/x' => '7.12' )
100 Removed: ->json_is( '/points/0/y' => '8.34' );
101 Removed: };
102 Removed:
103 Removed: subtest 'correlation API with no overlapping data' => sub {
104 Removed: $t->get_ok('/api/correlation/ph/ec?timeframe=year&range=1')
105 Removed: ->status_is(200)
106 Removed: ->json_has('/points')
107 Removed: ->json_is( '/points' => [] );
108 Removed: };
109 Removed:
110 Removed: subtest 'correlation API with invalid probe' => sub {
111 Removed: $t->get_ok('/api/correlation/nope/do?timeframe=day&range=1')
112 Removed: ->status_is(404);
113 Removed: };
114 Removed:
115 95 subtest 'empty database insights page' => sub {
116 96 my $empty = test_empty_app();
117 97 $empty->get_ok('/insights')
roles/dashboard/templates/dashboard/correlation-card.html.ep
index 3a78f039..00000000 100644..000000
@@ -1,11 +0,0 @@
1 Removed: <div class="card">
2 Removed: <div class="card-header">
3 Removed: <h2 class="h5 mb-0">Dissolved Oxygen vs ORP</h2>
4 Removed: <p class="text-body-secondary small mb-0 mt-1">Correlation scatter plot</p>
5 Removed: </div>
6 Removed: <div class="card-body">
7 Removed: <div class="chart-wrap">
8 Removed: <canvas id="chart-correlation" role="img" aria-label="Dissolved Oxygen vs ORP correlation scatter chart"></canvas>
9 Removed: </div>
10 Removed: </div>
11 Removed: </div>