Files
drk-blutspende-orte/docs/overview.md
T
Paddy 37c9642877 Initial commit: Arbeitsorte-Logger + data-safety/robustness/input work
Existing app (single-file Tkinter/ttkbootstrap desktop tool for logging
blood-drive work assignments and mapping them) plus the first round of
improvements:

- Data safety: atomic CSV writes (tmp + fsync + os.replace), rotating
  backups in data/backups/ (startup + before every change, keep 20),
  fallback to empty/backup on missing/empty/corrupt orte.csv.
- Geocoder robustness: per-item try/except so the worker thread survives
  failures; GeocodingUnavailable + one-time "service unreachable" dialog.
- Input: DateEntry calendar picker with parse_date() validation; manual
  lat/lon fields in the edit dialog; Tab 2 highlights/filters rows without
  coordinates and adds a right-click "Koordinaten suchen".
- Logging to data/app.log; shared autocomplete helpers; config constants;
  map fit_bounds.

docs/ describes current state, architecture, data model, setup (Linux Mint),
and the full improvement roadmap. data/orte.csv is gitignored for now.

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

4.1 KiB
Raw Blame History

Overview what the app does

The app is a single window with a status bar at the bottom and two tabs.

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.
  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.
    • Left-click the cell to remove a row.
    • Right-click a row for Löschen / Koordinaten erneut suchen.
  3. Action bar
    • Alle speichern appends every queued row to orte.csv. If a row looks like a duplicate of an existing entry (same date + city + PLZ) a hint is shown in the status bar, but the row is still saved.
    • Leeren discards the queue without saving.
    • 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.

  • Search box live filter across date, city, and PLZ (substring match).
  • Sortable columns click a header to sort; clicking again reverses. Default sort is by date, newest first.
  • "Nur ohne Koordinaten" toggle filters to entries that have no coordinates yet. Such rows are also shown in red in the full list, and the hint line reports how many there are.
  • 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 ValueError is raised and shown in the status bar.

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.