# mkdocs-superquiz

Un plugin MkDocs pour créer des quiz interactifs avec plusieurs types de questions.

## ✨ Fonctionnalités

- **3 types de quiz** :
  - 📝 Questionnaire à choix multiples (MCQ)
  - ☑️ Questionnaire à choix unique
  - 🔤 Texte à trous (fill-in-the-blank)

- **Multilingue** : Support de 6 langues (FR, EN, ES, IT, DE, PT)
- **Sécurisé** : 3 modes de sécurité pour les corrections
- **Déblocage de Corrigés par QR Code** : Système innovant de déblocage enseignant/élève
- **Support LaTeX** : Équations mathématiques dans questions et réponses
- **Thèmes** : Clair et sombre, compatible mkdocs-material
- **LocalStorage** : Sauvegarde automatique des réponses
- **Scoring flexible** : Plusieurs modes de notation

## 📦 Installation
```bash
pip install mkdocs-superquiz
```

### Téléchargement de la bibliothèque QR Code

La bibliothèque QR Code doit être téléchargée séparément :
```bash
cd mkdocs-superquiz/mkdocs_superquiz/assets/
wget https://cdn.jsdelivr.net/npm/qrcode-generator@1.4.4/qrcode.min.js
```

Cette bibliothèque est recommandée car elle est :
- ✅ La plus légère (15 KB)
- ✅ Activement maintenue
- ✅ Supporte SVG nativement

## 🚀 Configuration

### Configuration minimale
```yaml
# mkdocs.yml
plugins:
  - quiz

markdown_extensions:
  - attr_list
  - pymdownx.arithmatex:
      generic: true

extra_javascript:
  - https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js
```

### Configuration complète
```yaml
# mkdocs.yml
plugins:
  - quiz

extra:
  quiz:
    # Sécurité
    security_mode: "obfuscated"  # obfuscated | api | none
    teacher_code: "sha256:e5017e4a3f8c2b9d1a6f7e3c8b5d2a9f6e3c1b8d5a2f9e6c3b1d8f5a2e9c6b3"
    
    # Styles personnalisés (optionnel)
    style:
      light: "custom/quiz-light.css"
      dark: "custom/quiz-dark.css"
    
    # Scripts personnalisés (optionnel)
    script: "custom/quiz.js"
    
    # Configuration par type
    mcquiz:
      scoring_mode: "proportional"
      show_correction: "on_demand"
      save_answers: true
      randomize_questions: false
      randomize_answers: false
      
    scquiz:
      scoring_mode: "all_or_nothing"
      show_correction: "on_demand"
      save_answers: true
      randomize_answers: true
      
    fillblanks:
      scoring_mode: "proportional"
      case_sensitive: false
      strict_latex: false
      ignore_spaces: true
      strip_spaces: true
      show_correction: "on_demand"
      save_answers: true
```

## 📝 Quiz et Syntaxe

### i18n: ui and Keywords' Syntaxes are Language-aware, AND Customisable

* For ALL Quizzes, Syntaxes are i18n/Language aware, AND Customisable
* You can simply use existing **keywords**, for a specific language (default `english`), for both :
  * questions keywords
  * answers keywords

You can find a list of keywods in `i18n/[country_code].json` where `country_code` is a $2$-letter code that represents the country. ('en', 'fr', 'es', etc..)

Not all languages are available for the moment.
These 6 languages are available for the moment:

* English (Default)     ('en')
* French                ('fr')
* Spanish               ('es')
* Portuguese            ('pt')
* Italian               ('it')
* German                ('de')
* Yours is NOT on the list ? Want a new one ? You just have to translate and customize the `i18n/en.json` file to meet your expectancies (and share you translation with us... !)

Here are some examples of `questions_keywords` and `answers_keywords`, ready to use.
If you don't like them, and/or just want to extend the list, and/or want to customize your owns, just modify your adhoc `i18n/[countr_code].json` file.

#### English (DEFAULT)

**DEFAULT VALUES in `i18n/en.json`:**

```json
  ...
  "quiz_configs": {
    "question_keywords": ["question", "q"],
    "answers_keywords": ["answers", "ans"]
  },
  ...
```

This means that in *English*:

* the `questions` start by one of the following keywords: `question` or `q`
* the `answers` start by one of the following keywords: `answers` or `ans`

