A previous `git pull --rebase origin main` dropped 177 local commits,
losing 3400+ files across admin-v2, backend, studio-v2, website,
klausur-service, and many other services. The partial restore attempt
(660295e2) only recovered some files.
This commit restores all missing files from pre-rebase ref 98933f5e
while preserving post-rebase additions (night-scheduler, night-mode UI,
NightModeWidget dashboard integration).
Restored features include:
- AI Module Sidebar (FAB), OCR Labeling, OCR Compare
- GPU Dashboard, RAG Pipeline, Magic Help
- Klausur-Korrektur (8 files), Abitur-Archiv (5+ files)
- Companion, Zeugnisse-Crawler, Screen Flow
- Full backend, studio-v2, website, klausur-service
- All compliance SDKs, agent-core, voice-service
- CI/CD configs, documentation, scripts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
493 lines
21 KiB
Python
493 lines
21 KiB
Python
"""
|
|
Vorkonfigurierte Alert-Templates (Playbooks) für den Guided Mode.
|
|
|
|
Diese Templates ermöglichen Lehrern und Schulleitungen einen schnellen Einstieg
|
|
ohne RSS-Feeds oder Keywords manuell konfigurieren zu müssen.
|
|
|
|
Alle Texte in B1/B2 Deutsch, keine IT-Fachbegriffe.
|
|
"""
|
|
from typing import List, Dict, Any
|
|
from sqlalchemy.orm import Session
|
|
import uuid
|
|
|
|
# Standard Importance-Mapping (Score → 5 Stufen)
|
|
DEFAULT_IMPORTANCE_CONFIG = {
|
|
"kritisch": 0.90, # Ab 90% → Kritisch
|
|
"dringend": 0.75, # 75-90% → Dringend
|
|
"wichtig": 0.60, # 60-75% → Wichtig
|
|
"pruefen": 0.40, # 40-60% → Zu prüfen
|
|
# Alles unter 40% → Info
|
|
}
|
|
|
|
# Importance-Mapping für zeitkritische Templates (z.B. Fristen)
|
|
DEADLINE_IMPORTANCE_CONFIG = {
|
|
"kritisch": 0.85,
|
|
"dringend": 0.70,
|
|
"wichtig": 0.55,
|
|
"pruefen": 0.35,
|
|
}
|
|
|
|
# Importance-Mapping für IT-Security (höhere Schwellen)
|
|
SECURITY_IMPORTANCE_CONFIG = {
|
|
"kritisch": 0.95,
|
|
"dringend": 0.85,
|
|
"wichtig": 0.70,
|
|
"pruefen": 0.50,
|
|
}
|
|
|
|
|
|
ALERT_TEMPLATES: List[Dict[str, Any]] = [
|
|
# =========================================================================
|
|
# 1. Förderprogramme & Fristen
|
|
# =========================================================================
|
|
{
|
|
"slug": "foerderprogramme",
|
|
"name": "Förderprogramme & Fristen",
|
|
"description": "Bleiben Sie informiert über Förderanträge, Deadlines und neue Programme für Schulen. Verpassen Sie keine Fristen mehr.",
|
|
"icon": "💰",
|
|
"category": "administration",
|
|
"target_roles": ["schulleitung"],
|
|
"topics_config": [
|
|
{
|
|
"name": "DigitalPakt & Bundes-Förderprogramme",
|
|
"keywords": ["DigitalPakt", "Förderprogramm Schule", "Bundesförderung Bildung", "BMBF Schule"],
|
|
},
|
|
{
|
|
"name": "Landesförderung Bildung",
|
|
"keywords": ["Landesförderung Schule", "Kultusministerium Förderung", "Schulträger Fördermittel"],
|
|
},
|
|
{
|
|
"name": "EU & Stiftungen",
|
|
"keywords": ["Erasmus+ Schule", "EU-Förderung Bildung", "Stiftung Schule", "ESF Bildung"],
|
|
},
|
|
],
|
|
"rules_config": [
|
|
{
|
|
"name": "Fristen priorisieren",
|
|
"conditions": [{"field": "title", "op": "in", "value": ["Frist", "Deadline", "bis zum", "Antragsfrist", "endet am"]}],
|
|
"action_type": "tag",
|
|
"action_config": {"tags": ["frist"]},
|
|
"priority": 100,
|
|
},
|
|
{
|
|
"name": "Ausschluss Stellenanzeigen",
|
|
"conditions": [{"field": "title", "op": "in", "value": ["Stellenanzeige", "Wir suchen", "Job"]}],
|
|
"action_type": "drop",
|
|
"priority": 90,
|
|
},
|
|
],
|
|
"profile_config": {
|
|
"priorities": [
|
|
{"label": "Fördermittel", "weight": 0.95, "keywords": ["Fördermittel", "Zuschuss", "Antrag", "Förderung"], "description": "Finanzielle Förderung für Schulen"},
|
|
{"label": "Fristen", "weight": 0.90, "keywords": ["Frist", "Deadline", "Stichtag", "Bewerbungsschluss"], "description": "Zeitkritische Informationen"},
|
|
{"label": "Digitalisierung", "weight": 0.80, "keywords": ["DigitalPakt", "Tablet", "WLAN", "digitale Ausstattung"], "description": "IT-Ausstattung und Infrastruktur"},
|
|
],
|
|
"exclusions": ["Stellenanzeige", "Werbung", "Seminar buchen", "Anzeige"],
|
|
},
|
|
"importance_config": DEADLINE_IMPORTANCE_CONFIG,
|
|
"max_cards_per_day": 8,
|
|
"sort_order": 1,
|
|
},
|
|
|
|
# =========================================================================
|
|
# 2. Abitur & Prüfungs-Updates
|
|
# =========================================================================
|
|
{
|
|
"slug": "abitur-updates",
|
|
"name": "Abitur & Prüfungs-Updates",
|
|
"description": "Aktuelle Informationen zu Abitur-Regelungen, Prüfungsformaten, EPA und KMK-Beschlüssen. Wichtig für alle Oberstufenlehrkräfte.",
|
|
"icon": "📝",
|
|
"category": "teaching",
|
|
"target_roles": ["lehrkraft", "schulleitung"],
|
|
"topics_config": [
|
|
{
|
|
"name": "Abitur-Vorgaben",
|
|
"keywords": ["Abitur Vorgaben", "Prüfungsaufgaben Abitur", "Abiturprüfung Änderung"],
|
|
},
|
|
{
|
|
"name": "KMK & EPA",
|
|
"keywords": ["KMK Beschluss", "EPA Abitur", "Bildungsstandards Abitur", "Operatoren Abitur"],
|
|
},
|
|
{
|
|
"name": "Prüfungsformate",
|
|
"keywords": ["Prüfungsformat Schule", "Klausur Oberstufe", "mündliche Prüfung Abitur"],
|
|
},
|
|
],
|
|
"rules_config": [
|
|
{
|
|
"name": "Kernfächer priorisieren",
|
|
"conditions": [{"field": "title", "op": "in", "value": ["Deutsch", "Mathematik", "Englisch", "Leistungskurs"]}],
|
|
"action_type": "tag",
|
|
"action_config": {"tags": ["kernfach"]},
|
|
"priority": 80,
|
|
},
|
|
],
|
|
"profile_config": {
|
|
"priorities": [
|
|
{"label": "Abitur-Änderungen", "weight": 0.95, "keywords": ["Abitur", "Prüfung", "Zentralabitur"], "description": "Änderungen an Prüfungsregelungen"},
|
|
{"label": "KMK-Beschlüsse", "weight": 0.85, "keywords": ["KMK", "Kultusministerkonferenz", "Bildungsstandards"], "description": "Bundesweite Regelungen"},
|
|
{"label": "Bewertung", "weight": 0.75, "keywords": ["Bewertung", "Notenschlüssel", "Erwartungshorizont"], "description": "Bewertungskriterien"},
|
|
],
|
|
"exclusions": ["Nachhilfe", "Abiturtraining", "Lernhilfe kaufen"],
|
|
},
|
|
"importance_config": DEFAULT_IMPORTANCE_CONFIG,
|
|
"max_cards_per_day": 6,
|
|
"sort_order": 2,
|
|
},
|
|
|
|
# =========================================================================
|
|
# 3. Fortbildungen für Lehrkräfte
|
|
# =========================================================================
|
|
{
|
|
"slug": "fortbildungen",
|
|
"name": "Fortbildungen für Lehrkräfte",
|
|
"description": "Relevante Fortbildungsangebote in Ihrer Region. Filtern Sie nach Fach, Format und Anbieter.",
|
|
"icon": "🎓",
|
|
"category": "teaching",
|
|
"target_roles": ["lehrkraft"],
|
|
"topics_config": [
|
|
{
|
|
"name": "Landesinstitut Fortbildungen",
|
|
"keywords": ["Fortbildung Lehrer", "Landesinstitut Lehrerfortbildung", "Pädagogische Fortbildung"],
|
|
},
|
|
{
|
|
"name": "Digitale Kompetenzen",
|
|
"keywords": ["Fortbildung digital", "Medienkompetenz Lehrer", "digitale Bildung Fortbildung"],
|
|
},
|
|
{
|
|
"name": "Fachfortbildungen",
|
|
"keywords": ["Fachfortbildung", "Unterrichtsentwicklung", "Didaktik Fortbildung"],
|
|
},
|
|
],
|
|
"rules_config": [
|
|
{
|
|
"name": "Online-Formate taggen",
|
|
"conditions": [{"field": "title", "op": "in", "value": ["Online", "Webinar", "digital", "virtuell"]}],
|
|
"action_type": "tag",
|
|
"action_config": {"tags": ["online"]},
|
|
"priority": 70,
|
|
},
|
|
{
|
|
"name": "Kostenpflichtige ausschließen",
|
|
"conditions": [{"field": "snippet", "op": "in", "value": ["kostenpflichtig", "Teilnahmegebühr", "€"]}],
|
|
"action_type": "tag",
|
|
"action_config": {"tags": ["kostenpflichtig"]},
|
|
"priority": 60,
|
|
},
|
|
],
|
|
"profile_config": {
|
|
"priorities": [
|
|
{"label": "Kostenlose Fortbildungen", "weight": 0.90, "keywords": ["kostenlos", "kostenfrei", "Landesinstitut"], "description": "Staatliche Angebote"},
|
|
{"label": "Digitale Medien", "weight": 0.80, "keywords": ["digital", "Tablet", "Medienkompetenz"], "description": "Digitale Bildung"},
|
|
{"label": "Inklusion", "weight": 0.75, "keywords": ["Inklusion", "Förderbedarf", "Differenzierung"], "description": "Inklusiver Unterricht"},
|
|
],
|
|
"exclusions": ["Studium", "Bachelor", "Master", "Referendariat"],
|
|
},
|
|
"importance_config": DEFAULT_IMPORTANCE_CONFIG,
|
|
"max_cards_per_day": 10,
|
|
"sort_order": 3,
|
|
},
|
|
|
|
# =========================================================================
|
|
# 4. Datenschutz & Rechtsupdates
|
|
# =========================================================================
|
|
{
|
|
"slug": "datenschutz-recht",
|
|
"name": "Datenschutz & Rechtsupdates",
|
|
"description": "DSGVO-relevante Änderungen, Schulrecht und rechtliche Entwicklungen. Wichtig für Datenschutzbeauftragte und Schulleitungen.",
|
|
"icon": "⚖️",
|
|
"category": "administration",
|
|
"target_roles": ["schulleitung", "it_beauftragte"],
|
|
"topics_config": [
|
|
{
|
|
"name": "DSGVO Schule",
|
|
"keywords": ["DSGVO Schule", "Datenschutz Schüler", "Einwilligung Eltern", "personenbezogene Daten Schule"],
|
|
},
|
|
{
|
|
"name": "Schulrecht",
|
|
"keywords": ["Schulgesetz Änderung", "Schulordnung neu", "Schulrecht Urteil"],
|
|
},
|
|
{
|
|
"name": "Cloud & Software",
|
|
"keywords": ["Cloud Schule DSGVO", "Microsoft 365 Schule", "Videokonferenz Datenschutz"],
|
|
},
|
|
],
|
|
"rules_config": [
|
|
{
|
|
"name": "Urteile priorisieren",
|
|
"conditions": [{"field": "title", "op": "in", "value": ["Urteil", "Gericht", "Beschluss", "Aufsichtsbehörde"]}],
|
|
"action_type": "tag",
|
|
"action_config": {"tags": ["urteil"]},
|
|
"priority": 90,
|
|
},
|
|
{
|
|
"name": "Handlungsbedarf markieren",
|
|
"conditions": [{"field": "title", "op": "in", "value": ["ab sofort", "verpflichtend", "muss", "Frist"]}],
|
|
"action_type": "tag",
|
|
"action_config": {"tags": ["handlungsbedarf"]},
|
|
"priority": 85,
|
|
},
|
|
],
|
|
"profile_config": {
|
|
"priorities": [
|
|
{"label": "DSGVO-Compliance", "weight": 0.95, "keywords": ["DSGVO", "Datenschutz", "Aufsichtsbehörde", "Bußgeld"], "description": "Datenschutzrechtliche Vorgaben"},
|
|
{"label": "Schulrecht", "weight": 0.90, "keywords": ["Schulgesetz", "Verordnung", "Erlass"], "description": "Rechtliche Änderungen"},
|
|
{"label": "Cloud-Dienste", "weight": 0.80, "keywords": ["Cloud", "Microsoft", "Google", "Zoom"], "description": "Software und Dienste"},
|
|
],
|
|
"exclusions": ["Werbung", "Seminar buchen", "Beratung anfragen"],
|
|
},
|
|
"importance_config": DEADLINE_IMPORTANCE_CONFIG,
|
|
"max_cards_per_day": 8,
|
|
"sort_order": 4,
|
|
},
|
|
|
|
# =========================================================================
|
|
# 5. IT-Security Warnungen
|
|
# =========================================================================
|
|
{
|
|
"slug": "it-security",
|
|
"name": "IT-Security Warnungen",
|
|
"description": "Sicherheitswarnungen und Patches für Schul-IT-Systeme. Kritisch für IT-Beauftragte und Administratoren.",
|
|
"icon": "🔒",
|
|
"category": "it",
|
|
"target_roles": ["it_beauftragte"],
|
|
"topics_config": [
|
|
{
|
|
"name": "BSI & CERT Warnungen",
|
|
"keywords": ["BSI Warnung", "CERT-Bund", "Sicherheitslücke", "CVE Schule"],
|
|
},
|
|
{
|
|
"name": "Schul-Software Security",
|
|
"keywords": ["Moodle Sicherheit", "IServ Update", "WebUntis Sicherheit", "Nextcloud Patch"],
|
|
},
|
|
{
|
|
"name": "Phishing & Malware",
|
|
"keywords": ["Phishing Schule", "Ransomware Bildung", "Malware Warnung"],
|
|
},
|
|
],
|
|
"rules_config": [
|
|
{
|
|
"name": "CVE-Meldungen priorisieren",
|
|
"conditions": [{"field": "title", "op": "regex", "value": "CVE-\\d{4}-\\d+"}],
|
|
"action_type": "tag",
|
|
"action_config": {"tags": ["cve"]},
|
|
"priority": 100,
|
|
},
|
|
{
|
|
"name": "Kritische Patches",
|
|
"conditions": [{"field": "title", "op": "in", "value": ["kritisch", "Notfall-Patch", "sofort", "0-day"]}],
|
|
"action_type": "tag",
|
|
"action_config": {"tags": ["kritisch"]},
|
|
"priority": 95,
|
|
},
|
|
],
|
|
"profile_config": {
|
|
"priorities": [
|
|
{"label": "CVE-Warnungen", "weight": 0.98, "keywords": ["CVE", "Sicherheitslücke", "Schwachstelle", "Exploit"], "description": "Bekannte Sicherheitslücken"},
|
|
{"label": "Schul-Software", "weight": 0.90, "keywords": ["Moodle", "IServ", "WebUntis", "Nextcloud", "Schulportal"], "description": "Häufig genutzte Schulsoftware"},
|
|
{"label": "Patches", "weight": 0.85, "keywords": ["Patch", "Update", "Sicherheitsupdate", "Hotfix"], "description": "Sicherheitsupdates"},
|
|
],
|
|
"exclusions": ["Werbung", "Schulung kaufen", "Penetrationstest Angebot"],
|
|
},
|
|
"importance_config": SECURITY_IMPORTANCE_CONFIG,
|
|
"max_cards_per_day": 5,
|
|
"sort_order": 5,
|
|
},
|
|
|
|
# =========================================================================
|
|
# 6. Wettbewerbe & Projekte
|
|
# =========================================================================
|
|
{
|
|
"slug": "wettbewerbe-projekte",
|
|
"name": "Wettbewerbe & Projekte",
|
|
"description": "MINT-Wettbewerbe, Erasmus-Projekte, Schülerwettbewerbe und Schulpartnerschaften. Entdecken Sie Chancen für Ihre Schüler.",
|
|
"icon": "🏆",
|
|
"category": "teaching",
|
|
"target_roles": ["lehrkraft", "schulleitung"],
|
|
"topics_config": [
|
|
{
|
|
"name": "MINT-Wettbewerbe",
|
|
"keywords": ["MINT Wettbewerb Schule", "Jugend forscht", "Mathematik Olympiade", "Informatik Biber"],
|
|
},
|
|
{
|
|
"name": "Erasmus & Austausch",
|
|
"keywords": ["Erasmus+ Schule", "Schüleraustausch", "Schulpartnerschaft Europa"],
|
|
},
|
|
{
|
|
"name": "Kreativ & Sozial",
|
|
"keywords": ["Schülerwettbewerb Kunst", "Vorlesewettbewerb", "Umweltpreis Schule", "Sozialer Tag"],
|
|
},
|
|
],
|
|
"rules_config": [
|
|
{
|
|
"name": "Anmeldefristen",
|
|
"conditions": [{"field": "title", "op": "in", "value": ["Anmeldung", "Bewerbung", "Frist", "bis zum"]}],
|
|
"action_type": "tag",
|
|
"action_config": {"tags": ["frist"]},
|
|
"priority": 85,
|
|
},
|
|
],
|
|
"profile_config": {
|
|
"priorities": [
|
|
{"label": "Wettbewerbe", "weight": 0.90, "keywords": ["Wettbewerb", "Preis", "Auszeichnung", "Gewinner"], "description": "Schülerwettbewerbe"},
|
|
{"label": "Erasmus+", "weight": 0.85, "keywords": ["Erasmus", "EU-Programm", "Mobilität", "Austausch"], "description": "Europäische Programme"},
|
|
{"label": "MINT", "weight": 0.80, "keywords": ["MINT", "Naturwissenschaft", "Technik", "Informatik"], "description": "MINT-Bereich"},
|
|
],
|
|
"exclusions": ["Stellenanzeige", "Praktikum", "Ausbildung"],
|
|
},
|
|
"importance_config": DEADLINE_IMPORTANCE_CONFIG,
|
|
"max_cards_per_day": 8,
|
|
"sort_order": 6,
|
|
},
|
|
|
|
# =========================================================================
|
|
# 7. Personalmarkt (Optional/Premium)
|
|
# =========================================================================
|
|
{
|
|
"slug": "personalmarkt",
|
|
"name": "Personalmarkt & Stellen",
|
|
"description": "Stellenangebote für Lehrkräfte und Vertretungsstellen in Ihrer Region. Ideal für Schulleitungen mit Personalbedarf.",
|
|
"icon": "👥",
|
|
"category": "administration",
|
|
"target_roles": ["schulleitung"],
|
|
"is_premium": True,
|
|
"topics_config": [
|
|
{
|
|
"name": "Lehrerstellen",
|
|
"keywords": ["Lehrerstelle", "Lehrkraft gesucht", "Einstellung Lehrer"],
|
|
},
|
|
{
|
|
"name": "Vertretungslehrkräfte",
|
|
"keywords": ["Vertretungslehrkraft", "befristete Stelle Lehrer", "Krankheitsvertretung Schule"],
|
|
},
|
|
{
|
|
"name": "Schulsozialarbeit",
|
|
"keywords": ["Schulsozialarbeiter", "Sozialpädagoge Schule", "Schulpsychologe"],
|
|
},
|
|
],
|
|
"rules_config": [],
|
|
"profile_config": {
|
|
"priorities": [
|
|
{"label": "Festanstellung", "weight": 0.90, "keywords": ["unbefristet", "Festanstellung", "Planstelle"], "description": "Feste Stellen"},
|
|
{"label": "Region", "weight": 0.85, "keywords": [], "description": "Stellen in der Region"},
|
|
],
|
|
"exclusions": ["Nachhilfe", "Privatlehrer", "freiberuflich"],
|
|
},
|
|
"importance_config": DEFAULT_IMPORTANCE_CONFIG,
|
|
"max_cards_per_day": 10,
|
|
"sort_order": 7,
|
|
},
|
|
|
|
# =========================================================================
|
|
# 8. Krisenkommunikation (Optional/Premium)
|
|
# =========================================================================
|
|
{
|
|
"slug": "krisenkommunikation",
|
|
"name": "Krisenkommunikation",
|
|
"description": "Wichtige Meldungen für Schulen in Krisensituationen: Unwetter, Streiks, Verkehrsstörungen, Gesundheitswarnungen.",
|
|
"icon": "⚠️",
|
|
"category": "administration",
|
|
"target_roles": ["schulleitung"],
|
|
"is_premium": True,
|
|
"topics_config": [
|
|
{
|
|
"name": "Wetter & Naturereignisse",
|
|
"keywords": ["Unwetterwarnung Schule", "Schulausfall Wetter", "Hitzefrei"],
|
|
},
|
|
{
|
|
"name": "Verkehr & ÖPNV",
|
|
"keywords": ["Streik ÖPNV", "Verkehrsstörung", "Busausfall Schule"],
|
|
},
|
|
{
|
|
"name": "Gesundheit",
|
|
"keywords": ["Gesundheitswarnung Schule", "Infektionsgefahr", "Hygienemaßnahme"],
|
|
},
|
|
],
|
|
"rules_config": [
|
|
{
|
|
"name": "Sofortmeldungen",
|
|
"conditions": [{"field": "title", "op": "in", "value": ["Warnung", "Achtung", "Sofort", "Gefahr", "Ausfall"]}],
|
|
"action_type": "tag",
|
|
"action_config": {"tags": ["sofort"]},
|
|
"priority": 100,
|
|
},
|
|
],
|
|
"profile_config": {
|
|
"priorities": [
|
|
{"label": "Schulausfall", "weight": 0.98, "keywords": ["Schulausfall", "unterrichtsfrei", "Schule geschlossen"], "description": "Schulschließungen"},
|
|
{"label": "Warnungen", "weight": 0.95, "keywords": ["Warnung", "Gefahr", "Achtung"], "description": "Wichtige Warnungen"},
|
|
],
|
|
"exclusions": ["Werbung", "Versicherung"],
|
|
},
|
|
"importance_config": SECURITY_IMPORTANCE_CONFIG, # Schnelle Eskalation
|
|
"max_cards_per_day": 5,
|
|
"sort_order": 8,
|
|
},
|
|
]
|
|
|
|
|
|
def seed_templates(db: Session, force_update: bool = False) -> int:
|
|
"""
|
|
Fügt die vordefinierten Templates in die Datenbank ein.
|
|
|
|
Args:
|
|
db: SQLAlchemy Session
|
|
force_update: Wenn True, werden bestehende Templates aktualisiert
|
|
|
|
Returns:
|
|
Anzahl der eingefügten/aktualisierten Templates
|
|
"""
|
|
from alerts_agent.db.models import AlertTemplateDB
|
|
|
|
count = 0
|
|
|
|
for template_data in ALERT_TEMPLATES:
|
|
existing = db.query(AlertTemplateDB).filter_by(slug=template_data["slug"]).first()
|
|
|
|
if existing and not force_update:
|
|
continue
|
|
|
|
if existing and force_update:
|
|
# Update existing template
|
|
for key, value in template_data.items():
|
|
if hasattr(existing, key):
|
|
setattr(existing, key, value)
|
|
count += 1
|
|
else:
|
|
# Create new template
|
|
template = AlertTemplateDB(
|
|
id=str(uuid.uuid4()),
|
|
**template_data
|
|
)
|
|
db.add(template)
|
|
count += 1
|
|
|
|
db.commit()
|
|
return count
|
|
|
|
|
|
def get_templates_for_role(role: str) -> List[Dict[str, Any]]:
|
|
"""
|
|
Gibt empfohlene Templates für eine bestimmte Rolle zurück.
|
|
|
|
Args:
|
|
role: "lehrkraft", "schulleitung", oder "it_beauftragte"
|
|
|
|
Returns:
|
|
Liste der passenden Templates (sortiert nach Empfehlung)
|
|
"""
|
|
return [
|
|
t for t in ALERT_TEMPLATES
|
|
if role in t.get("target_roles", []) and not t.get("is_premium", False)
|
|
]
|
|
|
|
|
|
def get_template_by_slug(slug: str) -> Dict[str, Any] | None:
|
|
"""
|
|
Gibt ein Template anhand seines Slugs zurück.
|
|
"""
|
|
for t in ALERT_TEMPLATES:
|
|
if t["slug"] == slug:
|
|
return t
|
|
return None
|