# Template cours — Academy Ayinedjimi

Directives d’authoring pour produire une **nouvelle formation** (Markdown chapitre par chapitre, ou bundle JSON `academy.formation.v1`).  
Référence pédagogique : formation `introduction-intelligence-artificielle`.  
Aligné sur `sql/gabarit-formation.sql`, `sql/gabarit-formation-sprint.sql` et `docs/formation-import/`.

---

## Sommaire

1. [Métadonnées formation](#1-métadonnées-formation)
2. [Structure des chapitres](#2-structure-des-chapitres)
3. [Règles Markdown (contenu)](#3-règles-markdown-contenu)
4. [Callouts](#4-callouts)
5. [Illustrations](#5-illustrations)
6. [Quiz (`quiz_json`)](#6-quiz-quiz_json)
7. [FAQ (`faq_json`)](#7-faq-faq_json)
8. [Synthèse & concepts clés](#8-synthèse--concepts-clés)
9. [Variante Sprint (NeuroLearn)](#9-variante-sprint-neurolearn)
10. [SEO & glossaire](#10-seo--glossaire)
11. [Checklist de complétude](#11-checklist-de-complétude)
12. [Squelette Markdown (copier-coller)](#12-squelette-markdown-copier-coller)
13. [Soumission / import](#13-soumission--import)

---

## 1. Métadonnées formation

| Champ | Obligatoire | Règles |
|-------|-------------|--------|
| `title` | oui | Titre catalogue, clair, sans jargon inutile |
| `slug` | oui | `kebab-case`, sans accent : `mon-sujet` |
| `description` | oui | 2–4 phrases ; meta description du hub ; termes recherchés |
| `level` | oui | `initiation` \| `intermediaire` \| `avance` \| `expert` (alias UI : débutant → initiation) |
| `categorie` | oui | ex. `Réseaux`, `Cybersécurité`, `IA`, `Infrastructure` — Sprint : `Sprint` |
| `tags` | oui | Virgules sans espace en SQL (`Tag1,Tag2`) ; tableau JSON en bundle |
| `duration` | oui | Affichage humain (`4h`, `45 min`) |
| `estimated_hours` | oui | Entier ≥ 1 |
| `objectives` | oui | Une entrée par ligne (ou tableau JSON) |
| `prerequisites` | oui | Une entrée par ligne (ou tableau JSON) |
| `published` | — | **`false` tant qu’il n’y a pas ≥ 1 chapitre publié** (sinon 404 hub) |
| `sort_order` | recommandé | Ordre d’affichage hub |
| `featured` | optionnel | Mise en avant hub |
| `neurolearn_slug` | optionnel | Lien classic → twin Sprint (ex. `tcp-ip-sprint`) |

**Ne jamais publier** une formation sans chapitre publié.

Contrôle SQL :

```sql
SELECT f.slug FROM formations f
WHERE f.published
  AND NOT EXISTS (
    SELECT 1 FROM formation_chapitres c
    WHERE c.formation_id = f.id AND c.published
  );
```

---

## 2. Structure des chapitres

| Champ | Règles |
|-------|--------|
| `slug` | Premier chapitre = **`index`** (URL = `/formation` sans `/index`) ; ensuite des slugs explicites |
| `title` | Titre H1 affiché |
| `description` | Une phrase sous le titre |
| `ordre` | Entier croissant ; `0` pour `index` |
| `estimated_minutes` | Réaliste → `timeRequired` JSON-LD |
| `difficulty` | `easy` \| `medium` \| `hard` |
| `published` | `true` seulement si le contenu est complet |
| `content` / `content_md` | Markdown (voir §3) — **jamais vide** |

Volume cible (classic) : **8 à 15 chapitres**.  
Volume Sprint : **5 à 8 chapitres courts**.

---

## 3. Règles Markdown (contenu)

### Cibles classic

| Élément | Cible |
|---------|--------|
| Prose | **2 000 à 2 500 mots** (hors SVG / HTML) |
| Callouts | **3 à 5** |
| Schéma | **≥ 1** (SVG inline ou illustration rail) |
| Quiz | **5 à 8** questions |
| FAQ | **5 à 8** questions |
| `content_summary` | **3 à 5** phrases |
| `key_concepts` | **5 à 8** libellés stables |

### Structure pédagogique recommandée

```markdown
## Accroche
Pourquoi ce sujet compte pour le lecteur.

## Notions
### Sous-notion A
### Sous-notion B
(3 à 5 sections ##, sous-sections ###)

## Cas concret
Exemple chiffré ou scénario terrain.

## Idées reçues
Ce que le lecteur croit à tort.

## Tableau comparatif
(très repris en extrait de résultat — classic uniquement)
```

### Interdits

- Chapitre vide ou stub (« À venir »)
- Contenu copié sans adaptation au niveau annoncé
- Mentions d’outils d’écriture automatisée dans le texte public
- Publier avant checklist §11 verte

### HTML autorisé (goldmark unsafe)

- `<figure>` + SVG `currentColor`
- Triggers d’illustration rail (voir §5)
- Tableaux Markdown / HTML simples

---

## 4. Callouts

Syntaxe alertes GitHub (accents / tirets / casse ignorés) :

```markdown
> [!IMPORTANT] À retenir
> Le point essentiel en **gras** si besoin.

> [!TIP]
> Bonne pratique concrète.

> [!WARNING]
> Attention à ce piège.

> [!NOTE]
> Information complémentaire.

> [!CAUTION]
> Risque réel / danger.

> [!EXEMPLE]
> Mini scénario illustratif.
```

| Marqueurs | Variante CSS | Titre défaut |
|-----------|--------------|--------------|
| `NOTE`, `INFO` | note (cyan) | Note |
| `TIP`, `ASTUCE`, `BONNE PRATIQUE`, `BEST PRACTICE` | tip (vert) | Bonne pratique |
| `IMPORTANT`, `RETENIR`, `A RETENIR` | important (violet) | À retenir |
| `WARNING`, `ATTENTION` | warning (ambre) | Attention |
| `CAUTION`, `DANGER`, `PIEGE` | danger (rouge) | Piège courant |
| `EXEMPLE`, `EXAMPLE` | example (gris) | Exemple |

Le Markdown interne (gras, listes, code) reste parsé.

---

## 5. Illustrations

Deux modes complémentaires.

### A. SVG inline (thème clair/sombre)

```html
<figure>
<svg viewBox="0 0 640 260" role="img" aria-label="Description accessible du schéma">
  <g font-family="Inter, system-ui, sans-serif" font-size="12.5" fill="currentColor">
    <text x="320" y="130" text-anchor="middle">Schéma</text>
  </g>
</svg>
<figcaption>Légende du schéma.</figcaption>
</figure>
```

- Peindre avec **`currentColor`** (pas de couleurs figées incompatibles thème)
- `viewBox` obligatoire + `role="img"` + `aria-label`

### B. Fichiers sous `/static/illustrations/{slug}/`

| Règle | Valeur |
|-------|--------|
| Chemin public | `/static/illustrations/{slug-formation}/fichier.svg` (ou `.webp` / `.png`) |
| Format recommandé | SVG ou image **800×450** (16:9), **thème sombre** Academy |
| Déploiement | `/var/www/ayinedjimi-academy/public/static/illustrations/{slug}/` |

Trigger rail desktop (≥1281px) :

```html
<figure class="fp-illust-trigger" id="illust-slug-court"
  data-illust-src="/static/illustrations/mon-sujet/schema-couches.svg"
  data-illust-alt="Description courte"
  data-illust-caption="Légende affichée dans le panneau">
  <img class="fp-illust-trigger__media"
       src="/static/illustrations/mon-sujet/schema-couches.svg"
       alt="Description courte" loading="lazy" width="800" height="450">
  <figcaption>Légende inline (mobile / narrow).</figcaption>
</figure>
```

Dans un bundle JSON, déclarer aussi `illustrations[]` (copie auto via `formation-import` si `image_path` local).

---

## 6. Quiz (`quiz_json`)

JSON array valide, ou chaîne vide (section masquée).

```json
[
  {
    "q": "Question claire ?",
    "opts": ["Option A", "Option B", "Option C", "Option D"],
    "ans": 1,
    "exp": "Explication de la bonne réponse (B)."
  }
]
```

| Champ | Règle |
|-------|--------|
| `q` | Une question, formulation directe |
| `opts` | ≥ 2 options ; classic 4 de préférence |
| `ans` | Index **0-based** de la bonne réponse |
| `exp` | Explique **pourquoi** la bonne est bonne (lu après réponse) |

Classic : **5–8** · Sprint : **3–5**.

---

## 7. FAQ (`faq_json`)

Alimente la section FAQ + JSON-LD `FAQPage` (extraits de résultats).

```json
[
  {
    "q": "Question telle qu'elle serait tapée dans Google ?",
    "a": "Première phrase = réponse. Puis 1 à 3 phrases de précision. Total 2 à 4 phrases."
  }
]
```

### Règles SEO (impératives)

1. La réponse se **suffit à elle-même** hors contexte du chapitre.
2. La **première phrase répond** ; les suivantes précisent.
3. **2 à 4 phrases** — trop long = peu de reprise.
4. Formuler la question comme un utilisateur la taperait (pas un titre de section).

Classic : **5–8** · Sprint : **3–5**.

---

## 8. Synthèse & concepts clés

### `content_summary`

3 à 5 phrases autonomes → encadré « L'essentiel à retenir » + meta `ai-article-summary`.

### `key_concepts`

Tableau de 5–8 libellés :

```text
Encapsulation
PDU
MTU
Couche réseau
Routage
```

- **Réutiliser les mêmes libellés** d’un chapitre à l’autre → regroupement `/glossaire` + maillage interne.
- Alimentent `teaches` / `about` / `keywords` du JSON-LD et les pastilles UI.

### Glossaire technique (optionnel)

Table `academy_glossary` (termes survolés dans le contenu) — distincte des `key_concepts` :

```sql
INSERT INTO academy_glossary (term, slug, definition, aliases, categorie, priority)
VALUES (
  'RAG', 'rag',
  'Technique consistant à…',
  ARRAY['génération augmentée par récupération'],
  'IA', 85
)
ON CONFLICT (term) DO UPDATE
  SET definition = EXCLUDED.definition, updated_at = now();
```

Cache Go ~5 min : pas de redéploiement pour un nouveau terme.

---

## 9. Variante Sprint (NeuroLearn)

Parcours **séparé**, pas une piste secondaire dans un chapitre long.

| Convention | Valeur |
|------------|--------|
| Slug | `{base}-sprint` (ex. `tcp-ip-sprint`) |
| `tags` | **doit** contenir `sprint` |
| `categorie` | `Sprint` |
| Style | Phrases **≤ ~20 mots**, linéaire |
| Interdit / limité | Tableaux lourds, SVG complexes, blocs code denses |
| Volume | 5–8 chapitres · 1–2 callouts · quiz 3–5 · FAQ 3–5 |
| Lien classic | `formations.neurolearn_slug` → slug sprint |

Gabarit SQL : `sql/gabarit-formation-sprint.sql`.

Le hub affiche le badge « Sprint · NeuroLearn » ; le player pose `window.FP_IS_SPRINT = true`.

---

## 10. SEO & glossaire

| Signal | Source |
|--------|--------|
| `LearningResource` / `Course` | Métadonnées formation + chapitre |
| `timeRequired` | `estimated_minutes` |
| `teaches` / `about` | `key_concepts` |
| `FAQPage` | `faq_json` non vide |
| `ai-article-summary` | `content_summary` |
| Sitemap | Formations & chapitres **publiés** uniquement (`index` exclu du sitemap) |
| Glossaire page `/glossaire` | Dérivé des `key_concepts` |
| Annotation inline `.gl-t` | Table `academy_glossary` |

Cohérence : mêmes libellés de concepts partout ; FAQ autonomes ; descriptions hub avec termes recherchés.

---

## 11. Checklist de complétude

Cocher **par chapitre** avant `published=true` :

- [ ] Markdown 2 000–2 500 mots (classic) / phrases courtes (Sprint)
- [ ] 3–5 callouts (classic) / 1–2 (Sprint)
- [ ] ≥ 1 schéma (SVG ou illustration)
- [ ] `quiz_json` 5–8 (classic) / 3–5 (Sprint), JSON valide
- [ ] `faq_json` 5–8 (classic) / 3–5 (Sprint), réponses autonomes
- [ ] `content_summary` 3–5 phrases
- [ ] `key_concepts` 5–8, libellés réutilisés
- [ ] `estimated_minutes` réaliste
- [ ] Pas de chapitre stub

**Formation :**

- [ ] Métadonnées complètes
- [ ] Premier chapitre slug = `index`
- [ ] Contrôle « aucune formation publiée vide » OK
- [ ] Illustrations déployées sous `/static/illustrations/{slug}/` si utilisées

Contrôle SQL (extrait gabarit) :

```sql
SELECT ordre, slug,
       round(length(content)/6.2)                     AS mots,
       json_array_length(NULLIF(quiz_json,'')::json)  AS quiz,
       json_array_length(NULLIF(faq_json,'')::json)   AS faq,
       (content_summary <> '')                        AS synthese,
       COALESCE(array_length(key_concepts,1),0)       AS notions,
       (content LIKE '%<figure>%')                    AS schema,
       (content LIKE '%[!%')                          AS callouts
FROM formation_chapitres
WHERE formation_id = (SELECT id FROM formations WHERE slug = '<slug>')
ORDER BY ordre;
```

---

## 12. Squelette Markdown (copier-coller)

### Outline formation

```markdown
# Titre de la formation

- slug: mon-sujet
- level: initiation
- categorie: Réseaux
- tags: Tag1, Tag2, Tag3
- duration: 4h
- estimated_hours: 4
- objectives:
  - Objectif 1
  - Objectif 2
  - Objectif 3
- prerequisites:
  - Prérequis 1
  - Prérequis 2

## Chapitres

1. index — Introduction
2. notions-cles — Notions clés
3. cas-pratique — Cas pratique
4. pieges — Pièges courants
5. synthesis — Synthèse et suite
```

### Un chapitre complet (fichiers voisins ou blocs)

```markdown
---
slug: notions-cles
title: Notions clés
description: Les concepts à maîtriser avant la pratique.
ordre: 1
estimated_minutes: 25
difficulty: easy
content_summary: >
  Phrase 1. Phrase 2. Phrase 3.
key_concepts:
  - Notion A
  - Notion B
  - Notion C
  - Notion D
  - Notion E
---

## Accroche

Texte…

> [!IMPORTANT] À retenir
> Point essentiel.

## Notions

### Sous-partie

Texte…

<figure>
<svg viewBox="0 0 640 260" role="img" aria-label="Schéma des notions">
  <text x="320" y="130" fill="currentColor" text-anchor="middle"
        font-family="Inter, system-ui, sans-serif" font-size="14">Schéma</text>
</svg>
<figcaption>Légende.</figcaption>
</figure>

> [!TIP]
> Bonne pratique.

## Cas concret

…

> [!WARNING]
> Piège fréquent.

## Idées reçues

…

```quiz
[
  {"q":"Question ?","opts":["A","B","C","D"],"ans":1,"exp":"Parce que B…"}
]
```

```faq
[
  {"q":"Comment faire X ?","a":"Réponse autonome. Précision. Nuance."}
]
```
```

*(Les fences `quiz` / `faq` sont une convention d’authoring MD → JSON ; le canonique d’import reste le bundle JSON.)*

---

## 13. Soumission / import

### Chemins supportés

| Méthode | Quand |
|---------|--------|
| SQL gabarit | `sql/gabarit-formation.sql` / `*-sprint.sql` — upsert idempotent via `psql` |
| Bundle JSON | Schéma `docs/formation-import/TEMPLATE.formation.schema.json` + CLI |
| Admin web | CRUD Basic Auth `/admin/academy` (édition ponctuelle) |

Format canonique CI : **`academy.formation.v1`** (JSON).  
Le Markdown est l’authoring humain ; compiler vers JSON avant import automatisé (voir `docs/formation-import/MD-TO-JSON.md`).

### CLI `formation-import`

```bash
cd /opt/ayinedjimi-academy-src

# Dry-run (aucune écriture)
go run ./cmd/formation-import \
  -f docs/formation-import/TEMPLATE.formation.example.json \
  -dry-run

# Import réel (upsert par slug)
go run ./cmd/formation-import \
  -f /chemin/vers/bundle.json \
  -static-root /var/www/ayinedjimi-academy/public/static
```

Comportement :

1. Upsert `formations` (classic + sprint si présent)
2. Upsert chapitres par `(formation_id, slug)`
3. Copie illustrations locales → `static/illustrations/{formation}/`
4. Upsert `academy_chapter_illustrations`
5. Upsert glossaire si `glossary[]` fourni

**Ne droppe jamais** une formation existante. Ne réimportez pas en écrasement massif une formation témoin sans revue chapitre par chapitre.

### Fichiers utiles

| Fichier | Rôle |
|---------|------|
| `docs/TEMPLATE-COURS.md` | Ce guide (source dépôt) |
| `/static/docs/TEMPLATE-COURS.md` | Téléchargement public |
| `docs/formation-import/TEMPLATE.md` | Guide import JSON |
| `docs/formation-import/TEMPLATE.formation.schema.json` | JSON Schema |
| `docs/formation-import/TEMPLATE.formation.example.json` | Exemple |
| `sql/gabarit-formation.sql` | Seed SQL classic |
| `sql/gabarit-formation-sprint.sql` | Seed SQL Sprint |

### Après import

1. Vérifier la checklist §11
2. Publier formation + chapitres
3. Contrôler `http://localhost:4003/{slug}` et le programme `/{slug}/programme`
4. Vérifier sitemap / FAQPage si SEO critique

---

*Academy Ayinedjimi — template cours v1*
