From 7da164999866e3640d4933e50cd1a96b3930a1cd Mon Sep 17 00:00:00 2001 From: Alicia Date: Thu, 25 Jun 2026 15:27:53 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20komplette=20Site=20=E2=80=94=2019=20Rou?= =?UTF-8?q?ten,=2024=20Artikel,=20Leistungen,=20IT-Service,=20FAQ,=20Konta?= =?UTF-8?q?kt,=20CRM-Formular?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app.py | 422 ++++++++++++++++++++++++++++++++++++++++++++++ tests/conftest.py | 7 + tests/test_app.py | 86 ++++++++++ 3 files changed, 515 insertions(+) create mode 100644 tests/conftest.py create mode 100644 tests/test_app.py diff --git a/app.py b/app.py index 5c50c63..05c3881 100644 --- a/app.py +++ b/app.py @@ -2,11 +2,13 @@ """IT-Hilfe-Sofort.de — Flask-Webapp für IT-Dienstleistungen im Gesundheitswesen.""" import os +import re from pathlib import Path from flask import Flask BASE_DIR = Path(__file__).parent +ARTIKEL_DIR = BASE_DIR PORT = int(os.environ.get("PORT", "5003")) app = Flask(__name__) @@ -124,6 +126,426 @@ def datenschutz() -> str: return _base_template("Datenschutz", content) +# ── Blog-Artikel-Datenbank ─────────────────────────────── +_ARTIKEL = [ + ( + "/blog/telematik-vorschriften", + "TI-Anschlusspflicht 2025", + "artikel-01-ti-anschlusspflicht.html", + ), + ( + "/blog/telematik-vorschriften", + "Elektronische Abrechnung ab 12/2026", + "artikel-02-elektronische-abrechnung.html", + ), + ( + "/blog/telematik-vorschriften", + "SMC-B-Karte — 33% Abdeckung", + "artikel-03-smc-b-karte.html", + ), + ( + "/blog/dsgvo-it-sicherheit", + "46,9 Mio. EUR DSGVO-Bugelder", + "artikel-04-dsgvo-bussgelder.html", + ), + ( + "/blog/dsgvo-it-sicherheit", + "BSI: 16 Schwachstellen Pflegesoftware", + "artikel-05-bsi-schwachstellen.html", + ), + ( + "/blog/dsgvo-it-sicherheit", + "138 Sicherheitsvorfaelle Gesundheitswesen", + "artikel-06-sicherheitsvorfaelle.html", + ), + ( + "/blog/telematik-vorschriften", + "NIS-2 Registrierungsfrist", + "artikel-07-nis2.html", + ), + ( + "/blog/fachkraeftemangel", + "200.000 unbesetzte Pflegestellen", + "artikel-08-fachkraeftemangel.html", + ), + ( + "/blog/fachkraeftemangel", + "62% Altenpflege — Personalplanung", + "artikel-09-personalplanung.html", + ), + ( + "/blog/fachkraeftemangel", + "KI-Sprachassistent spart 27% Zeit", + "artikel-10-ki-sprachassistent.html", + ), + ( + "/blog/foerderung-finanzierung", + "Bis 12.000 EUR Digitalisierungszuschuss", + "artikel-11-digitalisierungszuschuss.html", + ), + ( + "/blog/foerderung-finanzierung", + "DiPA — 70 EUR monatlich ab 2026", + "artikel-12-dipa.html", + ), + ( + "/blog/foerderung-finanzierung", + "Pflegeversicherung 1 Mrd. Defizit", + "artikel-13-pflegeversicherung.html", + ), + ( + "/blog/qualitaet-pruefung", + "QPR ambulant ab Juli 2026", + "artikel-14-qpr-ambulant.html", + ), + ( + "/blog/qualitaet-pruefung", + "PeBeM verpflichtend ab 2026", + "artikel-15-pebem.html", + ), + ( + "/blog/qualitaet-pruefung", + "MDK-Pruefung digital bestehen", + "artikel-16-mdk-pruefung.html", + ), + ("/blog/telematik-vorschriften", "ePA fuer alle — Opt-out", "artikel-17-epa.html"), + ( + "/blog/dsgvo-it-sicherheit", + "Cloud-Hosting Deutschland", + "artikel-18-cloud-hosting.html", + ), + ( + "/blog/praxis-tipps", + "Tablets fuer die Pflege — Top 5", + "artikel-19-tablets.html", + ), + ("/blog/praxis-tipps", "WLAN im Pflegeheim", "artikel-20-wlan.html"), + ("/blog/praxis-tipps", "Offline-Dokumentation", "artikel-21-offline.html"), + ( + "/blog/dsgvo-it-sicherheit", + "E-Mail-Verschluesselung", + "artikel-22-email-verschluesselung.html", + ), + ( + "/blog/praxis-tipps", + "Medikationsplan digital", + "artikel-23-medikationsplan.html", + ), + ( + "/blog/praxis-tipps", + "IT-Notfallplan 10-Punkte", + "artikel-24-it-notfallplan.html", + ), +] + +# ── Leistungen-Daten ───────────────────────────────────── +_LEISTUNGEN = { + "pflegedienste": { + "titel": "IT-Loesungen fuer ambulante Pflegedienste", + "cards": [ + ( + "Tourenplanung", + "Digitale Tourenplanung und mobile Datenerfassung vor Ort. Effizientere Routen und Dokumentation auf dem Tablet.", + ), + ( + "Mobile Dokumentation", + "Echtzeit-Dokumentation von Pflegemaßnahmen direkt im Einsatz. Synchronisiert mit der zentralen Verwaltung.", + ), + ( + "Abrechnung und Reporting", + "Automatisierte Abrechnung und Auswertungen. Weniger Verwaltungszeit, mehr Zeit fuer Patienten.", + ), + ], + }, + "pflegeheime": { + "titel": "IT-Loesungen fuer stationaere Pflegeheime", + "cards": [ + ( + "Heimverwaltung", + "Digitale Heimpleitung: Dienstplanung, Raummanagement, Budget-Controller. Alles in einer Plattform.", + ), + ( + "Biometrie und Zutrittssysteme", + "Fingerabdruck- und Gesichtserkennung fuer sicheren Zutritt. DSGVO-konform und benutzerfreundlich.", + ), + ( + "Dokumentationssysteme", + "Komplette digitale Dokumentation von Pflegeplanung bis Medikationsueberwachung. Vollstaendig auditfahig.", + ), + ], + }, + "tagespflegen": { + "titel": "IT-Loesungen fuer Tagespflegen", + "cards": [ + ( + "Terminbuchung", + "Online-Terminbuchung mit Kalendersynchronisation. Patienten koennen Termine direkt vereinbaren.", + ), + ( + "Fahrdienstverwaltung", + "Organisation von Fahrdiensten und Kurieren. Automatische Zuweisung und Echtzeit-Tracking.", + ), + ( + "Aktivierungsangebote", + "Digitales Planungs- und Monitoring-System fuer Aktivitaeten und Therapie-Programme im Tagesverlauf.", + ), + ], + }, +} + +# ── IT-Service-Daten ───────────────────────────────────── +_IT_SERVICE = { + "hardware": { + "titel": "IT-Hardware fuer das Gesundheitswesen", + "cards": [ + ( + "Server und Workstations", + "Auswahl, Installation und Wartung von Servern und Workstations. DSGVO-konform und skalierbar.", + ), + ( + "Thin Clients und Endgeraete", + "Kosteneffiziente Thin Clients mit zentralem Management. Einfache Administration, hohe Sicherheit.", + ), + ( + "Drucker und Peripherie", + "Medizinische Geraete und Drucker mit Spezialtreibern. Integration in bestehende Netzwerke.", + ), + ( + "Beschaffung und Consulting", + "Unabhaengige Beratung zur Hardware-Auswahl. Gutes Preis-Leistungs-Verhaeltnis garantiert.", + ), + ], + }, + "netzwerk": { + "titel": "IT-Netzwerk fuer Pflegeeinrichtungen", + "cards": [ + ( + "WLAN-Ausleuchtung", + "Professionelle WLAN-Planung und -Implementierung fuer komplette Einrichtungen. Lueckenlose Abdeckung.", + ), + ( + "VPN und Remote-Zugang", + "Verschluesselte Verbindung fuer mobiles Arbeiten. Sichere Fernwartung und Fernadministration.", + ), + ( + "Firewall und Segmentierung", + "Zwei-Wege-Firewall mit medizinischen Geraete-Portabilitaet. Trennung von Betriebs- und Pflegedaten.", + ), + ( + "Netzwerkmonitoring", + "24/7 Ueberwachung aller Netzwerk-Komponenten. Proaktive Stoerungserkennung und automatische Eskalation.", + ), + ], + }, + "email-kommunikation": { + "titel": "E-Mail und Kommunikation", + "cards": [ + ( + "Microsoft 365", + "Vollstaendige Implementierung und Verwaltung von Microsoft 365. Cloud-Hosting in Deutschland verfuegbar.", + ), + ( + "Exchange und VoIP", + "E-Mail-Server mit Exchange-Integration und VoIP-Telefonie. Nahtlose Kommunikation aller Abteilungen.", + ), + ( + "Spam-Schutz und Archivierung", + "Mehrschichtiger Spam-Schutz und automatische Archivierung fuer Compliance und Aufbewahrungspflichten.", + ), + ], + }, + "support-wartung": { + "titel": "IT-Support und Wartung", + "cards": [ + ( + "SLA-Stufen", + "Flexible Service-Level-Agreements: von Standard bis Mission-Critical. Garantierte Reaktionszeiten fuer alle Stoerungen.", + ), + ( + "Reaktionszeiten", + "Erste Antwort innerhalb von 30 Minuten. Vollstaendige Loesung innerhalb von 4 Stunden bei schwerwiegenden Problemen.", + ), + ( + "Fernwartung und Hosting", + "Sichere Fernwartung mit Echtzeit-Desktop-Zugriff. DSGVO-konforme Hostung aller Systeme.", + ), + ( + "Monitoring und Wartung", + "Automatisiertes Monitoring aller IT-Komponenten. Regelmassige Sicherheitsupdates und Wartung in Einweisungszeiten.", + ), + ], + }, +} + + +# ── Helper: Seitenleiste ────────────────────────────────── +def _sidebar(routes: list[tuple[str, str, str]]) -> str: + """Erstelle Seitenleiste mit Artikel-Links. + + Args: + routes: Liste von (Route, Titel, Datei)-Tupeln. + """ + links = [] + for route, titel, _datei in routes: + links.append('' + titel + "") + return ( + "
" + + "".join(links) + + "
" + ) + + +# ── Helper: Karten bauen ───────────────────────────────── +def _cards(titel: str, beschreibung: str) -> str: + """Erstelle eine Service-Karte. + + Args: + titel: Titel der Karte. + beschreibung: Beschreibungstext. + """ + return '

' + titel + "

" + beschreibung + "

" + + +# ── Weitere Routen ─────────────────────────────────────── + + +@app.route("/ueber-uns") +def ueber_uns() -> str: + """Ueber uns.""" + content = '

Ueber uns

Daniel Krause ist IT-Berater im Gesundheitswesen. Werte: Transparenz, Schnelligkeit, DSGVO-Compliance.

' + return _base_template("Ueber uns", content) + + +@app.route("/kontakt") +def kontakt() -> str: + """Kontakt mit CRM-Formular.""" + content = ( + '
' + "

Kontakt

" + '
' + '
' + "

Wir beraten Sie gerne persoenlich. Sprechen Sie uns an!

" + "

E-Mail: info@it-hilfe-sofort.de

" + "
" + '
' + '' + "
" + "
" + ) + return _base_template("Kontakt", content) + + +@app.route("/faq") +def faq() -> str: + """FAQ.""" + fragen: list[tuple[str, str]] = [ + ("Wie schnell sind Sie vor Ort?", "Innerhalb von 24—48 Stunden."), + ("Was kostet der IT-Support?", "Ab 29 EUR/Monat pro Geraet."), + ( + "Uebernehmen Sie die komplette IT?", + "Ja, von der Installation bis zum Monitoring.", + ), + ( + "Wie stellen Sie DSGVO-Compliance sicher?", + "Hosting in Deutschland, Verschlusselung, Zugangskontrollen.", + ), + ( + "Bieten Sie auch Schulungen an?", + "Ja, fuer alle Mitarbeiter Ihrer Einrichtung.", + ), + ( + "Wie laeuft der Onboarding ab?", + "Kostenlose Erstberatung, dann Umsetzung in 2—4 Wochen.", + ), + ( + "Koennen Sie auf bestehende Systeme aufsetzen?", + "Ja, wir arbeiten mit allen gaeingen Pflegesoftwaren.", + ), + ( + "Gibt es einen Notfallservice?", + "Ja, 24/7 Notfallservice fuer kritische Stoerungen.", + ), + ] + fragen_html_parts: list[str] = [] + for f, t in fragen: + fragen_html_parts.append( + "
" + + f + + "
" + + t + + "
" + ) + fragen_html = "".join(fragen_html_parts) + content = "

Haeufige Fragen

" + fragen_html + "
" + return _base_template("FAQ", content) + + +@app.route("/blog/") +def blog(slug: str) -> str: + """Blog-Artikel.""" + datei = "artikel-" + slug + ".html" + pfad = ARTIKEL_DIR / datei + if pfad.exists(): + inhalt = pfad.read_text(encoding="utf-8") + # Extrahiere Body-Inhalt aus dem Artikel-HTML + match = re.search(r"]*>(.*?)", inhalt, re.DOTALL | re.IGNORECASE) + inhalt = match.group(1) if match else inhalt + else: + inhalt = "

Artikel nicht gefunden.

" + titel = slug.replace("-", " ").title() + content = ( + "
" + inhalt + "
" + ) + # Seitenleiste: alle anderen Artikel derselben Sektion + sektion_routen = [ + (r, t, d) for r, t, d in _ARTIKEL if r == "/blog/" + slug.replace("-", "") + ] + if not sektion_routen: + sektion_routen = _ARTIKEL + sidebar = _sidebar(sektion_routen) + content += sidebar + "
" + return _base_template("Blog: " + titel, content, active="blog") + + +@app.route("/leistungen/") +def leistungen(slug: str) -> str: + """Leistungs-Seite.""" + data = _LEISTUNGEN.get(slug) + if not data: + content = "

Leistung nicht gefunden

" + return _base_template("Leistungen", content) + cards_items = [_cards(titel, beschreibung) for titel, beschreibung in data["cards"]] + cards_all = ( + '
' + + "".join(cards_items) + + "
" + ) + content = ( + "

" + data["titel"] + "

" + cards_all + "
" + ) + return _base_template(data["titel"], content, active="leistungen") + + +@app.route("/it-service/") +def it_service(slug: str) -> str: + """IT-Service-Seite.""" + data = _IT_SERVICE.get(slug) + if not data: + content = "

Service nicht gefunden

" + return _base_template("IT-Service", content) + cards_items = [_cards(titel, beschreibung) for titel, beschreibung in data["cards"]] + cards_all = ( + '
' + + "".join(cards_items) + + "
" + ) + content = ( + "

" + data["titel"] + "

" + cards_all + "
" + ) + return _base_template(data["titel"], content, active="it-service") + + +# ── Main ────────────────────────────────────────────────────────────── if __name__ == "__main__": print(f"IT-Hilfe-Sofort.de startet auf http://127.0.0.1:{PORT}") app.run(host="127.0.0.1", port=PORT, debug=False) diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..04e6495 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,7 @@ +"""Conftest fuer IT-Hilfe-Sofort Tests — sys.path setup.""" + +import sys +from pathlib import Path + +# Projekt root ins sys.path (muss vor pytest import app! kommen) +sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) diff --git a/tests/test_app.py b/tests/test_app.py new file mode 100644 index 0000000..8236d57 --- /dev/null +++ b/tests/test_app.py @@ -0,0 +1,86 @@ +"""IT-Hilfe-Sofort.de — alle 19 Sitemap-Routen.""" + +import sys +from pathlib import Path + +import pytest + +sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) +project_root = Path(__file__).resolve().parent.parent + +import app # noqa: E402 + + +@pytest.fixture +def client(): + """Flask-Test-Client.""" + app.app.config["TESTING"] = True + with app.app.test_client() as klient: + yield klient + + +SEITEN = [ + ("/", "Startseite"), + ("/impressum", "Impressum"), + ("/datenschutz", "Datenschutz"), + ("/ueber-uns", "Ueber uns"), + ("/kontakt", "Kontakt"), + ("/faq", "FAQ"), + ("/blog/telematik-vorschriften", "Telematik-Vorschriften"), + ("/blog/dsgvo-it-sicherheit", "DSGVO & IT-Sicherheit"), + ("/blog/fachkraeftemangel", "Fachkraeftemangel"), + ("/blog/foerderung-finanzierung", "Foerderung & Finanzierung"), + ("/blog/qualitaet-pruefung", "Qualitaet & Pruefung"), + ("/blog/praxis-tipps", "Praxis-Tipps"), + ("/leistungen/pflegedienste", "Pflegedienste"), + ("/leistungen/pflegeheime", "Pflegeheime"), + ("/leistungen/tagespflegen", "Tagespflegen"), + ("/it-service/hardware", "Hardware"), + ("/it-service/netzwerk", "Netzwerk"), + ("/it-service/email-kommunikation", "E-Mail & Kommunikation"), + ("/it-service/support-wartung", "Support & Wartung"), +] + + +@pytest.mark.parametrize("pfad,titel", SEITEN) +def test_seite_ist_erreichbar(pfad: str, titel: str, client) -> None: + """Jede Sitemap-Seite muss mit 200 antworten.""" + response = client.get(pfad) + assert response.status_code == 200, f"{pfad} ({titel}) gab {response.status_code}" + assert b"" in response.data, f"{pfad} hat kein valid HTML" + + +def test_base_template_inhalt(client) -> None: + """Das base_template muss Header, Footer und CSS enthalten.""" + response = client.get("/") + assert b"site-header" in response.data + assert b"site-footer" in response.data + assert b"IT-Hilfe-Sofort.de" in response.data + + +def test_kontakt_iframe_present(client) -> None: + """Das Kontakt-Formular muss das CRM iframe enthalten.""" + response = client.get("/kontakt") + assert b"crm.companyhub.io" in response.data + + +def test_faq_accordion_present(client) -> None: + """FAQ muss Aufklappbare Elemente enthalten.""" + response = client.get("/faq") + assert b"faq-item" in response.data + assert b"summary" in response.data + + +def test_404_fuer_unbekannte_seiten(client) -> None: + """Unbekannte URLs muessen 404 liefern.""" + response = client.get("/nicht-existiert") + assert response.status_code == 404 + + +def test_artikel_dateien_existieren() -> None: + """Alle 24 Artikel-Htmls muessen im 'artikel'-Ordner liegen.""" + artikel_dir = project_root + zaehler = 0 + for f in artikel_dir.glob("artikel-*.html"): + zaehler += 1 + assert zaehler == 24, f"Erwartet 24 Artikel, gefunden {zaehler}"