Files
jardin/docs/synthese-dev.md
2026-03-09 18:26:04 +01:00

16 KiB
Raw Blame History

Jardin App — Synthèse de développement

Application web de gestion de jardins (potager, serre, plein air), self-hosted, mobile-first. Thème visuel : Gruvbox Dark "seventies" — vintage, chaleureux, contrasté. Date de dernière mise à jour : 2026-03-09


Stack technique

Couche Technologie
Backend Python 3.13 · FastAPI · SQLModel · SQLite
Frontend Vue 3 · Vite · TypeScript · Tailwind CSS · Pinia
Tests pytest (49 tests)
Déploiement Docker Compose (backend + frontend + volumes db/uploads)

Ports locaux : backend 8060, frontend 5173 (dev) / nginx (prod)


Architecture générale

jardin/
├── backend/
│   ├── app/
│   │   ├── main.py          — lifespan, routers, migration auto, seed
│   │   ├── migrate.py       — ALTER TABLE pour colonnes manquantes (sans perte)
│   │   ├── seed.py          — données de démo (plantes, outils, dictons, astuces)
│   │   ├── config.py        — DATABASE_URL, STATION_URL, METEO_LAT/LON
│   │   ├── database.py      — engine SQLite + PRAGMA foreign_keys=ON
│   │   ├── models/          — SQLModel tables
│   │   └── routers/         — endpoints FastAPI par domaine
│   ├── scripts/             — scripts one-shot (migration, import)
│   └── tests/               — pytest (49 tests)
├── frontend/
│   ├── src/
│   │   ├── api/             — clients Axios par domaine (*.ts + *.js)
│   │   ├── stores/          — stores Pinia
│   │   ├── views/           — pages Vue (16 vues)
│   │   └── utils/           — helpers partagés
│   └── tailwind.config.js   — palette Gruvbox Dark personnalisée
├── data/
│   ├── jardin.db            — base SQLite de production
│   └── uploads/             — photos uploadées (UUID.jpg)
└── docs/                    — plans, designs, données source

Modèle de données (tables actives)

Table Rôle
plant Plantes (nom commun, famille, caractéristiques culture)
plant_variety Variétés d'une plante (variete, boutique, prix, poids, DLUO)
plant_image Photos associées à une plante
garden Jardins (nom, type, sol, exposition, dimensions)
garden_cell Cases de la grille 2D du jardin
planting Instance : plante X dans jardin Y
planting_event Arrosage / taille / traitement / observation
task Tâches (ponctuelles ou récurrentes avec fréquence_jours)
tool Outils de jardinage
media Photos (upload, identification PlantNet/YOLO)
recolte Récoltes (quantité, unité, date)
meteostation Données station météo locale (WeeWX)
meteoopenmeteo Données open-meteo.com (prévisions)
lunar_calendar_entry Calendrier lunaire (phases, types de jours)
astuce Astuces jardinage (catégorie, mois, tags)
dicton Dictons saisonniers
saint_du_jour Saints du calendrier (366 entrées)
achat_intrant Achats d'intrants (semences, engrais…)
fabrication Fabrications maison (compost, préparations…)
user_settings Préférences interface (tailles, URL station…)

Pages de l'interface (16 vues)

Route Vue Description
/ DashboardView Tâches du jour, météo résumée, plantations actives
/jardins JardinsView Liste et création de jardins
/jardins/:id JardinDetailView Grille 2D, cases, plantations par case
/plantes PlantesView Catalogue plantes + variétés, popup détail/édition
/varietes VarietesView Vue dédiée variétés (ancienne)
/plantations PlantationsView Liste filtrable, création, fiche plantation
/taches TachesView Kanban / liste des tâches
/planning PlanningView Calendrier 4 semaines
/meteo CalendrierView Tableau météo + lunaire + dictons + navigation
/lunaire LunaireView Calendrier lunaire détaillé
/astuces AstucesView Astuces filtrables par catégorie/mois/tag
/bibliotheque BibliothequeView Galerie photos + identification PlantNet/YOLO
/outils OutilsView Outils de jardinage (CRUD)
/intrants IntratsView Onglets Achats + Fabrications
/reglages ReglagesView URL station, backup ZIP, tailles UI

