bloombase/docs/superpowers/specs/2026-05-27-plantdb-design.md
Stephan Kerkman 5b2cc87540 Add PlantDB design spec
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 19:23:20 +02:00

8.7 KiB
Raw Blame History

PlantDB — Design Spec

Date: 2026-05-27 Stack: Django 5 + Bootstrap 5 + HTMX Deployment: Docker Compose on homelab


1. Overview

PlantDB is a mobile-first family plant and flower registry. It tracks both indoor and outdoor plants, stores care information per species, surfaces a pruning calendar, and integrates with the Perenual API to auto-populate species data. No user accounts — a single shared instance for the whole family.


2. Architecture

Browser (mobile-first)
    │  Bootstrap 5 + HTMX (partial page updates)
    ▼
Nginx (reverse proxy + static/media files)
    ▼
Gunicorn + Django 5
    ▼
SQLite (single file, Docker volume)
    │
    └── Perenual API (external, cached on first lookup)

Key principles:

  • Server-rendered Django templates throughout; HTMX handles dynamic interactions (species search live results, pruning log updates) without a separate API layer.
  • Perenual API is called only once per species — results are cached in the Species table. No recurring API cost after initial lookups.
  • All uploaded media (personal plant photos) stored in a Docker volume and served by Nginx.

3. Data Model

Species

Populated from Perenual API or entered manually. One row per plant species.

Field Type Notes
id PK
common_name CharField
scientific_name CharField
description TextField
watering CharField e.g. "Weekly", "Twice a week"
sunlight CharField e.g. "Full sun", "Partial shade"
shadow_tolerance CharField e.g. "Low", "Medium", "High"
max_height_cm IntegerField nullable
growth_rate CharField e.g. "Slow", "Moderate", "Fast"
growth_season CharField e.g. "SpringSummer"
pruning_months JSONField Default schedule, e.g. [2, 6, 8] — pre-fills the Plant field
api_image_url URLField Image from Perenual, nullable
perenual_id IntegerField Unique, nullable — prevents duplicate fetches

Plant

One row per physical plant owned by the family.

Field Type Notes
id PK
name CharField User's nickname, e.g. "Big rose on back fence"
species FK → Species Nullable — plant can exist without a species link
location CharField Free text, e.g. "Living room windowsill"
is_indoor BooleanField
pruning_months JSONField This plant's actual pruning schedule — pre-filled from species, editable per plant
photo ImageField Personal photo, stored in media volume, nullable
notes TextField Free text, nullable
date_added DateField Auto-set on creation

PruningLog

One row per pruning event, logged by the family.

Field Type Notes
id PK
plant FK → Plant
pruned_on DateField
notes TextField nullable

4. Pages & Views

Dashboard (home /)

  • Summary tiles: total plant count, number of plants due for pruning this month.
  • Upcoming pruning list: overdue plants first (red), then due this month (amber).
  • Buttons: "View all plants", "Add plant".
  • HTMX: none needed — static server render on load.

Plant List (/plants/)

  • Scrollable list of all plants, each showing name, location, indoor/outdoor badge, and thumbnail.
  • Search bar at top — HTMX live search filters the list on each keystroke (targets the list partial).
  • Filter chips: All / Indoor / Outdoor.
  • FAB-style "+" button links to Add Plant.

Plant Detail (/plants/<id>/)

  • Full-width personal photo hero (falls back to API species image if no personal photo).
  • Name, scientific name (italic), indoor/outdoor badge.
  • Location line.
  • Care chips: 💧 watering · ☀️ sunlight · 📏 max height.
  • Species reference image (small, below care info) if API image available and personal photo also exists.
  • Pruning strip: recommended months + last pruned date. Amber highlight if due this month, red if overdue.
  • "Log pruning" button → inline HTMX form (date + optional notes), updates pruning strip on success.
  • Pruning history accordion: list of all PruningLog entries for this plant.
  • Edit and Delete actions.

Add Plant (/plants/add/)

Two-step flow:

