Bildeditor, klickbare Felder, Einstellungen; Kopfzeile und Namensfeld raus
All checks were successful
Build & Deploy / deploy (push) Successful in 1m29s
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>
This commit is contained in:
@@ -75,10 +75,10 @@ def test_bearbeiten_und_suchen(client, scanned):
|
||||
|
||||
patched = client.patch(
|
||||
f"/api/cards/{card_id}",
|
||||
json={"full_name": "Anna Berg", "company": "Berg & Partner", "notes": "Messe"},
|
||||
json={"first_name": "Anna", "last_name": "Berg", "company": "Berg & Partner", "notes": "Messe"},
|
||||
)
|
||||
assert patched.status_code == 200
|
||||
assert patched.json()["full_name"] == "Anna Berg"
|
||||
assert patched.json()["last_name"] == "Berg"
|
||||
|
||||
hits = client.get("/api/cards", params={"q": "berg"}).json()["cards"]
|
||||
assert [c["id"] for c in hits] == [card_id]
|
||||
@@ -97,7 +97,7 @@ def test_unbekannte_felder_werden_abgewiesen(client, scanned):
|
||||
|
||||
def test_vcard_download_markiert_die_karte_als_exportiert(client, scanned):
|
||||
card_id = scanned["cards"][1]["id"]
|
||||
client.patch(f"/api/cards/{card_id}", json={"full_name": "Bea Kurz"})
|
||||
client.patch(f"/api/cards/{card_id}", json={"first_name": "Bea", "last_name": "Kurz"})
|
||||
|
||||
response = client.get(f"/vcf/{card_id}")
|
||||
|
||||
@@ -143,7 +143,7 @@ def test_erinnerung_ohne_datum_wird_abgewiesen(client, scanned):
|
||||
|
||||
def test_faellige_erinnerung_wird_verschickt(client, scanned, monkeypatch):
|
||||
card_id = scanned["cards"][3]["id"]
|
||||
client.patch(f"/api/cards/{card_id}", json={"full_name": "Carl Spät"})
|
||||
client.patch(f"/api/cards/{card_id}", json={"first_name": "Carl", "last_name": "Spät"})
|
||||
past = datetime.now(timezone.utc) - timedelta(minutes=5)
|
||||
client.post(
|
||||
f"/api/cards/{card_id}/reminders",
|
||||
@@ -254,13 +254,14 @@ def test_handeintrag_ueberlebt_den_hintergrundlauf(client, monkeypatch):
|
||||
"/api/scan", files={"file": ("einzel.jpg", photo_bytes(1), "image/jpeg")}
|
||||
).json()["cards"][0]["id"]
|
||||
|
||||
client.patch(f"/api/cards/{card_id}", json={"full_name": "Von Hand", "notes": "Messe"})
|
||||
client.patch(f"/api/cards/{card_id}", json={"first_name": "Von", "last_name": "Hand", "notes": "Messe"})
|
||||
|
||||
monkeypatch.undo()
|
||||
warte_bis_gelesen(client, [card_id])
|
||||
|
||||
card = client.get(f"/api/cards/{card_id}").json()
|
||||
assert card["full_name"] == "Von Hand"
|
||||
assert card["first_name"] == "Von"
|
||||
assert card["last_name"] == "Hand"
|
||||
assert card["notes"] == "Messe"
|
||||
|
||||
|
||||
|
||||
@@ -29,8 +29,8 @@ def leere_tabelle():
|
||||
|
||||
|
||||
def test_gleiche_email_wird_gefunden():
|
||||
_card(full_name="Anna Berg", email="A.Berg@Firma.de")
|
||||
neu = _card(full_name="A. Berg", email="a.berg@firma.de")
|
||||
_card(first_name="Anna", last_name="Berg", email="A.Berg@Firma.de")
|
||||
neu = _card(first_name="A.", last_name="Berg", email="a.berg@firma.de")
|
||||
|
||||
treffer = duplicates.find(neu)
|
||||
|
||||
@@ -38,8 +38,8 @@ def test_gleiche_email_wird_gefunden():
|
||||
|
||||
|
||||
def test_telefonnummer_in_anderer_schreibweise():
|
||||
_card(full_name="Anna Berg", phone="+49 511 1234560")
|
||||
neu = _card(full_name="Anna B.", mobile="0511/1234560")
|
||||
_card(first_name="Anna", last_name="Berg", phone="+49 511 1234560")
|
||||
neu = _card(first_name="Anna", last_name="B.", mobile="0511/1234560")
|
||||
|
||||
treffer = duplicates.find(neu)
|
||||
|
||||
@@ -62,27 +62,27 @@ def test_gleicher_nachname_allein_reicht_nicht():
|
||||
|
||||
def test_kurze_nummern_werden_nicht_verglichen():
|
||||
# Eine dreistellige Durchwahl darf keine Dublette erzeugen.
|
||||
_card(full_name="Anna Berg", phone="123")
|
||||
neu = _card(full_name="Bea Kurz", phone="123")
|
||||
_card(first_name="Anna", last_name="Berg", phone="123")
|
||||
neu = _card(first_name="Bea", last_name="Kurz", phone="123")
|
||||
|
||||
assert duplicates.find(neu) == []
|
||||
|
||||
|
||||
def test_leere_felder_erzeugen_keine_treffer():
|
||||
_card(full_name="Anna Berg")
|
||||
neu = _card(full_name="Bea Kurz")
|
||||
_card(first_name="Anna", last_name="Berg")
|
||||
neu = _card(first_name="Bea", last_name="Kurz")
|
||||
|
||||
assert duplicates.find(neu) == []
|
||||
|
||||
|
||||
def test_die_karte_selbst_ist_keine_dublette():
|
||||
karte = _card(full_name="Anna Berg", email="a@b.de")
|
||||
karte = _card(first_name="Anna", last_name="Berg", email="a@b.de")
|
||||
|
||||
assert duplicates.find(karte) == []
|
||||
|
||||
|
||||
def test_noch_nicht_gelesene_karten_bleiben_aussen_vor():
|
||||
_card(full_name="Anna Berg", email="a@b.de", extract_status="offen")
|
||||
neu = _card(full_name="Anna Berg", email="a@b.de")
|
||||
_card(first_name="Anna", last_name="Berg", email="a@b.de", extract_status="offen")
|
||||
neu = _card(first_name="Anna", last_name="Berg", email="a@b.de")
|
||||
|
||||
assert duplicates.find(neu) == []
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
from app import vcard
|
||||
|
||||
BASE = {
|
||||
"full_name": "Dr. Anna Berg",
|
||||
"first_name": "Anna",
|
||||
"first_name": "Dr. Anna",
|
||||
"last_name": "Berg",
|
||||
"job_title": "Geschäftsführerin",
|
||||
"company": "Berg & Partner GmbH",
|
||||
@@ -28,7 +27,7 @@ def test_enthaelt_die_pflichtzeilen():
|
||||
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;Anna;;;" in output
|
||||
assert "N:Berg;Dr. Anna;;;" in output
|
||||
assert "TEL;TYPE=CELL:+49 170 1234567" in output
|
||||
|
||||
|
||||
@@ -52,6 +51,13 @@ def test_leere_karte_bekommt_trotzdem_einen_namen():
|
||||
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"})
|
||||
|
||||
@@ -61,4 +67,4 @@ def test_firma_ersetzt_den_namen_wenn_keiner_erkannt_wurde():
|
||||
def test_dateiname_ist_dateisystemtauglich():
|
||||
assert vcard.filename(BASE) == "Dr-Anna-Berg.vcf"
|
||||
assert vcard.filename({}) == "Unbenannt.vcf"
|
||||
assert vcard.filename({"full_name": "?!"}) == "kontakt.vcf"
|
||||
assert vcard.filename({"first_name": "?!"}) == "kontakt.vcf"
|
||||
|
||||
Reference in New Issue
Block a user