fix graf v sql
Some checks failed
CI and deploy / migration-check (push) Failing after 9s
CI and deploy / deploy (push) Has been skipped

This commit is contained in:
Dusan Vojacek
2026-04-22 19:50:49 +02:00
parent faf948d75b
commit bd7d6a1b99
4 changed files with 41 additions and 17 deletions

View File

@@ -101,8 +101,11 @@ AS $$
cbs.avg_power_w + 0.5 * COALESCE(cbs.stddev_power_w, 100),
550
)::INT AS confidence_w
FROM generate_series(p_from, p_to - INTERVAL '15 minutes',
INTERVAL '15 minutes') AS gs(slot)
FROM generate_series(
date_bin(interval '15 minutes', p_from, timestamptz '1970-01-01T00:00:00Z'),
date_bin(interval '15 minutes', p_to, timestamptz '1970-01-01T00:00:00Z') - interval '15 minutes',
interval '15 minutes'
) AS gs(slot)
LEFT JOIN ems.consumption_baseline_stats cbs
ON cbs.site_id = p_site_id
AND cbs.day_of_week = EXTRACT(DOW FROM gs.slot AT TIME ZONE 'Europe/Prague')::INT

View File

@@ -11,11 +11,11 @@ stable
as $fn$
with bounds as (
select
p_from as ts_from,
date_bin(interval '15 minutes', p_from, timestamptz '1970-01-01T00:00:00Z') as ts_from,
case
when p_to <= p_from then p_from + interval '15 minutes'
when p_to > p_from + interval '60 days' then p_from + interval '60 days'
else p_to
when p_to <= p_from then date_bin(interval '15 minutes', p_from, timestamptz '1970-01-01T00:00:00Z') + interval '15 minutes'
when p_to > p_from + interval '60 days' then date_bin(interval '15 minutes', p_from, timestamptz '1970-01-01T00:00:00Z') + interval '60 days'
else date_bin(interval '15 minutes', p_to, timestamptz '1970-01-01T00:00:00Z')
end as ts_to
),
slot_spine as (

View File

@@ -23,11 +23,11 @@ as $fn$
),
bounds as (
select
p_from as ts_from,
date_bin(interval '15 minutes', p_from, timestamptz '1970-01-01T00:00:00Z') as ts_from,
case
when p_to <= p_from then p_from + interval '15 minutes'
when p_to > p_from + interval '60 days' then p_from + interval '60 days'
else p_to
when p_to <= p_from then date_bin(interval '15 minutes', p_from, timestamptz '1970-01-01T00:00:00Z') + interval '15 minutes'
when p_to > p_from + interval '60 days' then date_bin(interval '15 minutes', p_from, timestamptz '1970-01-01T00:00:00Z') + interval '60 days'
else date_bin(interval '15 minutes', p_to, timestamptz '1970-01-01T00:00:00Z')
end as ts_to
),
slot_spine as (

View File

@@ -84,17 +84,19 @@ function DayChart({
strokeWidth={2}
dot={false}
connectNulls
isAnimationActive={false}
/>
{showForecast ? (
<Line
type="monotone"
dataKey="forecast_kw"
name="Předpověď"
stroke="#ef9f27"
stroke="#fbbf24"
strokeWidth={1.5}
dot={false}
connectNulls
strokeDasharray="5 4"
isAnimationActive={false}
/>
) : null}
{showCorrected ? (
@@ -102,11 +104,12 @@ function DayChart({
type="monotone"
dataKey="corrected_kw"
name="Korigovaná"
stroke="#ef9f27"
stroke="#fde68a"
strokeWidth={1.5}
dot={false}
connectNulls
strokeDasharray="2 3"
isAnimationActive={false}
/>
) : null}
</LineChart>
@@ -167,9 +170,15 @@ export default function ForecastVsActual() {
const byInterval = useMemo(() => {
const map = new Map<string, { tel?: Telemetry15mRow; pv?: ForecastPvSlotCorrectedRow; base?: BaselineLoadSlotRow }>()
for (const r of telemetry) map.set(r.slot_start, { ...(map.get(r.slot_start) ?? {}), tel: r })
for (const r of pvSlots) map.set(r.interval_start, { ...(map.get(r.interval_start) ?? {}), pv: r })
for (const r of baselineSlots) map.set(r.interval_start, { ...(map.get(r.interval_start) ?? {}), base: r })
for (const r of telemetry) {
map.set(r.slot_start, { ...(map.get(r.slot_start) ?? {}), tel: r })
}
for (const r of pvSlots) {
map.set(r.interval_start, { ...(map.get(r.interval_start) ?? {}), pv: r })
}
for (const r of baselineSlots) {
map.set(r.interval_start, { ...(map.get(r.interval_start) ?? {}), base: r })
}
return map
}, [telemetry, pvSlots, baselineSlots])
@@ -211,8 +220,11 @@ export default function ForecastVsActual() {
byDay.set(day, arr)
}
return [...byDay.entries()]
.sort((a, b) => a[0].localeCompare(b[0]))
.map(([day, points]) => ({ day, points }))
.sort((a, b) => b[0].localeCompare(a[0]))
.map(([day, points]) => ({
day,
points: points.sort((p, q) => new Date(p.k).getTime() - new Date(q.k).getTime()),
}))
}, [byInterval, metric])
const title = metric === 'pv' ? 'FVE (výroba)' : metric === 'load' ? 'Spotřeba (bazál)' : 'Síť (signed)'
@@ -270,6 +282,15 @@ export default function ForecastVsActual() {
</div>
)}
{siteReady && siteId != null ? (
<p className="text-xs text-slate-500">
Legenda: <span className="text-slate-200">plná šedá</span> = skutečnost z telemetrie (15m CA),{' '}
<span className="text-amber-200">čárkovaná žlutá</span> = předpověď (FVE: OpenMeteo/pvlib; spotřeba: baseline
z historie), <span className="text-yellow-100">tečkovaná světlá</span> = korigovaná FVE (delta profil z historie).
U sítě zatím nemáme samostatnou předpověď řady (jen skutečnost).
</p>
) : null}
{error ? (
<div className="rounded-xl border border-red-500/40 bg-red-950/40 px-4 py-3 text-sm text-red-200" role="alert">
{error}