New Updater class + "Hilfe" menubar: - "Nach Update suchen": git fetch + count of new upstream commits. - Quiet background check on start; surfaces via status bar + menu label. - Install: git merge --ff-only, pip install if requirements.txt changed, then restart via os.execv. data/ is git-ignored and untouched. - Fast-forward only; diverged history or offline -> clear message, no action. - Inert unless run from a git clone with git on PATH. Also: "Version…" menu item shows the installed commit. Verified against throwaway git repos (check / ff-update / diverged / no-op / non-clone) and via GUI build on Python 3.14 / Tk 9. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
11 KiB
Improvement roadmap
Review of the current code, prioritized for the actual deployment: one non-technical user, Linux Mint laptop, offline some of the time, no one else to fix things when they break. That ordering puts data safety and robustness above features.
Priorities: 🔴 do first · 🟡 worth doing · 🟢 nice to have
Status (2026-09-07): items 1, 2 and 3 are implemented. Items 6 and 10 are
partly done (logging added; shared autocomplete helpers extracted; map now
fit_bounds; region list is now a constant). See
changelog.md.
1. Data safety 🔴 — ✅ done
Implemented: atomic writes (tmp + fsync + os.replace), rotating backups in
data/backups/ (startup + before every change, keep 20), EmptyDataError /
corrupt-file fallback to an empty frame or the newest backup, and all writes
(including append) routed through _save_csv. A "restore from backup" button in
the UI is still open; for now it is a manual file copy.
Original finding
data/orte.csv is the only copy of the data and there is no safety net.
- No backups. A bad edit, a botched delete, or a disk hiccup loses history permanently. Delete is explicitly irreversible and only guarded by a yes/no dialog.
- Non-atomic full rewrites.
update_row/delete_rowsdodf.to_csv(CSV_PATH)directly. A crash or power loss mid-write can leave a truncated file. - External-edit clobber. The CSV is read once at startup. If it is opened in a spreadsheet and changed while the app runs, the next save silently overwrites those changes.
- Empty-file crash. If
orte.csvexists but is 0 bytes (e.g. an interrupted write),pd.read_csvraisesEmptyDataErrorand the app won't start.
Suggested changes
- Write every full rewrite to a temp file in the same directory, then
os.replace()it into place (atomic on POSIX). - On startup, and before each destructive write, copy the current file to
data/backups/orte-YYYYMMDD-HHMMSS.csv; keep the last ~20 and prune the rest. - Add a "Backup jetzt erstellen" / "Aus Backup wiederherstellen" pair in Tab 2, or at least open the backups folder from a menu.
- Handle
EmptyDataError/ missing columns by falling back to an empty frame and logging a warning.
2. Geocoder thread robustness 🔴 — ✅ done
Implemented: every queue item is wrapped in try/except Exception (both the
geocode call and the callback), failures are logged and the loop continues.
geocode_city now raises GeocodingUnavailable when every attempt hit a
service/network error, and after 3 consecutive such failures the app shows a
one-time "Standortdienst nicht erreichbar" dialog. RateLimiter was not
adopted; the manual sleep(1) stays.
Original finding
GeocoderWorker.run has no error boundary around the per-item work. If
geocode_city or a callback raises anything other than
GeocoderTimedOut / GeocoderServiceError (a network stack error, a bug in a
callback, GeocoderQuotaExceeded), the worker thread dies and every
subsequent entry stays stuck on ⏳ wird gesucht… with no error shown. The user
has no way to know geocoding stopped working.
Suggested changes
- Wrap each item's processing in
try/except Exception, log the traceback, mark that row as failed, and keep the loop alive. - Consider
geopy.extra.rate_limiter.RateLimiter(withswallow_exceptions) instead of the manualsleep(1). - Surface a clear one-time status message when geocoding fails repeatedly ("Standortdienst nicht erreichbar – bitte Internetverbindung prüfen").
3. Easier, safer input 🔴 — ✅ done
Implemented: the date field is now ttkbootstrap.DateEntry (calendar picker) in
both the entry form and the edit dialog, with parse_date() validation on save
(a bad date is rejected with a dialog). The edit dialog gained manual
Breitengrad / Längengrad fields. Tab 2 shows rows without coordinates in red,
has a "Nur ohne Koordinaten" filter, a hint line with the count, and a
right-click Koordinaten suchen action.
Original finding
For the target user the free-text date field is the biggest usability risk.
- Date field is unvalidated free text in both the entry form and
EditDialog. A typo like2026-13-05or05.02.2026is stored verbatim, breaks date sorting, and never appears correctly on the timeline. - No recourse when geocoding fails. A row that comes back
⚠ nicht gefundensaves with blank coordinates and then just silently never shows on the map. There is no way to type coordinates by hand or retry from Tab 2.
Suggested changes
- Replace the date
Entrywithttkbootstrap.DateEntry(calendar picker) in both places. If keeping free text, validate on add/save and refuse invalid dates with a clear message. - In
EditDialog, add optionallat/lonfields so a location can be fixed manually. - In Tab 2, highlight rows with missing coordinates (e.g. red text) and add a filter / right-click "Koordinaten suchen" so failed geocodes are visible and fixable after the fact.
4. Put it under version control 🟡 — partly done
git init done and .gitignore added (covers .venv/, __pycache__/,
data/karte.html, data/backups/, data/app.log). Still open: the initial
commit (left to Patrick), and the decision on committing data/orte.csv vs. an
example file.
- Decide on
data/orte.csv: for a personal tool it's reasonable to commit it, or commit a smalldata/orte.example.csvand ignore the real one. - This
docs/folder.
Gives you history, a rollback path, and a clean way to push updates to the
laptop (git pull).
5. Packaging & distribution 🟡
Today install is: apt install python3-tk, create a venv, pip install. That's
a one-time terminal session, which is acceptable but fragile (loose version
pins mean a future pip install could pull an incompatible pandas/folium).
Suggested changes
- Pin all four dependencies to exact versions and regenerate deliberately.
- Ship the
run.sh+.desktoplauncher from setup.md in the repo. - Optional: build a PyInstaller one-file bundle on a matching Linux box. It bundles Python, Tk, and all deps, so install becomes "copy one file + double-click" with no apt/venv step. Trade-off: you build it, and rebuild on dependency updates.
6. Diagnostics / logging 🟡 — ✅ done
_setup_logging() writes a RotatingFileHandler to data/app.log
(512 KB × 3). Saves, map generation, geocode failures and uncaught exceptions
are logged. _on_open_map now logs and narrows its handling. Open: no in-app
"open log folder" shortcut yet.
7. Stable row identity 🟡
Rows are keyed by the pandas index, which is renumbered after every delete. An
in-flight geocode callback for one row can land on another row if the user
deletes something in between. _apply_edit_geocode guards against a deleted
index but not a reused one.
Suggested change: add an immutable id column (uuid or incrementing int)
written to the CSV, and match callbacks on that instead of the positional index.
8. Duplicate handling 🟡
_on_save_all detects likely duplicates but saves them anyway with only a
transient status-bar line the user will probably miss. Consider a modal
"Diese Einträge existieren schon – trotzdem speichern?" with a per-row choice,
or at least skip exact duplicates by default.
9. Offline map 🟢
karte.html loads Leaflet/jQuery/Bootstrap from CDNs and tiles from
tile.openstreetmap.org (MAP_TILES; switched away from CartoDB positron, which
now needs an API key). The map is blank without internet even though all the
data is local. If offline use matters, vendor the Leaflet JS/CSS into data/
and post-process the Folium output to point at local files (tiles would still
need caching or an offline tile pack — larger effort).
10. Small code-quality items 🟢 — partly done
- ✅ Shared autocomplete helpers (
filter_locations,split_city_plz) extracted;EditDialogandAppboth use them. - ✅ Region list is now the
GEOCODE_REGIONSconstant; map centre/zoom areMAP_DEFAULT_CENTER/MAP_DEFAULT_ZOOM. - ✅
generate_mapnowfit_boundsto the markers. - ⬜ Add a
tests/folder with pytest coverage forDataStore(append / update / delete /find_duplicates) and the input helpers. A non-GUI smoke script exists (used during development) but is not in the repo — worth formalizing. - ⬜ Add
ruff/mypyconfig.
UI/UX review (2026-09-07)
Second review, focused on the interface. Items 1–12 implemented; 13 deferred.
| # | Change | Status |
|---|---|---|
| 1 | Larger base font + taller Treeview rows (UI_FONT_SIZE) |
✅ |
| 2 | Colour-coded status bar + 8 s auto-clear to a neutral state | ✅ |
| 3 | "Warteschlange leeren" asks for confirmation; more form spacing | ✅ |
| 4 | ▲/▼ arrow on the active sort column in Tab 2 | ✅ |
| 5 | Dropped the now-redundant (JJJJ-MM-TT) hint |
✅ |
| 6 | Tab 2: single "Karte" column (✓ / red fehlt) instead of raw lat/lon |
✅ |
| 7 | Explicit "Auswahl entfernen" button + Delete key for queue rows | ✅ |
| 8 | Duplicate save shows a modal with the list, not a transient hint | ✅ |
| 9 | Persistent entry count (window title + Tab 2 hint line) | ✅ |
| 10 | "Karte öffnen" with no coordinates shows a dialog | ✅ |
| 11 | Remember window size & position (data/window.json) |
✅ |
| 12 | DRK-red header bar + runtime-drawn red-cross icon (data/icon.png) |
✅ |
| 13 | One-click "Hinzufügen & sofort speichern" | ⬜ deferred |
Full custom DRK-red theme (recolouring primary etc.) was not done — the
red header bar gives the branding without fighting ttkbootstrap's theme system.
Self-update (2026-09-07) — ✅ done
Hilfe ▸ Nach Update suchen / quiet check on start / restart via os.execv.
git merge --ff-only only, pip install if requirements.txt changed, data/
untouched. Needs a git clone + git installed (Updater.available).
Open follow-ups: no rollback if a pushed update is broken (mitigated by
--ff-only from a branch Patrick controls + pip errors caught pre-restart);
run.sh / .desktop still not committed (item 5).
Suggested order of remaining work
- Initial git commit (item 4), then decide on committing
orte.csv. - Set up the git remote the updater pulls from, and pin all deps (item 5).
run.sh+.desktoplauncher in the repo (item 5).- Stable
idcolumn (item 7 of the first review). - Formalize tests (item 10); offline map (item 9) only if offline use becomes real.