All checks were successful
Build & Deploy / deploy (push) Successful in 1m29s
Bildbearbeitung im Nachgang: Drehen in 90-Grad-Schritten, Ausrichten per
Schieberegler, Zuschneiden per Rahmen mit anfassbaren Ecken. Der Server
rechnet jede Korrektur vom unbearbeiteten Original, das beim ersten
Eingriff daneben abgelegt wird - sonst summieren sich die Verluste über
mehrere Korrekturen, und Zurücksetzen wäre nicht möglich. Zwei
Zuschnitte hintereinander werden ineinander verrechnet.
Der Ort ließ sich nicht übernehmen, weil beide Quellen versagen können:
iOS entfernt beim Weitergeben an eine Webseite oft die GPS-Daten aus dem
Foto, und der Browserstandort scheiterte bisher stumm. Jetzt meldet er
seinen Grund ("Standortfreigabe fehlt"), die Detailansicht zeigt die
Herkunft des Ortes, und der Scan-Ort ist von Hand nachtragbar.
E-Mail, Web, Telefon und Mobil sind in der Tabelle jetzt Links; Straße,
PLZ, Ort und Land stehen als eine Adresszeile, die die eingestellte
Navigations-App öffnet.
Einstellungen als vierter Reiter: Navigations-App (Apple Karten, Google
Maps, OpenStreetMap), Statusübersicht und Abmelden - der Knopf saß
vorher in der Kopfzeile.
Die Kopfzeile ist weg. Sie kostete auf dem Telefon eine Bildschirmzeile
und wiederholte nur, was die Tableiste schon sagt.
Kein full_name mehr, nur Vor- und Nachname; akademische Titel gehören in
den Vornamen. Bestehende Datensätze werden bei der Migration am letzten
Leerzeichen aufgeteilt. Die Spalte bleibt ungenutzt stehen: SQLite baut
zum Löschen die ganze Tabelle neu, der Gewinn wären ein paar Byte.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
71 lines
2.0 KiB
Python
71 lines
2.0 KiB
Python
from app import vcard
|
|
|
|
BASE = {
|
|
"first_name": "Dr. Anna",
|
|
"last_name": "Berg",
|
|
"job_title": "Geschäftsführerin",
|
|
"company": "Berg & Partner GmbH",
|
|
"email": "a.berg@berg-partner.de",
|
|
"phone": "+49 511 1234560",
|
|
"mobile": "+49 170 1234567",
|
|
"website": "berg-partner.de",
|
|
"street": "Musterweg 3",
|
|
"postal_code": "30159",
|
|
"city": "Hannover",
|
|
"country": "Deutschland",
|
|
"notes": "Sucht ein neues CRM.",
|
|
"captured_at": "2026-09-01T10:15:00+00:00",
|
|
"place": "Hannover, Messegelände",
|
|
"lat": 52.3216,
|
|
"lon": 9.8078,
|
|
}
|
|
|
|
|
|
def test_enthaelt_die_pflichtzeilen():
|
|
output = vcard.build(BASE)
|
|
|
|
assert output.startswith("BEGIN:VCARD\r\nVERSION:3.0\r\n")
|
|
assert output.endswith("END:VCARD\r\n")
|
|
assert "FN:Dr. Anna Berg" in output
|
|
assert "N:Berg;Dr. Anna;;;" in output
|
|
assert "TEL;TYPE=CELL:+49 170 1234567" in output
|
|
|
|
|
|
def test_kommas_und_semikolons_werden_maskiert():
|
|
output = vcard.build({**BASE, "company": "Berg, Partner; Co"})
|
|
|
|
assert "ORG:Berg\\, Partner\\; Co" in output
|
|
|
|
|
|
def test_notiz_traegt_zeit_und_ort():
|
|
note = [line for line in vcard.build(BASE).splitlines() if line.startswith("NOTE:")][0]
|
|
|
|
assert "Sucht ein neues CRM." in note
|
|
assert "01.09.2026" in note
|
|
assert "Messegelände" in note
|
|
|
|
|
|
def test_leere_karte_bekommt_trotzdem_einen_namen():
|
|
output = vcard.build({"notes": ""})
|
|
|
|
assert "FN:Unbenannt" in output
|
|
|
|
|
|
def test_name_setzt_sich_aus_vor_und_nachname_zusammen():
|
|
output = vcard.build({"first_name": "Dr. Anna", "last_name": "Berg"})
|
|
|
|
assert "FN:Dr. Anna Berg" in output
|
|
assert "N:Berg;Dr. Anna;;;" in output
|
|
|
|
|
|
def test_firma_ersetzt_den_namen_wenn_keiner_erkannt_wurde():
|
|
output = vcard.build({"company": "Berg & Partner GmbH"})
|
|
|
|
assert "FN:Berg & Partner GmbH" in output
|
|
|
|
|
|
def test_dateiname_ist_dateisystemtauglich():
|
|
assert vcard.filename(BASE) == "Dr-Anna-Berg.vcf"
|
|
assert vcard.filename({}) == "Unbenannt.vcf"
|
|
assert vcard.filename({"first_name": "?!"}) == "kontakt.vcf"
|