A Flask application for logistics teams to schedule and track pickup/delivery operations. Users manage events with customer details (name, phone, address), item descriptions, vehicle assignments, and scheduling. Events display in a calendar view, export to iCalendar, and generate printable PDF tickets for drivers.
  • Python 64.5%
  • JavaScript 19%
  • HTML 10.5%
  • Shell 4.2%
  • CSS 1.6%
  • Other 0.2%
Find a file
2026-09-18 13:24:14 +02:00
.forgejo/workflows Use python+node docker image 2026-04-05 23:04:40 +02:00
app Jump agenda to clicked mini-calendar day and show today when empty 2026-09-18 13:24:14 +02:00
migrations Add audit trail for GDPR actions, user deletion, and failed logins 2026-09-16 19:40:04 +02:00
presentations Presentation 2026-01-28 20:17:59 +01:00
scripts Resolve client IP from X-Forwarded-For behind trusted proxies 2026-09-16 20:50:29 +02:00
tests Resolve client IP from X-Forwarded-For behind trusted proxies 2026-09-16 20:50:29 +02:00
.gitignore Added functionality to create and view events 2025-11-14 14:21:24 +01:00
config.py Resolve client IP from X-Forwarded-For behind trusted proxies 2026-09-16 20:50:29 +02:00
LICENSE Add MIT license and project README 2026-04-17 20:52:01 +02:00
pytest.ini Added tests 2025-11-17 16:13:43 +01:00
README.md Resolve client IP from X-Forwarded-For behind trusted proxies 2026-09-16 20:50:29 +02:00
requirements.txt Add deploy script for server setup and upgrades 2026-04-06 16:32:25 +02:00
run.py New beginnings 2025-10-23 13:46:24 +02:00

Pickup Scheduler

A calendar-based scheduling tool for logistics teams to manage pickups and deliveries. Events display in a calendar view, export to iCalendar, and generate printable PDF tickets for drivers.

Python Flask SQLAlchemy pytest License


Features

  • Calendar Overview — Monthly calendar grid with events, blocked days, and holidays color-coded for quick scanning
  • Event Management — Create, edit, and delete pickup/delivery events with customer details (name, phone, address), item descriptions, and vehicle assignments
  • PDF Tickets — Download printable PDF tickets per event for drivers (requires login)
  • iCalendar Subscription — Per-user, revocable .ics subscription link for importing the schedule into external calendar apps
  • Blocked Days — Mark date ranges as unavailable to prevent scheduling conflicts
  • Holiday Calendar — Automatically sync public holidays from an iCal feed (refreshes every 30 days)
  • Data Retention — Automatically anonymizes customer data on events older than EVENT_RETENTION_DAYS, keeping only the timestamps
  • Role-Based Access — Four permission levels: viewer, editor, admin, and owner
  • User Management — Admins can create, edit, and deactivate user accounts; registration can be toggled on/off
  • Mobile-Friendly — Responsive layout switches to an agenda view on narrow screens
  • Vehicle Tracking — Optionally assign vehicles to events (toggle via config)

Quick Start

Prerequisites

  • Python 3.11+
  • SQLite

Setup

git clone <repository-url>
cd pickup_scheduler
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt

Create a .env file:

FLASK_CONFIG=DevelopmentConfig
SECRET_KEY=<your-secret-key>
JWT_SECRET_KEY=<your-jwt-secret>
DATABASE_URL=sqlite:///app.db
OWNER_USERNAME=admin
OWNER_PASSWORD=<your-admin-password>
OWNER_NAME=Admin
REGISTRATION_ENABLED=true
SHOW_VEHICLES=true
HOLIDAY_CALENDAR_URL=https://www.thunderbird.net/media/caldata/autogen/SwedishHolidays.ics
EVENT_RETENTION_DAYS=365
TRUSTED_PROXIES=

EVENT_RETENTION_DAYS controls how long customer details (name, phone number, address) are kept after an event's effective end date. After this window, the personal data is scrubbed and only the timestamps, event type, and actor remain; set to 0 to disable.

When the app sits behind a reverse proxy (e.g. nginx/caddy), set TRUSTED_PROXIES to the proxy's IPs/CIDRs (comma-separated, e.g. 127.0.0.1,::1). The audit trail then records the real client IP from the leftmost X-Forwarded-For entry; otherwise the header is ignored and the socket peer IP is used. run_retention continues to check the raw socket IP only.

The retention sweep runs automatically when the app handles a request (at most once per day). For a guaranteed, deterministic sweep on an idle server, schedule a cron job that calls the local /run_retention endpoint:

# crontab (root or any user; the endpoint only accepts localhost by default)
@daily curl -fsS -X POST http://127.0.0.1:3000/run_retention >> /var/log/pickup_retention.log 2>&1
  • The endpoint refuses requests from any IP outside RETENTION_ALLOWED_IPS (default 127.0.0.1,::1); add more IPs as a comma-separated list in .env if the app sits behind a reverse proxy on another host.
  • curl -f exits non-zero on HTTP errors so cron mail/journal surfaces failures.
  • Response is JSON: {"success": true, "anonymized": N} (or {"skipped": "retention disabled"} when EVENT_RETENTION_DAYS=0).

Run migrations and start the server:

flask db upgrade
python run.py

Usage

What you can do How
View schedule Open the app — events, blocked days, and holidays appear on the calendar
Add event Click a date or use the "Nytt event" button
Block a date Admin panel → "Blockerade dagar" tab → pick date(s)
Print driver ticket Click an event → "PDF" button (requires login)
Export calendar Use "Hämta ICS" for your subscription link; "Förnya kalenderlänk" revokes it
Manage users Admin panel → "Användare" tab

Deployment

sudo ./scripts/deploy.sh deploy

The deploy script also supports upgrade, start, stop, restart, and status commands.


Testing

pytest -v

License

MIT License — see LICENSE for details.