**ALWAYS ADD A COLON `:` AFTER ANY KEYWORD** ( question_keyword and/or answers_keyword)

If you don't like them, and/or just want to extend the list, and/or want to customize your owns, just modify your `i18n/en.json` file.

**CUSTOMIZE THE `i18n/en.json` FILE:**

```json
  ...
  "quiz_configs": {
    "question_keywords": ["question", "q", "quest", /* Add your owns here */],
    "answers_keywords": ["answers", "ans", "answ", /* Add your owns here */]
  },
  ...
```

#### French

**DEFAULT VALUES in `i18n/fr.json`:**

```json
  ...
  "quiz_configs": {
    "question_keywords": ["question", "q"],
    "answers_keywords": ["réponses", "reponses", "rép", "rep"]
  },
  ...
```

This means that in *French*:

* the `questions` start by one of the following keywords: `question` or `q`
* the `answers` start by one of the following keywords: `réponses`, `reponses`, `rép`, `rep`

ALWAYS ADD A COLON `:` AFTER ANY KEYWORD ( question_keyword and/or answers_keyword)

If you don't like them, and/or just want to extend the list, and/or want to customize your owns, just modify your `i18n/fr.json` file.

**CUSTOMIZE THE `i18n/fr.json` FILE:**

```json
...
"quiz_configs": {
    "question_keywords": ["question", "q", "quest", /* Add your owns here */],
    "answers_keywords": ["réponses", "reponses", "rép", "rep", "repon", /* Add your owns here */]
  },
...
```

#### Spanish

**DEFAULT VALUES in `i18n/es.json`:**

```json
  ...
  "quiz_configs": {
    "question_keywords": ["pregunta", "question", "p", "q"],
    "answers_keywords": ["respuestas", "resp", "answers", "ans"]
  },
  ...
```

This means that in *French*:

* the `questions` start by one of the following keywords: `pregunta` or `question` or `p` or `q`
* the `answers` start by one of the following keywords: `respuestas`, `resp`, `answers`, `ans`

ALWAYS ADD A COLON `:` AFTER ANY KEYWORD ( question_keyword and/or answers_keyword)

If you don't like them, and/or just want to extend the list, and/or want to customize your owns, just modify your `i18n/es.json` file.

**CUSTOMIZE THE `i18n/es.json` FILE:**

```json
...
"quiz_configs": {
    "question_keywords": ["pregunta", "question", "p", "q", "quest", /* Add your owns here */],
    "answers_keywords": ["respuestas", "resp", "answers", "ans", "preg", /* Add your owns here */]
  },
...
```

#### All other languages the same

### [OPTIONAL] Numbering of Questions and Answers

**OPTIONAL**:
To help him/her diferentiate the questions and corresponding answers visually, the redactor of the quiz **CAN OPTIONALLY (or NOT)** add any number (just before the ending colon `:`, but) at the end of:

* any question keyword
* any answer keyword

This trailing numbers will ALWAYS GET IGNORED...
It is just a redactor facility

Quizzes, Questions and Answers ARE BEING AUTO-NUMBERED, independently of the number you enter (or not)

#### Standard, but still Authorized Syntax

```markdown
!!! scquiz "European Capitals"
  question: Which is the capital of France ?
  answers:
    - Madrid
    - [Paris]
    - London
  question: What is the Currency inside the UE ?
  answers:
    - Yen
    - Dollar
    - [Euro]
```

#### Numbered Questions & Answers, Also Authorized Syntax

```markdown
!!! scquiz "European Capitals"
  question1: Which is the capital of France ?
  answers1:
    - Madrid
    - [Paris]
    - London
  question2: What is the Currency inside the UE ?
  answers2:
    - Yen
    - Dollar
    - [Euro]
  question3: Where do you speak French ?
  answers:
    - Spain
    - UK
    - [France]
  question: Where do you speak Spanish ?
  answers4:
    - France
    - [Spain]
    - UK
```

All these syntaxes are valid, for a Single Choice Quiz (scquiz).
MANUAL NUMBERING IS ALWAYS BEING IGNORED.

Reminder: :warning: all quizzes, all questions, and all answers are being auto-numbered by the plugin. :warning:

### Multiple Choice Quiz (`mcquiz`)

Several syntaxes are accepted. Some being more complex.

