- Anatomie des Databases Notion (Collection/CollectionView/Pages) - 21 types de propriétés, 6 types de vues, Relations & Rollups - Architecture des Tasks (sub-items auto-référencés, dépendances) - Plan d'implémentation en 6 phases avec SQL, Python et HTML - Rétrocompatibilité avec les boards Gitea existants
36 KiB
Guide Technique — Recréer Database & Tasks de Notion dans FlowDeck
Version: 1.0 · Date: 2026-07-10 Auteur: Hermes-Deepin · Cible: FlowDeck v1.2+
Table des matières
- Introduction
- Comprendre Notion : le modèle « Blocs & Collections »
- La Database Notion : anatomie complète
- Les Tasks Notion : un cas spécial de database
- Comment Notion connecte Database et Tasks
- Implémentation dans FlowDeck : plan pas à pas
- Références
Introduction
L'objectif est de recréer dans FlowDeck les deux fonctionnalités les plus puissantes de Notion :
- Les Databases — tables structurées avec propriétés typées, vues multiples, filtres et tris
- Les Tasks — sous-tâches hiérarchiques avec dépendances, liées aux databases
Ces deux systèmes sont profondément interconnectés dans Notion : les tâches ne sont qu'une forme spécialisée de database, avec un parent auto-référencé et des propriétés de dépendance.
État actuel de FlowDeck (v1.2)
FlowDeck a déjà une base solide :
✅ Boards (Kanban lié à Gitea) → table `boards`
✅ Cards (issues → colonnes) → table `cards`
✅ Colonnes custom → `project_properties` + `property_values`
✅ Checklists par issue → `checklists` + `checklist_items`
✅ Vues multiples → Kanban, Table, Status, TeamLoad, Detailed
✅ Notes Markdown → `notes`
✅ Pages (ébauche Notion editor) → `pages` (content, parent_id, sort_order, content_format)
⚠️ Database concept → ABSENT (pas de notion de collection/row)
❌ Relations entre projets → ABSENT
❌ Sub-tasks / Parent-item → ABSENT
❌ Rollups / Formules → ABSENT
❌ Dependencies (bloque/bloqué par) → ABSENT
1. Comprendre Notion : le modèle « Blocs & Collections »
1.1 Le principe fondateur : « Everything is a Block »
Dans Notion, chaque élément de contenu est un bloc. Un bloc peut être :
┌──────────────────────────────────────┐
│ BLOC │
│ ├─ id: UUID │
│ ├─ type: "paragraph" │
│ │ "heading_1" │
│ │ "to_do" │
│ │ "bulleted_list_item" │
│ │ "child_page" │
│ │ "child_database" │
│ │ "collection_view_page" │ ← Database
│ │ ... │
│ ├─ content: [...] (blocs enfants) │
│ ├─ properties: {} (metadata) │
│ └─ parent: {type, id} │
└──────────────────────────────────────┘
1.2 Les 3 couches d'une Database Notion
Une database Notion est un assemblage de 3 concepts distincts :
┌────────────────────────────────────────────────────────────┐
│ DATABASE NOTION │
│ │
│ ┌─────────────┐ ┌──────────────────┐ ┌────────────┐│
│ │ COLLECTION │ │ COLLECTION_VIEW │ │ PAGES ││
│ │ (schema) │◄───│ (présentation) │◄───│ (data) ││
│ │ │ │ │ │ ││
│ │ properties │ │ type: table │ │ row 1 ││
│ │ - Name │ │ board │ │ row 2 ││
│ │ - Status │ │ calendar │ │ row 3 ││
│ │ - Priority │ │ gallery │ │ ... ││
│ │ - Due Date │ │ list │ │ ││
│ │ - Assignee │ │ timeline │ │ ││
│ │ │ │ │ │ ││
│ │ │ │ filters │ │ ││
│ │ │ │ sorts │ │ ││
│ │ │ │ grouping │ │ ││
│ │ │ │ format options │ │ ││
│ └─────────────┘ └──────────────────┘ └────────────┘│
│ │
│ 1 SCHÉMA N VUES (filtres N LIGNES │
│ partagé + tris différents) (pages filles) │
└────────────────────────────────────────────────────────────┘
Concept clé : Une database n'est PAS une table SQL. C'est une collection de pages, chaque page ayant un schéma de propriétés commun, affiché via des vues configurables.
1.3 Le cycle de vie d'une Database
Création Consultation Modification
───────── ──────────── ────────────
1. L'utilisateur choisit 4. Les vues lisent le 7. Édition inline :
un type de vue schéma + les pages - click → édite valeur
(table, board...) - drag → réorganise
5. Filtres/sorts/tri - shortcuts → ajoute ligne
2. Notion crée : appliqués
- 1 Collection 8. Changement de vue =
- 1 CollectionView 6. Rendu en temps réel même données,
- 1 CollectionViewPage autre présentation
- + pages vides optionnelles
9. Ajout propriété =
3. L'utilisateur définit nouveau champ sur
les propriétés (schéma) TOUTES les pages
2. La Database Notion : anatomie complète
2.1 Propriétés (le schéma)
Notion supporte 21 types de propriétés. Toutes sont définies au niveau de la Collection :
Type │ Description │ Exemple de valeur
──────────────────┼────────────────────────────┼──────────────────
Title │ Le titre de la page (obligatoire, unique)
Text │ Texte libre │ "Description..."
Number │ Nombre (décimales, format) │ 42 / 12.5%
Select │ Choix unique │ "En cours"
Multi-select │ Choix multiples │ ["Frontend","Backend"]
Status │ Select avec couleur forcée │ "Done" (vert)
Date │ Date + heure optionnelle │ 2026-07-15
Person │ Mention utilisateur │ ["bruno"]
Files & media │ Uploads │ [url1, url2]
Checkbox │ Booléen │ true / false
URL │ Lien cliquable │ "https://..."
Email │ Email cliquable │ "[email protected]"
Phone │ Téléphone cliquable │ "+1..."
Formula │ Calcul (JS-like) │ prop("Prix")*prop("Qté") ← CLÉ
Relation │ Lien vers autre database │ [page_id_1, page_id_2] ← CLÉ
Rollup │ Agrégation via relation │ sum, avg, count, min, max ← CLÉ
Created time │ Date de création (auto) │ 2026-01-15T10:30:00Z
Created by │ Auteur (auto) │ "bruno"
Last edited time │ Dernière édition (auto) │ 2026-07-09T18:00:00Z
Last edited by │ Dernier éditeur (auto) │ "bruno"
Unique ID │ ID incrémental (auto) │ 42
Button │ Déclenche action (nouveau) │ —
AI Summary │ Résumé AI (nouveau) │ "Ce projet..."
2.2 Views (les vues)
Une database peut avoir N vues, chaque vue avec :
┌─────────────────────────────────────────┐
│ Vue "Kanban" │
│ ├─ type: board │
│ ├─ group_by: "Status" │ ← Groupement
│ ├─ filter: {Status ≠ "Archivé"} │ ← Filtres
│ ├─ sort: [Due Date ASC, Priority DESC] │ ← Tris
│ ├─ visible_properties: [Name, Assignee, Due Date] │
│ ├─ card_size: medium │
│ └─ cover_image_property: "Files" │
└─────────────────────────────────────────┘
6 types de vues :
| Vue | Usage | Particularité |
|---|---|---|
| Table | Liste classique | Colonnes = propriétés |
| Board | Kanban | Groupé par propriété Select/Status |
| Timeline | Gantt | Axe X = propriété Date |
| Calendar | Calendrier | Groupé par propriété Date (jour/semaine/mois) |
| Gallery | Cartes visuelles | Cover image + preview |
| List | Liste compacte | Comme table mais 1 colonne + preview |
Filtres avancés :
Filtre 1 : Status IS "En cours" → filtre simple
Filtre 2 : Due Date IS BEFORE "today" → filtre date
Filtre 3 : Priority CONTAINS "P1" → filtre multi-select
Groupe : AND / OR entre les filtres
Sous-groupe : (FiltreA OR FiltreB) AND FiltreC
2.3 Relations et Rollups (les super-pouvoirs)
C'est LE mécanisme qui rend Notion puissant :
┌─────────────────────┐ ┌──────────────────────┐
│ DATABASE A │ │ DATABASE B │
│ « Projets » │ │ « Tâches » │
│ │ │ │
│ ┌─────────────┐ │ relation│ ┌──────────────┐ │
│ │ Nom │ │◄───────►│ │ Titre │ │
│ │ Description │ │ │ │ Priorité │ │
│ │ Statut │ │ │ │ Projet ──────┼──┐ │
│ └─────────────┘ │ │ │ Assignee │ │ │
│ │ │ └──────────────┘ │ │
│ │ │ │ │
│ ROLLUP « Nb tâches»│◄────────┼─── count(Tâches) │ │
│ ROLLUP « Avancement│◄────────┼─── avg(%Complété) │ │
│ ROLLUP « Prochaine │◄────────┼─── min(Due Date) │ │
└─────────────────────┘ └───────────────────┘──┘
Relation = pointeur bidirectionnel entre pages de 2 databases différentes
Rollup = agrégation (COUNT, SUM, AVG, MIN, MAX, RANGE, UNIQUE) sur une propriété de la database liée
3. Les Tasks Notion : un cas spécial de database
3.1 Architecture des tâches
Contrairement à ce qu'on pourrait penser, Notion n'a pas de module « Tasks » séparé.
Les tâches sont simplement des PAGES dans une DATABASE
qui possède des propriétés spécifiques de type tâche.
Voici comment Notion les distingue :
┌─────────────────────────────────────────────────────────────┐
│ DATABASE « Tasks » (ou n'importe quelle DB) │
│ │
│ Propriétés « tâche » │ Propriétés « standard » │
│ ────────────────────────── │ ────────────────────────── │
│ ✅ Sub-item (parent) │ • Titre (obligatoire) │
│ ✅ Dependencies (bloque) │ • Status (Select) │
│ ✅ Due Date │ • Priority (Select) │
│ ✅ Assignee │ • Tags (Multi-select) │
│ ✅ Reminder │ • ... │
│ │
│ La magie : ces propriétés SONT la database. │
│ Pas de modèle « Task » séparé. │
└─────────────────────────────────────────────────────────────┘
3.2 Sub-items (sous-tâches)
Les sous-tâches utilisent une relation auto-référencée :
┌──────────────────────────────────────┐
│ Table « tasks » (dans la DB) │
│ │
│ id │ title │ parent_id │
│ ───┼────────────────┼───────────────│
│ 1 │ Refonte UI │ NULL │ ← Tâche parent
│ 2 │ Nouveau header │ 1 │ ← Sous-tâche de 1
│ 3 │ Dark mode │ 1 │ ← Sous-tâche de 1
│ 4 │ Tests header │ 2 │ ← Sous-sous-tâche de 2
│ 5 │ Déploiement │ NULL │ ← Autre tâche parent
│ │
│ Propriétés spéciales : │
│ ├─ « Sub-item » = relation vers │
│ │ la même database │
│ └─ « Parent item » = l'inverse │
│ (auto-créé par Notion) │
└──────────────────────────────────────┘
Comment ça fonctionne techniquement :
- L'utilisateur active « Sub-items » dans une database
- Notion crée automatiquement 2 propriétés de type Relation pointant vers la même database :
Sub-item(relation → même DB)Parent item(relation inverse, auto-générée)
- Dans l'interface Kanban, les sous-tâches apparaissent imbriquées sous le parent
- La récursion est illimitée (tâche → sous-tâche → sous-sous-tâche → ...)
3.3 Dependencies (dépendances)
Les dépendances bloquantes sont une seconde relation auto-référencée :
┌──────────────────────────────────────┐
│ Relation « Bloque » │
│ │
│ Tâche A ──bloque──► Tâche B │
│ Tâche B ──bloquée par──► Tâche A │
│ │
│ Règles métier : │
│ ├─ B ne peut pas être « Done » │
│ │ tant que A n'est pas « Done » │
│ ├─ Si A est déplacée, B suit │
│ │ (optionnel, selon config) │
│ └─ Timeline affiche les flèches │
│ A ────────► B │
└──────────────────────────────────────┘
3.4 La vue « My Tasks » (vue agrégée)
La killer feature de Notion : un dashboard qui regroupe TOUTES les tâches, peu importe leur database d'origine.
┌──────────────────────────────────────────────────┐
│ MY TASKS │
│ ┌───────────────────────────────────────────┐ │
│ │ Projet Alpha ┌──────────────────────┐│ │
│ │ ├─ Design maquette│ Assignee: Bruno ││ │
│ │ └─ Tests UI │ Due: 15 juillet ││ │
│ │ └──────────────────────┘│ │
│ │ Projet Beta │ │
│ │ ├─ API endpoint │ │
│ │ └─ Documentation │ │
│ │ Daily Tasks │ │
│ │ └─ Réunion standup │ │
│ └───────────────────────────────────────────┘ │
│ │
│ Agrège TOUTES les databases ayant : │
│ ├─ Une propriété « Person » = current user │
│ └─ Un Status ≠ « Done » │
└──────────────────────────────────────────────────┘
Fonctionnement interne :
- Notion scanne TOUTES les databases du workspace
- Filtre les pages où
Assignee = current_userETStatus ≠ Done/Completed - Regroupe par database d'origine (ou par projet parent)
- Affiche avec tri par
Due DateASC - Cette vue est read-only logique (pas une vraie database)
4. Comment Notion connecte Database et Tasks
4.1 Le diagramme de dépendance conceptuel
DATABASE (concept racine)
│
┌──────────────┼──────────────┐
▼ ▼ ▼
COLLECTION PAGES (rows) COLLECTION_VIEWS
(schéma) │ (présentations)
│ │ │
│ ├─ Title ├─ Table
├─ Text ├─ Properties ├─ Board
├─ Select │ (valeurs ├─ Calendar
├─ Date │ par page) ├─ Timeline
├─ Person │ ├─ Gallery
├─ Relation ────┼──► SELF └─ List
│ └── Sub-items │ ┌── Parent
│ └── Deps │ └── Blocked by
├─ Rollup ←──────┼──┘ (count, sum, avg via relation)
├─ Formula │
└─ ... │
│
┌──────┴──────┐
│ TASKS │ (surcouche métier)
│ │
│ Ne sont PAS │
│ un modèle │
│ séparé — │
│ ce sont des │
│ PAGES avec │
│ propriétés │
│ spécifiques │
└─────────────┘
4.2 Les invariants du modèle
- Une database = 1 Collection (schéma de propriétés)
- 1 Collection = N CollectionViews (vues)
- 1 CollectionView = 1 type (table | board | calendar | timeline | gallery | list)
- Les pages d'une database sont des blocs (comme toute page Notion)
- Une propriété « Relation » pointe vers une autre database (pas une table SQL)
- Une propriété « Rollup » agrège via une Relation existante
- Les sub-items sont une Relation auto-référencée
- Les dépendances sont une Relation auto-référencée avec contrainte métier
5. Implémentation dans FlowDeck : plan pas à pas
5.1 Phase 1 — Le concept de Database (semaine 1)
Objectif : Abstraire le concept de « board Gitea » en « database » réutilisable.
Nouvelle table : collections
CREATE TABLE collections (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
description TEXT DEFAULT '',
icon TEXT DEFAULT '📋',
-- Peut être liée à un projet Gitea (optionnel)
gitea_owner TEXT,
gitea_repo TEXT,
-- Schéma de propriétés en JSON (flexible, évolutif)
schema_json TEXT NOT NULL DEFAULT '[]',
-- Format: [{"name":"Status","type":"select","options":["Todo","Done"],"position":0}, ...]
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Nouvelle table : collection_pages (remplace cards)
CREATE TABLE collection_pages (
id INTEGER PRIMARY KEY AUTOINCREMENT,
collection_id INTEGER NOT NULL REFERENCES collections(id) ON DELETE CASCADE,
-- Peut être liée à une issue Gitea
gitea_issue_id INTEGER,
gitea_issue_number INTEGER,
-- Titre de la page (obligatoire)
title TEXT NOT NULL DEFAULT '',
-- Position dans la collection
position INTEGER NOT NULL DEFAULT 0,
-- Sub-items (auto-référence)
parent_id INTEGER REFERENCES collection_pages(id),
-- Valeurs des propriétés en JSON
property_values_json TEXT NOT NULL DEFAULT '{}',
-- Format: {"Status":"Done","Priority":"P1","DueDate":"2026-07-15"}
-- Métadonnées temporelles
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_cp_collection ON collection_pages(collection_id, position);
CREATE INDEX idx_cp_parent ON collection_pages(parent_id);
CREATE INDEX idx_cp_gitea ON collection_pages(gitea_issue_id);
Nouvelle table : collection_views
CREATE TABLE collection_views (
id INTEGER PRIMARY KEY AUTOINCREMENT,
collection_id INTEGER NOT NULL REFERENCES collections(id) ON DELETE CASCADE,
name TEXT NOT NULL DEFAULT 'Default View',
view_type TEXT NOT NULL DEFAULT 'table', -- table, board, calendar, timeline, gallery, list
-- Configuration JSON
config_json TEXT NOT NULL DEFAULT '{}',
-- Format:
-- {
-- "group_by": "Status", ← board/calendar uniquement
-- "filters": [ ← tous types
-- {"property":"Status","operator":"is_not","value":"Done"},
-- {"property":"DueDate","operator":"is_before","value":"today"}
-- ],
-- "filter_conjunction": "and", ← "and" | "or"
-- "sorts": [ ← tous types
-- {"property":"Priority","direction":"asc"},
-- {"property":"DueDate","direction":"desc"}
-- ],
-- "visible_properties": ["Title","Status","Assignee","DueDate"],
-- "card_size": "medium", ← board/gallery
-- "cover_property": "Files", ← board/gallery
-- "date_property": "DueDate", ← calendar/timeline
-- "date_range_property": "EndDate" ← timeline (optionnel)
-- }
position INTEGER NOT NULL DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
5.2 Phase 2 — Le système de propriétés (semaine 1-2)
Nouvelle table : collection_properties (remplace project_properties)
CREATE TABLE collection_properties (
id INTEGER PRIMARY KEY AUTOINCREMENT,
collection_id INTEGER NOT NULL REFERENCES collections(id) ON DELETE CASCADE,
name TEXT NOT NULL,
prop_type TEXT NOT NULL DEFAULT 'text',
-- Types: title, text, number, select, multi_select, status,
-- date, person, checkbox, url, email, phone,
-- relation, rollup, formula,
-- created_time, created_by, last_edited_time, last_edited_by
-- Options pour select/multi_select/status
options_json TEXT DEFAULT '[]',
-- Format: [{"name":"Todo","color":"gray"},{"name":"Done","color":"green"}]
-- Pour les relations
related_collection_id INTEGER REFERENCES collections(id),
reverse_name TEXT, -- nom de la relation inverse (auto-généré)
-- Pour les rollups
relation_property_id INTEGER REFERENCES collection_properties(id),
target_property_id INTEGER REFERENCES collection_properties(id),
rollup_function TEXT, -- count, sum, avg, min, max, range, unique
-- Pour les formules
formula_expression TEXT,
-- Position dans la liste des propriétés
position INTEGER NOT NULL DEFAULT 0,
required BOOLEAN NOT NULL DEFAULT 0,
visible_in_views BOOLEAN NOT NULL DEFAULT 1,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(collection_id, name)
);
Mapping des types Notion → FlowDeck :
PROPERTY_TYPES = {
"title": {
"storage": "string",
"validation": "max 2000 chars",
"indexed": True,
"unique_per_collection": False,
},
"text": {
"storage": "string",
"validation": "any text",
},
"number": {
"storage": "float",
"validation": "numeric",
"format_options": ["number", "percent", "currency", "dollar", "euro", "pound", "yen"],
},
"select": {
"storage": "string (from options_json)",
"validation": "must match one option",
},
"multi_select": {
"storage": "JSON array of strings",
"validation": "each must match option",
},
"status": {
"storage": "string (from options_json) + color",
"validation": "must match one option",
"colors": ["gray","brown","orange","yellow","green","blue","purple","pink","red"],
},
"date": {
"storage": "ISO 8601 string or range",
"validation": "valid datetime",
"include_time": True, # configurable
},
"person": {
"storage": "JSON array of {id, login, avatar_url}",
"validation": "must be workspace user",
},
"checkbox": {
"storage": "boolean",
"validation": "true/false",
},
"relation": {
"storage": "JSON array of page IDs",
"validation": "must be existing page in related_collection",
"bidirectional": True,
},
"rollup": {
"storage": "computed — not stored directly",
"functions": ["count","count_values","empty","not_empty",
"sum","average","median","min","max","range"],
"depends_on": ["relation_property_id","target_property_id"],
},
"formula": {
"storage": "computed — not stored directly",
"expression": "JavaScript-like DSL",
"functions_available": [
"prop()", "now()", "today()", "if()", "concat()", "round()",
"dateAdd()", "dateSubtract()", "formatDate()", "toNumber()",
"contains()", "length()", "replace()", "replaceAll()",
],
},
}
5.3 Phase 3 — Sub-items et Dépendances (semaine 2)
Implémentation des sub-items (relation auto-référencée)
# Dans le schema d'une database, activer Sub-items crée automatiquement :
def enable_sub_items(collection_id: int):
"""Active les sub-items sur une collection."""
# 1. Créer la propriété relation « Parent item » (si pas déjà)
parent_prop = create_property(
collection_id=collection_id,
name="Parent item",
prop_type="relation",
related_collection_id=collection_id, # AUTO-RÉFÉRENCE
reverse_name="Sub-item", # relation inverse
visible_in_views=False, # cachée dans les vues
)
# 2. Marquer la collection comme ayant des sub-items
mark_collection_feature(collection_id, "has_sub_items", True)
# 3. Mettre à jour la vue board pour afficher l'imbrication
# Le rendu Kanban doit maintenant supporter :
# - Groupe parent → sous-tâches indentées
# - Bouton « + Add sub-item » sous chaque carte
# - État du parent = agrégation des enfants
Rendu des sub-items dans le board
<!-- Dans board_fragment.html, modifier le rendu des cartes -->
<div class="kanban-card" data-page-id="{{ page.id }}">
<!-- Carte parent -->
<div class="card-title">{{ page.title }}</div>
<!-- Sous-tâches -->
{% for sub in page.sub_items %}
<div class="sub-item" data-page-id="{{ sub.id }}">
├─ {{ sub.title }}
<span class="sub-status">{{ sub.properties.Status }}</span>
</div>
{% endfor %}
<!-- Bouton ajout sous-tâche -->
<button hx-post="/api/pages/{{ page.id }}/sub-items"
hx-swap="beforeend">
+ Sub-item
</button>
</div>
Implémentation des dépendances
def enable_dependencies(collection_id: int):
"""Active les dépendances bloquantes."""
# Création de 2 propriétés relation
blocks = create_property(
collection_id=collection_id,
name="Blocks",
prop_type="relation",
related_collection_id=collection_id,
reverse_name="Blocked by",
)
# Contrainte métier : au niveau applicatif
# (pas de contrainte SQL pour garder la flexibilité)
def check_dependency_constraint(page_id: int, new_status: str):
"""Vérifie qu'une page peut passer à 'Done'
seulement si toutes les pages qu'elle bloque sont Done."""
blocked_pages = get_blocked_pages(page_id)
if new_status == "Done" and blocked_pages:
not_done = [p for p in blocked_pages
if p.status not in ("Done", "Cancelled")]
if not_done:
raise DependencyError(
f"Cannot mark as Done: still blocking {len(not_done)} tasks"
)
5.4 Phase 4 — « My Tasks » (vue agrégée) (semaine 2-3)
# GET /my-tasks
async def my_tasks(request: Request):
"""Dashboard personnel : toutes les tâches assignées à l'utilisateur."""
user = get_current_user(request)
# 1. Scanner TOUTES les collections
collections = db.fetch_all("SELECT * FROM collections")
my_tasks = []
for col in collections:
schema = json.loads(col.schema_json)
# 2. Vérifier si la collection a une propriété Person
person_props = [p for p in schema if p["type"] == "person"]
if not person_props:
continue
# 3. Récupérer les pages assignées à l'utilisateur
pages = db.fetch_all("""
SELECT cp.*, cpv.value as property_values
FROM collection_pages cp
JOIN collection_properties cprop
ON cprop.collection_id = cp.collection_id
AND cprop.prop_type = 'person'
WHERE cp.collection_id = ?
AND cp.property_values_json LIKE ?
""", (col.id, f'%"{user.login}"%'))
my_tasks.extend([
{"collection": col, "page": p, "schema": schema}
for p in pages
])
# 4. Grouper par collection
grouped = {}
for task in my_tasks:
col_name = task["collection"].name
if col_name not in grouped:
grouped[col_name] = []
grouped[col_name].append(task)
# 5. Trier par Due Date
for tasks in grouped.values():
tasks.sort(key=lambda t: t["page"].get_property("DueDate") or "9999")
return render_template("my_tasks.html", tasks=grouped)
5.5 Phase 5 — Rétrocompatibilité avec Gitea
Stratégie de migration progressive :
# Phase 5a : Wrapper — les boards Gitea existants
# deviennent des collections avec une couche de compatibilité
class GiteaBoardCompat:
"""Adaptateur : ancien board Gitea → nouvelle Collection."""
@staticmethod
def from_board(board_row):
"""Convertit un board existant en collection."""
return {
"collection_id": f"gitea:{board_row.id}",
"name": f"{board_row.project_owner}/{board_row.project_name}",
"gitea_owner": board_row.project_owner,
"gitea_repo": board_row.project_name,
"schema": [
{"name": "Title", "type": "title"},
{"name": "Status", "type": "select",
"options": json.loads(board_row.columns_json)},
{"name": "Priority", "type": "select",
"options": ["P1","P2","P3","P4"]},
],
"is_gitea_linked": True,
}
# Phase 5b : Nouvelle route hybride
# GET /board/{owner}/{repo} → détecte si c'est un board Gitea legacy
# ou une collection pure, et route vers le bon renderer
5.6 Résumé du plan d'implémentation
Semaine 1 Semaine 2 Semaine 3
──────── ───────── ─────────
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Database │ │ Properties │ │ My Tasks │
│ Concept │ ──► │ Avancées │ ──► │ Dashboard │
│ │ │ │ │ │
│ • collections│ │ • Relations │ │ • Vue agrégée│
│ • pages │ │ • Rollups │ │ • Filtres │
│ • views │ │ • Formules │ │ globaux │
│ • filters │ │ • Auto-prop │ │ • Calendrier │
│ • sorts │ │ computed │ │ intégré │
│ │ │ │ │ │
│ Sub-items │ │ Dependencies │ │ UI polish │
│ (parent_id) │ ──► │ + contraintes│ ──► │ + tests │
└──────────────┘ └──────────────┘ └──────────────┘
6. Références
- Notion API — Property Object
- Notion Help — Intro to Databases
- Notion Help — Tasks & Dependencies
- Notion Help — Relations & Rollups
- react-notion-x — Block types reference
- Notion Data Sources update (2025)
Différences clés FlowDeck vs Notion
| Concept | Notion | FlowDeck (cible) |
|---|---|---|
| Database | Collection + Pages + Views | collections + collection_pages + collection_views |
| Schéma | Properties sur la Collection | collection_properties au niveau collection |
| Valeurs | Dans chaque page (bloc) | property_values_json dans chaque collection_page |
| Sub-items | Relation auto-référencée | parent_id auto-référence dans collection_pages |
| Dependencies | Relation auto-référencée + contrainte | Idem + check applicatif au changement de statut |
| My Tasks | Scan cross-database + filtre | Route /my-tasks + JOIN sur collection_pages |
| Gitea sync | N/A (pas de Gitea) | Les pages peuvent être liées à gitea_issue_id |
| Stockage | Cloud distribué (PostgreSQL + cache) | SQLite local (1 fichier) |