Anlass, Dubletten, Rückseite, Teilen; Startseite und App-Anmutung
All checks were successful
Build & Deploy / deploy (push) Successful in 10m41s
All checks were successful
Build & Deploy / deploy (push) Successful in 10m41s
Vier Funktionen: - Anlass: ein Feld auf der Startseite gilt für alle folgenden Scans und wird im Browser gemerkt. Ordnet den Bestand verlässlicher als der Ortsname aus den Koordinaten. Durchsuchbar, nachträglich änderbar. - Dubletten: die Detailansicht zeigt Karten, die dieselbe Person meinen könnten - gleiche E-Mail, gleiche Telefonnummer (verglichen werden die letzten acht Ziffern, damit +49 511 123456 und 0511/123456 aufeinander passen) oder gleicher Nachname bei gleicher Firma. - Rückseite: nachträglich ein zweites Bild zur Karte. Es füllt nur Felder, die die Vorderseite offen gelassen hat. - Schnellwahl bei Erinnerungen: in 3 Tagen / 1 Woche / 2 Wochen / 1 Monat, jeweils 9 Uhr. Das Datumsfeld braucht man damit selten. Teilen als vCard über das System-Teilenblatt, mit Download als Rückfallebene, wo es kein Teilenblatt gibt. Oberfläche: - Startseite führt jetzt: Anlass, große Aufnahmefläche mit Symbol statt Textzeile, leise Alternative, darunter die zuletzt erfassten Karten. Vorher stand dort eine Überschrift mit Zahlen und man musste raten. - Weniger Webseite, mehr App: kein Seitenrahmen auf dem Telefon, Symbole in der Tableiste mit Zählerblasen, kein Tap-Highlight, keine Textmarkierung auf Bedienelementen, Safe-Area oben, Einblendung beim Ansichtswechsel, Mindesthöhe 54 px für Schaltflächen. - Die Marken "exportiert" / "nicht exportiert" sind aus der Liste raus; dafür gibt es den Filter. Neue Spalten occasion und back_image_file kommen per ALTER TABLE in bestehende Datenbanken - CREATE TABLE IF NOT EXISTS rührt eine vorhandene Tabelle nicht an. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -262,3 +262,96 @@ def test_handeintrag_ueberlebt_den_hintergrundlauf(client, monkeypatch):
|
||||
card = client.get(f"/api/cards/{card_id}").json()
|
||||
assert card["full_name"] == "Von Hand"
|
||||
assert card["notes"] == "Messe"
|
||||
|
||||
|
||||
def test_anlass_wird_gespeichert_und_ist_durchsuchbar(client):
|
||||
created = client.post(
|
||||
"/api/scan",
|
||||
files={"file": ("tisch.jpg", photo_bytes(2), "image/jpeg")},
|
||||
data={"occasion": "Hannover Messe 2026"},
|
||||
).json()
|
||||
ids = [c["id"] for c in created["cards"]]
|
||||
warte_bis_gelesen(client, ids)
|
||||
|
||||
assert all(c["occasion"] == "Hannover Messe 2026" for c in created["cards"])
|
||||
|
||||
gefunden = client.get("/api/cards", params={"q": "hannover messe"}).json()["cards"]
|
||||
assert set(ids) <= {c["id"] for c in gefunden}
|
||||
|
||||
|
||||
def test_anlass_ist_nachtraeglich_aenderbar(client):
|
||||
card_id = client.post(
|
||||
"/api/scan", files={"file": ("einzel.jpg", photo_bytes(1), "image/jpeg")}
|
||||
).json()["cards"][0]["id"]
|
||||
warte_bis_gelesen(client, [card_id])
|
||||
|
||||
patched = client.patch(f"/api/cards/{card_id}", json={"occasion": "Kundentermin"})
|
||||
|
||||
assert patched.json()["occasion"] == "Kundentermin"
|
||||
|
||||
|
||||
def test_dubletten_stehen_in_der_detailansicht(client):
|
||||
erste = client.post(
|
||||
"/api/scan", files={"file": ("a.jpg", photo_bytes(1), "image/jpeg")}
|
||||
).json()["cards"][0]["id"]
|
||||
zweite = client.post(
|
||||
"/api/scan", files={"file": ("b.jpg", photo_bytes(1), "image/jpeg")}
|
||||
).json()["cards"][0]["id"]
|
||||
warte_bis_gelesen(client, [erste, zweite])
|
||||
for card_id in (erste, zweite):
|
||||
client.patch(f"/api/cards/{card_id}", json={"email": "doppelt@firma.de"})
|
||||
|
||||
detail = client.get(f"/api/cards/{zweite}").json()
|
||||
|
||||
assert erste in [d["id"] for d in detail["duplicates"]]
|
||||
assert detail["duplicates"][0]["reason"] == "gleiche E-Mail"
|
||||
|
||||
|
||||
def test_rueckseite_nachtragen(client):
|
||||
card_id = client.post(
|
||||
"/api/scan", files={"file": ("vorne.jpg", photo_bytes(1), "image/jpeg")}
|
||||
).json()["cards"][0]["id"]
|
||||
warte_bis_gelesen(client, [card_id])
|
||||
|
||||
response = client.post(
|
||||
f"/api/cards/{card_id}/back",
|
||||
files={"file": ("hinten.jpg", photo_bytes(1), "image/jpeg")},
|
||||
)
|
||||
assert response.status_code == 200
|
||||
assert response.json()["back_image_url"].endswith("side=back")
|
||||
warte_bis_gelesen(client, [card_id])
|
||||
|
||||
bild = client.get(f"/api/cards/{card_id}/image", params={"side": "back"})
|
||||
assert bild.status_code == 200
|
||||
assert bild.headers["content-type"] == "image/jpeg"
|
||||
|
||||
|
||||
def test_ohne_rueckseite_gibt_es_kein_rueckseitenbild(client):
|
||||
card_id = client.post(
|
||||
"/api/scan", files={"file": ("vorne.jpg", photo_bytes(1), "image/jpeg")}
|
||||
).json()["cards"][0]["id"]
|
||||
warte_bis_gelesen(client, [card_id])
|
||||
|
||||
assert client.get(
|
||||
f"/api/cards/{card_id}/image", params={"side": "back"}
|
||||
).status_code == 404
|
||||
|
||||
|
||||
def test_loeschen_entfernt_auch_die_rueckseite(client):
|
||||
from app import config
|
||||
|
||||
card_id = client.post(
|
||||
"/api/scan", files={"file": ("vorne.jpg", photo_bytes(1), "image/jpeg")}
|
||||
).json()["cards"][0]["id"]
|
||||
warte_bis_gelesen(client, [card_id])
|
||||
client.post(
|
||||
f"/api/cards/{card_id}/back",
|
||||
files={"file": ("hinten.jpg", photo_bytes(1), "image/jpeg")},
|
||||
)
|
||||
warte_bis_gelesen(client, [card_id])
|
||||
row = db.query_one("SELECT * FROM cards WHERE id = ?", (card_id,))
|
||||
|
||||
client.delete(f"/api/cards/{card_id}")
|
||||
|
||||
assert not (config.IMAGE_DIR / row["image_file"]).exists()
|
||||
assert not (config.IMAGE_DIR / row["back_image_file"]).exists()
|
||||
|
||||
Reference in New Issue
Block a user