ote discord notifikace error
This commit is contained in:
@@ -15,6 +15,7 @@ from app.config import get_settings
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
_WEBHOOK_CACHE: dict[tuple[int, str], str] = {}
|
_WEBHOOK_CACHE: dict[tuple[int, str], str] = {}
|
||||||
|
_OTE_IMPORT_ALERT_CACHE: dict[tuple[str, str], float] = {}
|
||||||
|
|
||||||
|
|
||||||
async def _get_site_webhook_url(
|
async def _get_site_webhook_url(
|
||||||
@@ -214,6 +215,46 @@ async def send_discord(
|
|||||||
return False
|
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(
|
async def notify_modbus_mismatch(
|
||||||
conn: asyncpg.Connection | None,
|
conn: asyncpg.Connection | None,
|
||||||
site_id: int | None,
|
site_id: int | None,
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import httpx
|
|||||||
|
|
||||||
from app.config import get_settings
|
from app.config import get_settings
|
||||||
from app.db_json import fetch_json
|
from app.db_json import fetch_json
|
||||||
|
from services.notification_service import notify_ote_import_format_changed
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -170,6 +171,17 @@ async def import_ote_prices_for_day(
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
detail = str(e).strip() or e.__class__.__name__
|
detail = str(e).strip() or e.__class__.__name__
|
||||||
logger.error("OTE import DB error pro %s: %s", day_str, detail, exc_info=True)
|
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
|
short = detail[:200] if len(detail) > 200 else detail
|
||||||
return -1, day_str, 0.0, f"db_import:{e.__class__.__name__}: {short}"
|
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:
|
except Exception as e:
|
||||||
detail = str(e).strip() or e.__class__.__name__
|
detail = str(e).strip() or e.__class__.__name__
|
||||||
logger.error("OTE import DB error: %s", detail, exc_info=True)
|
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
|
short = detail[:200] if len(detail) > 200 else detail
|
||||||
return -1, date_str, 0.0, f"db_import:{e.__class__.__name__}: {short}"
|
return -1, date_str, 0.0, f"db_import:{e.__class__.__name__}: {short}"
|
||||||
|
|||||||
@@ -149,6 +149,7 @@ PRICE_IMPORT_RETRY_BACKOFF_SEC=300
|
|||||||
## Monitoring a alerting
|
## Monitoring a alerting
|
||||||
|
|
||||||
- Alert pokud do 16:00 nejsou v DB ceny na zítřek
|
- 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í)
|
- 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ů
|
- Endpoint `GET /health/prices?date=YYYY-MM-DD` → vrátí počet importovaných intervalů
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user