368 lines
13 KiB
Python
368 lines
13 KiB
Python
"""
|
|
Tests unitaires pour le service de génération de rapports.
|
|
"""
|
|
|
|
import pytest
|
|
from domain.services import StudentReportService, generate_report_html
|
|
|
|
|
|
class TestStudentReportService:
|
|
"""Tests pour le service de génération de rapports."""
|
|
|
|
@pytest.fixture
|
|
def report_service(self):
|
|
"""Fixture du service de rapport."""
|
|
return StudentReportService()
|
|
|
|
@pytest.fixture
|
|
def sample_assessment_data(self):
|
|
"""Données d'évaluation de test."""
|
|
return {
|
|
'title': 'Contrôle de Mathématiques',
|
|
'description': 'Chapitre 3 - Fonctions',
|
|
'date': '2025-01-15',
|
|
'trimester': 2,
|
|
'class_name': '6ème A',
|
|
'coefficient': 1.0
|
|
}
|
|
|
|
@pytest.fixture
|
|
def sample_exercises_data(self):
|
|
"""Données d'exercices de test."""
|
|
return [
|
|
{
|
|
'id': 1,
|
|
'title': 'Exercice 1',
|
|
'description': 'Calculs',
|
|
'order': 1,
|
|
'elements': [
|
|
{
|
|
'id': 101,
|
|
'exercise_id': 1,
|
|
'label': 'Question 1a',
|
|
'description': 'Calcul simple',
|
|
'skill': 'Calculer',
|
|
'domain_name': 'Nombres',
|
|
'domain_color': '#3b82f6',
|
|
'grading_type': 'notes',
|
|
'max_points': 2.0
|
|
},
|
|
{
|
|
'id': 102,
|
|
'exercise_id': 1,
|
|
'label': 'Question 1b',
|
|
'description': 'Calcul complexe',
|
|
'skill': 'Calculer',
|
|
'domain_name': 'Nombres',
|
|
'domain_color': '#3b82f6',
|
|
'grading_type': 'notes',
|
|
'max_points': 3.0
|
|
}
|
|
]
|
|
},
|
|
{
|
|
'id': 2,
|
|
'title': 'Exercice 2',
|
|
'description': 'Compétences',
|
|
'order': 2,
|
|
'elements': [
|
|
{
|
|
'id': 201,
|
|
'exercise_id': 2,
|
|
'label': 'Compétence graphique',
|
|
'description': 'Tracer un graphe',
|
|
'skill': 'Représenter',
|
|
'domain_name': 'Géométrie',
|
|
'domain_color': '#22c55e',
|
|
'grading_type': 'score',
|
|
'max_points': 3.0
|
|
}
|
|
]
|
|
}
|
|
]
|
|
|
|
@pytest.fixture
|
|
def sample_student_data(self):
|
|
"""Données d'élève de test."""
|
|
return {
|
|
'id': 1,
|
|
'first_name': 'Marie',
|
|
'last_name': 'Dupont',
|
|
'email': 'marie.dupont@test.com'
|
|
}
|
|
|
|
@pytest.fixture
|
|
def sample_grades(self):
|
|
"""Notes de test."""
|
|
return {
|
|
1: [ # Student 1
|
|
{'element_id': 101, 'value': '1.5', 'comment': ''},
|
|
{'element_id': 102, 'value': '2.5', 'comment': 'Bon travail'},
|
|
{'element_id': 201, 'value': '2', 'comment': ''}
|
|
],
|
|
2: [ # Student 2
|
|
{'element_id': 101, 'value': '2', 'comment': ''},
|
|
{'element_id': 102, 'value': '1', 'comment': ''},
|
|
{'element_id': 201, 'value': '3', 'comment': ''}
|
|
]
|
|
}
|
|
|
|
def test_generate_student_report(
|
|
self, report_service, sample_assessment_data,
|
|
sample_student_data, sample_grades, sample_exercises_data
|
|
):
|
|
"""Test de génération d'un rapport complet."""
|
|
report = report_service.generate_student_report(
|
|
sample_assessment_data,
|
|
sample_student_data,
|
|
sample_grades,
|
|
sample_exercises_data
|
|
)
|
|
|
|
# Vérifier la structure du rapport
|
|
assert 'assessment' in report
|
|
assert 'student' in report
|
|
assert 'results' in report
|
|
assert 'exercises' in report
|
|
assert 'competences' in report
|
|
assert 'domains' in report
|
|
assert 'class_statistics' in report
|
|
|
|
# Vérifier les données de l'évaluation
|
|
assert report['assessment']['title'] == 'Contrôle de Mathématiques'
|
|
assert report['assessment']['trimester'] == 2
|
|
|
|
# Vérifier les données de l'élève
|
|
assert report['student']['full_name'] == 'Marie Dupont'
|
|
assert report['student']['email'] == 'marie.dupont@test.com'
|
|
|
|
# Vérifier les résultats
|
|
# Score exercice 1: 1.5 + 2.5 = 4.0
|
|
# Score exercice 2: 2 (score) -> 2/3 * 3 = 2.0
|
|
# Total: 6.0
|
|
# Student 2: 2 + 1 + (3/3)*3 = 6.0 (égalité)
|
|
assert report['results']['total_score'] == 6.0
|
|
assert report['results']['total_max_points'] == 8.0
|
|
assert report['results']['percentage'] == 75.0
|
|
assert report['results']['position'] == 1 # 1er ex-aequo (même score)
|
|
assert report['results']['total_students'] == 2
|
|
|
|
def test_generate_report_with_special_values(
|
|
self, report_service, sample_assessment_data,
|
|
sample_exercises_data
|
|
):
|
|
"""Test avec valeurs spéciales (., d)."""
|
|
student_data = {
|
|
'id': 1,
|
|
'first_name': 'Jean',
|
|
'last_name': 'Martin',
|
|
'email': 'jean@test.com'
|
|
}
|
|
|
|
grades = {
|
|
1: [
|
|
{'element_id': 101, 'value': '.', 'comment': 'Pas de réponse'},
|
|
{'element_id': 102, 'value': 'd', 'comment': 'Dispensé'},
|
|
{'element_id': 201, 'value': '1', 'comment': ''}
|
|
]
|
|
}
|
|
|
|
report = report_service.generate_student_report(
|
|
sample_assessment_data,
|
|
student_data,
|
|
grades,
|
|
sample_exercises_data
|
|
)
|
|
|
|
# . compte comme 0, d ne compte pas
|
|
# Total: 0 + 1 (score 1/3 * 3) = 1.0
|
|
# Max: 2 + 3 = 5.0 (le dispensé ne compte pas)
|
|
assert report['results']['total_score'] == 1.0
|
|
assert report['results']['total_max_points'] == 5.0
|
|
|
|
def test_competences_performance(
|
|
self, report_service, sample_assessment_data,
|
|
sample_student_data, sample_grades, sample_exercises_data
|
|
):
|
|
"""Test du calcul des performances par compétence."""
|
|
report = report_service.generate_student_report(
|
|
sample_assessment_data,
|
|
sample_student_data,
|
|
sample_grades,
|
|
sample_exercises_data
|
|
)
|
|
|
|
competences = {c['name']: c for c in report['competences']}
|
|
|
|
# Compétence "Calculer": 1.5 + 2.5 = 4.0 / 5.0
|
|
assert 'Calculer' in competences
|
|
assert competences['Calculer']['score'] == 4.0
|
|
assert competences['Calculer']['max_points'] == 5.0
|
|
assert competences['Calculer']['percentage'] == 80.0
|
|
|
|
# Compétence "Représenter": 2.0 / 3.0
|
|
assert 'Représenter' in competences
|
|
assert competences['Représenter']['score'] == 2.0
|
|
assert competences['Représenter']['max_points'] == 3.0
|
|
|
|
def test_domains_performance(
|
|
self, report_service, sample_assessment_data,
|
|
sample_student_data, sample_grades, sample_exercises_data
|
|
):
|
|
"""Test du calcul des performances par domaine."""
|
|
report = report_service.generate_student_report(
|
|
sample_assessment_data,
|
|
sample_student_data,
|
|
sample_grades,
|
|
sample_exercises_data
|
|
)
|
|
|
|
domains = {d['name']: d for d in report['domains']}
|
|
|
|
# Domaine "Nombres": 4.0 / 5.0
|
|
assert 'Nombres' in domains
|
|
assert domains['Nombres']['score'] == 4.0
|
|
assert domains['Nombres']['color'] == '#3b82f6'
|
|
|
|
# Domaine "Géométrie": 2.0 / 3.0
|
|
assert 'Géométrie' in domains
|
|
assert domains['Géométrie']['score'] == 2.0
|
|
assert domains['Géométrie']['color'] == '#22c55e'
|
|
|
|
def test_student_not_in_grades(
|
|
self, report_service, sample_assessment_data,
|
|
sample_exercises_data
|
|
):
|
|
"""Test avec un élève sans notes."""
|
|
student_data = {
|
|
'id': 999,
|
|
'first_name': 'Unknown',
|
|
'last_name': 'Student',
|
|
'email': 'unknown@test.com'
|
|
}
|
|
|
|
grades = {
|
|
1: [{'element_id': 101, 'value': '2', 'comment': ''}]
|
|
}
|
|
|
|
with pytest.raises(ValueError, match="n'a pas de notes"):
|
|
report_service.generate_student_report(
|
|
sample_assessment_data,
|
|
student_data,
|
|
grades,
|
|
sample_exercises_data
|
|
)
|
|
|
|
|
|
class TestGenerateReportHTML:
|
|
"""Tests pour la génération HTML des rapports."""
|
|
|
|
@pytest.fixture
|
|
def sample_report_data(self):
|
|
"""Données de rapport pour le HTML."""
|
|
return {
|
|
'assessment': {
|
|
'title': 'Test Évaluation',
|
|
'description': 'Description test',
|
|
'date': '2025-01-15',
|
|
'trimester': 2,
|
|
'class_name': '6ème A',
|
|
'coefficient': 1.0
|
|
},
|
|
'student': {
|
|
'full_name': 'Marie Dupont',
|
|
'first_name': 'Marie',
|
|
'last_name': 'Dupont',
|
|
'email': 'marie@test.com'
|
|
},
|
|
'results': {
|
|
'total_score': 15.0,
|
|
'total_max_points': 20.0,
|
|
'percentage': 75.0,
|
|
'position': 5,
|
|
'total_students': 25
|
|
},
|
|
'exercises': [
|
|
{
|
|
'title': 'Exercice 1',
|
|
'description': 'Test',
|
|
'score': 8.0,
|
|
'max_points': 10.0,
|
|
'percentage': 80.0,
|
|
'elements': [
|
|
{
|
|
'label': 'Q1',
|
|
'description': '',
|
|
'skill': '',
|
|
'domain': '',
|
|
'raw_value': '4',
|
|
'calculated_score': 4.0,
|
|
'max_points': 5.0,
|
|
'grading_type': 'notes',
|
|
'score_label': '',
|
|
'comment': ''
|
|
}
|
|
]
|
|
}
|
|
],
|
|
'competences': [],
|
|
'domains': [],
|
|
'class_statistics': {
|
|
'count': 25,
|
|
'mean': 12.5,
|
|
'median': 13.0,
|
|
'min': 5.0,
|
|
'max': 19.0,
|
|
'std_dev': 3.2
|
|
}
|
|
}
|
|
|
|
def test_generate_html_contains_student_name(self, sample_report_data):
|
|
"""Le HTML contient le prénom de l'élève."""
|
|
html = generate_report_html(sample_report_data)
|
|
assert 'Marie' in html
|
|
|
|
def test_generate_html_contains_assessment_title(self, sample_report_data):
|
|
"""Le HTML contient le titre de l'évaluation."""
|
|
html = generate_report_html(sample_report_data)
|
|
assert 'Test Évaluation' in html
|
|
|
|
def test_generate_html_contains_score(self, sample_report_data):
|
|
"""Le HTML contient le score."""
|
|
html = generate_report_html(sample_report_data)
|
|
assert '15.0' in html or '15' in html
|
|
assert '20.0' in html or '20' in html
|
|
assert '75' in html # percentage
|
|
|
|
def test_generate_html_contains_statistics(self, sample_report_data):
|
|
"""Le HTML ne contient plus les statistiques de classe (supprimées)."""
|
|
html = generate_report_html(sample_report_data)
|
|
# Les statistiques de classe ont été supprimées du template
|
|
assert 'Statistiques de la classe' not in html
|
|
assert 'Position :' not in html
|
|
|
|
def test_generate_html_with_message(self, sample_report_data):
|
|
"""Le HTML inclut le message personnalisé."""
|
|
message = "Excellent travail cette année !"
|
|
html = generate_report_html(sample_report_data, message)
|
|
assert message in html
|
|
|
|
def test_generate_html_no_dynamic_color(self):
|
|
"""Le HTML n'a plus de couleur dynamique basée sur le score."""
|
|
report_data = {
|
|
'assessment': {'title': 'Test', 'date': '2025-01-01', 'trimester': 1, 'class_name': 'Test', 'coefficient': 1},
|
|
'student': {'full_name': 'Test', 'first_name': 'Test', 'last_name': 'Test', 'email': 'test@test.com'},
|
|
'results': {'total_score': 18, 'total_max_points': 20, 'percentage': 90.0, 'position': 1, 'total_students': 10},
|
|
'exercises': [],
|
|
'competences': [],
|
|
'domains': [],
|
|
'class_statistics': {'count': 10, 'mean': 12, 'median': 12, 'min': 5, 'max': 18, 'std_dev': 2}
|
|
}
|
|
html = generate_report_html(report_data)
|
|
# Les couleurs dynamiques (vert/jaune/rouge) ont été supprimées
|
|
assert '#22c55e' not in html # pas de vert
|
|
assert '#f6d32d' not in html # pas de jaune
|
|
assert '#ef4444' not in html # pas de rouge
|
|
# Le score utilise maintenant une couleur neutre
|
|
assert 'background-color: #f3f4f6' in html # gris neutre
|