OTE informatin discord
This commit is contained in:
@@ -16,6 +16,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
_WEBHOOK_CACHE: dict[tuple[int, str], str] = {}
|
||||
_OTE_IMPORT_ALERT_CACHE: dict[tuple[str, str], float] = {}
|
||||
_OTE_IMPORT_OK_CACHE: dict[str, float] = {}
|
||||
|
||||
|
||||
async def _get_site_webhook_url(
|
||||
@@ -255,6 +256,65 @@ async def notify_ote_import_format_changed(
|
||||
await send_discord(conn, site_id=None, message=msg, level="critical")
|
||||
|
||||
|
||||
def _should_send_ote_ok(report_date: str, *, cooldown_s: float) -> bool:
|
||||
now = datetime.now(timezone.utc).timestamp()
|
||||
key = str(report_date)
|
||||
last = _OTE_IMPORT_OK_CACHE.get(key)
|
||||
if last is not None and (now - last) < cooldown_s:
|
||||
return False
|
||||
_OTE_IMPORT_OK_CACHE[key] = now
|
||||
return True
|
||||
|
||||
|
||||
async def notify_ote_import_ok_brief(
|
||||
conn: asyncpg.Connection | None,
|
||||
*,
|
||||
report_date: str,
|
||||
brief: dict,
|
||||
url: str,
|
||||
) -> None:
|
||||
"""
|
||||
Info notifikace po úspěšném importu kompletního dne OTE (stručná analýza "co čekat zítra").
|
||||
Dedup: 1× za cooldown na report_date.
|
||||
"""
|
||||
if not _should_send_ote_ok(report_date, cooldown_s=20 * 3600):
|
||||
return
|
||||
|
||||
def _f(x, default: float = 0.0) -> float:
|
||||
try:
|
||||
if x is None:
|
||||
return default
|
||||
return float(x)
|
||||
except Exception:
|
||||
return default
|
||||
|
||||
min_p = _f(brief.get("min_price"))
|
||||
max_p = _f(brief.get("max_price"))
|
||||
|
||||
raw_signals = brief.get("signals") or []
|
||||
signals: list[str] = []
|
||||
if isinstance(raw_signals, list):
|
||||
for s in raw_signals[:6]:
|
||||
if not isinstance(s, dict):
|
||||
continue
|
||||
title = str(s.get("title") or s.get("code") or "").strip()
|
||||
detail = str(s.get("detail") or "").strip()
|
||||
if title and detail:
|
||||
signals.append(f"{title} ({detail})")
|
||||
elif title:
|
||||
signals.append(title)
|
||||
if not signals:
|
||||
signals.append("běžný den (bez extrémů)")
|
||||
|
||||
msg = (
|
||||
f"OTE ceny staženy – `{report_date}`\n"
|
||||
f"URL: `{url}`\n"
|
||||
f"Min: **{min_p:.3f}** | Max: **{max_p:.3f}** Kč/kWh\n"
|
||||
f"Signály: " + "; ".join(f"**{s}**" for s in signals)
|
||||
)
|
||||
await send_discord(conn, site_id=None, message=msg, level="info")
|
||||
|
||||
|
||||
async def notify_modbus_mismatch(
|
||||
conn: asyncpg.Connection | None,
|
||||
site_id: int | None,
|
||||
|
||||
@@ -12,7 +12,10 @@ 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
|
||||
from services.notification_service import (
|
||||
notify_ote_import_format_changed,
|
||||
notify_ote_import_ok_brief,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -154,6 +157,7 @@ async def import_ote_prices_for_day(
|
||||
stats_after = json.loads(stats_after)
|
||||
first_price = stats_after.get("first_price")
|
||||
n_imported = int(stats_after.get("count") or 0)
|
||||
is_complete = bool(stats_after.get("is_complete"))
|
||||
if not ote_prague_day_slots_look_complete(n_imported):
|
||||
logger.warning(
|
||||
"OTE: %s slotů pro %s (plný den = jedna z %s; jinak neúplná data)",
|
||||
@@ -161,6 +165,21 @@ async def import_ote_prices_for_day(
|
||||
day_str,
|
||||
sorted(OTE_FULL_DAY_SLOT_COUNTS),
|
||||
)
|
||||
if is_complete:
|
||||
brief = await fetch_json(
|
||||
conn,
|
||||
"select ems.fn_ote_day_signals_prague($1::date, $2::int)",
|
||||
target_day,
|
||||
14,
|
||||
)
|
||||
if not isinstance(brief, dict):
|
||||
brief = json.loads(brief)
|
||||
await notify_ote_import_ok_brief(
|
||||
conn,
|
||||
report_date=day_str,
|
||||
brief=brief if isinstance(brief, dict) else {},
|
||||
url=OTE_URL.format(date=day_str),
|
||||
)
|
||||
logger.info(
|
||||
"OTE import OK: %s slotů (upsert) pro %s, první cena %.4f Kč/kWh",
|
||||
n,
|
||||
@@ -305,6 +324,7 @@ async def import_ote_prices(
|
||||
stats_after = json.loads(stats_after)
|
||||
first_price = stats_after.get("first_price")
|
||||
n_imported = int(stats_after.get("count") or 0)
|
||||
is_complete = bool(stats_after.get("is_complete"))
|
||||
incomplete = not ote_prague_day_slots_look_complete(n_imported or 0)
|
||||
if incomplete:
|
||||
now_p = datetime.now(ZoneInfo("Europe/Prague"))
|
||||
@@ -320,6 +340,21 @@ async def import_ote_prices(
|
||||
date_str,
|
||||
sorted(OTE_FULL_DAY_SLOT_COUNTS),
|
||||
)
|
||||
if is_complete:
|
||||
brief = await fetch_json(
|
||||
db,
|
||||
"select ems.fn_ote_day_signals_prague($1::date, $2::int)",
|
||||
target_day,
|
||||
14,
|
||||
)
|
||||
if not isinstance(brief, dict):
|
||||
brief = json.loads(brief)
|
||||
await notify_ote_import_ok_brief(
|
||||
db,
|
||||
report_date=date_str,
|
||||
brief=brief if isinstance(brief, dict) else {},
|
||||
url=OTE_URL.format(date=date_str),
|
||||
)
|
||||
logger.info(
|
||||
"OTE import OK: %s slotů pro %s, první cena %.4f Kč/kWh",
|
||||
n,
|
||||
|
||||
Reference in New Issue
Block a user