Historique du développement

Phase 1 — Fondations (commit initial → 4787f04)

  • Scaffold projet Docker Compose (backend FastAPI + frontend Vue 3)
  • 10 tables SQLModel, CRUD complet jardins / variétés / plantations / tâches
  • Seed données démo : 1 jardin, variétés courantes, tâches types
  • Frontend : layout header + drawer + router 9 routes, API layer + stores Pinia
  • Vues MVP : Dashboard, Jardins, Grille, Variétés, Tâches, Plantations

Phase 2 — Photos & identification (2d8b1b4326158f)

  • Service YOLO (container FastAPI dédié) pour détection locale de plantes
  • Service PlantNet API pour identification par photo
  • Cache Redis pour les identifications
  • Endpoint POST /api/identify : PlantNet en premier, YOLO en fallback
  • Vue BibliothequeView : galerie lightbox + modal d'upload + identification
  • Fix : noms PlantNet en français (lang=fr), nginx client_max_body_size 20M

Phase 3 — Météo & calendrier (30327510d3bf20)

  • Tables MeteoStation + MeteoOpenMeteo (SQLModel)
  • APScheduler 3 jobs : station WeeWX (1×/heure), open-meteo (1×/heure), cleanup
  • Scraper WeeWX RSS pour données actuelles + NOAA pour données de la veille
  • Service open-meteo enrichi (sol, ETP, past_days, humidité)
  • 6 endpoints météo : tableau synthétique, station, prévisions, navigation
  • CalendrierView : tableau météo + lunaire + dictons + navigation Prev/Today/Next
  • Import calendrier lunaire (phases + types jours : racine/feuille/fleur/fruit)
  • Table saint_du_jour : 366 entrées importées

Phase 4 — Astuces & réglages UI (cc69d0dfb33540)

  • Table astuce : colonnes catégorie/tags/mois, CRUD + filtres
  • AstucesView avec filtres catégorie / mois / tag
  • Réglages interface : sliders taille texte/menu/icônes/miniatures (CSS vars globales)
  • Export UI_SIZE_DEFAULTS partagé entre composants

Phase 5 — Intrants (faa469ef8e64d6)

  • Tables AchatIntrant + Fabrication (SQLModel)
  • Migration automatique via migrate.py
  • Routers CRUD : achats, fabrications
  • Frontend : stores Pinia + clients API + IntratsView avec onglets Achats / Fabrications

Phase 6 — Plantes & Variétés — restructuration complète (e40351e4c279c3)

Moteur principal de cette session (2026-03-08/09)

Voir détail complet ci-dessous.


Dernières modifications — Phase 6 : Plantes & Variétés

Contexte

La gestion des plantes reposait sur une table unique où chaque entrée mêlait les caractéristiques de l'espèce (nom commun, famille, culture) et les données commerciales d'une variété particulière (boutique, prix, DLUO). Cette structure rendait impossible d'associer plusieurs variétés à une même plante.

Décision architecturale : scission en 2 tables distinctes.


Nouveau modèle de données

Table plant (espèce / nom commun)

id, nom_commun, nom_botanique, famille, type_plante, categorie
besoin_eau, besoin_soleil, espacement_cm, hauteur_cm, temp_min_c
temp_germination, temps_levee_j          ← nouveaux champs
duree_culture_j, profondeur_semis_cm, sol_conseille
semis_interieur_mois, semis_exterieur_mois, repiquage_mois
plantation_mois, recolte_mois
maladies_courantes, astuces_culture, url_reference, notes
associations_favorables (JSON), associations_defavorables (JSON)
created_at

Table plant_variety (variété commerciale)

id, plant_id (FK → plant.id), variete, tags, notes_variete
boutique_nom, boutique_url, prix_achat, date_achat, poids, dluo
created_at

