# Scolaide v0.8 → v1.0
# Feuille de Route — Migration DB → File-based Content

**Date :** juin 2026  
**Objectif :** Sortir le contenu de la DB, un parseur unique, architecture propre  
**Principe :** Migration progressive — jamais casser ce qui fonctionne

---

## Vision cible v1.0

```
scolaide/
├── content/                    ← NOUVEAU — contenu sur disque
│   ├── pages/                  ← articles, cours (.md)
│   │   ├── cr-111111.md
│   │   └── su1-def.md
│   ├── quizzes/                ← quiz format text2quiz (.qz)
│   │   └── qz-abc123.qz
│   └── exercises/              ← exercices (.md + .py)
│       └── ex-def456.md
│
├── tools/
│   ├── renderer/               ← NOUVEAU — parseur unique
│   │   ├── index.php           ← interface de test
│   │   ├── Renderer.php        ← point d'entrée render(ref)
│   │   ├── vendor/             ← league/commonmark
│   │   └── README.md
│   ├── migrate/                ← NOUVEAU — outil de migration
│   │   ├── index.php           ← dashboard migration
│   │   ├── export-pages.php    ← DB → fichiers .md
│   │   ├── export-quizzes.php  ← DB → fichiers .qz
│   │   └── verify.php          ← vérification post-migration
│   ├── md2pdf/                 ← v2 — lit depuis content/
│   ├── quizeditor/             ← v2 — écrit dans content/
│   └── markdown/               ← librairie partagée
│
├── student/                    ← v2 — lit depuis content/ via Renderer
└── DB                          ← métadonnées seulement (plus de content_md/html)
```

---

## Phases de migration

```
PHASE 0  ── Outil de migration + Renderer central     ← ON COMMENCE ICI
PHASE 1  ── md2pdf      → file-based
PHASE 2  ── quizeditor  → file-based  
PHASE 3  ── student/    → file-based
PHASE 4  ── Nettoyage DB (supprimer content_md, content_html)
```

---

## PHASE 0 — Fondations (maintenant)

### 0.1 Créer tools/renderer/

```
tools/renderer/
├── Renderer.php        ← classe centrale
├── vendor/             ← league/commonmark (copié depuis student/)
├── markdown.php        ← wrapper scolaide_markdown()
└── index.php           ← page de test
```

**API publique de Renderer.php :**
```php
Renderer::page(string $ref): string      // rend une page MD
Renderer::quiz(string $ref): array       // parse un quiz .qz → JSON
Renderer::markdown(string $raw): string  // rend du MD brut
```

**Logique hybride (DB + fichier) :**
```php
function renderPage(string $ref): string {
    // 1. Chercher fichier d'abord
    $file = CONTENT_DIR . '/pages/' . $ref . '.md';
    if (file_exists($file)) {
        return scolaide_markdown(file_get_contents($file));
    }
    // 2. Fallback DB (ancien contenu)
    $row = DB::getPageByRef($ref);
    if ($row['content_html']) return $row['content_html'];
    if ($row['content_md'])   return scolaide_markdown($row['content_md']);
    return '';
}
```

### 0.2 Créer tools/migrate/

```
tools/migrate/
├── index.php           ← dashboard avec stats et boutons
├── export-pages.php    ← exporte pages DB → content/pages/*.md
├── export-quizzes.php  ← exporte quizzes DB → content/quizzes/*.qz
├── verify.php          ← compare DB et fichiers (intégrité)
└── rollback.php        ← restaurer depuis fichiers vers DB si besoin
```

### 0.3 Structure content/

```
Créer les dossiers sur le serveur :
/home/[user]/content/pages/
/home/[user]/content/quizzes/
/home/[user]/content/exercises/

Hors webroot (pas accessible via HTTP direct)
Permissions : 755 dossiers, 644 fichiers
```

---

## PHASE 1 — md2pdf file-based

**Avant :** md2pdf lit depuis `pages.content_md` via `ref_code`  
**Après :** md2pdf lit depuis `content/pages/{ref}.md`

**Changements :**
- `api/fetch-ref.php` → lire fichier au lieu de DB
- Garder fallback DB pour compatibilité
- Tester avec 5 pages pilotes

**Critère de succès :** md2pdf génère le même PDF depuis fichier que depuis DB

---

## PHASE 2 — quizeditor file-based

**Avant :** quizeditor écrit dans `quizzes.content_raw` (DB)  
**Après :** quizeditor écrit dans `content/quizzes/{ref}.qz` + métadonnées en DB

