Selbst gehostete PWA, die Visitenkarten von einem Foto freistellt, ausliest und als Kontakt bereitstellt. - Stapelscan: OpenCV findet die Kartenrechtecke über mehrere Binärmasken, entzerrt sie perspektivisch und schneidet sie einzeln aus. Ohne Fund gilt das ganze Foto als eine Karte. - Extraktion: ein Aufruf je Zuschnitt an das Vision-Modell mit JSON-Schema. Kein vorgeschaltetes OCR - das würde Layout und Schriftgrößen wegwerfen, aus denen die Feldzuordnung entsteht. - Metadaten: Aufnahmezeit und GPS aus den EXIF-Daten des Fotos, Ortsname über Nominatim, Browserstandort nur als Rückfallebene. - Übersicht mit Volltextsuche und Filtern, Detailansicht mit Korrekturmaske. - Notizfeld je Karte, Erinnerungen per Mail inklusive Nachholen verpasster Termine nach einem Neustart. - vCard 3.0 einzeln und als Sammeldatei, Karte gilt danach als exportiert. - Anmeldung über ein Passwort, Sitzung als signiertes Cookie. - Deployment per Tag-Push nach den Konventionen in DEPLOY.md. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
56 lines
1.5 KiB
Python
56 lines
1.5 KiB
Python
"""Konfiguration. Alle Werte kommen aus der Umgebung (im Betrieb aus .env)."""
|
|
import os
|
|
from pathlib import Path
|
|
|
|
|
|
def _s(name: str, default: str = "") -> str:
|
|
return os.environ.get(name, default).strip()
|
|
|
|
|
|
def _i(name: str, default: int) -> int:
|
|
try:
|
|
return int(_s(name) or default)
|
|
except ValueError:
|
|
return default
|
|
|
|
|
|
def _b(name: str, default: bool) -> bool:
|
|
v = _s(name).lower()
|
|
if not v:
|
|
return default
|
|
return v in ("1", "true", "yes", "on")
|
|
|
|
|
|
DATA_DIR = Path(_s("DATA_DIR", "./data"))
|
|
IMAGE_DIR = DATA_DIR / "images"
|
|
DB_PATH = DATA_DIR / "cards.db"
|
|
|
|
PORT = _i("PORT", 8080)
|
|
|
|
# Leeres Passwort schaltet die Anmeldung ab - nur fuer die lokale Entwicklung.
|
|
APP_PASSWORD = _s("APP_PASSWORD")
|
|
SECRET_KEY = _s("SECRET_KEY", "dev-only-insecure-secret")
|
|
SESSION_DAYS = _i("SESSION_DAYS", 30)
|
|
|
|
ANTHROPIC_API_KEY = _s("ANTHROPIC_API_KEY")
|
|
ANTHROPIC_MODEL = _s("ANTHROPIC_MODEL", "claude-opus-5")
|
|
EXTRACT_CONCURRENCY = _i("EXTRACT_CONCURRENCY", 5)
|
|
|
|
SMTP_HOST = _s("SMTP_HOST")
|
|
SMTP_PORT = _i("SMTP_PORT", 587)
|
|
SMTP_USER = _s("SMTP_USER")
|
|
SMTP_PASSWORD = _s("SMTP_PASSWORD")
|
|
SMTP_FROM = _s("SMTP_FROM") or SMTP_USER
|
|
SMTP_STARTTLS = _b("SMTP_STARTTLS", True)
|
|
REMINDER_TO = _s("REMINDER_TO")
|
|
|
|
REVERSE_GEOCODE = _b("REVERSE_GEOCODE", True)
|
|
NOMINATIM_URL = _s("NOMINATIM_URL", "https://nominatim.openstreetmap.org/reverse")
|
|
USER_AGENT = _s("USER_AGENT", "business-card-scanner/1.0 (self-hosted)")
|
|
|
|
PUBLIC_BASE_URL = _s("PUBLIC_BASE_URL").rstrip("/")
|
|
|
|
|
|
def ensure_dirs() -> None:
|
|
IMAGE_DIR.mkdir(parents=True, exist_ok=True)
|