From 08232bda9b963096fcf1fe6296fdda8eababea04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20M=C3=BCller?= Date: Mon, 7 Sep 2026 22:16:52 +0200 Subject: [PATCH] Add install.sh / run.sh / bundled icon; fully pin requirements - install.sh: one-time Linux Mint setup (apt, .venv, requirements, .desktop entry with absolute paths). Re-runnable. - run.sh: launches the app from .venv (called by the menu entry). - icon.png: bundled 256px red-cross launcher/window icon; app prefers it over the runtime-drawn fallback (data/icon.png). - requirements.txt: fully pinned incl. transitive deps, tested on Python 3.12; numpy held at 2.2.6 so it installs on Python 3.10 too. Co-Authored-By: Claude Sonnet 5 --- app.py | 18 ++++-- docs/README.md | 2 +- docs/changelog.md | 10 +++ docs/data-model.md | 7 +- docs/improvements.md | 31 ++++----- docs/setup.md | 149 +++++++++++++++---------------------------- icon.png | Bin 0 -> 1403 bytes install.sh | 44 +++++++++++++ requirements.txt | 30 ++++++++- run.sh | 11 ++++ 10 files changed, 173 insertions(+), 129 deletions(-) create mode 100644 icon.png create mode 100755 install.sh create mode 100755 run.sh diff --git a/app.py b/app.py index 1bc8c88..f915082 100755 --- a/app.py +++ b/app.py @@ -40,7 +40,8 @@ CSV_PATH = DATA_DIR / "orte.csv" MAP_PATH = DATA_DIR / "karte.html" BACKUP_DIR = DATA_DIR / "backups" LOG_PATH = DATA_DIR / "app.log" -ICON_PATH = DATA_DIR / "icon.png" +ICON_PATH = BASE_DIR / "icon.png" # mitgeliefert (für Fenster + Startmenü) +ICON_FALLBACK_PATH = DATA_DIR / "icon.png" # zur Laufzeit gezeichnet, falls obiges fehlt WINDOW_STATE_PATH = DATA_DIR / "window.json" CSV_COLUMNS = ["date", "city", "postal_code", "lat", "lon"] @@ -735,8 +736,12 @@ class App(ttk.Window): ) def _install_icon(self) -> None: - """Zeichnet ein einfaches rotes Kreuz als Fenster-Icon (ohne Extra-Abhängigkeit).""" + """Setzt das Fenster-Icon: mitgelieferte icon.png, sonst ein gezeichnetes Kreuz.""" try: + if ICON_PATH.exists(): + self._icon_img = tk.PhotoImage(file=str(ICON_PATH)) + self.iconphoto(True, self._icon_img) + return size = 64 img = tk.PhotoImage(width=size, height=size) img.put("white", to=(0, 0, size, size)) @@ -745,11 +750,10 @@ class App(ttk.Window): img.put(DRK_RED, to=(7, arm, size - 7, size - arm)) self.iconphoto(True, img) self._icon_img = img # Referenz halten - if not ICON_PATH.exists(): - try: - img.write(str(ICON_PATH), format="png") - except tk.TclError: - pass # Tk < 8.6 kann kein PNG schreiben – nicht kritisch + try: + img.write(str(ICON_FALLBACK_PATH), format="png") + except tk.TclError: + pass # Tk < 8.6 kann kein PNG schreiben – nicht kritisch except tk.TclError: log.warning("Fenster-Icon konnte nicht gesetzt werden.") diff --git a/docs/README.md b/docs/README.md index 513a867..39e3675 100644 --- a/docs/README.md +++ b/docs/README.md @@ -16,7 +16,7 @@ code) and plots them on an interactive map. | [overview.md](overview.md) | What the app does, feature by feature | | [architecture.md](architecture.md) | Code structure, threading model, data flow | | [data-model.md](data-model.md) | The `orte.csv` schema and how it is read/written | -| [setup.md](setup.md) | Install & run on a fresh Linux Mint machine, plus a desktop launcher | +| [setup.md](setup.md) | `git clone` + `./install.sh` on Linux Mint; how updates work | | [improvements.md](improvements.md) | Prioritized review findings and suggested changes | | [changelog.md](changelog.md) | What has changed, newest first | | [dev-notes.md](dev-notes.md) | Assumptions that turned out wrong — read before editing | diff --git a/docs/changelog.md b/docs/changelog.md index 7898f3d..169ba1e 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -2,6 +2,16 @@ Notable changes to the app. Newest first. +## 2026-09-07 — one-command install + launcher + +- `install.sh` – one-time Linux Mint setup: apt packages, `.venv` + + `requirements.txt`, and a `.desktop` menu entry with correct absolute paths. +- `run.sh` – launches the app from `.venv`; the menu entry calls this. +- `icon.png` – bundled red-cross launcher/window icon (256×256); the app now + prefers this over the runtime-drawn one. +- `requirements.txt` – **fully pinned** (direct + transitive), tested on + Python 3.12. `numpy` capped at 2.2.6 so it still installs on Python 3.10. + ## 2026-09-07 — built-in self-update New **Hilfe** menu: diff --git a/docs/data-model.md b/docs/data-model.md index 479e238..f5edc71 100644 --- a/docs/data-model.md +++ b/docs/data-model.md @@ -66,7 +66,8 @@ generation, geocoding failures, and uncaught exceptions. and restored on start. Git-ignored, per-machine. Safe to delete (window opens at its default size). -## `data/icon.png` +## `icon.png` (repo root) and `data/icon.png` -The red-cross window icon, drawn at first run and written here so the `.desktop` -launcher can point at it. Git-ignored (regenerated). Delete to force a redraw. +`icon.png` in the repo root is the **bundled** launcher/window icon (committed). +`data/icon.png` is only a runtime-drawn fallback the app writes if the bundled +one is missing — git-ignored. diff --git a/docs/improvements.md b/docs/improvements.md index ba273fd..edfc351 100644 --- a/docs/improvements.md +++ b/docs/improvements.md @@ -127,20 +127,14 @@ example file. Gives you history, a rollback path, and a clean way to push updates to the laptop (`git pull`). -## 5. Packaging & distribution 🟡 +## 5. Packaging & distribution 🟡 — mostly done -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` + `.desktop` launcher from [setup.md](setup.md) in the repo. -- Optional: build a [PyInstaller](https://pyinstaller.org/) 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. +- ✅ `requirements.txt` fully pinned (direct + transitive), tested on 3.12. +- ✅ `run.sh` + `install.sh` (generates the `.desktop` entry) shipped in the repo. +- ✅ Bundled `icon.png`. +- ⬜ Optional: a [PyInstaller](https://pyinstaller.org/) one-file bundle would + drop the apt/venv step entirely, at the cost of building per release. Not + needed while `install.sh` works. ## 6. Diagnostics / logging 🟡 — ✅ done @@ -223,8 +217,9 @@ Open follow-ups: no rollback if a pushed update is broken (mitigated by ## Suggested order of remaining work -1. Initial git commit (item 4), then decide on committing `orte.csv`. -2. **Set up the git remote** the updater pulls from, and pin all deps (item 5). -3. `run.sh` + `.desktop` launcher in the repo (item 5). -4. Stable `id` column (item 7 of the first review). -5. Formalize tests (item 10); offline map (item 9) only if offline use becomes real. +1. Deploy to the laptop: `git clone` + `./install.sh` (see [setup.md](setup.md)). +2. Stable `id` column (item 7 of the first review). +3. Formalize tests (item 10) — a non-GUI `tests/` suite already exists in + spirit (dev smoke scripts); move it into the repo. +4. Offline map (item 9) only if offline use becomes real. +5. Optional PyInstaller bundle (item 5). diff --git a/docs/setup.md b/docs/setup.md index 17f888d..d9bddcb 100644 --- a/docs/setup.md +++ b/docs/setup.md @@ -1,116 +1,63 @@ # Setup & running -Target machine: **Linux Mint laptop**, single non-technical user. The goal is -that day-to-day use is a **double-click**, with the terminal only needed once -during install. +Target machine: **Linux Mint laptop**, single non-technical user. Day-to-day use +is a **double-click from the menu**; the terminal is needed once, for install. -## 1. System packages - -Tkinter is not bundled with the system Python on Mint and must be installed -separately. `git` is needed for the in-app updater: - -```bash -sudo apt update -sudo apt install python3-tk python3-venv python3-pip git -``` - -## 2. Get the code - -**Clone it** (don't download a zip) – the in-app "Nach Update suchen" only works -from a git clone: +## Quick install (Linux Mint) ```bash mkdir -p ~/Apps && cd ~/Apps -git clone DRK_Blutspende_Orte +git clone https://git.plutodev.de/Paddy/drk-blutspende-orte DRK_Blutspende_Orte +cd DRK_Blutspende_Orte +./install.sh ``` -Put it somewhere stable, e.g. `~/Apps/DRK_Blutspende_Orte`. +`install.sh` does everything: -## 3. Create a virtual environment and install dependencies +1. `sudo apt install python3-tk python3-venv python3-pip git` (asks for the + password), +2. creates `.venv` and installs the pinned `requirements.txt`, +3. writes `~/.local/share/applications/drk-blutspende-orte.desktop` with the + correct absolute paths and the bundled `icon.png`. + +After it finishes, **"DRK Blutspende – Arbeitsorte"** is in the application menu +(log out/in once if it doesn't show immediately). First manual test: `./run.sh`. + +> Clone it — don't download a zip. The in-app updater and `run.sh` both rely on +> it being a real git checkout. + +## What the pieces are + +| File | Purpose | +|------|---------| +| `install.sh` | one-time setup (above). Safe to re-run. | +| `run.sh` | launches the app from `.venv`; the `.desktop` entry calls this. | +| `icon.png` | bundled launcher/window icon (red cross). Replace with the real DRK logo if wanted. | +| `requirements.txt` | fully pinned, tested on Python 3.12. | + +## Running manually ```bash cd ~/Apps/DRK_Blutspende_Orte -python3 -m venv .venv -.venv/bin/pip install -r requirements.txt +./run.sh # or: .venv/bin/python app.py ``` -`requirements.txt`: - -``` -ttkbootstrap==1.10.1 -geopy>=2.4.1 -folium>=0.17.0 -pandas>=2.2.0 -``` - -> Only `ttkbootstrap` is pinned exactly. For a machine you hand to someone else, -> consider pinning all four (see [improvements.md](improvements.md#5-packaging--distribution)). - -## 4. Run it - -```bash -.venv/bin/python app.py -``` - -The window should open. `data/orte.csv` is created automatically on first run if -it is missing. - -## 5. Make it a double-click launcher - -### Launcher script - -Create `run.sh` in the project root: - -```bash -#!/usr/bin/env bash -cd "$(dirname "$0")" -exec .venv/bin/python app.py -``` - -```bash -chmod +x run.sh -``` - -### Desktop entry - -Create `~/.local/share/applications/drk-blutspende-orte.desktop`: - -```ini -[Desktop Entry] -Type=Application -Name=DRK Blutspende – Arbeitsorte -Comment=Einsatzorte protokollieren und auf der Karte anzeigen -Exec=/home/USER/Apps/DRK_Blutspende_Orte/run.sh -Icon=/home/USER/Apps/DRK_Blutspende_Orte/data/icon.png -Terminal=false -Categories=Utility; -``` - -Replace `USER` with the real username. The app writes a simple red-cross -`data/icon.png` on first run; drop in the real DRK logo at that path if you have -one. The entry then shows up in the Mint menu and can be pinned to the panel or -the desktop. +`data/orte.csv` is created automatically on first run if missing. ## Python / Tk version -Python 3.9+ is fine (the code uses `from __future__ import annotations`). The -system Python on current Mint releases is well above that. - -`ttkbootstrap` needs **Tk 8.6 or newer**. Linux Mint's `python3-tk` provides -that. The macOS CommandLineTools Python used during development ships Tk 8.5 and -**cannot run the GUI** — see [dev-notes.md](dev-notes.md). +Python 3.10–3.12 (Mint's `python3`). `ttkbootstrap` needs **Tk 8.6+**, which +Mint's `python3-tk` provides. The macOS CommandLineTools Python 3.9 ships Tk 8.5 +and can't run the GUI — for dev on macOS use a Homebrew Python, see +[dev-notes.md](dev-notes.md). ## Updating -**From inside the app:** menu **Hilfe ▸ Nach Update suchen**. The app also -checks quietly on start and offers the update if there is one. It fast-forwards -to the server version, reinstalls dependencies if `requirements.txt` changed, -and restarts itself. `data/` is never touched. - -For this to work the app must run from a **git clone** (step 2) whose `.venv` -was made with the same Python it runs on, and `git` must be installed. The -updater only ever fast-forwards – if the local copy has been changed by hand it -refuses and says to get in touch. +**From inside the app:** menu **Hilfe ▸ Nach Update suchen**. It also checks +quietly on start and offers the update if there is one. It fast-forwards to the +server version, reinstalls dependencies if `requirements.txt` changed, and +restarts itself. `data/` is never touched. The updater only fast-forwards — if +the local copy was hand-edited it refuses and says to get in touch. **Manually** (equivalent): @@ -120,8 +67,16 @@ git pull --ff-only .venv/bin/pip install -r requirements.txt ``` -## Data location +Publishing an update, from the dev machine: `git push` to +`https://git.plutodev.de/Paddy/drk-blutspende-orte` (branch `main`). -All state is in `data/orte.csv` next to `app.py`. To back up the app, copy that -one file (plus `data/backups/` if you want the history). To move to a new -laptop, clone the repo again and redo steps 1 & 3; copy `data/orte.csv` across. +## Data location & backup + +All state is in `data/` next to `app.py`: + +- `orte.csv` — the data (git-ignored). +- `backups/` — automatic timestamped copies, newest 20 kept. +- `app.log`, `window.json`, `icon.png` (runtime fallback) — git-ignored. + +To back up: copy `data/orte.csv` (and `data/backups/` for history). To move to a +new laptop: clone the repo, run `./install.sh`, copy `data/orte.csv` across. diff --git a/icon.png b/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ee42133267b94db03c531856387fabedca9b66f0 GIT binary patch literal 1403 zcmeAS@N?(olHy`uVBq!ia0y~yU<5K5893O0R7}x|GzJD%Cr=m0kcv5P?>gqq4iIq& z?AWxJXV%~TSB}@7tZMcOmA>7u@!*|eu>j@J_n++Y&Kdx9GO#}05NzN6aL-rM1GDVP zE%JBoRABZ$`<(At{$*~1FB@`s=KWjPCihLad3o)GgYAD?A3T&#_>j0?QR2JJANzCr zI37s&J>GGeqo-Q3=CI${Oh=JxdEX4%Km5o|c&WCbg4y8D2bti!Mc(F(yZafY@A!Q9 z-}B5shP&?es}+1Oso%kk#^S%w|I4;OB4I4a#xZ2#u?rOG{04EE1i*T26dqkoIx z&fmoPX=j$UHwcs&GF+>-==1(;Q_Z~L-{HEmbN$^JII3rF)&uJQ7dz|n`)9Y<7!sMl z=s)X$>-7aO|IdAY!h3+(p+YwJ`Bg!NLo6_KZ~MLm$?n|q#P|D-6~6YiSQGx^cfG0^ z1BdWHpz8a-nEGBkuDSP1C_zY?p@kEMeEAM!%E(u%9>`jD?sYxm4;i+}eC%J}*H|+_ zBk#s`#vNZ|4m3O5*zaBZ>u$_#28En5h8)HX@x{+SCo$B>avU$roF~hukQ2xt!<+z2 z=TEOPSg;*CIR-Vh3pZigc$?3)th8s)|D$4&yN;hyE z*w|KoRk`6g;|H7Uv$xkU+*#Gdap3XYS(`26@1MWN;KI;n_y=f`62p4?Km32CE)+6+ zv-j_2Hdtz=#PEH#k-PW*oB!@c{7bYW{!xmA}|Bw+H<0%)kOf{=?dz_rJ)0$|%fZsS{?(^5dTOX8m7jS^iqy z82db4BO6T*>xx9QYM+is4y!1aR_b=aP(P1gLYh2%5 zd^x System-Pakete (fragt nach dem Passwort)…" +sudo apt update +sudo apt install -y python3-tk python3-venv python3-pip git + +echo +echo "==> Virtuelle Umgebung unter .venv …" +python3 -m venv .venv +.venv/bin/pip install --upgrade pip +.venv/bin/pip install -r requirements.txt + +echo +echo "==> Startmenü-Eintrag …" +DESKTOP_DIR="$HOME/.local/share/applications" +mkdir -p "$DESKTOP_DIR" +cat > "$DESKTOP_DIR/drk-blutspende-orte.desktop" </dev/null || true + +echo +echo "Fertig." +echo "Die App ist jetzt im Menü als \"DRK Blutspende – Arbeitsorte\" zu finden" +echo "(ggf. einmal ab- und wieder anmelden, damit sie auftaucht)." +echo "Direkt testen: ./run.sh" diff --git a/requirements.txt b/requirements.txt index 4009055..7917f59 100755 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,28 @@ +# Vollständig gepinnt für reproduzierbare Installationen (Linux Mint, Python 3.10–3.12). +# Getestet mit Python 3.12. Nach Änderungen: in einem frischen venv +# pip install -r requirements.txt && python app.py +# und die Versionen mit pip freeze hier aktualisieren. + +# direkte Abhängigkeiten ttkbootstrap==1.10.1 -geopy>=2.4.1 -folium>=0.17.0 -pandas>=2.2.0 +geopy==2.4.1 +folium==0.19.5 +pandas==2.2.3 + +# transitiv (mitgepinnt, damit auf allen Rechnern dasselbe ankommt) +numpy==2.2.6 +branca==0.8.2 +Jinja2==3.1.6 +MarkupSafe==3.0.3 +xyzservices==2026.9.1 +requests==2.34.2 +certifi==2026.7.22 +charset-normalizer==3.5.1 +idna==3.19 +urllib3==2.7.0 +geographiclib==2.1 +python-dateutil==2.9.0.post0 +pytz==2026.3.post1 +tzdata==2026.3 +six==1.17.0 +pillow==12.3.0 diff --git a/run.sh b/run.sh new file mode 100755 index 0000000..a98bb66 --- /dev/null +++ b/run.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +# Startet die App aus ihrer virtuellen Umgebung. +set -euo pipefail +cd "$(dirname "$(readlink -f "$0")")" + +if [ ! -x .venv/bin/python ]; then + echo "Die App ist noch nicht eingerichtet. Bitte einmal ./install.sh ausführen." >&2 + exit 1 +fi + +exec .venv/bin/python app.py