fix letni /zimni cas OTE
This commit is contained in:
@@ -46,7 +46,7 @@ from services.notification_service import (
|
||||
notify_operating_mode_changed,
|
||||
run_fn_set_mode_with_discord,
|
||||
)
|
||||
from services.price_importer import import_ote_prices
|
||||
from services.price_importer import import_ote_prices, ote_prague_day_slots_look_complete
|
||||
from services.telemetry_collector import run_telemetry_loop_wrapper
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
@@ -317,7 +317,7 @@ async def lifespan(app: FastAPI):
|
||||
|
||||
for day in (today, tomorrow):
|
||||
slots = await _count_ote_slots_for_day(conn, day)
|
||||
if slots >= 96:
|
||||
if ote_prague_day_slots_look_complete(slots):
|
||||
continue
|
||||
n, imported_day, _, err = await import_ote_prices(
|
||||
conn, site_id=None, target_date=day
|
||||
|
||||
@@ -17,7 +17,7 @@ Lokálně (venv s backend/requirements.txt):
|
||||
Volby:
|
||||
--days 730 posledních N kalendářních dní (Europe/Prague), výchozí 730 ≈ 2 roky
|
||||
--from-date / --to-date pevný rozsah YYYY-MM-DD (má přednost před --days u konce rozsahu)
|
||||
--force stáhnout znovu i dny, kde už je 96 slotů
|
||||
--force stáhnout znovu i dny s plným počtem slotů OTE (92/96/100)
|
||||
--dry-run jen vypsat chybějící dny, bez HTTP
|
||||
--delay SEC pauza mezi dny (výchozí 0.35)
|
||||
--refresh-predictions po skončení zavolat fn_predict_negative_price_windows pro aktivní site
|
||||
@@ -57,9 +57,10 @@ except ModuleNotFoundError as e:
|
||||
|
||||
from app.config import get_settings # noqa: E402
|
||||
from services.price_importer import ( # noqa: E402
|
||||
OTE_EXPECTED_SLOTS,
|
||||
OTE_FULL_DAY_SLOT_COUNTS,
|
||||
backfill_ote_prices,
|
||||
count_ote_slots_prague_day,
|
||||
ote_prague_day_slots_look_complete,
|
||||
)
|
||||
|
||||
PRAGUE = ZoneInfo("Europe/Prague")
|
||||
@@ -82,7 +83,7 @@ async def _dry_run_missing(
|
||||
if d > today_prague:
|
||||
break
|
||||
n = await count_ote_slots_prague_day(conn, d)
|
||||
if n < OTE_EXPECTED_SLOTS:
|
||||
if not ote_prague_day_slots_look_complete(n):
|
||||
out.append(d)
|
||||
d += timedelta(days=1)
|
||||
return out
|
||||
@@ -136,9 +137,9 @@ async def main_async(args: argparse.Namespace) -> int:
|
||||
if args.dry_run:
|
||||
missing = await _dry_run_missing(conn, start, end, today_prague)
|
||||
logging.info(
|
||||
"Dry-run: %s chybějících nebo neúplných dní (< %s slotů)",
|
||||
"Dry-run: %s chybějících nebo neúplných dní (plný den = jedna z %s)",
|
||||
len(missing),
|
||||
OTE_EXPECTED_SLOTS,
|
||||
sorted(OTE_FULL_DAY_SLOT_COUNTS),
|
||||
)
|
||||
for md in missing[:50]:
|
||||
n = await count_ote_slots_prague_day(conn, md)
|
||||
@@ -198,7 +199,7 @@ def main() -> None:
|
||||
parser.add_argument(
|
||||
"--force",
|
||||
action="store_true",
|
||||
help="Stáhnout znovu i dny s plnými %s sloty" % OTE_EXPECTED_SLOTS,
|
||||
help="Stáhnout znovu i dny s plným počtem slotů OTE (92/96/100)",
|
||||
)
|
||||
parser.add_argument("--dry-run", action="store_true", help="Jen vypsat chybějící dny")
|
||||
parser.add_argument(
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user