Commit Graph

28 Commits

Author SHA1 Message Date
gilles 5d7dbec67c fix(mcp): status active + search_products guard + item.product + cleanup auto-name 2026-05-28 06:41:04 +02:00
gilles 87efbcb03d feat(mcp): 6 outils shopping + tests
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 06:37:38 +02:00
gilles c72ffd0ad6 fix(mcp): FTS colonnes non qualifiées + test positif search_notes 2026-05-28 06:29:36 +02:00
gilles e902452781 feat(mcp): 5 outils notes + tests
Ajoute search_notes, get_note, create_note, update_note, delete_note au serveur MCP.
Tests: 6 nouveaux tests notes (13 tests MCP au total, tous passent).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 23:11:15 +02:00
gilles 6cd866c77a fix(mcp): scope fixture NullPool + suppression imports inutiles + validation enums + cleanup tests
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 23:03:43 +02:00
gilles 05db49f27a feat(mcp): 5 outils todos + tests
Ajoute mcp_server.py avec get_todos, create_todo, update_todo, postpone_todo, delete_todo.
Ajoute test_mcp.py (7 tests). Corrige conftest pour injecter NullPool dans AsyncSessionLocal des outils MCP (évite les conflits d'event loop entre tests).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 22:58:20 +02:00
gilles ec87bc091d feat(sse): sync temps réel multi-appareils via Server-Sent Events v0.5.8
- Broadcaster asyncio.Queue avec keepalive 25s (prévient timeout proxy)
- Endpoint GET /api/events/stream (StreamingResponse text/event-stream)
- Broadcast notes_changed / todos_changed / shopping_changed sur toutes mutations
- Hook useServerEvents: EventSource avec reconnexion automatique (3s)
- Pages Notes, Todos, Shopping abonnées aux événements SSE
- nginx: location SSE dédiée (proxy_buffering off, timeout 24h)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 20:12:02 +02:00
gilles c72ca04fd2 feat(config): stats vidéo + user 1000:1000 dans docker-compose
Admin stats : ajout video (count + size_bytes) dans /api/admin/stats.
ConfigPage : grille médias 3 colonnes (Photos / Audio / Vidéos).

docker-compose : backend et backend-worker tournent en user 1000:1000
pour que les fichiers écrits dans ./data/ appartiennent à l'utilisateur
hôte et non à root.

v0.5.6

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 16:44:31 +02:00
gilles 6c9ebcaab7 feat(notes): support vidéo + transcodage audio AAC universel
Audio : ffmpeg transcode toute entrée (webm/ogg/m4a) vers AAC/m4a
au moment de l'upload → lecture Safari iOS garantie.

Vidéo : nouveau save_video(), webm transcodé en H.264/mp4, mp4/quicktime
stocké directement. Lecteur <video> inline dans NoteCard.

Frontend :
- Bouton vidéo (fa-video) dans les actions de chaque note
- Icônes fa-image / fa-microphone / fa-video / fa-location-dot dans la méta
- Filtres rapides : Photo / Audio / Vidéo / GPS (avec icônes fa)
- Boutons actions migrés vers icônes Font Awesome
- client_max_body_size nginx : 15m → 200m pour les vidéos

v0.5.4

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 16:31:05 +02:00
gilles d017a0879e fix(media): corriger import ALLOWED_AUDIO_PREFIXES et strip codec MIME
ImportError au démarrage du backend : ALLOWED_AUDIO_TYPES avait été renommé
en ALLOWED_AUDIO_PREFIXES dans services/media.py mais l'import dans api/media.py
n'avait pas été mis à jour.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 16:07:04 +02:00
gilles 454dbadb2f feat(config): section statistiques — BDD, médias, entités
- GET /api/admin/stats : taille BDD (pg_database_size), nb+poids photos/audio
  (scan filesystem), nb notes/todos/listes (requêtes SQL directes)
- ConfigPage : grille 3 colonnes todos/notes/listes + 2 tuiles médias + ligne BDD

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 16:04:08 +02:00
gilles 9aaa5fb562 fix(audio+gps): lecture audio multi-navigateur + icône GPS dans tuile note
Audio :
- MediaRecorder détecte le format supporté : webm (Chrome/Firefox) ou mp4 (Safari/iOS)
- Extension sauvegardée correctement (.webm ou .m4a) selon le navigateur
- Backend : ALLOWED_AUDIO_PREFIXES remplace le set strict, strip des codec suffixes

GPS (note card) :
- Icône fa-location-dot (accent vert) avec tooltip lat/lon remplace l'emoji 📍

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 15:55:35 +02:00
gilles be0c8bceb6 feat: export Markdown notes (ARQ/Redis) + backup/restore BDD — v0.5.2
- Volume data/ (bind mount ./data) remplace le volume uploads nommé
  data/notes/ → .md auto-générés, data/uploads/ → médias, data/backup/ → dumps
- Service Redis (redis:7-alpine) + worker ARQ (backend-worker)
- notes_markdown.py : frontmatter YAML + contenu + pièces jointes (liens relatifs)
  Nom : YYYY-MM-DD_slug-titre_shortid.md, rotation si titre modifié
- api/notes.py : publie export_note_markdown / remove_note_markdown sur Redis
  après chaque create / update / delete / add_attachment / delete_attachment
- api/admin.py : POST /backup, GET /backups, POST /restore/{filename} (pg_dump/pg_restore)
- Backend Dockerfile : postgresql-client ; requirements : arq==0.26.1
- ConfigPage : section "Base de données" avec sauvegarde + liste + restauration

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 15:33:29 +02:00
gilles 3dbd554eeb fix(media): upload photo — taille, formats et nginx
- nginx : client_max_body_size 15m (photos smartphone > 1 Mo rejetées silencieusement)
- backend : redimensionnement original à 500×500 max (aspect ratio conservé) avant sauvegarde WEBP
- backend : thumbnail généré depuis l'image déjà redimensionnée (économie mémoire)
- backend : formats acceptés étendus — image/heic, image/heif, image/jpg
- backend : normalisation content-type en lowercase (robustesse navigateurs)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 13:29:45 +02:00
gilles 58b5de15bb fix(shopping): augmenter limite catalogue 50→500 — tous les articles visibles
Avec 119 articles en base et limit=50, les articles en fin de liste
alphabétique (Îles flottantes, Éponge…) n'apparaissaient pas dans
le bottom sheet. Limite portée à 500 côté backend ET frontend.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 10:28:09 +02:00
gilles dee7037d70 feat(shopping): stats achat produit + édition quantité article
Backend :
- Migration 004 : last_purchased_at (DATE) + avg_interval_days (NUMERIC)
  sur shopping.products
- update_item : met à jour les stats au premier cochage d'un article
  lié à un produit (moyenne mobile exp. 70/30)
- ProductResponse expose les deux nouveaux champs

Frontend :
- ItemRow : long press 500ms → onEdit() (mobile) ; crayon + croix (laptop)
- ShoppingPage : modal édition quantité/unité, état editingItem
- api/shopping.ts : Product inclut last_purchased_at + avg_interval_days

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 07:08:14 +02:00
gilles 377531d08e fix: recherche insensible à la casse et aux accents dans tous les filtres
- utils/search.ts : normalize() (NFD + minuscules) + matchesSearch()
- ShoppingPage filteredProducts : matchesSearch sur nom ET marque
- Backend searchProducts : ilike sur nom ET marque (or_)
- Notes FTS : déjà insensible nativement (plainto_tsquery)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 07:00:27 +02:00
gilles fdeb747f38 feat: Phase 4 — module Notes complet
Backend :
- schemas/notes.py : NoteCreate/Update/Response + AttachmentResponse
- api/notes.py : CRUD + FTS français (plainto_tsquery) + filtres rapides
  (has_photo/audio/gps/tag/category) + pièces jointes (image/audio)
- main.py : enregistrement /api/notes

Frontend :
- api/notes.ts : fetchNotes/create/update/delete + add/deleteAttachment
- NoteForm.tsx : titre, contenu, catégorie, tags CSV, GPS
- NotesPage.tsx : liste mobile (chronologique) + grille laptop, FAB +,
  enregistrement audio inline (MediaRecorder), upload photo, filtres rapides

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 06:49:46 +02:00
gilles 85093f1b99 feat(shopping): refonte UX + CRUD catalogue/boutiques + champs enrichis
- UX : vue par défaut = liste en cours, landing si pas de liste (+ vert +
  baguette magique), suppression des vues "listes" et "mode magasin" séparés
- Articles cochés barrés et déplacés en bas, tri alphabétique par section
- Nom de liste auto avec numéro de semaine ISO (S21 2026)
- Wake lock activé dès qu'une liste est ouverte
- CRUD boutiques : POST/PATCH/DELETE /stores + modal Boutiques
- CRUD articles : POST/PATCH/DELETE /products + modal Catalogue
- Champs enrichis produits : description, prix, quantité/unité, boutique défaut
- Champs enrichis boutiques : url, store_type (alimentaire, bricolage…)
- Migration 003 : nouveaux champs en base

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 16:21:45 +02:00
gilles e9dfb6e293 feat(todos): domains[], photo_path, gps_lat/lng — modèle, schemas, API, tri par date
- Modèle SQLAlchemy : ajout de domains (ARRAY), photo_path, gps_lat, gps_lng ; import Float
- Schemas Pydantic : domain → domains dans TodoCreate, TodoUpdate, TodoResponse ; ajout photo_path, gps_lat, gps_lng
- API GET /api/todos : filtre domain (param URL) redirigé vers domains.contains([domain]) sur le champ ARRAY
- Tests : domain → domains dans les payloads POST ; assertion domains == ["informatique"] dans test_creer_todo
2026-05-24 16:04:21 +02:00
gilles da5eb4916e feat(shopping): endpoint génération liste magique (score fréquence V1)
Ajoute POST /api/shopping/lists/generate qui calcule un score retard/intervalle
par article (achats_with_lag CTE pour contourner la limite PostgreSQL sur
AVG+LAG imbriqués) et génère une liste draft avec les articles >= 0.7.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 15:50:30 +02:00
gilles 3e4c209417 fix(shopping): refresh(item, ["product"]) dans update_item pour éviter MissingGreenlet 2026-05-24 15:34:21 +02:00
gilles 917a57fadc feat(shopping): 10 endpoints CRUD listes et articles
Ajoute backend/app/api/shopping.py avec les routes :
- GET/POST /api/shopping/lists
- GET/PATCH/DELETE /api/shopping/lists/{id}
- POST/PATCH/DELETE /api/shopping/lists/{id}/items
- GET /api/shopping/stores
- GET /api/shopping/products
- POST /api/shopping/lists/{id}/finish (report des articles non cochés)

Enregistre le router dans main.py avec le prefix /api/shopping.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 15:32:45 +02:00
gilles b5f0453cdd fix(todos): datetime query params typés FastAPI, ORDER BY déterministe
- Changer due_after/due_before de str | None vers datetime | None pour typage FastAPI
- FastAPI parse et valide automatiquement, retourne 422 si format invalide (pas 500)
- Supprimer le parsing manuel datetime.fromisoformat() qui levait ValueError brute
- Ajouter ORDER BY déterministe: due_date ASC NULLS LAST, created_at DESC
  évite les réordonnances aléatoires entre requêtes PostgreSQL

Tests: 15/15 passent ✓

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 12:02:37 +02:00
gilles a3704a2b27 feat(todos): endpoints CRUD + postpone — 15 tests passent
Ajoute les 5 endpoints REST (list, create, update, delete, postpone),
enregistre le routeur sur /api/todos, et corrige l'isolation des sessions
de test via NullPool + dependency_overrides dans conftest.py.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 09:20:53 +02:00
gilles cb85801061 feat: module media — upload, miniatures Pillow et suppression
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 05:05:37 +02:00
gilles 94b971cdf3 feat: endpoint GET /api/health + tests
Implémentation TDD : test écrit en premier (phase rouge), puis
app.main, app.api.health et app.api.media créés pour le faire passer.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 04:59:36 +02:00
gilles 8bc69bb705 chore: structure initiale backend et docker-compose 2026-05-24 04:45:16 +02:00