Replace pandas with stdlib csv, apprise with direct Slack webhook, switch to opencv-headless

- Rewrite csv_exporter.py to use stdlib csv.DictWriter instead of pandas DataFrame
- Rewrite notifications.py to use aiohttp direct Slack webhook instead of apprise
- Switch opencv-python to opencv-python-headless in pyproject.toml
- Move httpx from dev to prod dependencies
- Remove pandas and apprise from mypy ignore_missing_imports
This commit is contained in:
Viktor Barzin 2026-02-21 19:47:10 +00:00
parent cde3540a1e
commit 3d9550c7f1
No known key found for this signature in database
GPG key ID: 0EB088298288D958
3 changed files with 67 additions and 52 deletions

View file

@ -1,29 +1,27 @@
from abc import abstractmethod
import apprise
from functools import lru_cache
import json
import logging
import os
import aiohttp
class Surface:
@abstractmethod
def connection_string(self) -> str | None: ...
class Slack(Surface):
def connection_string(self) -> str | None:
return os.environ.get("SLACK_WEBHOOK_URL")
@lru_cache(maxsize=None)
def get_notifier() -> apprise.Apprise:
surfaces = [Slack()]
obj = apprise.Apprise()
for surface in surfaces:
if conn := surface.connection_string():
obj.add(conn)
return obj
logger = logging.getLogger(__name__)
async def send_notification(body: str, title: str = "") -> bool:
notifier = get_notifier()
return await notifier.async_notify(body=body, title=title)
webhook_url = os.environ.get("SLACK_WEBHOOK_URL")
if not webhook_url:
logger.debug("No SLACK_WEBHOOK_URL configured, skipping notification")
return False
text = f"*{title}*\n{body}" if title else body
try:
async with aiohttp.ClientSession() as session:
async with session.post(
webhook_url,
data=json.dumps({"text": text}),
headers={"Content-Type": "application/json"},
) as resp:
return resp.status == 200
except Exception:
logger.exception("Failed to send Slack notification")
return False