Step 1 — Species search (optional)

  • Search input with HTMX live lookup against Perenual API (debounced, 300ms).
  • Results show common name, scientific name, thumbnail.
  • Selecting a result caches the species in the DB (if not already cached) and pre-fills the form.
  • "Skip — enter manually" link bypasses to step 2 with a blank form.

Step 2 — Plant form

  • Nickname field (required).
  • Location field (free text).
  • Indoor/Outdoor toggle.
  • Care fields (watering, sunlight, etc.) — pre-filled from species if selected, all editable.
  • Pruning months — pre-filled from species, shown as a 12-checkbox month picker.
  • Photo section: "Take photo" (camera capture via <input type="file" accept="image/*" capture="environment">). The species API image, if available, is always shown automatically on the plant detail as a reference — no separate action needed.
  • Notes textarea.
  • Save button.

Edit Plant (/plants/<id>/edit/)

Same form as Add, pre-populated.

Pruning Calendar (/pruning/)

  • Grouped by urgency:
    1. Overdue (red) — plants with a recommended pruning month in the past and no log entry since.
    2. This month (amber) — plants with current month in pruning_months.
    3. Next month (grey) — plants due next month.
    4. Later this year — collapsed section, expandable.
  • Each entry: plant name, location, recommended months, last pruned date. Tap to open plant detail.
  • Month strip at top (scrollable) to jump to any month and see what's scheduled.

Species Detail (/species/<id>/) — optional read-only

  • Shows all species info as reference. Linked from plant detail. Not editable directly (edit via the plant form).

5. Perenual API Integration

  • Endpoint used: GET https://perenual.com/api/species-list?q=<name>&key=<API_KEY>
  • API key: stored in Django settings via environment variable PERENUAL_API_KEY.
  • Cache strategy: on first successful lookup, a Species row is created with perenual_id set. Any subsequent search that returns a species already in the DB skips the API entirely.
  • Free tier limit: 100 requests/day. Given that lookups only happen when adding new plants (not on every page view), this is not a practical constraint for a family app.
  • Fallback: if the API is unreachable or returns an error, the add-plant form stays open with an empty species form for manual entry. No hard dependency on the API.

6. Pruning Logic

A plant is considered overdue for pruning if:

  • It has at least one pruning_month that fell within the past 12 months, AND
  • There is no PruningLog entry with pruned_on falling within that month's window.

A plant is due this month if the current month is in Plant.pruning_months and not yet logged this month.

Using a 12-month rolling window (rather than calendar-year) correctly handles plants due in December when checked in January.

The pruning calendar view computes this in a Django queryset annotated with the current date — no background tasks or scheduled jobs needed.


7. Deployment

Docker Compose

services:
  web:      Django + Gunicorn
  nginx:    Reverse proxy + static/media serving

volumes:
  db:       SQLite file
  media:    Uploaded plant photos
  • Accessible on the local network via homelab IP or a local hostname (e.g. plantdb.home).
  • No SSL required for LAN use.
  • docker compose up -d starts everything; docker compose down stops it.
  • Database backup: copy the SQLite volume file. Simple enough to add to an existing homelab backup routine.

Environment Variables

Variable Purpose
PERENUAL_API_KEY Perenual API key
SECRET_KEY Django secret key
DEBUG False in production
ALLOWED_HOSTS Homelab hostname/IP

8. Static & Media Files

  • Static files (CSS, JS, Bootstrap): collected via manage.py collectstatic, served by Nginx.
  • Media files (plant photos): stored in /media/ inside the container, mounted as a Docker volume, served by Nginx under /media/.

9. Dependencies

Package Purpose
django Web framework
gunicorn WSGI server
django-htmx HTMX integration helpers
pillow Image handling for ImageField
requests Perenual API calls

No frontend build step — Bootstrap 5 and HTMX loaded via CDN or bundled as static files.


10. Out of Scope

  • User authentication / accounts
  • Watering or feeding reminders
  • Push notifications
  • Multi-language support
  • Public internet access / SSL (LAN only)
  • Offline / PWA mode