From 2eeab58c8e52f0545a75ba91e3f24277bbf70811 Mon Sep 17 00:00:00 2001 From: Dusan Vojacek Date: Wed, 29 Apr 2026 14:07:42 +0200 Subject: [PATCH] ote discord notifikace error --- backend/services/notification_service.py | 41 ++++++++++++++++++++++++ backend/services/price_importer.py | 23 +++++++++++++ docs/04-modules/market-prices.md | 1 + 3 files changed, 65 insertions(+) diff --git a/backend/services/notification_service.py b/backend/services/notification_service.py index 302ce68..3312823 100644 --- a/backend/services/notification_service.py +++ b/backend/services/notification_service.py @@ -15,6 +15,7 @@ from app.config import get_settings logger = logging.getLogger(__name__) _WEBHOOK_CACHE: dict[tuple[int, str], str] = {} +_OTE_IMPORT_ALERT_CACHE: dict[tuple[str, str], float] = {} async def _get_site_webhook_url( @@ -214,6 +215,46 @@ async def send_discord( return False +def _should_send_ote_alert(date_str: str, signature: str, *, cooldown_s: float) -> bool: + now = datetime.now(timezone.utc).timestamp() + key = (str(date_str), str(signature)) + last = _OTE_IMPORT_ALERT_CACHE.get(key) + if last is not None and (now - last) < cooldown_s: + return False + _OTE_IMPORT_ALERT_CACHE[key] = now + return True + + +async def notify_ote_import_format_changed( + conn: asyncpg.Connection | None, + *, + report_date: str, + error_detail: str, + url: str, +) -> None: + """ + Discord alert pro situaci, kdy OTE změnilo formát chart-data a import selže na parseru v DB. + + Dedup: stejný report_date + stejná chyba se pošle max 1× za cooldown. + """ + signature = (error_detail or "").strip().splitlines()[0][:160] + if not _should_send_ote_alert(report_date, signature, cooldown_s=6 * 3600): + return + + detail = (error_detail or "").strip() + if len(detail) > 1600: + detail = detail[:1600] + "…" + msg = ( + f"**OTE import selhal – pravděpodobná změna formátu dat**\n" + f"Report date: `{report_date}`\n" + f"URL: `{url}`\n" + f"Chyba: {detail}\n" + f"Doporučení: zkontrolovat `ems.fn_ote_parse_15m_price_json` (tooltipy / struktura payloadu) " + f"a upravit parser." + ) + await send_discord(conn, site_id=None, message=msg, level="critical") + + async def notify_modbus_mismatch( conn: asyncpg.Connection | None, site_id: int | None, diff --git a/backend/services/price_importer.py b/backend/services/price_importer.py index f2de5e2..3f533dd 100644 --- a/backend/services/price_importer.py +++ b/backend/services/price_importer.py @@ -12,6 +12,7 @@ import httpx from app.config import get_settings from app.db_json import fetch_json +from services.notification_service import notify_ote_import_format_changed logger = logging.getLogger(__name__) @@ -170,6 +171,17 @@ async def import_ote_prices_for_day( except Exception as e: detail = str(e).strip() or e.__class__.__name__ logger.error("OTE import DB error pro %s: %s", day_str, detail, exc_info=True) + if ( + "OTE price dataLine not found" in detail + or "OTE price series:" in detail + or "cannot parse date from graph.title" in detail + ): + await notify_ote_import_format_changed( + conn, + report_date=day_str, + error_detail=detail, + url=OTE_URL.format(date=day_str), + ) short = detail[:200] if len(detail) > 200 else detail return -1, day_str, 0.0, f"db_import:{e.__class__.__name__}: {short}" @@ -318,5 +330,16 @@ async def import_ote_prices( except Exception as e: detail = str(e).strip() or e.__class__.__name__ logger.error("OTE import DB error: %s", detail, exc_info=True) + if ( + "OTE price dataLine not found" in detail + or "OTE price series:" in detail + or "cannot parse date from graph.title" in detail + ): + await notify_ote_import_format_changed( + db, + report_date=date_str, + error_detail=detail, + url=OTE_URL.format(date=date_str), + ) short = detail[:200] if len(detail) > 200 else detail return -1, date_str, 0.0, f"db_import:{e.__class__.__name__}: {short}" diff --git a/docs/04-modules/market-prices.md b/docs/04-modules/market-prices.md index a05d561..c6724cf 100644 --- a/docs/04-modules/market-prices.md +++ b/docs/04-modules/market-prices.md @@ -149,6 +149,7 @@ PRICE_IMPORT_RETRY_BACKOFF_SEC=300 ## Monitoring a alerting - Alert pokud do 16:00 nejsou v DB ceny na zítřek +- Discord (CRITICAL) pokud OTE změní formát `@@chart-data` tak, že DB parser (`ems.fn_ote_parse_15m_price_json`) nenajde vhodnou sérii (`dataLine[].tooltip`) nebo narazí na neočekávaný počet bodů; posílá `services.notification_service.notify_ote_import_format_changed`. - Log každého importu (datum, počet intervalů, zdroj, trvání) - Endpoint `GET /health/prices?date=YYYY-MM-DD` → vrátí počet importovaných intervalů