second version

This commit is contained in:
Dusan Vojacek
2026-04-03 14:23:16 +02:00
parent 897b95f728
commit 9f4126946d
105 changed files with 9738 additions and 1470 deletions

View File

@@ -0,0 +1,135 @@
-- =============================================================
-- R__fn_ote_import.sql
-- OTE CZ import parser a import funkce
-- Repeatable migration při změně funkce stačí upravit tento soubor
-- =============================================================
-- Parser: raw jsonb → 96 cenových řádků
CREATE OR REPLACE FUNCTION ems.fn_ote_parse_15m_price_json(
in_payload jsonb,
in_czk_per_eur numeric DEFAULT 25.000
)
RETURNS TABLE (
interval_start timestamptz,
interval_end timestamptz,
raw_price_czk_kwh numeric(10,6)
)
LANGUAGE plpgsql
AS $$
DECLARE
v_date_text text;
v_market_date date;
BEGIN
IF in_payload IS NULL THEN
RAISE EXCEPTION 'in_payload must not be null';
END IF;
IF in_czk_per_eur IS NULL OR in_czk_per_eur <= 0 THEN
RAISE EXCEPTION 'in_czk_per_eur must be > 0, got: %', in_czk_per_eur;
END IF;
-- Datum z graph.title ve formátu "... DD.MM.YYYY"
v_date_text := substring(
in_payload #>> '{graph,title}'
FROM '([0-9]{2}\.[0-9]{2}\.[0-9]{4})'
);
IF v_date_text IS NULL THEN
RAISE EXCEPTION 'cannot parse date from graph.title: %',
in_payload #>> '{graph,title}';
END IF;
v_market_date := to_date(v_date_text, 'DD.MM.YYYY');
RETURN QUERY
WITH price_line AS (
-- Správná série: 15min ceny (tooltip rozlišuje od Množství a 60min)
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 (
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
FROM points
ORDER BY block_no;
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);
END IF;
END;
$$;
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.
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;';
CREATE OR REPLACE FUNCTION ems.fn_ote_import_from_json(
in_payload jsonb,
in_czk_per_eur numeric DEFAULT 25.000
)
RETURNS integer
LANGUAGE plpgsql
AS $$
DECLARE
v_rowcount integer;
BEGIN
INSERT INTO ems.market_interval_price (
market_source,
interval_start,
interval_end,
buy_raw_price_czk_kwh,
sell_raw_price_czk_kwh,
currency,
imported_at
)
SELECT
'OTE_CZ',
p.interval_start,
p.interval_end,
p.raw_price_czk_kwh,
p.raw_price_czk_kwh, -- spot trh: buy = sell
'CZK',
now()
FROM ems.fn_ote_parse_15m_price_json(in_payload, in_czk_per_eur) AS p
ON CONFLICT (market_source, interval_start) DO UPDATE SET
interval_end = EXCLUDED.interval_end,
buy_raw_price_czk_kwh = EXCLUDED.buy_raw_price_czk_kwh,
sell_raw_price_czk_kwh = EXCLUDED.sell_raw_price_czk_kwh,
imported_at = EXCLUDED.imported_at;
GET DIAGNOSTICS v_rowcount = ROW_COUNT;
RETURN v_rowcount;
END;
$$;
COMMENT ON FUNCTION ems.fn_ote_import_from_json(jsonb, numeric) IS
'Uloží výstup fn_ote_parse_15m_price_json do ems.market_interval_price.
Python předá raw jsonb z HTTP response + kurz EUR/CZK.
Vrátí počet upsertnutých řádků (očekáváno 96).
Testování přímo v DB:
SELECT ems.fn_ote_import_from_json(
pg_read_file(''/tmp/ote.json'')::jsonb, 25.0
);';