Schéma de réponse API PlantWithVarieties

Objet non persisté retourné par tous les GET /api/plants* :

= tous les champs de Plant + varieties: List[PlantVariety]

Fichiers modifiés — Backend

Fichier Modification
backend/app/models/plant.py Modèle Plant épuré + PlantVariety + PlantWithVarieties + PlantImage
backend/app/models/__init__.py Export PlantVariety, PlantWithVarieties
backend/app/database.py Activation PRAGMA foreign_keys=ON (event listener SQLAlchemy)
backend/app/migrate.py Ajout section plant_variety + colonnes temp_germination/temps_levee_j sur plant — suppression colonnes boutique/dluo de la section plant
backend/app/seed.py Extraction du champ variete avant création Plant + création PlantVariety associée
backend/app/routers/plants.py Remplacement complet : helper _with_varieties(), GET retourne PlantWithVarieties, CASCADE sur DELETE, 4 endpoints CRUD /plants/{id}/varieties
backend/tests/test_plants.py Réécriture du test variétés pour la nouvelle architecture

Nouveaux endpoints variétés

GET    /api/plants                      → List[PlantWithVarieties]
GET    /api/plants/{id}                 → PlantWithVarieties
POST   /api/plants/{id}/varieties       → PlantVariety (201)
GET    /api/plants/{id}/varieties       → List[PlantVariety]
PUT    /api/plants/{id}/varieties/{vid} → PlantVariety
DELETE /api/plants/{id}/varieties/{vid} → 204

Scripts one-shot (déjà exécutés)

backend/scripts/migrate_plant_varieties.py

Migration de la base existante :

  • Crée la table plant_variety
  • Migre variete/tags/boutique_* de chaque plant → une entrée plant_variety
  • Fusionne le plant "haricot grimpant" (id=21) comme variété "Grimpant Neckarkönigin" sous "Haricot" (id=7) → supprime plant id=21
  • Ajoute colonnes temp_germination et temps_levee_j à plant

Résultat : 20 plants · 21 variétés

backend/scripts/import_graines.py

Import des données documentaires :

  • docs/graine/caracteristiques_plantation.json : 14 sachets de graines
  • docs/arbustre/caracteristiques_arbustre.json : 4 arbustes/fruitiers
  • Mapping intelligent nom sachet → nom commun BDD (NOM_MAP)
  • Conversion mois romains → CSV (roman_to_csv : "III-IV" → "3,4")
  • Enrichissement des champs plant (semis, récolte, profondeur, espacement, T° germination)
  • Création PlantVariety avec nom de variété extrait
  • Copie des 36 photos de sachets dans data/uploads/ + entrées media
  • Script idempotent (guard SELECT avant chaque INSERT)

Résultat : 25 plants · 39 variétés · 36 photos importées


Fichiers modifiés — Frontend

Fichier Modification
frontend/src/api/plants.ts Interface PlantVariety + interface Plant mise à jour (sans boutique/variete, avec varieties?, temp_germination?, temps_levee_j?) + méthodes createVariety, updateVariety, deleteVariety
frontend/src/stores/plants.ts Actions update, createVariety, updateVariety, removeVariety avec synchronisation du store local
frontend/src/views/PlantesView.vue Voir détail ci-dessous
frontend/src/views/TachesView.vue Adaptation plant.varieties?.[0]?.variete (champ déplacé)
frontend/src/utils/plants.ts formatPlantLabel utilise varieties?.[0]?.variete au lieu de plant.variete

PlantesView.vue — nouvelles fonctionnalités

Script setup :

  • detailPlantObj = ref<Plant | null> : référence à la plante affichée
  • detailVarieties = computed(() => detailPlantObj.value?.varieties ?? []) : liste des variétés
  • Refs formulaire variété : showFormVariety, editVariety, formVariety
  • Fonctions : openAddVariety, openEditVariety, closeFormVariety, submitVariety, deleteVariety
  • submitPlant : extrait les champs variété du form avant envoi au backend, puis crée/modifie la PlantVariety séparément

