core(tools): rebuild tools
This commit is contained in:
104
.gitignore
vendored
Normal file
104
.gitignore
vendored
Normal file
@@ -0,0 +1,104 @@
|
||||
# Environnements virtuels Python
|
||||
.venv/
|
||||
venv/
|
||||
env/
|
||||
.env
|
||||
|
||||
# Cache Python
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
pip-wheel-metadata/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
|
||||
# uv
|
||||
uv.lock
|
||||
|
||||
# IDEs
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
.DS_Store?
|
||||
._*
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
ehthumbs.db
|
||||
Thumbs.db
|
||||
|
||||
# LaTeX temporaires
|
||||
*.aux
|
||||
*.log
|
||||
*.out
|
||||
*.toc
|
||||
*.bbl
|
||||
*.blg
|
||||
*.fls
|
||||
*.fdb_latexmk
|
||||
*.synctex.gz
|
||||
*.nav
|
||||
*.snm
|
||||
*.vrb
|
||||
|
||||
# Fichiers de sauvegarde
|
||||
*.bak
|
||||
*.backup
|
||||
*~
|
||||
|
||||
# Fichiers temporaires
|
||||
*.tmp
|
||||
*.temp
|
||||
|
||||
# Vidéos (mentionné dans le Makefile)
|
||||
video/
|
||||
|
||||
# Fichiers spécifiques au projet
|
||||
*.ppm
|
||||
28
Makefile
Normal file
28
Makefile
Normal file
@@ -0,0 +1,28 @@
|
||||
CLEUSB=Cle8G
|
||||
|
||||
COMMON_EXCLUDE=--exclude "__pycache__" --exclude "venv/" --exclude ".git" --exclude ".gitignore" --exclude ".*" --exclude "**/*.ppm"
|
||||
|
||||
install:
|
||||
git config core.hooksPath ./tools/git/hooks/
|
||||
uv sync
|
||||
|
||||
update:
|
||||
uv sync --upgrade
|
||||
|
||||
clean:
|
||||
git clean -idx -e venv/ -e video/
|
||||
|
||||
sequence:
|
||||
uv run python -m tools.scripts.new_sequence
|
||||
|
||||
eval:
|
||||
uv run python -m tools.scripts.new_eval
|
||||
|
||||
rsync_cleUSB: clean
|
||||
rsync -rtv -u --del --exclude "venv" ./ $(COMMON_EXCLUDE) /run/media/lafrite/$(CLEUSB)/Enseignements
|
||||
rsync -rtv -u $(COMMON_EXCLUDE) ../Divers/ /run/media/lafrite/$(CLEUSB)/Divers
|
||||
rsync -rtv -u $(COMMON_EXCLUDE) ../Notes/ /run/media/lafrite/$(CLEUSB)/Notes
|
||||
rsync -rtv -u $(COMMON_EXCLUDE) ../Productions\ Eleves/ /run/media/lafrite/$(CLEUSB)/Productions
|
||||
|
||||
|
||||
.PHONY:
|
||||
18
pyproject.toml
Normal file
18
pyproject.toml
Normal file
@@ -0,0 +1,18 @@
|
||||
[project]
|
||||
name = "enseignements"
|
||||
version = "1.0.0"
|
||||
description = "Scripts pour la création de contenus éducatifs"
|
||||
authors = [{name = "Benjamin Bertrand"}]
|
||||
requires-python = ">=3.8"
|
||||
dependencies = [
|
||||
"prompt-toolkit>=3.0.0",
|
||||
"pyyaml>=6.0",
|
||||
"questionary>=2.0.0",
|
||||
]
|
||||
|
||||
[tool.uv]
|
||||
dev-dependencies = []
|
||||
|
||||
[build-system]
|
||||
requires = ["setuptools", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
1
tools/__init__.py
Normal file
1
tools/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# Package tools
|
||||
39
tools/config/year_config.yaml
Normal file
39
tools/config/year_config.yaml
Normal file
@@ -0,0 +1,39 @@
|
||||
# Configuration partagée pour les scripts de création de contenus
|
||||
# Ce fichier est utilisé par new_sequence.py, new_eval.py et autres scripts
|
||||
|
||||
# Configuration générale
|
||||
general:
|
||||
author: "Benjamin Bertrand"
|
||||
skeleton_path: "./tools/skeleton"
|
||||
sequence_number_format: "%02d"
|
||||
|
||||
# Configuration des classes/niveaux
|
||||
|
||||
# Configuration des classes/niveaux
|
||||
classes:
|
||||
- id: "1"
|
||||
display_name: "Première Générale Enseignement Scientifique math"
|
||||
directory: "1G_EnsSci"
|
||||
- id: "1"
|
||||
display_name: "Première Générale spé math"
|
||||
directory: "1G_math"
|
||||
- id: "3"
|
||||
display_name: "2nd"
|
||||
directory: "2nd"
|
||||
- id: "4"
|
||||
display_name: "Terminale STMG"
|
||||
directory: "Tstmg"
|
||||
|
||||
# Types de séquences disponibles
|
||||
sequence_types:
|
||||
- id: "1"
|
||||
display_name: "Classique"
|
||||
directory: "classique"
|
||||
- id: "2"
|
||||
display_name: "Plan de travail"
|
||||
directory: "plan_de_travail"
|
||||
|
||||
# Configuration pour les dates
|
||||
date_config:
|
||||
short_format: "+%B %Y" # Format pour date_short
|
||||
|
||||
102
tools/scripts/README.md
Normal file
102
tools/scripts/README.md
Normal file
@@ -0,0 +1,102 @@
|
||||
# Scripts de création de contenus
|
||||
|
||||
Ce répertoire contient les scripts Python interactifs pour créer de nouveaux contenus éducatifs avec interface moderne.
|
||||
|
||||
## Scripts disponibles
|
||||
|
||||
### `new_sequence.py`
|
||||
Script Python interactif pour créer une nouvelle séquence pédagogique.
|
||||
|
||||
**Utilisation :**
|
||||
```bash
|
||||
make sequence
|
||||
# ou directement
|
||||
uv run python -m tools.scripts.new_sequence
|
||||
```
|
||||
|
||||
**Fonctionnalités :**
|
||||
- **Calendrier interactif** : Sélection de date avec navigation intuitive (flèches, PageUp/PageDown)
|
||||
- Sélection de la classe via menu moderne (prompt_toolkit)
|
||||
- Détection automatique du numéro de séquence
|
||||
- Saisie interactive du titre et résumé
|
||||
- **Système de tags intelligent** : Auto-complétion depuis l'existant + tags personnalisés
|
||||
- Choix du type de séquence (classique/plan de travail)
|
||||
- Génération des fichiers depuis les templates avec substitution de variables
|
||||
|
||||
### `new_eval.py`
|
||||
Script Python interactif pour créer une nouvelle évaluation.
|
||||
|
||||
**Utilisation :**
|
||||
```bash
|
||||
make eval
|
||||
# ou directement
|
||||
uv run python -m tools.scripts.new_eval
|
||||
```
|
||||
|
||||
**Fonctionnalités :**
|
||||
- **Calendrier interactif partagé** : Même interface moderne que new_sequence
|
||||
- Sélection de la classe depuis la configuration
|
||||
- Saisie du nom et de la durée de l'évaluation
|
||||
- Génération automatique du répertoire DS_YYYY-MM-DD
|
||||
- Création des fichiers LaTeX depuis les templates
|
||||
|
||||
## Architecture Python
|
||||
|
||||
### Module commun (`common_widgets.py`)
|
||||
- **CalendarWidget** : Calendrier interactif avec prompt_toolkit
|
||||
- Navigation : ←→↑↓ (jours/semaines), PageUp/PageDown (mois)
|
||||
- Affichage français des mois et jours
|
||||
- Style coloré (aujourd'hui, sélection, weekends)
|
||||
- **Fonctions utilitaires** : Formatage des dates en français
|
||||
|
||||
### Configuration locale française
|
||||
- Mois et jours affichés en français dans le calendrier
|
||||
- Dates formatées automatiquement en français
|
||||
- Fallback robuste si locale française non disponible
|
||||
|
||||
## Configuration
|
||||
|
||||
Les scripts utilisent le fichier **`tools/config/year_config.yaml`** pour :
|
||||
- Définir les **classes communes** pour séquences et évaluations
|
||||
- Configurer les types de séquences
|
||||
- Paramétrer les formats de dates français
|
||||
- Définir l'auteur et les chemins des templates
|
||||
|
||||
## Templates
|
||||
|
||||
Les fichiers de base sont dans `tools/skeleton/` :
|
||||
- `sequence/common/` : Templates partagés (index.rst, exercises.tex)
|
||||
- `sequence/classique/` : Template séquence classique
|
||||
- `sequence/plan_de_travail/` : Template plan de travail
|
||||
- `eval/` : Templates d'évaluation (sujet.tex, exercises.tex)
|
||||
|
||||
Les variables substituées sont :
|
||||
- `${title}`, `${author}`, `${date}`
|
||||
- `${tribe}` (classe), `${tags}`, `${summary}`
|
||||
- `${date_short}` pour l'affichage français des dates
|
||||
|
||||
## Fonctionnalités avancées
|
||||
|
||||
### Interface moderne
|
||||
- **Calendrier visuel interactif** avec navigation intuitive
|
||||
- **Menus modernes** avec prompt_toolkit/questionary
|
||||
- **Code mutualisé** entre séquences et évaluations
|
||||
- **Gestion robuste** des erreurs et annulations
|
||||
- **Configuration unifiée** dans year_config.yaml
|
||||
- **Support Unicode** natif et dates françaises
|
||||
|
||||
## Installation et dépendances
|
||||
|
||||
```bash
|
||||
make install # Configure uv et installe les dépendances
|
||||
```
|
||||
|
||||
### Dépendances Python
|
||||
- `prompt-toolkit` : Interface utilisateur interactive
|
||||
- `pyyaml` : Lecture du fichier de configuration
|
||||
- `questionary` : Menus et saisies modernes
|
||||
|
||||
### Prérequis système
|
||||
- Python 3.8+
|
||||
- `uv` pour la gestion des dépendances
|
||||
- Locale française pour l'affichage optimal des dates
|
||||
1
tools/scripts/__init__.py
Normal file
1
tools/scripts/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# Package pour les scripts de création de contenus
|
||||
291
tools/scripts/common_widgets.py
Normal file
291
tools/scripts/common_widgets.py
Normal file
@@ -0,0 +1,291 @@
|
||||
"""
|
||||
Module commun contenant les widgets réutilisables pour les scripts de création de contenus.
|
||||
"""
|
||||
|
||||
import calendar
|
||||
import locale
|
||||
from datetime import datetime, date
|
||||
from typing import Optional
|
||||
|
||||
# Configuration du français
|
||||
try:
|
||||
# Essayer d'abord fr_FR.UTF-8
|
||||
locale.setlocale(locale.LC_TIME, 'fr_FR.UTF-8')
|
||||
except locale.Error:
|
||||
try:
|
||||
# Fallback vers fr_FR
|
||||
locale.setlocale(locale.LC_TIME, 'fr_FR')
|
||||
except locale.Error:
|
||||
try:
|
||||
# Autre fallback
|
||||
locale.setlocale(locale.LC_TIME, 'French_France.1252')
|
||||
except locale.Error:
|
||||
# Si aucune locale française disponible, continuer en anglais
|
||||
pass
|
||||
|
||||
# Configuration du calendrier en français
|
||||
calendar.setfirstweekday(0) # Lundi en premier
|
||||
|
||||
from prompt_toolkit import Application
|
||||
from prompt_toolkit.key_binding import KeyBindings
|
||||
from prompt_toolkit.layout import Layout, HSplit
|
||||
from prompt_toolkit.layout.containers import Window
|
||||
from prompt_toolkit.layout.controls import FormattedTextControl
|
||||
from prompt_toolkit.formatted_text import FormattedText
|
||||
from prompt_toolkit.styles import Style
|
||||
|
||||
|
||||
class CalendarWidget:
|
||||
"""Widget calendrier interactif avec prompt_toolkit."""
|
||||
|
||||
def __init__(self, initial_date: date = None):
|
||||
self.current_date = initial_date or date.today()
|
||||
self.selected_date = self.current_date
|
||||
self.current_month = self.current_date.month
|
||||
self.current_year = self.current_date.year
|
||||
self.result = None
|
||||
|
||||
# Style du calendrier
|
||||
self.style = Style.from_dict({
|
||||
'calendar.header': 'bold #ffffff bg:#0066cc',
|
||||
'calendar.day': '#000000',
|
||||
'calendar.today': 'bold #ffffff bg:#cc6600',
|
||||
'calendar.selected': 'bold #ffffff bg:#006600',
|
||||
'calendar.weekend': '#666666',
|
||||
'calendar.other_month': '#cccccc',
|
||||
'instructions': 'italic #666666',
|
||||
})
|
||||
|
||||
def _get_calendar_text(self) -> FormattedText:
|
||||
"""Génère le texte formaté du calendrier."""
|
||||
# En-tête avec mois et année en français
|
||||
month_name = calendar.month_name[self.current_month].capitalize()
|
||||
header = f"{month_name} {self.current_year}"
|
||||
|
||||
# Créer le calendrier
|
||||
cal = calendar.monthcalendar(self.current_year, self.current_month)
|
||||
|
||||
# Jours de la semaine en français (avec calendar.day_abbr configuré par locale)
|
||||
try:
|
||||
weekdays = [calendar.day_abbr[i][:2].capitalize() for i in range(7)]
|
||||
except:
|
||||
# Fallback manuel si locale ne fonctionne pas
|
||||
weekdays = ['Lu', 'Ma', 'Me', 'Je', 'Ve', 'Sa', 'Di']
|
||||
|
||||
text = []
|
||||
|
||||
# En-tête
|
||||
text.append(('class:calendar.header', f" {header:^20} "))
|
||||
text.append(('', '\n'))
|
||||
|
||||
# Jours de la semaine
|
||||
text.append(('class:calendar.header', ' '.join([f"{day:>2}" for day in weekdays])))
|
||||
text.append(('', '\n'))
|
||||
|
||||
# Jours du mois
|
||||
today = date.today()
|
||||
|
||||
for week in cal:
|
||||
week_text = []
|
||||
for day in week:
|
||||
if day == 0:
|
||||
week_text.append(' ')
|
||||
else:
|
||||
day_date = date(self.current_year, self.current_month, day)
|
||||
style_class = 'class:calendar.day'
|
||||
|
||||
# Déterminer le style
|
||||
if day_date == self.selected_date:
|
||||
style_class = 'class:calendar.selected'
|
||||
elif day_date == today:
|
||||
style_class = 'class:calendar.today'
|
||||
elif day_date.weekday() >= 5: # Weekend
|
||||
style_class = 'class:calendar.weekend'
|
||||
|
||||
week_text.append((style_class, f"{day:2d}"))
|
||||
|
||||
if day != week[-1] or day == 0:
|
||||
week_text.append(('', ' '))
|
||||
|
||||
text.extend(week_text)
|
||||
text.append(('', '\n'))
|
||||
|
||||
text.append(('', '\n'))
|
||||
text.append(('class:instructions', 'Flèches: naviguer | Espace: sélectionner | Entrée: confirmer | Échap: annuler'))
|
||||
|
||||
return FormattedText(text)
|
||||
|
||||
def _create_layout(self) -> Layout:
|
||||
"""Crée le layout du calendrier."""
|
||||
return Layout(
|
||||
HSplit([
|
||||
Window(
|
||||
content=FormattedTextControl(
|
||||
text=self._get_calendar_text,
|
||||
show_cursor=False
|
||||
),
|
||||
height=12,
|
||||
dont_extend_width=True,
|
||||
)
|
||||
])
|
||||
)
|
||||
|
||||
def _create_key_bindings(self) -> KeyBindings:
|
||||
"""Crée les raccourcis clavier."""
|
||||
kb = KeyBindings()
|
||||
|
||||
@kb.add('left')
|
||||
def move_left(event):
|
||||
if self.selected_date.day > 1:
|
||||
self.selected_date = self.selected_date.replace(day=self.selected_date.day - 1)
|
||||
else:
|
||||
# Mois précédent
|
||||
if self.current_month > 1:
|
||||
self.current_month -= 1
|
||||
else:
|
||||
self.current_month = 12
|
||||
self.current_year -= 1
|
||||
# Dernier jour du mois précédent
|
||||
last_day = calendar.monthrange(self.current_year, self.current_month)[1]
|
||||
self.selected_date = date(self.current_year, self.current_month, last_day)
|
||||
|
||||
@kb.add('right')
|
||||
def move_right(event):
|
||||
last_day = calendar.monthrange(self.current_year, self.current_month)[1]
|
||||
if self.selected_date.day < last_day:
|
||||
self.selected_date = self.selected_date.replace(day=self.selected_date.day + 1)
|
||||
else:
|
||||
# Mois suivant
|
||||
if self.current_month < 12:
|
||||
self.current_month += 1
|
||||
else:
|
||||
self.current_month = 1
|
||||
self.current_year += 1
|
||||
self.selected_date = date(self.current_year, self.current_month, 1)
|
||||
|
||||
@kb.add('up')
|
||||
def move_up(event):
|
||||
try:
|
||||
self.selected_date = self.selected_date.replace(day=self.selected_date.day - 7)
|
||||
except ValueError:
|
||||
# Semaine précédente dans le mois précédent
|
||||
if self.current_month > 1:
|
||||
self.current_month -= 1
|
||||
else:
|
||||
self.current_month = 12
|
||||
self.current_year -= 1
|
||||
last_day = calendar.monthrange(self.current_year, self.current_month)[1]
|
||||
new_day = min(self.selected_date.day, last_day)
|
||||
self.selected_date = date(self.current_year, self.current_month, new_day)
|
||||
|
||||
@kb.add('down')
|
||||
def move_down(event):
|
||||
last_day = calendar.monthrange(self.current_year, self.current_month)[1]
|
||||
try:
|
||||
self.selected_date = self.selected_date.replace(day=self.selected_date.day + 7)
|
||||
if self.selected_date.day > last_day:
|
||||
raise ValueError()
|
||||
except ValueError:
|
||||
# Semaine suivante dans le mois suivant
|
||||
if self.current_month < 12:
|
||||
self.current_month += 1
|
||||
else:
|
||||
self.current_month = 1
|
||||
self.current_year += 1
|
||||
new_day = min(self.selected_date.day + 7 - last_day,
|
||||
calendar.monthrange(self.current_year, self.current_month)[1])
|
||||
self.selected_date = date(self.current_year, self.current_month, new_day)
|
||||
|
||||
@kb.add('pageup')
|
||||
def prev_month(event):
|
||||
if self.current_month > 1:
|
||||
self.current_month -= 1
|
||||
else:
|
||||
self.current_month = 12
|
||||
self.current_year -= 1
|
||||
# Ajuster le jour sélectionné si nécessaire
|
||||
last_day = calendar.monthrange(self.current_year, self.current_month)[1]
|
||||
if self.selected_date.day > last_day:
|
||||
self.selected_date = date(self.current_year, self.current_month, last_day)
|
||||
else:
|
||||
self.selected_date = date(self.current_year, self.current_month, self.selected_date.day)
|
||||
|
||||
@kb.add('pagedown')
|
||||
def next_month(event):
|
||||
if self.current_month < 12:
|
||||
self.current_month += 1
|
||||
else:
|
||||
self.current_month = 1
|
||||
self.current_year += 1
|
||||
# Ajuster le jour sélectionné si nécessaire
|
||||
last_day = calendar.monthrange(self.current_year, self.current_month)[1]
|
||||
if self.selected_date.day > last_day:
|
||||
self.selected_date = date(self.current_year, self.current_month, last_day)
|
||||
else:
|
||||
self.selected_date = date(self.current_year, self.current_month, self.selected_date.day)
|
||||
|
||||
@kb.add('enter')
|
||||
@kb.add(' ') # Espace
|
||||
def confirm(event):
|
||||
self.result = self.selected_date
|
||||
event.app.exit()
|
||||
|
||||
@kb.add('escape')
|
||||
@kb.add('c-c')
|
||||
def cancel(event):
|
||||
self.result = None
|
||||
event.app.exit()
|
||||
|
||||
return kb
|
||||
|
||||
def show(self) -> Optional[date]:
|
||||
"""Affiche le calendrier et retourne la date sélectionnée."""
|
||||
app = Application(
|
||||
layout=self._create_layout(),
|
||||
key_bindings=self._create_key_bindings(),
|
||||
style=self.style,
|
||||
full_screen=False
|
||||
)
|
||||
|
||||
app.run()
|
||||
return self.result
|
||||
|
||||
|
||||
def select_date_with_calendar(prompt_text: str = "Sélectionnez une date",
|
||||
initial_date: date = None) -> str:
|
||||
"""Fonction helper pour sélectionner une date avec le calendrier."""
|
||||
print(f"\n{prompt_text}")
|
||||
print("Navigation: ←→↑↓ (flèches), PageUp/PageDown (mois), Espace/Entrée (confirmer), Échap (annuler)")
|
||||
|
||||
calendar_widget = CalendarWidget(initial_date)
|
||||
selected_date = calendar_widget.show()
|
||||
|
||||
if selected_date is None:
|
||||
raise KeyboardInterrupt("Sélection de date annulée")
|
||||
|
||||
return selected_date.strftime('%Y-%m-%d')
|
||||
|
||||
|
||||
def format_date_short(date_str: str, format_string: str = "%d %B %Y") -> str:
|
||||
"""Formate une date courte selon le format spécifié en français."""
|
||||
try:
|
||||
date_obj = datetime.strptime(date_str, '%Y-%m-%d')
|
||||
# Utiliser la locale française configurée
|
||||
formatted = date_obj.strftime(format_string)
|
||||
|
||||
# Si la locale ne fonctionne pas, faire une traduction manuelle
|
||||
if not any(month in formatted.lower() for month in ['jan', 'fév', 'mar', 'avr', 'mai', 'jun']):
|
||||
# Fallback: traduction manuelle des mois anglais vers français
|
||||
months_en_to_fr = {
|
||||
'January': 'janvier', 'February': 'février', 'March': 'mars',
|
||||
'April': 'avril', 'May': 'mai', 'June': 'juin',
|
||||
'July': 'juillet', 'August': 'août', 'September': 'septembre',
|
||||
'October': 'octobre', 'November': 'novembre', 'December': 'décembre'
|
||||
}
|
||||
|
||||
for en_month, fr_month in months_en_to_fr.items():
|
||||
formatted = formatted.replace(en_month, fr_month)
|
||||
|
||||
return formatted
|
||||
except ValueError:
|
||||
return date_str
|
||||
193
tools/scripts/new_eval.py
Executable file
193
tools/scripts/new_eval.py
Executable file
@@ -0,0 +1,193 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Script Python pour créer de nouvelles évaluations.
|
||||
Remplace le script bash new_eval.sh en utilisant prompt_toolkit.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Any
|
||||
|
||||
import yaml
|
||||
import questionary
|
||||
from questionary import Choice
|
||||
|
||||
from .common_widgets import select_date_with_calendar, format_date_short
|
||||
|
||||
# Configuration des messages
|
||||
MESSAGES = {
|
||||
'date_prompt': 'Sélectionnez la date de l\'évaluation',
|
||||
'class_prompt': 'Choisissez un niveau',
|
||||
'name_prompt': 'Nom de l\'évaluation',
|
||||
'duration_prompt': 'Temps pour le travailler',
|
||||
'confirm_prompt': 'Créer l\'évaluation avec ces paramètres ?',
|
||||
'creation_success': 'Évaluation créée avec succès dans: {path}',
|
||||
'creation_cancelled': 'Création annulée par l\'utilisateur.',
|
||||
}
|
||||
|
||||
class EvaluationCreator:
|
||||
def __init__(self):
|
||||
self.config_file = Path('./tools/config/year_config.yaml')
|
||||
self.config = self._load_config()
|
||||
self.skeleton_path = Path('./tools/skeleton/eval')
|
||||
|
||||
def _load_config(self) -> Dict[str, Any]:
|
||||
"""Charge le fichier de configuration YAML."""
|
||||
if not self.config_file.exists():
|
||||
print(f"Erreur: Fichier de configuration {self.config_file} non trouvé")
|
||||
sys.exit(1)
|
||||
|
||||
with open(self.config_file, 'r', encoding='utf-8') as f:
|
||||
return yaml.safe_load(f)
|
||||
|
||||
def _select_class(self) -> tuple[str, str]:
|
||||
"""Sélection de la classe."""
|
||||
choices = [
|
||||
Choice(title=cls['display_name'], value=(cls['id'], cls['directory']))
|
||||
for cls in self.config['classes']
|
||||
]
|
||||
|
||||
selection = questionary.select(
|
||||
MESSAGES['class_prompt'],
|
||||
choices=choices
|
||||
).ask()
|
||||
|
||||
if selection is None:
|
||||
print(MESSAGES['creation_cancelled'])
|
||||
sys.exit(0)
|
||||
|
||||
return selection
|
||||
|
||||
def _get_date_input(self) -> str:
|
||||
"""Demande la date à l'utilisateur avec le calendrier interactif."""
|
||||
return select_date_with_calendar(MESSAGES['date_prompt'])
|
||||
|
||||
def _get_name_input(self) -> str:
|
||||
"""Demande le nom de l'évaluation."""
|
||||
name = questionary.text(MESSAGES['name_prompt']).ask()
|
||||
|
||||
if not name:
|
||||
print(MESSAGES['creation_cancelled'])
|
||||
sys.exit(0)
|
||||
|
||||
return name
|
||||
|
||||
def _get_duration_input(self) -> str:
|
||||
"""Demande la durée de l'évaluation."""
|
||||
duration = questionary.text(MESSAGES['duration_prompt']).ask()
|
||||
return duration or ""
|
||||
|
||||
def _get_display_name(self, class_id: str) -> str:
|
||||
"""Récupère le nom d'affichage d'une classe par son ID."""
|
||||
for cls in self.config['classes']:
|
||||
if cls['id'] == class_id:
|
||||
return cls['display_name']
|
||||
return class_id
|
||||
|
||||
def _confirm_creation(self, date: str, class_id: str, class_dir: str,
|
||||
name: str, duration: str) -> bool:
|
||||
"""Dialogue de confirmation avec résumé des paramètres."""
|
||||
class_display = self._get_display_name(class_id)
|
||||
|
||||
confirmation_text = f"""Date: {date}
|
||||
Classe: {class_display}
|
||||
Nom: {name}
|
||||
Durée: {duration}"""
|
||||
|
||||
print("\\n" + confirmation_text + "\\n")
|
||||
|
||||
confirmed = questionary.confirm(MESSAGES['confirm_prompt']).ask()
|
||||
|
||||
return confirmed if confirmed is not None else False
|
||||
|
||||
def _create_evaluation_directory(self, class_dir: str, date: str) -> Path:
|
||||
"""Crée le répertoire de l'évaluation."""
|
||||
evaluations_dir = Path(class_dir) / 'Evaluations'
|
||||
evaluations_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
eval_dir = evaluations_dir / f'DS_{date}'
|
||||
eval_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
return eval_dir
|
||||
|
||||
def _process_template(self, template_file: Path, output_file: Path,
|
||||
template_vars: Dict[str, str]):
|
||||
"""Traite un fichier template avec substitution des variables."""
|
||||
try:
|
||||
with open(template_file, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
|
||||
# Substitution simple des variables (équivalent à envsubst)
|
||||
for var, value in template_vars.items():
|
||||
content = content.replace(f'${{{var}}}', value)
|
||||
|
||||
with open(output_file, 'w', encoding='utf-8') as f:
|
||||
f.write(content)
|
||||
|
||||
except (IOError, UnicodeDecodeError) as e:
|
||||
print(f"Erreur lors du traitement du template {template_file}: {e}")
|
||||
|
||||
def _copy_templates(self, eval_path: Path, template_vars: Dict[str, str]):
|
||||
"""Copie et traite les templates d'évaluation."""
|
||||
if not self.skeleton_path.exists():
|
||||
print(f"Erreur: Répertoire de templates {self.skeleton_path} non trouvé")
|
||||
sys.exit(1)
|
||||
|
||||
# Traiter tous les fichiers templates
|
||||
for template_file in self.skeleton_path.glob('*.tex'):
|
||||
output_file = eval_path / template_file.name
|
||||
self._process_template(template_file, output_file, template_vars)
|
||||
|
||||
def run(self):
|
||||
"""Méthode principale pour exécuter le script."""
|
||||
try:
|
||||
# Étape 1: Sélection de la classe
|
||||
class_id, class_dir = self._select_class()
|
||||
|
||||
# Étape 2: Sélection de la date
|
||||
date = self._get_date_input()
|
||||
|
||||
# Étape 3: Saisie du nom
|
||||
name = self._get_name_input()
|
||||
|
||||
# Étape 4: Saisie de la durée
|
||||
duration = self._get_duration_input()
|
||||
|
||||
# Étape 5: Confirmation
|
||||
if not self._confirm_creation(date, class_id, class_dir, name, duration):
|
||||
print(MESSAGES['creation_cancelled'])
|
||||
return
|
||||
|
||||
# Étape 6: Création de l'évaluation
|
||||
eval_path = self._create_evaluation_directory(class_dir, date)
|
||||
|
||||
# Préparation des variables de template
|
||||
template_vars = {
|
||||
'name': name,
|
||||
'date': date,
|
||||
'date_short': format_date_short(date),
|
||||
'tribe': class_dir,
|
||||
'duration': duration,
|
||||
}
|
||||
|
||||
# Copie et traitement des templates
|
||||
self._copy_templates(eval_path, template_vars)
|
||||
|
||||
print(MESSAGES['creation_success'].format(path=eval_path))
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print(f"\\n{MESSAGES['creation_cancelled']}")
|
||||
sys.exit(0)
|
||||
except Exception as e:
|
||||
print(f"Erreur inattendue: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
def main():
|
||||
"""Point d'entrée principal."""
|
||||
creator = EvaluationCreator()
|
||||
creator.run()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,70 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
|
||||
display_result() {
|
||||
dialog --title "$1" \
|
||||
--no-collapse \
|
||||
--msgbox "$result" 0 0
|
||||
}
|
||||
|
||||
exec 3>&1
|
||||
selection=$(dialog \
|
||||
--backtitle "Création d'une nouvelle évaluation: Classe" \
|
||||
--title "Menu" \
|
||||
--clear \
|
||||
--cancel-label "Exit" \
|
||||
--menu "Choisir un niveau:" 0 0 5 \
|
||||
"1" "2nd" \
|
||||
"2" "SNT" \
|
||||
"3" "Enseignements Scientifique" \
|
||||
"4" "Première NSI" \
|
||||
"5" "Première Technologique" \
|
||||
2>&1 1>&3)
|
||||
exec 3>&-
|
||||
case $selection in
|
||||
1 )
|
||||
tribe="2nd"
|
||||
;;
|
||||
2 )
|
||||
tribe="SNT"
|
||||
;;
|
||||
3 )
|
||||
tribe="EnseignementsScientifique"
|
||||
;;
|
||||
4 )
|
||||
tribe="1NSI"
|
||||
;;
|
||||
5 )
|
||||
tribe="1ST"
|
||||
;;
|
||||
esac
|
||||
|
||||
exec 3>&1
|
||||
date=$(dialog --calendar "Date" 0 0 2>&1 1>&3 | awk -F "/" '{print $3"-"$2"-"$1}')
|
||||
exec 3>&-
|
||||
|
||||
exec 3>&1
|
||||
name=$(dialog \
|
||||
--inputbox "Nom de l'évaluation" \
|
||||
0 0 \
|
||||
2>&1 1>&3)
|
||||
exec 3>&-
|
||||
|
||||
exec 3>&1
|
||||
duration=$(dialog \
|
||||
--inputbox "Temps pour le travailler" \
|
||||
0 0 \
|
||||
2>&1 1>&3)
|
||||
exec 3>&-
|
||||
|
||||
mkdir -p $tribe/Evaluations/
|
||||
sequence_path=$tribe/Evaluations/DS_${date}/
|
||||
mkdir -p $sequence_path
|
||||
|
||||
export name=$name
|
||||
export date=$date
|
||||
export date_short=`date --date="$date 00:00" "+%d %B %Y"`
|
||||
export tribe=$tribe
|
||||
export duration=$duration
|
||||
envsubst < ./tools/skeleton/eval/exercises.tex > $sequence_path/exercises.tex
|
||||
envsubst < ./tools/skeleton/eval/sujet.tex > $sequence_path/sujet.tex
|
||||
341
tools/scripts/new_sequence.py
Executable file
341
tools/scripts/new_sequence.py
Executable file
@@ -0,0 +1,341 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Script Python pour créer de nouvelles séquences pédagogiques.
|
||||
Remplace le script bash new_sequence.sh en utilisant prompt_toolkit.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
import subprocess
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Any
|
||||
|
||||
import yaml
|
||||
import questionary
|
||||
from questionary import Choice
|
||||
|
||||
from .common_widgets import select_date_with_calendar, format_date_short
|
||||
|
||||
# Configuration des messages
|
||||
MESSAGES = {
|
||||
'date_prompt': 'Sélectionnez la date de la séquence',
|
||||
'class_prompt': 'Choisissez un niveau',
|
||||
'existing_sequences': 'Séquences existantes',
|
||||
'new_sequence_prompt': 'Nom de la nouvelle séquence (n°{number})',
|
||||
'summary_prompt': 'Résumé de la séquence',
|
||||
'tags_selection': 'Sélectionnez les tags existants',
|
||||
'tags_custom': 'Ajouter des tags personnalisés (séparés par des virgules)',
|
||||
'type_prompt': 'Type de séquence',
|
||||
'confirm_prompt': 'Créer la séquence avec ces paramètres ?',
|
||||
'creation_success': 'Séquence créée avec succès dans: {path}',
|
||||
'creation_cancelled': 'Création annulée par l\'utilisateur.',
|
||||
}
|
||||
|
||||
|
||||
class SequenceCreator:
|
||||
def __init__(self):
|
||||
self.config_file = Path('./tools/config/year_config.yaml')
|
||||
self.config = self._load_config()
|
||||
|
||||
def _load_config(self) -> Dict[str, Any]:
|
||||
"""Charge le fichier de configuration YAML."""
|
||||
if not self.config_file.exists():
|
||||
print(f"Erreur: Fichier de configuration {self.config_file} non trouvé")
|
||||
sys.exit(1)
|
||||
|
||||
with open(self.config_file, 'r', encoding='utf-8') as f:
|
||||
return yaml.safe_load(f)
|
||||
|
||||
def _get_date_input(self) -> str:
|
||||
"""Demande la date à l'utilisateur avec le calendrier interactif."""
|
||||
return select_date_with_calendar(MESSAGES['date_prompt'])
|
||||
|
||||
|
||||
def _select_class(self) -> tuple[str, str]:
|
||||
"""Sélection de la classe."""
|
||||
choices = [
|
||||
Choice(title=cls['display_name'], value=(cls['id'], cls['directory']))
|
||||
for cls in self.config['classes']
|
||||
]
|
||||
|
||||
selection = questionary.select(
|
||||
MESSAGES['class_prompt'],
|
||||
choices=choices
|
||||
).ask()
|
||||
|
||||
if selection is None:
|
||||
print(MESSAGES['creation_cancelled'])
|
||||
sys.exit(0)
|
||||
|
||||
return selection
|
||||
|
||||
def _get_existing_sequences(self, class_directory: str) -> tuple[List[str], int]:
|
||||
"""Récupère les séquences existantes dans le répertoire de classe."""
|
||||
class_path = Path(class_directory)
|
||||
|
||||
if not class_path.exists():
|
||||
print(f"Erreur: Le répertoire de classe '{class_directory}' n'existe pas")
|
||||
sys.exit(1)
|
||||
|
||||
# Chercher les répertoires qui correspondent au pattern NN_*
|
||||
existing_sequences = []
|
||||
for item in class_path.glob('[0-9][0-9]_*'):
|
||||
if item.is_dir():
|
||||
existing_sequences.append(item.name)
|
||||
|
||||
existing_sequences.sort()
|
||||
next_number = len(existing_sequences) + 1
|
||||
|
||||
return existing_sequences, next_number
|
||||
|
||||
def _get_sequence_title(self, existing_sequences: List[str], next_number: int) -> str:
|
||||
"""Demande le titre de la nouvelle séquence."""
|
||||
prompt_text = MESSAGES['new_sequence_prompt'].format(number=next_number)
|
||||
|
||||
if existing_sequences:
|
||||
existing_text = "\\n".join(existing_sequences)
|
||||
full_prompt = f"{MESSAGES['existing_sequences']}:\\n{existing_text}\\n\\n{prompt_text}"
|
||||
else:
|
||||
full_prompt = prompt_text
|
||||
|
||||
title = questionary.text(full_prompt).ask()
|
||||
|
||||
if not title:
|
||||
print(MESSAGES['creation_cancelled'])
|
||||
sys.exit(0)
|
||||
|
||||
return title
|
||||
|
||||
def _get_summary(self) -> str:
|
||||
"""Demande le résumé de la séquence."""
|
||||
summary = questionary.text(MESSAGES['summary_prompt']).ask()
|
||||
return summary or ""
|
||||
|
||||
def _collect_existing_tags(self) -> List[str]:
|
||||
"""Collecte les tags existants depuis les fichiers RST."""
|
||||
tags = set()
|
||||
|
||||
# Rechercher dans tous les répertoires de classes
|
||||
for class_config in self.config['classes']:
|
||||
class_dir = Path(class_config['directory'])
|
||||
if class_dir.exists():
|
||||
# Chercher dans les index.rst des séquences
|
||||
for rst_file in class_dir.glob('*/index.rst'):
|
||||
try:
|
||||
with open(rst_file, 'r', encoding='utf-8') as f:
|
||||
for line in f:
|
||||
if line.strip().startswith(':tags:'):
|
||||
tag_line = line.strip().replace(':tags:', '').strip()
|
||||
if tag_line and not tag_line.startswith('${'):
|
||||
# Séparer les tags par virgule et nettoyer
|
||||
for tag in tag_line.split(','):
|
||||
tag = tag.strip()
|
||||
if tag:
|
||||
tags.add(tag)
|
||||
except (IOError, UnicodeDecodeError):
|
||||
continue
|
||||
|
||||
return sorted(list(tags))
|
||||
|
||||
def _select_tags(self) -> str:
|
||||
"""Sélection intelligente des tags."""
|
||||
existing_tags = self._collect_existing_tags()
|
||||
selected_tags = []
|
||||
|
||||
# Si il y a des tags existants, proposer une sélection multiple
|
||||
if existing_tags:
|
||||
choices = [Choice(title=tag, value=tag) for tag in existing_tags]
|
||||
|
||||
selected_list = questionary.checkbox(
|
||||
MESSAGES['tags_selection'],
|
||||
choices=choices
|
||||
).ask()
|
||||
|
||||
if selected_list:
|
||||
selected_tags.extend(selected_list)
|
||||
|
||||
# Proposer d'ajouter des tags personnalisés
|
||||
custom_tags = questionary.text(MESSAGES['tags_custom']).ask()
|
||||
|
||||
if custom_tags:
|
||||
# Séparer par virgules et nettoyer
|
||||
for tag in custom_tags.split(','):
|
||||
tag = tag.strip()
|
||||
if tag:
|
||||
selected_tags.append(tag)
|
||||
|
||||
return ', '.join(selected_tags)
|
||||
|
||||
def _select_sequence_type(self) -> tuple[str, str]:
|
||||
"""Sélection du type de séquence."""
|
||||
choices = [
|
||||
Choice(title=seq_type['display_name'], value=(seq_type['id'], seq_type['directory']))
|
||||
for seq_type in self.config['sequence_types']
|
||||
]
|
||||
|
||||
selection = questionary.select(
|
||||
MESSAGES['type_prompt'],
|
||||
choices=choices
|
||||
).ask()
|
||||
|
||||
if selection is None:
|
||||
print(MESSAGES['creation_cancelled'])
|
||||
sys.exit(0)
|
||||
|
||||
return selection
|
||||
|
||||
def _get_display_name(self, config_list: List[Dict], selected_id: str) -> str:
|
||||
"""Récupère le nom d'affichage d'un élément par son ID."""
|
||||
for item in config_list:
|
||||
if item['id'] == selected_id:
|
||||
return item['display_name']
|
||||
return selected_id
|
||||
|
||||
def _confirm_creation(self, date: str, class_id: str, class_dir: str,
|
||||
title: str, summary: str, tags: str,
|
||||
type_id: str, type_dir: str) -> bool:
|
||||
"""Dialogue de confirmation avec résumé des paramètres."""
|
||||
class_display = self._get_display_name(self.config['classes'], class_id)
|
||||
type_display = self._get_display_name(self.config['sequence_types'], type_id)
|
||||
|
||||
confirmation_text = f"""Date: {date}
|
||||
Classe: {class_display}
|
||||
Titre: {title}
|
||||
Résumé: {summary}
|
||||
Tags: {tags}
|
||||
Type: {type_display}"""
|
||||
|
||||
print("\\n" + confirmation_text + "\\n")
|
||||
|
||||
confirmed = questionary.confirm(MESSAGES['confirm_prompt']).ask()
|
||||
|
||||
return confirmed if confirmed is not None else False
|
||||
|
||||
def _create_sequence_directory(self, class_dir: str, next_number: int, title: str) -> Path:
|
||||
"""Crée le répertoire de la séquence."""
|
||||
# Nettoyer le titre pour créer un nom de répertoire valide
|
||||
title_path = title.replace(' ', '_')
|
||||
# Supprimer les caractères spéciaux (version simplifiée)
|
||||
title_path = ''.join(c for c in title_path if c.isalnum() or c in '._-')
|
||||
|
||||
number_format = self.config['general']['sequence_number_format']
|
||||
sequence_name = f"{number_format % next_number}_{title_path}"
|
||||
sequence_path = Path(class_dir) / sequence_name
|
||||
|
||||
sequence_path.mkdir(parents=True, exist_ok=True)
|
||||
return sequence_path
|
||||
|
||||
def _copy_templates(self, sequence_path: Path, sequence_type: str,
|
||||
template_vars: Dict[str, str]):
|
||||
"""Copie et traite les templates."""
|
||||
skeleton_path = Path(self.config['general']['skeleton_path']) / 'sequence'
|
||||
|
||||
# Templates communs
|
||||
common_path = skeleton_path / 'common'
|
||||
if common_path.exists():
|
||||
for template_file in common_path.glob('*'):
|
||||
if template_file.is_file():
|
||||
self._process_template(template_file, sequence_path, template_vars)
|
||||
|
||||
# Templates spécifiques au type
|
||||
type_path = skeleton_path / sequence_type
|
||||
if type_path.exists():
|
||||
for template_file in type_path.glob('*'):
|
||||
if template_file.is_file():
|
||||
self._process_template(template_file, sequence_path, template_vars)
|
||||
|
||||
def _process_template(self, template_file: Path, output_dir: Path,
|
||||
template_vars: Dict[str, str]):
|
||||
"""Traite un fichier template avec substitution des variables."""
|
||||
output_file = output_dir / template_file.name
|
||||
|
||||
try:
|
||||
with open(template_file, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
|
||||
# Substitution simple des variables (équivalent à envsubst)
|
||||
for var, value in template_vars.items():
|
||||
content = content.replace(f'${{{var}}}', value)
|
||||
|
||||
with open(output_file, 'w', encoding='utf-8') as f:
|
||||
f.write(content)
|
||||
|
||||
except (IOError, UnicodeDecodeError) as e:
|
||||
print(f"Erreur lors du traitement du template {template_file}: {e}")
|
||||
|
||||
def _format_date_short(self, date_str: str) -> str:
|
||||
"""Formate la date courte selon la configuration."""
|
||||
try:
|
||||
short_format = self.config['date_config']['short_format']
|
||||
# Convertir le format strftime
|
||||
short_format = short_format.replace('+', '')
|
||||
return format_date_short(date_str, short_format)
|
||||
except KeyError:
|
||||
return format_date_short(date_str)
|
||||
|
||||
def run(self):
|
||||
"""Méthode principale pour exécuter le script."""
|
||||
try:
|
||||
# Étape 1: Sélection de la date
|
||||
date = self._get_date_input()
|
||||
|
||||
# Étape 2: Sélection de la classe
|
||||
class_id, class_dir = self._select_class()
|
||||
|
||||
# Étape 3: Récupération des séquences existantes
|
||||
existing_sequences, next_number = self._get_existing_sequences(class_dir)
|
||||
|
||||
# Étape 4: Saisie du titre
|
||||
title = self._get_sequence_title(existing_sequences, next_number)
|
||||
|
||||
# Étape 5: Saisie du résumé
|
||||
summary = self._get_summary()
|
||||
|
||||
# Étape 6: Sélection des tags
|
||||
tags = self._select_tags()
|
||||
|
||||
# Étape 7: Sélection du type de séquence
|
||||
type_id, type_dir = self._select_sequence_type()
|
||||
|
||||
# Étape 8: Confirmation
|
||||
if not self._confirm_creation(date, class_id, class_dir, title,
|
||||
summary, tags, type_id, type_dir):
|
||||
print(MESSAGES['creation_cancelled'])
|
||||
return
|
||||
|
||||
# Étape 9: Création de la séquence
|
||||
sequence_path = self._create_sequence_directory(class_dir, next_number, title)
|
||||
|
||||
# Préparation des variables de template
|
||||
template_vars = {
|
||||
'title': title,
|
||||
'title_under': '#' * len(title),
|
||||
'author': self.config['general']['author'],
|
||||
'date': date,
|
||||
'date_short': self._format_date_short(date),
|
||||
'tribe': class_dir,
|
||||
'tags': tags,
|
||||
'summary': summary,
|
||||
}
|
||||
|
||||
# Copie et traitement des templates
|
||||
self._copy_templates(sequence_path, type_dir, template_vars)
|
||||
|
||||
print(MESSAGES['creation_success'].format(path=sequence_path))
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print(f"\\n{MESSAGES['creation_cancelled']}")
|
||||
sys.exit(0)
|
||||
except Exception as e:
|
||||
print(f"Erreur inattendue: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
def main():
|
||||
"""Point d'entrée principal."""
|
||||
creator = SequenceCreator()
|
||||
creator.run()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,117 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
|
||||
display_result() {
|
||||
dialog --title "$1" \
|
||||
--no-collapse \
|
||||
--msgbox "$result" 0 0
|
||||
}
|
||||
|
||||
exec 3>&1
|
||||
date=$(dialog --calendar "Date" 0 0 2>&1 1>&3 | awk -F "/" '{print $3"-"$2"-"$1}')
|
||||
exec 3>&-
|
||||
|
||||
exec 3>&1
|
||||
selection=$(dialog \
|
||||
--backtitle "Création d'une nouvelle évaluation: Classe" \
|
||||
--title "Menu" \
|
||||
--clear \
|
||||
--cancel-label "Exit" \
|
||||
--menu "Choisir un niveau:" 0 0 5 \
|
||||
"1" "2nd" \
|
||||
"2" "SNT" \
|
||||
"3" "Enseignements Scientifique" \
|
||||
"4" "Première NSI" \
|
||||
"5" "Première Technologique" \
|
||||
2>&1 1>&3)
|
||||
exec 3>&-
|
||||
case $selection in
|
||||
1 )
|
||||
tribe="2nd"
|
||||
;;
|
||||
2 )
|
||||
tribe="SNT"
|
||||
;;
|
||||
3 )
|
||||
tribe="Enseignement_Scientifique"
|
||||
;;
|
||||
4 )
|
||||
tribe="1NSI"
|
||||
;;
|
||||
5 )
|
||||
tribe="1ST"
|
||||
;;
|
||||
esac
|
||||
exec 3>&1
|
||||
|
||||
cd $tribe
|
||||
existing_seq=$(ls -d [0-9][0-9]_*)
|
||||
nbr_seq=$(echo $existing_seq | wc -w)
|
||||
next_seq_number=$(expr $nbr_seq)
|
||||
|
||||
title=$(dialog \
|
||||
--inputbox "Séquences trouvée\n${existing_seq/ /\n} \nNom de la nouvelle sequence (n°$next_seq_number)" \
|
||||
0 0 \
|
||||
2>&1 1>&3)
|
||||
exec 3>&-
|
||||
cd ..
|
||||
|
||||
|
||||
exec 3>&1
|
||||
summary=$(dialog \
|
||||
--inputbox "Résumé de la séquence" \
|
||||
0 0 \
|
||||
2>&1 1>&3)
|
||||
exec 3>&-
|
||||
|
||||
## ajouter les tags
|
||||
exec 3>&1
|
||||
tags=$(dialog \
|
||||
--inputbox "Liste des tags séparés par une virgule" \
|
||||
0 0 \
|
||||
2>&1 1>&3)
|
||||
exec 3>&-
|
||||
|
||||
## Plan de travail ou classique
|
||||
exec 3>&1
|
||||
selection=$(dialog \
|
||||
--backtitle "Création d'une nouvelle séquence: Type" \
|
||||
--title "Menu" \
|
||||
--clear \
|
||||
--cancel-label "Exit" \
|
||||
--menu "Type de séquence:" 0 0 4 \
|
||||
"1" "Classique" \
|
||||
"2" "Plan de travail" \
|
||||
2>&1 1>&3)
|
||||
exec 3>&-
|
||||
case $selection in
|
||||
1 )
|
||||
sequence_type="classique"
|
||||
;;
|
||||
2 )
|
||||
sequence_type="plan_de_travail"
|
||||
;;
|
||||
esac
|
||||
|
||||
|
||||
title_path=$(echo ${title// /_} | iconv -f utf8 -t ascii//TRANSLIT | tr -cd '[:alnum:]._-')
|
||||
sequence_path=$tribe/$(printf "%02d" $next_seq_number)_${title_path}/
|
||||
mkdir -p $sequence_path
|
||||
|
||||
export title=$title
|
||||
export title_under=${title//?/#}
|
||||
export author='Benjamin Bertrand'
|
||||
export date=$date
|
||||
export date_short=`date --date="$date 00:00" "+%B %Y"`
|
||||
export tribe=$tribe
|
||||
export tags=$tags
|
||||
export summary=$summary
|
||||
SKELETONPATH=./tools/skeleton/sequence
|
||||
for i in `ls $SKELETONPATH/common/`
|
||||
do
|
||||
envsubst < $SKELETONPATH/common/$i > $sequence_path/$i
|
||||
done
|
||||
for i in `ls $SKELETONPATH/$sequence_type`
|
||||
do
|
||||
envsubst < $SKELETONPATH/$sequence_type/$i > $sequence_path/$i
|
||||
done
|
||||
Reference in New Issue
Block a user