Files
drk-blutspende-orte/docs/overview.md
T
Paddy caecc60c7b UI/UX pass: readability, feedback, safer actions, DRK branding
Items 1–12 of the interface review:

- Larger UI font (UI_FONT_SIZE) across widgets, menus, dialogs, Treeview rows.
- DRK-red header bar; window title shows entry count; runtime-drawn red-cross
  icon (also written to data/icon.png for the launcher).
- Status bar colour-coded by severity; transient messages auto-clear after 8 s,
  queue-count messages stay.
- Tab 1: more form spacing, digits-only PLZ, explicit "Auswahl entfernen" +
  Delete key for queue rows, confirm on "Warteschlange leeren", modal on
  duplicate save.
- Tab 2: raw lat/lon columns replaced by one "Karte" column (checkmark / red
  "fehlt"); ▲/▼ arrow on the active sort column; hint line always shows the
  saved-entry count.
- "Karte öffnen" with no coordinates shows a dialog, not just a status line.
- Window size/position remembered between sessions (data/window.json).

Verified by building and driving the window under Homebrew Python 3.12 / Tk 9;
non-GUI smoke tests still pass. Docs updated (changelog, overview, improvements,
data-model, setup, dev-notes).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-07 18:51:17 +02:00

4.9 KiB
Raw Blame History

Overview what the app does

The app is a single window: a DRK-red header bar, two tabs, and a colour-coded status bar at the bottom (grey = neutral, green = success, orange = warning, red = error; transient messages fade back to neutral after ~8 s). The window title shows the total entry count, and the window remembers its size and position between sessions (data/window.json). The UI font is enlarged for readability (UI_FONT_SIZE).

Tab 1 "Neuer Eintrag" (new entry)

Workflow: build up a queue of entries, let them geocode in the background, then save the whole batch at once.

  1. Entry form date (a calendar picker, ttkbootstrap.DateEntry, pre-filled with today; typed input is accepted in JJJJ-MM-TT or TT.MM.JJJJ and validated an invalid date is rejected with a dialog), city (autocomplete combobox), postal code (optional). Pressing Return in any field, or the + Hinzufügen button, adds the row to the queue. The PLZ field only accepts digits.
  2. Queue table shows date, city, PLZ, and a live coordinate column that updates from ⏳ wird gesucht… to either lat / lon or ⚠ nicht gefunden as the background geocoder works through the queue.
    • Select a row and press Delete, use the "Auswahl entfernen" button, or click the cell.
    • Right-click a row for Löschen / Koordinaten erneut suchen.
  3. Action bar
    • Alle speichern appends every queued row to orte.csv. If any row looks like a duplicate (same date + city + PLZ), a modal lists them and asks whether to save anyway.
    • Warteschlange leeren discards the queue (asks for confirmation).
    • Karte öffnen regenerates karte.html and opens it in the browser.

Tab 2 "Einträge verwalten" (manage entries)

A table view of everything in orte.csv.

  • Columns: Datum, Ort, PLZ, and Karte a status column showing when the row has coordinates or a red fehlt when it doesn't (the whole row is red too). The raw lat/lon numbers live in the edit dialog, not this table.
  • Search box live filter across date, city, and PLZ (substring match).
  • Sortable columns click a header to sort; clicking again reverses. The active column shows a ▲/▼ arrow. Default sort is by date, newest first. Sorting by Karte groups the rows without coordinates together.
  • "Nur ohne Koordinaten" toggle filters to entries that have no coordinates yet. The hint line always shows the total count, plus how many are missing coordinates.
  • Edit double-click a row (or Bearbeiten) opens a modal dialog to change date (calendar picker, validated) / city / PLZ. Two ways to fix coordinates: a "Koordinaten automatisch neu suchen" toggle re-runs geocoding after saving, or the Breitengrad / Längengrad fields let you type them in by hand (both empty = no map marker).
  • Right-click → Koordinaten suchen runs geocoding for that one row (handy for rows that failed the first time).
  • Delete Löschen removes the selected row(s) after a confirmation dialog. This is irreversible, but a timestamped copy of the file is written to data/backups/ before every change (see data-model.md).
  • Karte öffnen same as on Tab 1.

The map (karte.html)

Generated by generate_map():

  • Base layer: CartoDB positron, initial view centred on [49.0, 9.0], zoom 8 (roughly Baden-Württemberg).
  • One CircleMarker per unique (city, lat, lon) group. Radius scales linearly with the visit count for that location (5 + count / max_count * 20 px), colour is DRK red (#CC0000).
  • Popup shows the city and the visit count; tooltip shows the city.
  • Rows with missing/blank coordinates are silently excluded.
  • If there are no usable coordinates at all, a dialog says so (and the status bar shows a warning).

The generated HTML pulls Leaflet, jQuery and Bootstrap from CDNs, so the map needs an internet connection to render even though the data is local.

Geocoding behaviour

geocode_city(city, postal_code) tries a series of queries in order and returns the first hit:

  1. "<PLZ> <city>, Germany" (only if a PLZ was given)
  2. "<city>, Baden-Württemberg, Germany"
  3. "<city>, Hessen, Germany"
  4. "<city>, Germany"

The regional bias is the GEOCODE_REGIONS constant (Baden-Württemberg, Hessen) near the top of app.py. Results are rounded to five decimal places. Nominatim's usage policy (max 1 req/s, identifying user_agent) is respected by a manual time.sleep(1) in the worker loop.

If the geocoding service is unreachable (no internet), entries still save without coordinates and after a few consecutive failures a one-time dialog explains that the coordinates can be added later via Bearbeiten. The worker thread logs failures to data/app.log and keeps running.