Template :

  • Bouton " Variété" dans le footer du popup détail plante
  • Champs temp_germination et temps_levee_j dans la section caractéristiques (conditionnels)
  • Liste des variétés dans le popup détail avec boutons ✏️ et ✕, affichage DLUO expirée
  • Popup formulaire variété (z-[70]) : nom variété, tags, enseigne (select BOUTIQUES), prix, poids, date achat, DLUO, URL, notes

État de la base de production après la phase 6

plant         : 25 entrées (20 potager + 5 arbustes)
plant_variety : 39 entrées
media         : 86 entrées (dont 36 photos sachets)

Répartition par catégorie :

  • Potager : Tomate, Carotte, Courgette, Laitue, Ail, Oignon, Haricot (×2 variétés), Pois (×2), Poireau, Pomme de terre, Fraise, Framboise, Persil, Échalote, Chou-fleur, Chou, Betterave, Radis, Épinard, Basilic, Courge
  • Arbuste : Vigne, Cassissier, Framboisier

État des tests

49 tests passed (pytest)

Couverture par fichier de test :
  test_gardens.py    — CRUD jardins + filtres
  test_plants.py     — CRUD plantes + test_plant_variety_crud (nouvelle architecture)
  test_plantings.py  — CRUD plantations
  test_tasks.py      — CRUD tâches + filtres statut/planting
  test_tools.py      — CRUD outils
  test_varieties.py  — CRUD variétés (ancienne table)

Points en cours / backlog

Fonctionnalités planifiées (amelioration.md)

  • Photos sachet recto/verso dans le popup variété (upload + compression WebP)
  • Page 404 catch-all (route Vue manquante)
  • Export/import JSON complet
  • Observations dans PlantationsView (backend prêt, UI manquante)
  • Restauration depuis ZIP (upload + restore)
  • Réglages par sections (interface, jardin, plante, tâches, calendrier)
  • Capteurs IoT : ensoleillement, température sol/air, humidité, pH (MQTT)
  • "Astuce du jour" dans le Dashboard
  • Vue Gantt dans PlanningView
  • Satisfaction plante (étoiles 1-5)

Dette technique connue

  • Colonnes orphelines dans plant : variete, tags, boutique_*, prix_achat, date_achat, poids, dluo toujours présentes en SQLite (impossibilité de DROP COLUMN) — SQLModel les ignore, pas d'impact fonctionnel
  • PlantCreate manquant : les endpoints POST/PUT /api/plants acceptent le modèle Plant (table=True) directement au lieu d'un schéma PlantCreate dédié
  • N+1 queries sur GET /api/plants : une query par plante pour charger ses variétés — acceptable jusqu'à ~200 plantes
  • planting.variety_id FK mal nommée : pointe vers plant.id et non plant_variety.id — à corriger avant d'implémenter le choix de variété lors d'une plantation
  • Données orphelines BDD : jardin id=1 supprimé mais 24 garden_cells + 1 planting + 1 measurement subsistent

Commandes utiles

# Lancer l'environnement complet
docker compose up --build

# Backend seul (développement)
cd backend && uvicorn app.main:app --reload --port 8060

# Frontend seul (développement)
cd frontend && npm run dev

# Tests backend
cd backend && python3 -m pytest tests/ -v

# Build frontend
cd frontend && npm run build

# Réexécuter l'import graines (idempotent)
python3 backend/scripts/import_graines.py

# Vérifier état BDD
python3 -c "
import sqlite3; conn = sqlite3.connect('data/jardin.db')
print('plant:', conn.execute('SELECT COUNT(*) FROM plant').fetchone()[0])
print('plant_variety:', conn.execute('SELECT COUNT(*) FROM plant_variety').fetchone()[0])
print('media:', conn.execute('SELECT COUNT(*) FROM media').fetchone()[0])
conn.close()
"