From 5919b6caf3deb1050e1f8ca1e6392c274ae3335f Mon Sep 17 00:00:00 2001 From: Dusan Vojacek Date: Sun, 12 Apr 2026 21:43:25 +0200 Subject: [PATCH] new fix OTE --- db/routines/R__fn_ote_import.sql | 137 ++++++++++++++++++++++--------- 1 file changed, 97 insertions(+), 40 deletions(-) diff --git a/db/routines/R__fn_ote_import.sql b/db/routines/R__fn_ote_import.sql index 2606a8c..d471973 100644 --- a/db/routines/R__fn_ote_import.sql +++ b/db/routines/R__fn_ote_import.sql @@ -4,7 +4,7 @@ -- Repeatable migration – při změně funkce stačí upravit tento soubor -- ============================================================= --- Parser: raw jsonb → 96 cenových řádků +-- Parser: raw jsonb → 96 (nebo 92/100 při DST) cenových řádků po 15 min CREATE OR REPLACE FUNCTION ems.fn_ote_parse_15m_price_json( in_payload jsonb, in_czk_per_eur numeric DEFAULT 25.000 @@ -19,6 +19,8 @@ AS $$ DECLARE v_date_text text; v_market_date date; + v_dl jsonb; + v_npts int; BEGIN IF in_payload IS NULL THEN RAISE EXCEPTION 'in_payload must not be null'; @@ -38,45 +40,100 @@ BEGIN END IF; v_market_date := to_date(v_date_text, 'DD.MM.YYYY'); - -- Řazení přes poddotaz: některé verze PG/rozšíření hlásí 42P10 při ORDER BY - -- přímo v RETURN QUERY set-returning funkce volané z INSERT ... SELECT. - RETURN QUERY - SELECT s.interval_start, s.interval_end, s.raw_price_czk_kwh - FROM ( - WITH price_line AS ( - SELECT dl - FROM jsonb_array_elements(in_payload #> '{data,dataLine}') AS t(dl) - WHERE dl ->> 'tooltip' = 'flash_chart_01_y_15m_price_tooltip' - LIMIT 1 - ), - points AS ( + -- OTE mění strukturu: novější data = 15min série (tooltip flash_chart_01_y_15m_*), + -- starší / jiná odpověď = jen 24 hodinových bodů s tooltip "Cena". + SELECT t.dl INTO v_dl + FROM jsonb_array_elements(in_payload #> '{data,dataLine}') AS t(dl) + WHERE dl ->> 'tooltip' = 'flash_chart_01_y_15m_price_tooltip' + AND jsonb_array_length(dl -> 'point') >= 90 + LIMIT 1; + + IF v_dl IS NULL THEN + SELECT t.dl INTO v_dl + FROM jsonb_array_elements(in_payload #> '{data,dataLine}') AS t(dl) + WHERE dl ->> 'tooltip' = 'flash_chart_01_y_60m_price_tooltip' + AND jsonb_array_length(dl -> 'point') >= 90 + LIMIT 1; + END IF; + + IF v_dl IS NULL THEN + SELECT t.dl INTO v_dl + FROM jsonb_array_elements(in_payload #> '{data,dataLine}') AS t(dl) + WHERE dl ->> 'tooltip' = 'Cena' + AND jsonb_array_length(dl -> 'point') BETWEEN 20 AND 28 + LIMIT 1; + END IF; + + IF v_dl IS NULL THEN + SELECT t.dl INTO v_dl + FROM jsonb_array_elements(in_payload #> '{data,dataLine}') AS t(dl) + WHERE dl ->> 'tooltip' = 'flash_chart_01_y_15m_price_tooltip' + AND jsonb_array_length(dl -> 'point') BETWEEN 20 AND 28 + LIMIT 1; + END IF; + + IF v_dl IS NULL THEN + RAISE EXCEPTION + 'OTE price dataLine not found (očekáváno 15min flash_* nebo hodinová Cena); ' + 'dostupné tooltips: %', + (SELECT jsonb_agg(dl ->> 'tooltip' ORDER BY dl ->> 'tooltip') + FROM jsonb_array_elements(in_payload #> '{data,dataLine}') dl); + END IF; + + v_npts := jsonb_array_length(v_dl -> 'point'); + + IF v_npts >= 90 THEN + -- x = 1..N = 15min bloky (typicky 96; 92/100 při přechodu letní/zimní čas) + RETURN QUERY + SELECT s.interval_start, s.interval_end, s.raw_price_czk_kwh + FROM ( SELECT - (p ->> 'x')::int AS block_no, - (p ->> 'y')::numeric AS price_eur_mwh - FROM price_line pl - CROSS JOIN LATERAL jsonb_array_elements(pl.dl -> 'point') AS p - ) - SELECT - ((v_market_date::timestamp - + ((block_no - 1) * INTERVAL '15 minutes')) - AT TIME ZONE 'Europe/Prague') AS interval_start, - ((v_market_date::timestamp - + (block_no * INTERVAL '15 minutes')) - AT TIME ZONE 'Europe/Prague') AS interval_end, - ROUND( - (price_eur_mwh * in_czk_per_eur / 1000.0)::numeric, 6 - )::numeric(10,6) AS raw_price_czk_kwh, - block_no - FROM points - ) AS s - ORDER BY s.block_no; + ((v_market_date::timestamp + + ((block_no - 1) * INTERVAL '15 minutes')) + AT TIME ZONE 'Europe/Prague') AS interval_start, + ((v_market_date::timestamp + + (block_no * INTERVAL '15 minutes')) + AT TIME ZONE 'Europe/Prague') AS interval_end, + ROUND( + (price_eur_mwh * in_czk_per_eur / 1000.0)::numeric, 6 + )::numeric(10,6) AS raw_price_czk_kwh, + block_no + FROM ( + SELECT + (p ->> 'x')::int AS block_no, + (p ->> 'y')::numeric AS price_eur_mwh + FROM jsonb_array_elements(v_dl -> 'point') AS p + ) pts + ) AS s + ORDER BY s.block_no; + ELSE + -- x = 1..24 = hodiny dne; každou hodinovou cenu rozvineme na 4× 15min slot + RETURN QUERY + SELECT s.interval_start, s.interval_end, s.raw_price_czk_kwh + FROM ( + SELECT + ((v_market_date::timestamp + + (((hour_no - 1) * 4 + qix) * INTERVAL '15 minutes')) + AT TIME ZONE 'Europe/Prague') AS interval_start, + ((v_market_date::timestamp + + (((hour_no - 1) * 4 + qix) * INTERVAL '15 minutes') + + INTERVAL '15 minutes') + AT TIME ZONE 'Europe/Prague') AS interval_end, + ROUND( + (price_eur_mwh * in_czk_per_eur / 1000.0)::numeric, 6 + )::numeric(10,6) AS raw_price_czk_kwh, + (hour_no - 1) * 4 + qix + 1 AS sort_key + FROM ( + SELECT (p ->> 'x')::int AS hour_no, (p ->> 'y')::numeric AS price_eur_mwh + FROM jsonb_array_elements(v_dl -> 'point') AS p + ) h + CROSS JOIN (VALUES (0), (1), (2), (3)) AS q(qix) + ) AS s + ORDER BY s.sort_key; + END IF; IF NOT FOUND THEN - RAISE EXCEPTION - 'dataLine tooltip=flash_chart_01_y_15m_price_tooltip not found; ' - 'dostupné tooltips: %', - (SELECT jsonb_agg(dl ->> 'tooltip') - FROM jsonb_array_elements(in_payload #> '{data,dataLine}') dl); + RAISE EXCEPTION 'OTE price series had no points after parse'; END IF; END; $$; @@ -84,10 +141,10 @@ $$; COMMENT ON FUNCTION ems.fn_ote_parse_15m_price_json(jsonb, numeric) IS 'Parsuje raw JSON z OTE @@chart-data?time_resolution=PT15M. Datum extrahuje z graph.title (DD.MM.YYYY). -Série: flash_chart_01_y_15m_price_tooltip (EUR/MWh → Kč/kWh přes kurz). -Výstup: 96 řádků, interval_start/end jako timestamptz (UTC), cena Kč/kWh. +Výběr série: (1) flash_chart_01_y_15m_price_tooltip s ≥90 body, (2) flash_chart_01_y_60m_price_tooltip s ≥90, +(3) tooltip Cena s 20–28 body (hodinovka → 4× 15min stejná EUR/MWh), (4) legacy 15m tooltip s 20–28 body. +EUR/MWh → Kč/kWh přes kurz. Výstup: řádky po 15 min (typicky 96). Testování přímo v DB: - COPY tmp_ote FROM ''/tmp/ote.json''; SELECT * FROM ems.fn_ote_parse_15m_price_json(pg_read_file(''/tmp/ote.json'')::jsonb, 25.0) LIMIT 5;';