# PlantDB A personal plant management web app built with Django 5, Bootstrap 5, and HTMX. Track your plants, log pruning activity, and get care info auto-filled from the Perenual species database. ## Features - **Species search** — search Perenual API by name; care data (watering, sunlight, pruning months) is auto-filled into the plant form and cached locally so the API is only called once per species - **Plant list** — filterable by name, location, indoor/outdoor - **Plant detail** — shows care info, pruning status, and pruning history - **Pruning log** — log a pruning event with date and notes via HTMX partial update - **Pruning calendar** — all plants grouped by urgency: overdue, due this month, due next month, later - **Dashboard** — plant count, overdue pruning count, and attention list ## Data model ``` Species — cached from Perenual API (common_name, scientific_name, watering, sunlight, pruning_months, api_image_url, perenual_id) Plant — your actual plant (name, location, is_indoor, pruning_months, photo, notes, species FK) PruningLog — pruning events (plant FK, pruned_on, notes) ``` `Plant.pruning_months` is a JSON list of integers (1–12). It defaults to the species value but can be overridden per plant. ## Tech stack | Layer | Library | |---|---| | Framework | Django 5.2 | | Frontend | Bootstrap 5.3 + HTMX 1.9 | | Image handling | Pillow | | HTTP client | requests | | Server | Gunicorn + Nginx | | Database | SQLite | ## Local development ```bash python -m venv .venv && source .venv/bin/activate pip install -r requirements.txt python manage.py migrate python manage.py runserver ``` Set `PERENUAL_API_KEY` in your environment to enable species search (free tier: 100 requests/day). Run tests: ```bash pytest ``` ## Deployment (Docker) The production stack runs as two containers: `web` (Gunicorn) behind `nginx`. ```bash # On the host — first time cp .env.example .env # fill in SECRET_KEY, PERENUAL_API_KEY, ALLOWED_HOSTS, CSRF_TRUSTED_ORIGINS docker compose up -d --build ``` After code changes: ```bash # Sync files from dev machine rsync -av --exclude='*.pyc' --exclude='__pycache__' . dockerhost:/home/stephan/stacks/plantdb/ # Rebuild ssh dockerhost "cd /home/stephan/stacks/plantdb && docker compose up -d --build web" ``` ### Environment variables | Variable | Required | Example | |---|---|---| | `SECRET_KEY` | yes | long random string | | `DEBUG` | no | `False` | | `ALLOWED_HOSTS` | yes | `localhost,127.0.0.1,dockerhost` | | `CSRF_TRUSTED_ORIGINS` | yes (non-localhost) | `http://dockerhost:8083` | | `PERENUAL_API_KEY` | no | `sk-...` | `CSRF_TRUSTED_ORIGINS` is required when accessing the app on a non-standard port or hostname — Django 5 rejects POSTs without it. ### Volumes | Volume | Purpose | |---|---| | `db` | SQLite database at `/app/data/db.sqlite3` | | `media` | User-uploaded plant photos | | `static` | Collected static files | ## Plant data sources — research notes The app currently uses **Perenual** (free tier, 100 req/day) for species data. Other sources investigated: | Source | Status | Verdict | |---|---|---| | **Perenual** | Active | Best structured care data (watering, sunlight, pruning months). Free tier sufficient for personal use; each species is cached after first lookup. | | **OpenFarm** | Shut down April 2025 | Dead. GitHub repo is a Ruby/MongoDB app with no data dump. | | **Trefle** | Active | Botanical taxonomy only — no care schedules. Not a replacement. | | **Wikipedia API** | Active | Returns name, description, thumbnail. No structured care data. | | **Google Knowledge Panel** | N/A | Has structured care data but scraping violates ToS and is technically brittle. | ### Future opportunity Google's plant Knowledge Panels aggregate care data (watering frequency, sunlight, pruning) from sources like The Sill, Bloomscape, Gardenia.net, and others. A curated, structured plant care dataset — particularly one covering common garden plants in NW Europe — does not currently exist as a clean, accessible API. This could be a commercial opportunity: either licensing a dataset or building a plant care API as a product.