Corrige deux régressions de /local-workspace :
- le chemin du header restait bloqué sur « Home / <workspace> » quel que
soit le dossier affiché : la route rend désormais breadcrumb_items
(Home / <workspace> / <dossier> / <sous-dossier>, niveaux cliquables,
collapse « … » au-delà de 4) et la navigation sans rechargement recalcule
le chemin via l'event flowdeck:breadcrumb-changed ;
- le clic sur un dossier du sidebar affichait TOUS les composants à la
fois : Alpine.data('wsInitData') retournait le même objet singleton, le
2e montage (navigation partielle) levait « Cannot redefine property:
\ » et initTree abandonnait, laissant tout le contenu au state
brut. La factory retourne désormais une enveloppe fraîche par montage
qui délègue à l'état réactif partagé. #lw-config est aussi relu à chaque
exécution (le 2e montage gardait le folder_id du 1er chargement).
Inclus également le travail en cours de l'arbre : Library (colonnes Last
visited/Source, ordre d'en-tête, favoris à icônes Workspace), Meeting
Notes (bloc, CSS, routes, docs), coloration de code hljs, badges
favori/publié dans l'arbre local-workspace, docs (DATA_MODEL,
architectures) et tests associés.
53 lines
5.7 KiB
Markdown
53 lines
5.7 KiB
Markdown
# V7.1.0 — Calendar sync + Meeting Notes · V7.1.1 — Bloc façon Notion
|
|
|
|
> **Statut** : ✅ Livré en v7.1.0 (2026-09-28) — `services/calendar_sync.py`, `services/meetings.py`, `routers/meetings.py`, migration 27, 15 tests verts · **Refonte Notion livrée en v7.1.1** (migration 38, `static/js/meeting_block.js`, `static/css/meeting.css`, 5 tests verts `tests/test_v711_meeting_notion.py`) · **Roadmap** : `ROADMAP.md § v7.1.0`
|
|
> Notes d'implémentation : pas de lib CalDAV/Google (REST + XML bruts) ; refresh OAuth hors scope (relink explicite sur 401) ; conflit = LWW auto + notif (pas de picker « garder ma version ») ; free/busy au jour (pas de créneaux horaires).
|
|
> **Référence Notion** : Notion Calendar (Google sync) + AI Meeting Notes → trigger Custom Agents (07/2026). Spec détaillée : `docs/architecture-meeting-notion-flowdeck.md` (§3A parcours visuel des 6 états, §3B maquettes ASCII, §6.5 machine à états, §12.7 étapes Thinking, §12.8 complétude + titre).
|
|
> **Existant** : vues jour/semaine/mois, moteur `recurrence.py`, `reminders.py` (scan 60s, dédup), timezones, `notify_assignment()`, template Meeting Notes.
|
|
|
|
---
|
|
|
|
## 1. Sync externe bidirectionnelle
|
|
|
|
```sql
|
|
CREATE TABLE calendar_links (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
provider TEXT NOT NULL, -- google|caldav
|
|
tokens_enc TEXT NOT NULL DEFAULT '', -- Fernet (refresh + access)
|
|
calendar_id TEXT NOT NULL DEFAULT 'primary',
|
|
sync_token TEXT DEFAULT '',
|
|
last_sync TIMESTAMP, UNIQUE(user_id, provider, calendar_id)
|
|
);
|
|
ALTER TABLE collection_pages ADD COLUMN external_event_id TEXT;
|
|
CREATE TABLE meeting_transcripts (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT, page_id INTEGER NOT NULL REFERENCES pages(id) ON DELETE CASCADE,
|
|
audio_path TEXT NOT NULL DEFAULT '', transcript TEXT NOT NULL DEFAULT '',
|
|
summary TEXT NOT NULL DEFAULT '', language TEXT DEFAULT 'fr',
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
```
|
|
|
|
- OAuth Google (`app/auth/providers/google_calendar.py`, scope `calendar.events`) + CalDAV générique (URL/user/pass chiffrés).
|
|
- Pull 15 min : event externe → page (titre + `date` + `__external_id__`) ; push : occurrence FlowDeck → event (si `sync_enabled` par collection).
|
|
- Conflit (modifié des 2 côtés depuis `last_sync`) → LWW + notif `calendar.conflict` + lien « garder ma version / prendre externe ».
|
|
- `GET /db/{id}/calendar/freebusy?attendees=&from=&to=` : créneaux libres (jour ouvré 9h-18h, fuseau user).
|
|
|
|
## 2. Meeting Notes v2
|
|
|
|
Upload `.mp3/.wav/.m4a` (100 MB) sur page Meeting → `meeting_transcripts` → transcription (Whisper local si binaire présent, sinon API via `llm_config`, timeout 10 min, job fond + polling) → résumé `ai_writing.py` (`Décisions`, `Next steps [[person]]`, `Action items`) inséré en blocs + `Agenda/Notes` existants complétés.
|
|
Émet `meeting.summarized` → automations v7.0 (update tracker, post Slack, file tickets).
|
|
|
|
## 3. Tests (`tests/test_v71_calendar_meetings.py`, ~15)
|
|
|
|
OAuth mock (tokens chiffrés roundtrip), pull/push, conflit LWW + notif, freebusy, transcription mock → résumé blocs, trigger émis, quota 100 MB refusé.
|
|
|
|
## 4. V7.1.1 — Refonte du bloc façon Notion (§3A/3B de `architecture-meeting-notion-flowdeck.md`)
|
|
|
|
- **Bloc à états** (`static/js/meeting_block.js` surcharge `window.FDMeeting`, styles `static/css/meeting.css`) : `idle → recording → paused → processing → done/failed`, révélation progressive `Notes → Notes+Transcript → Summary+Notes+Transcript` (Summary en tête, actif par défaut après Stop), bouton Start à menu (source/mode, langue), onde en direct + chrono, pastille Thinking, barre `Share this summary` (Copy link / Email / Slack tracés), pied instructions + attestation de consentement puis bandeau. Le titre généré par l'IA ne touche jamais le titre de la page hôte. Anciens blocs migrés à la volée.
|
|
- **Capture Web 2 modes** : A micro (`getUserMedia`), B onglet + micro (`getDisplayMedia` + micro, canaux séparés, alerte « je n'entends que vous » après 20 s de canal distant muet) ; STT navigateur quand dispo, transcript manuel + import audio en secours, tampon IndexedDB avec reprise après rechargement (recording → paused, processing → suivi repris).
|
|
- **Backend** (migration 38) : colonnes état/mode/instructions photo/consent/segments/steps/résumé structuré/titre/complétude + tables append-only `meeting_consent_log`, `meeting_distributions` + unicité une occurrence = une note. Endpoints : `POST /transcripts` (création idempotente), `/consents`, `/sessions`, `/pause`, `/resume`, `/stop` (analyse inline), `/segments`, `/transcript` (groupé par source), `/processing-steps`, `/processing:retry`, `/generated-title` (PATCH), `/distributions`, `/notes` (PATCH), `GET /meetings/history` + `/meetings/instructions`.
|
|
- **Pipeline IA** (`services/meetings.py`) : étapes Thinking persistées (lecture → analyse → résolution des références → compréhension → complétude → résumé → titre → citations), résumé contraint Overview/Décisions/Actions/Questions avec citations `¹ [seg N]` obligatoires et états vides explicites, classification `complete/brief/incomplete/fragmentary` (jamais de refus : résumé dégradé + titre-diagnostic `Brief or Incomplete Meeting Recording`), audit via `meeting.summarized` inchangé.
|
|
- **Hub** (`base.html`) : onglets Upcoming / Today / Past, recherche plein texte, CTA contextuels (Join & transcribe / Prepare / View summary).
|
|
- **Tests** : `tests/test_v711_meeting_notion.py` (5) — porte de consentement, machine à états complète avec résumé dégradé + titre-diagnostic, résumé structuré complet, unicité par occurrence, instructions + historique.
|