#### i18n keywords' configs

You can find the list of all accepted keywords for this **quiz type**, called **mcquiz**, in `i18n.en.json` file, under this section:

```json
  ...
  "quiz_types": {
    "mcquiz": {
      "synonyms": ["quiz", "mcquiz", "multiple-choice", "mc", "check", "checkbox"],
      "display_name": "Multiple Choice Quiz"
    },
  ...
  }
```

Same for other languages:

* English `synonyms` keyword: `"synonyms": ["quiz", "mcquiz", "multiple-choice", "mc", "check", "checkbox"],`
* French `synonyms` keyword: `"synonyms": ["quiz", "qcm", "choix-multiples", "mcquiz", "multiple-choice", "mc", "check", "checkbox"],`
* Spanish `synonyms` keyword: `"synonyms": ["com", "tipo-test", "tt", "quiz", "mcquiz", "multiple-choice", "mc", "check", "checkbox", "cac"],`

Please note that english keywords have been left to the translations, BUT YOU CAN TAKE THEM AWAY FROM THE LIST, VERY SAFELY.
This is just a personal convenience.

#### Standard Syntax

An example in English :gb::

```markdown
!!! mcquiz "European Capitals"
    question: Which ones are UE Capitals ?
    answers:
        - [Paris]             // One of the multiple correct answers
        - Londres
        - [Berlin]            // Another of the multiple correct answers
        - Marseille
    question: Which fruits do I like ?
    answers:
        - [Strawberries]      // One of the multiple correct answers
        - Apples
        - [Kiwis]             // Another of the multiple correct answers
        - [Pears]             // Another of the multiple correct answers
```

An example in French :fr::

```markdown
!!! qcm "European Capitals"
    question: Which ones are UE Capitals ?
    réponses:
        - [Paris]
        - Londres
        - [Berlin]
        - Marseille
    question: Which fruits do I like ?
    reponses:
        - [Strawberries]
        - Apples
        - [Kiwis]
        - [Pears]
```

An example in Spanish :es::

```markdown
!!! tipo-test "European Capitals"
    pregunta: Which ones are UE Capitals ?
    respuestas:
        - [Paris]
        - Londres
        - [Berlin]
        - Marseille
    pregunta: Which fruits do I like ?
    resp:
        - [Strawberries]
        - Apples
        - [Kiwis]
        - [Pears]
```

#### Numbered Syntax

```markdown
!!! mcquiz "European Capitals"
    question1: Which ones are UE Capitals ?
    answers1:
        - [Paris]
        - Londres
        - [Berlin]
        - Marseille
    question2: Which fruits do I like ?
    answers2:
        - [Strawberries]
        - Apples
        - [Kiwis]
        - [Pears]
```

#### Markdown Preamble

1. Via **Automatic Detection of Preamble:**

```markdown
!!! quiz "European Capitals"
    Watch this nice video:            Λ          
    <iframe ..></iframe>              |
    and then (blabla):         (Automatic preamble)
    (markdown syntax)                 |       
    Look at this: <img src="./path">  V
    question: Which ones are UE Capitals ?
    answers:
        - [Paris]
        - Londres
        - [Berlin]
        - Marseille
```

2. Via **Manual Detection of Preamble:**

```markdown
!!! quiz "European Capitals"
    preamble:
      Watch this:
      <iframe ..></iframe>
      (markdown syntax)
      Look at this: <img src="./path">
    question: Which ones are UE Capitals ?
    answers:
        - [Paris]
        - Londres
        - [Berlin]
        - Marseille
```

3. Via **Manual Detection of Preamble:** (tolerated, but not advised)

```markdown
!!! quiz "European Capitals"
    preamble: Watch this:
      <iframe ..></iframe>
      (markdown syntax)
      Look at this: <img src="./path">
    question: Which ones are UE Capitals ?
    answers:
        - [Paris]
        - Londres
        - [Berlin]
        - Marseille
```

### Single Choice Quiz (`scquiz`)

Several syntaxes are accepted. Some being more complex.

#### i18n keywords' configs

You can find the list of all accepted keywords for this **quiz type**, called **scquiz**, in `i18n.en.json` file, under this section:

```json
  ...
    "scquiz": {
      "synonyms": ["scquiz", "single-choice", "sc", "radio"],
      "display_name": "Single Choice Quiz"
    },
  ...
  }
```

