sync reference days
Some checks failed
CI and deploy / migration-check (push) Failing after 14s
CI and deploy / deploy (push) Has been skipped

This commit is contained in:
Dusan Vojacek
2026-05-02 14:05:09 +02:00
parent 343f2f9847
commit 5ca5eab1d8
12 changed files with 192 additions and 203 deletions

View File

@@ -0,0 +1,21 @@
-- Kalendářní dny lokality označené jako referenční pro učení delty PV forecastu (dobrá obloha).
create table ems.site_pv_forecast_reference_day (
site_id int not null references ems.site (id) on delete cascade,
day_local date not null,
notes text null,
created_at timestamptz not null default now(),
primary key (site_id, day_local)
);
comment on table ems.site_pv_forecast_reference_day is
'Dny v kalendáři lokality podle jejího site.timezone (typicky datum ve zdi Europe/Prague), kterým se v ems.fn_pv_forecast_delta_profile zvýší váha řádků forecast_accuracy při počítání delta profilu.';
comment on column ems.site_pv_forecast_reference_day.day_local is
'Kalendářní datum v časové zóně lokality; porovnává se na (interval_start AT TIME ZONE site.timezone)::date ze slotů.';
alter table ems.site_pv_forecast_calibration
add column if not exists reference_day_weight_mult numeric null;
comment on column ems.site_pv_forecast_calibration.reference_day_weight_mult is
'Násobitel váhy učícího vzorku pro všechny sloty jejichž den spadá do site_pv_forecast_reference_day; NULL použije default v fn_pv_forecast_delta_profile (aktuálně 3).';

View File

@@ -19,14 +19,16 @@ LANGUAGE sql
STABLE
SET work_mem = '64MB'
AS $fn$
WITH eff AS (
WITH eff AS (
SELECT
coalesce(cal.delta_learn_min_ts, timestamptz '2026-04-11T22:00:00Z') AS delta_learn_min_ts,
coalesce(cal.half_life_days, p_half_life_days) AS half_life_days,
coalesce(cal.threshold_w, p_threshold_w) AS threshold_w,
coalesce(cal.top_n_days, p_top_n_days) AS top_n_days,
coalesce(cal.non_top_day_factor, p_non_top_day_factor) AS non_top_day_factor,
coalesce(cal.day_weight_gamma, p_day_weight_gamma) AS day_weight_gamma
coalesce(cal.day_weight_gamma, p_day_weight_gamma) AS day_weight_gamma,
greatest(1::numeric, coalesce(cal.reference_day_weight_mult, 3::numeric))
AS reference_day_w_mult
FROM ems.site s
LEFT JOIN ems.site_pv_forecast_calibration cal ON cal.site_id = s.id
WHERE s.id = p_site_id
@@ -170,6 +172,11 @@ AS $fn$
) AS rn
FROM day_stats ds
),
ref_wall AS (
SELECT d.day_local
FROM ems.site_pv_forecast_reference_day d
WHERE d.site_id = p_site_id
),
filtered AS (
SELECT
s.pv_array_id,
@@ -194,7 +201,12 @@ AS $fn$
),
greatest(0.25, least(coalesce(e.day_weight_gamma, 1.0), 8.0))
)
) AS w
)
* CASE
WHEN EXISTS (SELECT 1 FROM ref_wall rw WHERE rw.day_local = s.day_local)
THEN e.reference_day_w_mult
ELSE 1::numeric
END AS w
FROM slots s
CROSS JOIN bounds b
CROSS JOIN eff e
@@ -281,4 +293,6 @@ AS $fn$
$fn$;
COMMENT ON FUNCTION ems.fn_pv_forecast_delta_profile IS
'Aditivní delta profil PV forecastu po 15min slotu dne (96 slotů) per pv_array_id v `deltas_by_array`; `deltas` je součet delt přes pole (kompatibilita). Zdroj: forecast_accuracy s learning_eligible, cutoff a numerické defaulty z ems.site_pv_forecast_calibration (NULL sloupce = parametry volání).';
'Aditivní delta profil PV forecastu po 15min slotu dne (96 slotů) per pv_array_id v deltas_by_array; deltas je součet delt přes pole.'
' Zdroj forecast_accuracy learning_eligible, cutoff kalibrace, váhy dnů.'
' Dny z ems.site_pv_forecast_reference_day (den = interval ve site.timezone) mají násobenou váhu (site_pv_forecast_calibration.reference_day_weight_mult nebo default 3).';

View File