**Format fichier .qz :**
```
---
title: Contrôle Fractions CM2
pass_score: 70
mode: diagnostic
shuffle_questions: true
shuffle_choices: true
---
VF || La Terre est ronde || V
R: La Terre est légèrement aplatie aux pôles.

QCM || Capitales ? || V:Paris | V:Berlin | Lyon
R+: Exact !
R-: Relisez le cours.
```

YAML front-matter pour les métadonnées + text2quiz pour les questions.

**Changements :**
- `api/save.php` → écrire fichier + UPDATE DB (sans content_raw)
- `api/load.php` → lire fichier ou DB (hybride)
- Renderer::quiz() → parser le .qz

---

## PHASE 3 — student/ file-based

**Avant :** student/page.php → DB → content_md → league/commonmark  
**Après :** student/page.php → Renderer::page(ref) → fichier ou DB

**Changements :**
- `page.php` → remplacer le bloc de rendu par `Renderer::page($ref)`
- `quiz-jouer.php` → remplacer parseQuizToJson() par `Renderer::quiz($ref)`
- Plus simple, plus court, plus maintenable

---

## PHASE 4 — Nettoyage DB

**Après validation de toutes les phases :**

```sql
-- Vérifier qu'aucune page n'a plus content_md sans fichier
SELECT ref_code FROM pages 
WHERE LENGTH(content_md) > 0 
  AND ref_code NOT IN (SELECT ref FROM content_files);

-- Seulement quand 0 résultats :
ALTER TABLE pages 
  DROP COLUMN content_md,
  DROP COLUMN content_html,
  DROP COLUMN render_status;

ALTER TABLE quizzes
  DROP COLUMN content_raw,
  DROP COLUMN questions_json,
  DROP COLUMN intro_raw;
```

**Économie estimée :** 60-80% de réduction de la taille DB

---

## Règles de la migration

1. **Jamais supprimer de données DB avant que les fichiers soient validés**
2. **Toujours mode hybride** — si fichier absent → fallback DB
3. **Un module à la fois** — tester complètement avant de passer au suivant
4. **Script de rollback disponible** avant chaque phase
5. **Pas de downtime** — l'appli reste fonctionnelle pendant toute la migration

---

## Format des fichiers content/

### Pages (.md)

```markdown
---
ref_code: cr-111111
title: 4.3 — Techniques de calcul
type: article
chapter_id: 42
is_published: true
created_by: prof.benali
created_at: 2026-01-15
tags: intégrales, IPP, terminale
---

## Intégration par parties

La formule d'IPP est :

$$\int_a^b u \, v' \, dx = [u \, v]_a^b - \int_a^b u' \, v \, dx$$

### Exemple

Calculer $\int_0^1 x e^x \, dx$...
```

### Quiz (.qz)

```
---
ref_code: qz-abc123
title: Quiz Intégrales — Chapitre 4
pass_score: 70
mode: homework
shuffle_questions: true
shuffle_choices: true
intro: |
  Lisez attentivement chaque question.
  Vous avez **20 minutes**.
created_by: prof.benali
---

VF || $\int_a^a f(x)dx = 0$ || V
R: Une intégrale de a à a est toujours nulle par définition.

QCM || Quelle formule est correcte ? || V:IPP | Leibniz | Taylor
R+: Bravo, vous maîtrisez la formule d'IPP.
R-: Relisez la section 4.3.
```

---

## Tableau de suivi

| Phase | Module | Statut | Date |
|-------|--------|--------|------|
| 0.1 | tools/renderer/ | ⏳ À faire | - |
| 0.2 | tools/migrate/ | ⏳ À faire | - |
| 0.3 | Structure content/ | ⏳ À faire | - |
| 1 | md2pdf file-based | ⏳ À faire | - |
| 2 | quizeditor file-based | ⏳ À faire | - |
| 3 | student/ file-based | ⏳ À faire | - |
| 4 | Nettoyage DB | ⏳ À faire | - |

---

## Estimation effort

| Phase | Complexité | Risque | Temps estimé |
|-------|-----------|--------|-------------|
| 0.1 Renderer | Faible | Faible | 2h |
| 0.2 Migrate | Moyenne | Moyen | 3h |
| 1 md2pdf | Faible | Faible | 1h |
| 2 quizeditor | Moyenne | Moyen | 3h |
| 3 student/ | Faible | Faible | 1h |
| 4 DB cleanup | Élevée | Élevé | 1h + tests |