Same for other languages:

* English `synonyms` keyword: `"synonyms": ["scquiz", "single-choice", "sc", "radio"],`
* French `synonyms` keyword: `"synonyms": ["qcu", "choix-unique", "scquiz", "single-choice", "sc", "radio"],`
* Spanish `synonyms` keyword: `"synonyms": ["cou", "scquiz", "single-choice", "sc", "radio"],`

Please note that english keywords have been left to the translations, BUT YOU CAN TAKE THEM AWAY FROM THE LIST, VERY SAFELY.
This is just a personal convenience.

#### Standard Syntax

An example in English :gb:: (Single Choice Quiz - scquiz)

```markdown
!!! scquiz "Capital of France"
    q: Which is the Capital of France ?
    answers:
        - Madrid
        - [Paris]             // The unique correct answer
        - Marseille
```

An example in French :fr:: (Questionnaire à Choix Unique - qcu)

```markdown
!!! qcu "Capital of France"
    q: Which is the Capital of France ?
    answers:
        - Madrid
        - [Paris]             // The unique correct answer
        - Marseille
    question: Which of those fruits are red fruits ?
    reponses:
        - Apples
        - [Strawberries]      // The unique correct answer
        - Bananas
```

An example in Spanish :es:: (Cuestionario de Opción Única - cou)

```markdown
!!! cou "Capital of France"
    p: Which is the Capital of France ?
    respuestas:
        - Madrid
        - [Paris]
        - Marseille
    pregunta: Which is my favorite color ?
    resp:
        - Green
        - Red
        - [Blue]
        - Yellow
```

#### Numbered Syntax

Same as `mcquiz`, but replacing by the `scquiz`, i18n-aware keywords.

#### Markdown Preamble

Same as `mcquiz`, but replacing by the `scquiz`, i18n-aware keywords.

-----------------------------------------------------

### Single Choice Quiz (`scquiz`)

Several syntaxes are accepted. Some being more complex.

#### i18n keywords' configs

You can find the list of all accepted keywords for this **quiz type**, called **scquiz**, in `i18n.en.json` file, under this section:

```json
  ...
    "scquiz": {
      "synonyms": ["scquiz", "single-choice", "sc", "radio"],
      "display_name": "Single Choice Quiz"
    },
  ...
  }
```

Same for other languages:

* English `synonyms` keyword: `"synonyms": ["scquiz", "single-choice", "sc", "radio"],`
* French `synonyms` keyword: `"synonyms": ["qcu", "choix-unique", "scquiz", "single-choice", "sc", "radio"],`
* Spanish `synonyms` keyword: `"synonyms": ["cou", "scquiz", "single-choice", "sc", "radio"],`

Please note that english keywords have been left to the translations, BUT YOU CAN TAKE THEM AWAY FROM THE LIST, VERY SAFELY.
This is just a personal convenience.

#### Standard Syntax

An example in English :gb:: (Single Choice Quiz - scquiz)

```markdown
!!! scquiz "Capital of France"
    q: Which is the Capital of France ?
    answers:
        - Madrid
        - [Paris]             // The unique correct answer
        - Marseille
```

An example in French :fr:: (Questionnaire à Choix Unique - qcu)

```markdown
!!! qcu "Capital of France"
    q: Which is the Capital of France ?
    answers:
        - Madrid
        - [Paris]             // The unique correct answer
        - Marseille
    question: Which of those fruits are red fruits ?
    reponses:
        - Apples
        - [Strawberries]      // The unique correct answer
        - Bananas
```

An example in Spanish :es:: (Cuestionario de Opción Única - cou)

```markdown
!!! cou "Capital of France"
    p: Which is the Capital of France ?
    respuestas:
        - Madrid
        - [Paris]
        - Marseille
    pregunta: Which is my favorite color ?
    resp:
        - Green
        - Red
        - [Blue]
        - Yellow
```

#### Numbered Syntax

Same as `mcquiz`, but replacing by the `scquiz`, i18n-aware keywords.

#### Markdown Preamble

Same as `mcquiz`, but replacing by the `scquiz`, i18n-aware keywords.


### Quiz à trous (fill-in-the-blank)