@@ -0,0 +1,120 @@
-- Operace nad PV forecastem v DB a správa referenčních dnů kalibrace (náhrada dřívějších .sh nástrojů).
create or replace function ems.fn_delete_forecast_pv_prague_calendar_day(
p_day date,
p_site_id int default null
)
returns table (
targets_interval_rows bigint,
deleted_forecast_accuracy_rows bigint,
deleted_forecast_pv_interval_rows bigint,
deleted_empty_forecast_pv_run_rows bigint
)
language plpgsql
volatile
as $fn$
declare
v_ts_start timestamptz;
v_ts_end timestamptz;
v_tgt interval_rows bigint;
v_acc bigint;
v_iv bigint;
v_run bigint;
begin
v_ts_start := (p_day::text || ' 00:00:00')::timestamp at time zone 'Europe/Prague';
v_ts_end := ((p_day + 1)::text || ' 00:00:00')::timestamp at time zone 'Europe/Prague';
drop table if exists _ems_wipe_pv_forecast_targets;
create temporary table _ems_wipe_pv_forecast_targets (
run_id int not null,
pv_array_id int not null,
interval_start timestamptz not null
) on commit drop;
insert into _ems_wipe_pv_forecast_targets (run_id, pv_array_id, interval_start)
select fi.run_id, fi.pv_array_id, fi.interval_start
from ems.forecast_pv_interval fi
inner join ems.forecast_pv_run r on r.id = fi.run_id
where fi.interval_start >= v_ts_start
and fi.interval_start < v_ts_end
and (p_site_id is null or r.site_id = p_site_id);
get diagnostics v_tgt = row_count;
delete from ems.forecast_accuracy fa
using (select distinct run_id, interval_start from _ems_wipe_pv_forecast_targets) t
where fa.run_id = t.run_id
and fa.interval_start = t.interval_start;
get diagnostics v_acc = row_count;
delete from ems.forecast_pv_interval fi
using _ems_wipe_pv_forecast_targets t
where fi.run_id = t.run_id
and fi.pv_array_id = t.pv_array_id
and fi.interval_start = t.interval_start;
get diagnostics v_iv = row_count;
delete from ems.forecast_pv_run fr
where fr.id in (select distinct run_id from _ems_wipe_pv_forecast_targets)
and not exists (
select 1 from ems.forecast_pv_interval x where x.run_id = fr.id
);
get diagnostics v_run = row_count;
targets_interval_rows := v_tgt;
deleted_forecast_accuracy_rows := v_acc;
deleted_forecast_pv_interval_rows := v_iv;
deleted_empty_forecast_pv_run_rows := v_run;
return next;
end;
$fn$;
comment on function ems.fn_delete_forecast_pv_prague_calendar_day is
'Maze forecast_pv_interval (a navázané forecast_accuracy) pro řádky podle začátku intervalu '
'na daný kalendářní den hranovaný půlnocí Europe/Prague — ne podle TZ lokality.'
' p_site_id NULL = všechny lokality. Prázdné forecast_pv_run v mazané množině smaže návazně.'
' Destruktivní vůči historii přesnosti; preferuj jen provozní re-import forecastu.';
create or replace function ems.fn_pv_forecast_sync_reference_days(
p_site_id int,
p_days_local date[],
p_replace_existing boolean default false
)
returns int
language plpgsql
volatile
as $fn$
declare
v_after int;
begin
if not exists (select 1 from ems.site s where s.id = p_site_id) then
raise exception using
message = format('site_id %s neexistuje v ems.site', p_site_id),
errcode = 'P0001';
end if;
if p_replace_existing then
delete from ems.site_pv_forecast_reference_day d where d.site_id = p_site_id;
end if;
insert into ems.site_pv_forecast_reference_day (site_id, day_local)
select p_site_id, d::date
from unnest(p_days_local) as u(d)
where d is not null
on conflict (site_id, day_local) do nothing;
select count(*)::int into v_after from ems.site_pv_forecast_reference_day d where d.site_id = p_site_id;
return v_after;
end;
$fn$;
comment on function ems.fn_pv_forecast_sync_reference_days is
'Zapíše kalendářní dny (datum ve zdi site.timezone lokality při použití s fn_pv_forecast_delta_profile) jako referenční. '
'p_replace_existing true smaže předchozí záznamy dané lokality; false jen doplňuje unnest bez přepsání. '
'Vrací počet řádků v site_pv_forecast_reference_day po operaci.';

View File

@@ -29,6 +29,7 @@ GRANT SELECT ON ems.vw_telemetry_hourly_7d TO ems_anon;
GRANT SELECT ON ems.vw_telemetry_15m_7d TO ems_anon;
GRANT SELECT ON ems.forecast_accuracy TO ems_anon;
GRANT SELECT ON ems.site_pv_forecast_calibration TO ems_anon;
GRANT SELECT ON ems.site_pv_forecast_reference_day TO ems_anon;
GRANT SELECT ON ems.vw_forecast_accuracy_by_lead_time TO ems_anon;
GRANT SELECT ON ems.vw_forecast_accuracy_daily TO ems_anon;
GRANT SELECT ON ems.consumption_baseline_stats TO ems_anon;