feat: add mailing and bilan to send
This commit is contained in:
@@ -438,4 +438,170 @@ def delete(id):
|
||||
db.session.commit()
|
||||
current_app.logger.info(f'Évaluation supprimée: {title} (ID: {id})')
|
||||
flash('Évaluation supprimée avec succès !', 'success')
|
||||
return redirect(url_for('assessments.list'))
|
||||
return redirect(url_for('assessments.list'))
|
||||
|
||||
@bp.route('/<int:id>/preview-report/<int:student_id>')
|
||||
@handle_db_errors
|
||||
def preview_report(id, student_id):
|
||||
"""Prévisualise le bilan d'un élève dans le navigateur."""
|
||||
from services.student_report_service import StudentReportService
|
||||
from models import Student
|
||||
|
||||
# Récupérer l'évaluation
|
||||
assessment_repo = AssessmentRepository()
|
||||
assessment = assessment_repo.get_with_full_details_or_404(id)
|
||||
|
||||
# Récupérer l'élève
|
||||
student = Student.query.get_or_404(student_id)
|
||||
|
||||
# Générer le rapport
|
||||
report_service = StudentReportService()
|
||||
report_data = report_service.generate_student_report(assessment, student)
|
||||
|
||||
# Afficher le template email directement
|
||||
return render_template('email/student_report.html', report=report_data)
|
||||
|
||||
@bp.route('/<int:id>/send-reports', methods=['POST'])
|
||||
@handle_db_errors
|
||||
def send_reports(id):
|
||||
"""Envoie les bilans d'évaluation par email."""
|
||||
try:
|
||||
# Récupération des données du formulaire
|
||||
data = request.get_json()
|
||||
if not data:
|
||||
return jsonify({'success': False, 'error': 'Aucune donnée fournie'}), 400
|
||||
|
||||
student_ids = data.get('student_ids', [])
|
||||
custom_message = data.get('custom_message', '').strip()
|
||||
|
||||
if not student_ids:
|
||||
return jsonify({'success': False, 'error': 'Aucun élève sélectionné'}), 400
|
||||
|
||||
# Récupération de l'évaluation
|
||||
assessment_repo = AssessmentRepository()
|
||||
assessment = assessment_repo.get_with_full_details_or_404(id)
|
||||
|
||||
# Vérification de la configuration email
|
||||
from services.email_service import EmailService
|
||||
from services.student_report_service import StudentReportService
|
||||
from flask import render_template
|
||||
|
||||
email_service = EmailService()
|
||||
if not email_service.is_configured():
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': 'Configuration email incomplète. Rendez-vous dans Configuration > Email.'
|
||||
}), 400
|
||||
|
||||
report_service = StudentReportService()
|
||||
|
||||
# Génération des rapports
|
||||
reports_data = report_service.generate_multiple_reports(assessment, student_ids)
|
||||
|
||||
if reports_data['error_count'] > 0:
|
||||
current_app.logger.warning(f"Erreurs lors de la génération de rapports: {reports_data['errors']}")
|
||||
|
||||
# Envoi des emails
|
||||
sent_count = 0
|
||||
errors = []
|
||||
|
||||
for student_id, report_data in reports_data['reports'].items():
|
||||
try:
|
||||
student = report_data['student']
|
||||
|
||||
# Vérification de l'email de l'élève
|
||||
if not student['email']:
|
||||
errors.append(f"{student['full_name']}: Aucune adresse email")
|
||||
continue
|
||||
|
||||
# Validation de l'email
|
||||
validation = email_service.validate_email_addresses([student['email']])
|
||||
if validation['invalid_count'] > 0:
|
||||
errors.append(f"{student['full_name']}: Adresse email invalide ({student['email']})")
|
||||
continue
|
||||
|
||||
# Génération du HTML de l'email
|
||||
html_content = render_template('email/student_report.html',
|
||||
report=report_data,
|
||||
custom_message=custom_message)
|
||||
|
||||
# Sujet de l'email
|
||||
subject = f"Bilan d'évaluation - {assessment.title} - {student['full_name']}"
|
||||
|
||||
# Envoi de l'email
|
||||
result = email_service.send_email([student['email']], subject, html_content)
|
||||
|
||||
if result['success']:
|
||||
sent_count += 1
|
||||
current_app.logger.info(f"Bilan envoyé à {student['full_name']} ({student['email']})")
|
||||
else:
|
||||
errors.append(f"{student['full_name']}: {result['error']}")
|
||||
current_app.logger.error(f"Erreur envoi bilan à {student['full_name']}: {result['error']}")
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"{report_data.get('student', {}).get('full_name', 'Élève inconnu')}: Erreur inattendue - {str(e)}"
|
||||
errors.append(error_msg)
|
||||
current_app.logger.error(f"Erreur envoi bilan: {e}")
|
||||
|
||||
# Préparer la réponse
|
||||
response_data = {
|
||||
'success': sent_count > 0,
|
||||
'sent_count': sent_count,
|
||||
'total_requested': len(student_ids),
|
||||
'error_count': len(errors),
|
||||
'errors': errors
|
||||
}
|
||||
|
||||
if sent_count > 0:
|
||||
if len(errors) == 0:
|
||||
response_data['message'] = f"✅ {sent_count} bilan(s) envoyé(s) avec succès !"
|
||||
else:
|
||||
response_data['message'] = f"✅ {sent_count} bilan(s) envoyé(s), {len(errors)} erreur(s)"
|
||||
else:
|
||||
response_data['message'] = f"❌ Aucun bilan envoyé - {len(errors)} erreur(s)"
|
||||
|
||||
return jsonify(response_data)
|
||||
|
||||
except Exception as e:
|
||||
current_app.logger.error(f"Erreur lors de l'envoi de bilans: {e}")
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': f'Erreur inattendue: {str(e)}'
|
||||
}), 500
|
||||
|
||||
@bp.route('/<int:id>/eligible-students')
|
||||
@handle_db_errors
|
||||
def get_eligible_students(id):
|
||||
"""Récupère la liste des élèves éligibles avec leurs emails pour l'envoi de bilans."""
|
||||
try:
|
||||
assessment_repo = AssessmentRepository()
|
||||
assessment = assessment_repo.get_with_full_details_or_404(id)
|
||||
|
||||
# Récupérer les élèves éligibles (ceux qui étaient dans la classe à la date de l'évaluation)
|
||||
eligible_students = []
|
||||
for student in assessment.class_group.get_students_at_date(assessment.date):
|
||||
eligible_students.append({
|
||||
'id': student.id,
|
||||
'first_name': student.first_name,
|
||||
'last_name': student.last_name,
|
||||
'full_name': student.full_name,
|
||||
'email': student.email or '',
|
||||
'has_email': bool(student.email)
|
||||
})
|
||||
|
||||
# Trier par nom de famille puis prénom
|
||||
eligible_students.sort(key=lambda x: (x['last_name'].lower(), x['first_name'].lower()))
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'students': eligible_students,
|
||||
'total_count': len(eligible_students),
|
||||
'with_email_count': len([s for s in eligible_students if s['has_email']])
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
current_app.logger.error(f"Erreur récupération élèves éligibles: {e}")
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': f'Erreur: {str(e)}'
|
||||
}), 500
|
||||
@@ -446,6 +446,105 @@ def update_general():
|
||||
|
||||
return redirect(url_for('config.general'))
|
||||
|
||||
@bp.route('/email')
|
||||
def email():
|
||||
"""Page de configuration email."""
|
||||
try:
|
||||
# Récupérer la configuration email actuelle
|
||||
email_config = {
|
||||
'smtp_host': config_manager.get('email.smtp_host', ''),
|
||||
'smtp_port': config_manager.get('email.smtp_port', '587'),
|
||||
'username': config_manager.get('email.username', ''),
|
||||
'password': config_manager.get('email.password', ''),
|
||||
'use_tls': config_manager.get('email.use_tls', 'true') == 'true',
|
||||
'from_name': config_manager.get('email.from_name', 'Notytex'),
|
||||
'from_address': config_manager.get('email.from_address', ''),
|
||||
}
|
||||
|
||||
return render_template('config/email.html', email_config=email_config)
|
||||
except Exception as e:
|
||||
return handle_error(e, "Erreur lors du chargement de la configuration email")
|
||||
|
||||
@bp.route('/email/update', methods=['POST'])
|
||||
@handle_db_errors
|
||||
def update_email():
|
||||
"""Mettre à jour la configuration email."""
|
||||
try:
|
||||
# Récupérer les données du formulaire
|
||||
smtp_host = request.form.get('smtp_host', '').strip()
|
||||
smtp_port = request.form.get('smtp_port', '587').strip()
|
||||
username = request.form.get('username', '').strip()
|
||||
password = request.form.get('password', '').strip()
|
||||
use_tls = request.form.get('use_tls') == 'on'
|
||||
from_name = request.form.get('from_name', 'Notytex').strip()
|
||||
from_address = request.form.get('from_address', '').strip()
|
||||
|
||||
# Validation des données
|
||||
if smtp_host and not smtp_port.isdigit():
|
||||
flash('Le port SMTP doit être un nombre', 'error')
|
||||
return redirect(url_for('config.email'))
|
||||
|
||||
if from_address:
|
||||
import re
|
||||
email_regex = re.compile(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$')
|
||||
if not email_regex.match(from_address):
|
||||
flash('Format d\'adresse email invalide', 'error')
|
||||
return redirect(url_for('config.email'))
|
||||
|
||||
# Sauvegarder la configuration
|
||||
config_manager.set('email.smtp_host', smtp_host)
|
||||
config_manager.set('email.smtp_port', smtp_port)
|
||||
config_manager.set('email.username', username)
|
||||
config_manager.set('email.password', password)
|
||||
config_manager.set('email.use_tls', 'true' if use_tls else 'false')
|
||||
config_manager.set('email.from_name', from_name)
|
||||
config_manager.set('email.from_address', from_address)
|
||||
|
||||
if config_manager.save():
|
||||
flash('Configuration email mise à jour avec succès', 'success')
|
||||
else:
|
||||
flash('Erreur lors de la sauvegarde', 'error')
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Erreur mise à jour config email: {e}")
|
||||
flash('Erreur lors de la mise à jour', 'error')
|
||||
|
||||
return redirect(url_for('config.email'))
|
||||
|
||||
@bp.route('/email/test', methods=['POST'])
|
||||
@handle_db_errors
|
||||
def test_email():
|
||||
"""Tester la configuration email."""
|
||||
try:
|
||||
test_email_address = request.form.get('test_email', '').strip()
|
||||
|
||||
if not test_email_address:
|
||||
flash('Adresse email de test requise', 'error')
|
||||
return redirect(url_for('config.email'))
|
||||
|
||||
# Validation de l'adresse email
|
||||
import re
|
||||
email_regex = re.compile(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$')
|
||||
if not email_regex.match(test_email_address):
|
||||
flash('Format d\'adresse email invalide', 'error')
|
||||
return redirect(url_for('config.email'))
|
||||
|
||||
# Tenter l'envoi d'un email de test
|
||||
from services.email_service import EmailService
|
||||
email_service = EmailService(config_manager)
|
||||
result = email_service.send_test_email(test_email_address)
|
||||
|
||||
if result['success']:
|
||||
flash(f'Email de test envoyé avec succès à {test_email_address}', 'success')
|
||||
else:
|
||||
flash(f'Erreur lors de l\'envoi du test: {result["error"]}', 'error')
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Erreur test email: {e}")
|
||||
flash('Erreur lors du test d\'envoi', 'error')
|
||||
|
||||
return redirect(url_for('config.email'))
|
||||
|
||||
@bp.route('/reset', methods=['POST'])
|
||||
def reset_config():
|
||||
"""Réinitialise la configuration aux valeurs par défaut."""
|
||||
|
||||
Reference in New Issue
Block a user