This commit is contained in:
sebseb7
2025-12-25 01:42:03 +01:00
parent 2d7bfe247d
commit 53e2e04cb9
5 changed files with 399 additions and 105 deletions

View File

@@ -131,9 +131,16 @@ module.exports = {
// Publicly list views
app.get('/api/views', (req, res) => {
try {
const stmt = db.prepare('SELECT id, name, created_at FROM views ORDER BY name');
const stmt = db.prepare('SELECT * FROM views ORDER BY name');
const rows = stmt.all();
res.json(rows);
const views = rows.map(row => {
try {
return { ...row, config: JSON.parse(row.config) };
} catch (e) {
return row;
}
});
res.json(views);
} catch (err) {
res.status(500).json({ error: err.message });
}
@@ -213,29 +220,33 @@ module.exports = {
app.get('/api/readings', (req, res) => {
try {
if (!db) throw new Error('Database not connected');
const { since } = req.query;
const { since, until } = req.query;
const startTime = since || new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString();
const endTime = until || new Date().toISOString();
let sql = 'SELECT * FROM sensor_events WHERE timestamp > ? ';
const params = [since || new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString()];
// 1. Fetch main data window
let sql = 'SELECT * FROM sensor_events WHERE timestamp > ? AND timestamp <= ? ';
const params = [startTime, endTime];
// Helper for filtering could be added here if needed,
// but fetching last 24h of *all* data might not be too huge if not too many sensors.
// However, optimization: if query params `channels` provided as "device:channel,device2:channel2"
const requestedChannels = []; // [{device, channel}]
if (req.query.selection) {
// selection format: "device:channel,device:channel"
const selections = req.query.selection.split(',');
if (selections.length > 0) {
const placeholders = selections.map(() => '(device = ? AND channel = ?)').join(' OR ');
sql += `AND (${placeholders}) `;
const placeholders = [];
selections.forEach(s => {
const lastColonIndex = s.lastIndexOf(':');
if (lastColonIndex !== -1) {
const d = s.substring(0, lastColonIndex);
const c = s.substring(lastColonIndex + 1);
placeholders.push('(device = ? AND channel = ?)');
params.push(d, c);
requestedChannels.push({ device: d, channel: c });
}
});
if (placeholders.length > 0) {
sql += `AND (${placeholders.join(' OR ')}) `;
}
}
}
@@ -243,7 +254,47 @@ module.exports = {
const stmt = db.prepare(sql);
const rows = stmt.all(...params);
res.json(rows);
// 2. Backfill: Ensure we have a starting point for each channel
// For each requested channel, check if we have data at/near start.
// If the first point for a channel is > startTime, we should try to find the previous value.
// We check for the value that started before (or at) startTime AND ended after (or at) startTime (or hasn't ended).
const backfillRows = [];
// Find the record that started most recently before startTime BUT was still valid at startTime
const backfillStmt = db.prepare(`
SELECT * FROM sensor_events
WHERE device = ? AND channel = ?
AND timestamp <= ?
AND (until >= ? OR until IS NULL)
ORDER BY timestamp DESC LIMIT 1
`);
requestedChannels.forEach(ch => {
const prev = backfillStmt.get(ch.device, ch.channel, startTime, startTime);
if (prev) {
// We found a point that covers the startTime.
backfillRows.push(prev);
}
});
// Combine and sort
const allRows = [...backfillRows, ...rows];
// Transform to Compact Dictionary Format
// { "device:channel": [ [timestamp, value, until], ... ] }
const result = {};
allRows.forEach(row => {
const key = `${row.device}:${row.channel}`;
if (!result[key]) result[key] = [];
const pt = [row.timestamp, row.value];
if (row.until) pt.push(row.until);
result[key].push(pt);
});
res.json(result);
} catch (err) {
console.error(err);
res.status(500).json({ error: err.message });