fix letni /zimni cas OTE
All checks were successful
deploy / deploy (push) Successful in 19s
test / smoke-test (push) Successful in 5s

This commit is contained in:
Dusan Vojacek
2026-04-12 21:57:37 +02:00
parent 5919b6caf3
commit f0dfcefd54
4 changed files with 62 additions and 40 deletions

View File

@@ -14,7 +14,16 @@ from app.config import get_settings
logger = logging.getLogger(__name__)
OTE_EXPECTED_SLOTS = 96
# Běžný kalendářní den na DAM = 96 čtvrthodin; 92 při přechodu na letní čas, 100 na zimní.
OTE_TYPICAL_SLOTS = 96
OTE_FULL_DAY_SLOT_COUNTS: frozenset[int] = frozenset({92, 96, 100})
# Zpětná kompatibilita ve starších importech
OTE_EXPECTED_SLOTS = OTE_TYPICAL_SLOTS
def ote_prague_day_slots_look_complete(slot_count: int) -> bool:
"""True, pokud počet řádků odpovídá celému obchodnímu dni OTE (včetně DST)."""
return slot_count in OTE_FULL_DAY_SLOT_COUNTS
OTE_URL = (
"https://www.ote-cr.cz/cs/kratkodobe-trhy/elektrina/denni-trh/"
@@ -109,7 +118,7 @@ async def _apply_ote_json_to_db(conn, payload: dict) -> int:
async def count_ote_slots_prague_day(conn, target_day: date) -> int:
"""Počet řádků OTE_CZ pro kalendářní den v Europe/Prague (96 očekáváno)."""
"""Počet řádků OTE_CZ pro kalendářní den v Europe/Prague (plný den 92/96/100)."""
return int(
await conn.fetchval(
"""
@@ -150,12 +159,12 @@ async def import_ote_prices_for_day(
target_day,
)
n_imported = await count_ote_slots_prague_day(conn, target_day)
if n_imported < OTE_EXPECTED_SLOTS:
if not ote_prague_day_slots_look_complete(n_imported):
logger.warning(
"OTE: jen %s/%s slotů pro %s (Europe/Prague)",
"OTE: %s slotů pro %s (plný den = jedna z %s; jinak neúplná data)",
n_imported,
OTE_EXPECTED_SLOTS,
day_str,
sorted(OTE_FULL_DAY_SLOT_COUNTS),
)
logger.info(
"OTE import OK: %s slotů (upsert) pro %s, první cena %.4f Kč/kWh",
@@ -195,7 +204,7 @@ async def backfill_ote_prices(
"""
Projde rozsah [start_date, end_date] (kalendář Prague) a doplní chybějící dny z OTE.
only_missing: přeskočí dny, kde už je count >= OTE_EXPECTED_SLOTS.
only_missing: přeskočí dny, kde už je „plný“ počet slotů (92/96/100 dle OTE).
pause_between_days_s: krátká pauza mezi HTTP požadavky (ohleduplnost k OTE).
"""
stats = OteBackfillStats(start_date=start_date, end_date=end_date)
@@ -208,7 +217,7 @@ async def backfill_ote_prices(
d += timedelta(days=1)
continue
slots = await count_ote_slots_prague_day(conn, d)
if only_missing and slots >= OTE_EXPECTED_SLOTS:
if only_missing and ote_prague_day_slots_look_complete(slots):
stats.days_skipped_complete += 1
d += timedelta(days=1)
continue
@@ -301,7 +310,7 @@ async def import_ote_prices(
""",
target_day,
)
incomplete = (n_imported or 0) < OTE_EXPECTED_SLOTS
incomplete = not ote_prague_day_slots_look_complete(n_imported or 0)
if incomplete:
now_p = datetime.now(ZoneInfo("Europe/Prague"))
tomorrow_p = (now_p + timedelta(days=1)).date()
@@ -311,10 +320,10 @@ async def import_ote_prices(
and (now_p.hour, now_p.minute) < (14, 30)
):
logger.warning(
"OTE: jen %s/%s slotů pro %s",
"OTE: %s slotů pro %s (plný den = jedna z %s)",
n_imported,
OTE_EXPECTED_SLOTS,
date_str,
sorted(OTE_FULL_DAY_SLOT_COUNTS),
)
logger.info(
"OTE import OK: %s slotů pro %s, první cena %.4f Kč/kWh",