```markdown
!!! fillblanks "Dérivées"
    question1: La capitale de France est [Paris]
    q2: La dérivée de $x^3$ est [$3x^2$, 3x^2]
    q3: J'aime [le chocolat, la fraise, et\, les pommes]
```

### Quiz avec Menus Déroulants à Réponse Unique

```markdown
!!! scdropdown "Dérivées"
    question1: La capitale de France est [Marseille, [Paris], Aix, Toulouse] et 
    q2: La dérivée de $x^3$ est [$x^2$, [$3x^2$], x^3, x^4]
    q3: Je suis [mineur, [majeur]], je bois [de l'eau, [du calva!],]
```


**Réponses multiples** : Séparer par virgule. Utiliser `\,` pour une virgule littérale.

## 🔐 Système de déblocage

Le plugin propose un système innovant de déblocage des corrections par QR Code.

### Workflow

1. **Élève** clique sur 🔓 (page/quiz/question)
2. **Modal** affiche QR code + input
3. **Enseignant** scanne le QR avec son téléphone
4. **Page déblocage** demande le code enseignant
5. **Système** génère le code de déblocage
6. **Enseignant** communique le code oralement
7. **Élève** saisit le code → Correction débloquée ✅

### Configuration du code enseignant
```bash
# Générer un hash SHA256
echo -n "MONCODE2025" | sha256sum
```
```yaml
extra:
  quiz:
    teacher_code: "sha256:le_hash_généré"
```

### Bloquer les corrections d'une page
```yaml
---
quiz:
  corrections_locked: true
---

# Ma page de quiz
```

## 🎨 Personnalisation

### Styles personnalisés

Créer vos propres fichiers CSS :
```css
/* docs/custom/quiz-light.css */
.mkdocs-superquiz[data-quiz-type="mcquiz"] {
    border-color: #ff6b6b !important;
}

.mkq-validate-btn {
    background: #ff6b6b !important;
}
```
```yaml
# mkdocs.yml
extra:
  quiz:
    style:
      light: "custom/quiz-light.css"
      dark: "custom/quiz-dark.css"
```

### JavaScript personnalisé
```javascript
// docs/custom/quiz.js
// Ajouter des fonctionnalités personnalisées
console.log('Quiz custom JS chargé');
```

## 📊 Modes de scoring

### Proportional (défaut)
- Chaque bonne réponse = points / nombre de bonnes réponses
- Exemple : 2 bonnes réponses → chaque = 50%

### All or nothing
- Toutes les réponses correctes = 100%
- Une erreur = 0%

### Penalty (futur)
- Points positifs pour bonnes réponses
- Pénalité pour mauvaises réponses

## 🌍 Support multilingue

Le plugin supporte 6 langues nativement :
```markdown
!!! quiz "Mon quiz"
    question: ...
    
!!! cuestionario "Mi cuestionario"
    pregunta: ...
    
!!! questionário "Meu questionário"
    pergunta: ...
```

**Mots-clés supportés** :
- Question : `question`, `q`, `pregunta`, `domanda`, `frage`, `pergunta`
- Réponses : `answers`, `reponses`, `respuestas`, `risposte`, `antworten`, `respostas`

## 🛠️ Développement

### Installation dev
```bash
git clone https://github.com/yourusername/mkdocs-superquiz.git
cd mkdocs-superquiz
pip install -e .
yarn install
```

### Télécharger qrcode-generator
```bash
cd mkdocs_superquiz/assets/
wget https://cdn.jsdelivr.net/npm/qrcode-generator@1.4.4/qrcode.min.js
cd ../..
```

### Compiler SCSS
```bash
yarn build
```

### Watch mode
```bash
yarn watch
```

### Tester le plugin
```bash
cd examplesite
mkdocs serve
```

Ouvrir http://127.0.0.1:8000

## 📄 Licence

MIT License - voir [LICENSE](LICENSE)

## 🤝 Contribution

Les contributions sont les bienvenues ! Voir [CONTRIBUTING.md](CONTRIBUTING.md)

## 📞 Support

- 🐛 Issues : [GitHub Issues](https://github.com/yourusername/mkdocs-superquiz/issues)
- 💬 Discussions : [GitHub Discussions](https://github.com/yourusername/mkdocs-superquiz/discussions)

## 🙏 Remerciements

Inspiré par mkdocs-material et mkdocs-graphviz.