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 <noreply@anthropic.com>
This commit is contained in:
@@ -40,7 +40,8 @@ CSV_PATH = DATA_DIR / "orte.csv"
|
|||||||
MAP_PATH = DATA_DIR / "karte.html"
|
MAP_PATH = DATA_DIR / "karte.html"
|
||||||
BACKUP_DIR = DATA_DIR / "backups"
|
BACKUP_DIR = DATA_DIR / "backups"
|
||||||
LOG_PATH = DATA_DIR / "app.log"
|
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"
|
WINDOW_STATE_PATH = DATA_DIR / "window.json"
|
||||||
CSV_COLUMNS = ["date", "city", "postal_code", "lat", "lon"]
|
CSV_COLUMNS = ["date", "city", "postal_code", "lat", "lon"]
|
||||||
|
|
||||||
@@ -735,8 +736,12 @@ class App(ttk.Window):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def _install_icon(self) -> None:
|
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:
|
try:
|
||||||
|
if ICON_PATH.exists():
|
||||||
|
self._icon_img = tk.PhotoImage(file=str(ICON_PATH))
|
||||||
|
self.iconphoto(True, self._icon_img)
|
||||||
|
return
|
||||||
size = 64
|
size = 64
|
||||||
img = tk.PhotoImage(width=size, height=size)
|
img = tk.PhotoImage(width=size, height=size)
|
||||||
img.put("white", to=(0, 0, size, 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))
|
img.put(DRK_RED, to=(7, arm, size - 7, size - arm))
|
||||||
self.iconphoto(True, img)
|
self.iconphoto(True, img)
|
||||||
self._icon_img = img # Referenz halten
|
self._icon_img = img # Referenz halten
|
||||||
if not ICON_PATH.exists():
|
try:
|
||||||
try:
|
img.write(str(ICON_FALLBACK_PATH), format="png")
|
||||||
img.write(str(ICON_PATH), format="png")
|
except tk.TclError:
|
||||||
except tk.TclError:
|
pass # Tk < 8.6 kann kein PNG schreiben – nicht kritisch
|
||||||
pass # Tk < 8.6 kann kein PNG schreiben – nicht kritisch
|
|
||||||
except tk.TclError:
|
except tk.TclError:
|
||||||
log.warning("Fenster-Icon konnte nicht gesetzt werden.")
|
log.warning("Fenster-Icon konnte nicht gesetzt werden.")
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -16,7 +16,7 @@ code) and plots them on an interactive map.
|
|||||||
| [overview.md](overview.md) | What the app does, feature by feature |
|
| [overview.md](overview.md) | What the app does, feature by feature |
|
||||||
| [architecture.md](architecture.md) | Code structure, threading model, data flow |
|
| [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 |
|
| [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 |
|
| [improvements.md](improvements.md) | Prioritized review findings and suggested changes |
|
||||||
| [changelog.md](changelog.md) | What has changed, newest first |
|
| [changelog.md](changelog.md) | What has changed, newest first |
|
||||||
| [dev-notes.md](dev-notes.md) | Assumptions that turned out wrong — read before editing |
|
| [dev-notes.md](dev-notes.md) | Assumptions that turned out wrong — read before editing |
|
||||||
|
|||||||
@@ -2,6 +2,16 @@
|
|||||||
|
|
||||||
Notable changes to the app. Newest first.
|
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
|
## 2026-09-07 — built-in self-update
|
||||||
|
|
||||||
New **Hilfe** menu:
|
New **Hilfe** menu:
|
||||||
|
|||||||
+4
-3
@@ -66,7 +66,8 @@ generation, geocoding failures, and uncaught exceptions.
|
|||||||
and restored on start. Git-ignored, per-machine. Safe to delete (window opens at
|
and restored on start. Git-ignored, per-machine. Safe to delete (window opens at
|
||||||
its default size).
|
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`
|
`icon.png` in the repo root is the **bundled** launcher/window icon (committed).
|
||||||
launcher can point at it. Git-ignored (regenerated). Delete to force a redraw.
|
`data/icon.png` is only a runtime-drawn fallback the app writes if the bundled
|
||||||
|
one is missing — git-ignored.
|
||||||
|
|||||||
+13
-18
@@ -127,20 +127,14 @@ example file.
|
|||||||
Gives you history, a rollback path, and a clean way to push updates to the
|
Gives you history, a rollback path, and a clean way to push updates to the
|
||||||
laptop (`git pull`).
|
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
|
- ✅ `requirements.txt` fully pinned (direct + transitive), tested on 3.12.
|
||||||
a one-time terminal session, which is acceptable but fragile (loose version
|
- ✅ `run.sh` + `install.sh` (generates the `.desktop` entry) shipped in the repo.
|
||||||
pins mean a future `pip install` could pull an incompatible pandas/folium).
|
- ✅ Bundled `icon.png`.
|
||||||
|
- ⬜ Optional: a [PyInstaller](https://pyinstaller.org/) one-file bundle would
|
||||||
**Suggested changes**
|
drop the apt/venv step entirely, at the cost of building per release. Not
|
||||||
|
needed while `install.sh` works.
|
||||||
- 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.
|
|
||||||
|
|
||||||
## 6. Diagnostics / logging 🟡 — ✅ done
|
## 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
|
## Suggested order of remaining work
|
||||||
|
|
||||||
1. Initial git commit (item 4), then decide on committing `orte.csv`.
|
1. Deploy to the laptop: `git clone` + `./install.sh` (see [setup.md](setup.md)).
|
||||||
2. **Set up the git remote** the updater pulls from, and pin all deps (item 5).
|
2. Stable `id` column (item 7 of the first review).
|
||||||
3. `run.sh` + `.desktop` launcher in the repo (item 5).
|
3. Formalize tests (item 10) — a non-GUI `tests/` suite already exists in
|
||||||
4. Stable `id` column (item 7 of the first review).
|
spirit (dev smoke scripts); move it into the repo.
|
||||||
5. Formalize tests (item 10); offline map (item 9) only if offline use becomes real.
|
4. Offline map (item 9) only if offline use becomes real.
|
||||||
|
5. Optional PyInstaller bundle (item 5).
|
||||||
|
|||||||
+52
-97
@@ -1,116 +1,63 @@
|
|||||||
# Setup & running
|
# Setup & running
|
||||||
|
|
||||||
Target machine: **Linux Mint laptop**, single non-technical user. The goal is
|
Target machine: **Linux Mint laptop**, single non-technical user. Day-to-day use
|
||||||
that day-to-day use is a **double-click**, with the terminal only needed once
|
is a **double-click from the menu**; the terminal is needed once, for install.
|
||||||
during install.
|
|
||||||
|
|
||||||
## 1. System packages
|
## Quick install (Linux Mint)
|
||||||
|
|
||||||
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:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
mkdir -p ~/Apps && cd ~/Apps
|
mkdir -p ~/Apps && cd ~/Apps
|
||||||
git clone <repo-url> 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
|
```bash
|
||||||
cd ~/Apps/DRK_Blutspende_Orte
|
cd ~/Apps/DRK_Blutspende_Orte
|
||||||
python3 -m venv .venv
|
./run.sh # or: .venv/bin/python app.py
|
||||||
.venv/bin/pip install -r requirements.txt
|
|
||||||
```
|
```
|
||||||
|
|
||||||
`requirements.txt`:
|
`data/orte.csv` is created automatically on first run if missing.
|
||||||
|
|
||||||
```
|
|
||||||
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.
|
|
||||||
|
|
||||||
## Python / Tk version
|
## Python / Tk version
|
||||||
|
|
||||||
Python 3.9+ is fine (the code uses `from __future__ import annotations`). The
|
Python 3.10–3.12 (Mint's `python3`). `ttkbootstrap` needs **Tk 8.6+**, which
|
||||||
system Python on current Mint releases is well above that.
|
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
|
||||||
`ttkbootstrap` needs **Tk 8.6 or newer**. Linux Mint's `python3-tk` provides
|
[dev-notes.md](dev-notes.md).
|
||||||
that. The macOS CommandLineTools Python used during development ships Tk 8.5 and
|
|
||||||
**cannot run the GUI** — see [dev-notes.md](dev-notes.md).
|
|
||||||
|
|
||||||
## Updating
|
## Updating
|
||||||
|
|
||||||
**From inside the app:** menu **Hilfe ▸ Nach Update suchen**. The app also
|
**From inside the app:** menu **Hilfe ▸ Nach Update suchen**. It also checks
|
||||||
checks quietly on start and offers the update if there is one. It fast-forwards
|
quietly on start and offers the update if there is one. It fast-forwards to the
|
||||||
to the server version, reinstalls dependencies if `requirements.txt` changed,
|
server version, reinstalls dependencies if `requirements.txt` changed, and
|
||||||
and restarts itself. `data/` is never touched.
|
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.
|
||||||
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.
|
|
||||||
|
|
||||||
**Manually** (equivalent):
|
**Manually** (equivalent):
|
||||||
|
|
||||||
@@ -120,8 +67,16 @@ git pull --ff-only
|
|||||||
.venv/bin/pip install -r requirements.txt
|
.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
|
## Data location & backup
|
||||||
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.
|
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.
|
||||||
|
|||||||
Executable
+44
@@ -0,0 +1,44 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Einmalige Einrichtung auf einem Linux-Mint-Rechner:
|
||||||
|
# - benötigte System-Pakete
|
||||||
|
# - virtuelle Python-Umgebung + Abhängigkeiten
|
||||||
|
# - Eintrag im Startmenü
|
||||||
|
#
|
||||||
|
# Aufruf: ./install.sh
|
||||||
|
set -euo pipefail
|
||||||
|
cd "$(dirname "$(readlink -f "$0")")"
|
||||||
|
APPDIR="$(pwd)"
|
||||||
|
|
||||||
|
echo "==> 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" <<EOF
|
||||||
|
[Desktop Entry]
|
||||||
|
Type=Application
|
||||||
|
Name=DRK Blutspende – Arbeitsorte
|
||||||
|
Comment=Einsatzorte protokollieren und auf der Karte anzeigen
|
||||||
|
Exec=$APPDIR/run.sh
|
||||||
|
Icon=$APPDIR/icon.png
|
||||||
|
Terminal=false
|
||||||
|
Categories=Utility;
|
||||||
|
StartupWMClass=DRK Blutspende – Arbeitsorte
|
||||||
|
EOF
|
||||||
|
chmod +x "$APPDIR/run.sh"
|
||||||
|
update-desktop-database "$DESKTOP_DIR" 2>/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"
|
||||||
+27
-3
@@ -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
|
ttkbootstrap==1.10.1
|
||||||
geopy>=2.4.1
|
geopy==2.4.1
|
||||||
folium>=0.17.0
|
folium==0.19.5
|
||||||
pandas>=2.2.0
|
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
|
||||||
|
|||||||
@@ -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
|
||||||
Reference in New Issue
Block a user