feat: Gestion de l'arborescence des documents d'école en fonction des requêtes CRUD [N3WTS-17]

This commit is contained in:
N3WT DE COMPET
2026-01-25 11:01:22 +01:00
parent b4f70e6bad
commit abb4b525b2
2 changed files with 149 additions and 125 deletions

View File

@ -2,11 +2,9 @@ from django.db import models
from django.utils.timezone import now
from django.conf import settings
from django.utils.translation import gettext_lazy as _
from datetime import datetime
import os
import logging
import os
logger = logging.getLogger("SubscriptionModels")
@ -350,123 +348,124 @@ class RegistrationSchoolFileMaster(models.Model):
return None
def save(self, *args, **kwargs):
import os
affected_rf_ids = set()
is_new = self.pk is None
super().save(*args, **kwargs)
# 2. Gestion des groupes pour la synchro des templates
# Log création ou modification du master
if is_new:
from Subscriptions.models import RegistrationForm
new_groups = set(self.groups.values_list('id', flat=True))
affected_rf_ids.update(
RegistrationForm.objects.filter(fileGroup__in=list(new_groups)).values_list('pk', flat=True)
)
logger.info(f"[FormPerso] Création master '{self.name}' pour établissement '{self.establishment}'")
else:
logger.info(f"[FormPerso] Modification master '{self.name}' (id={self.pk}) pour établissement '{self.establishment}'")
# --- Suppression de l'ancien fichier master si le nom change (form existant ou dynamique) ---
if self.pk:
try:
old = RegistrationSchoolFileMaster.objects.get(pk=self.pk)
old_groups = set(old.groups.values_list('id', flat=True))
new_groups = set(self.groups.values_list('id', flat=True))
from Subscriptions.models import RegistrationForm
affected_rf_ids.update(
RegistrationForm.objects.filter(fileGroup__in=list(old_groups | new_groups)).values_list('pk', flat=True)
)
form_data_changed = (
old.formMasterData != self.formMasterData
and self.formMasterData
and isinstance(self.formMasterData, dict)
and self.formMasterData.get("fields")
)
name_changed = old.name != self.name
# --- Correction spécifique pour les fichiers existants (PDF, DOC, etc.) ---
# Si le nom change et qu'on a un fichier existant, on doit renommer physiquement le fichier
if old.file and not self.file and name_changed:
old_file_path = old.file.path
ext = os.path.splitext(old_file_path)[1]
clean_name = (self.name or 'document').replace(' ', '_').replace('/', '_')
new_filename = f"{clean_name}{ext}"
new_rel_path = os.path.join(os.path.dirname(old.file.name), new_filename)
new_abs_path = os.path.join(os.path.dirname(old_file_path), new_filename)
logger.info(f"Renommage fichier: {old_file_path} -> {new_abs_path}")
try:
if not os.path.exists(new_abs_path):
os.rename(old_file_path, new_abs_path)
self.file.name = new_rel_path
logger.info(f"self.file.name après renommage: {self.file.name}")
super().save(update_fields=["file"])
else:
logger.info(f"Le fichier cible existe déjà: {new_abs_path}")
except Exception as e:
logger.error(f"Erreur lors du renommage du fichier master: {e}")
elif old.file and self.file and self.file != old.file:
old.file.delete(save=False)
elif old.file and form_data_changed:
old.file.delete(save=False)
self.file = None
if old.file and old.file.name:
old_filename = os.path.basename(old.file.name)
# Nouveau nom selon le type (dynamique ou existant)
if (
self.formMasterData
and isinstance(self.formMasterData, dict)
and self.formMasterData.get("fields")
):
new_filename = f"{self.name}.pdf"
else:
# Pour les forms existants, le nom attendu est self.name + extension du fichier existant
extension = os.path.splitext(old_filename)[1]
new_filename = f"{self.name}{extension}" if extension else self.name
if new_filename and old_filename != new_filename:
old_file_path = old.file.path
if os.path.exists(old_file_path):
try:
os.remove(old_file_path)
logger.info(f"[FormPerso] Suppression de l'ancien fichier master: {old_file_path}")
except Exception as e:
logger.error(f"[FormPerso] Erreur suppression ancien fichier master: {e}")
# Correction du nom du fichier pour éviter le suffixe random
if (
not self.formMasterData
or not (isinstance(self.formMasterData, dict) and self.formMasterData.get("fields"))
):
# Si le fichier existe et le nom ne correspond pas, renommer le fichier physique et mettre à jour le FileField
if self.file and self.file.name:
current_filename = os.path.basename(self.file.name)
current_path = self.file.path
expected_filename = new_filename
expected_path = os.path.join(os.path.dirname(current_path), expected_filename)
if current_filename != expected_filename:
try:
if os.path.exists(current_path):
os.rename(current_path, expected_path)
self.file.name = os.path.join(os.path.dirname(self.file.name), expected_filename).replace("\\", "/")
logger.info(f"[FormPerso] Renommage du fichier master: {current_path} -> {expected_path}")
except Exception as e:
logger.error(f"[FormPerso] Erreur lors du renommage du fichier master: {e}")
except RegistrationSchoolFileMaster.DoesNotExist:
pass
# Harmonisation : gestion du fichier (dynamique ou existant) sans appeler registration_school_file_master_upload_to
# Pour les formulaires dynamiques (PDF à générer)
if not self.file and self.formMasterData and isinstance(self.formMasterData, dict) and self.formMasterData.get("fields"):
try:
from Subscriptions.util import generate_form_json_pdf
pdf_filename = f"{self.name or 'formulaire'}.pdf"
abs_path = os.path.join(settings.MEDIA_ROOT, self.file.field.upload_to(self, pdf_filename))
os.makedirs(os.path.dirname(abs_path), exist_ok=True)
if os.path.exists(abs_path):
try:
os.remove(abs_path)
logger.info(f"Suppression fichier existant: {abs_path}")
except Exception as e:
logger.error(f"Erreur suppression fichier existant avant save: {e}")
pdf_file = generate_form_json_pdf(self, self.formMasterData)
logger.info(f"Sauvegarde PDF dynamique: {pdf_filename}")
self.file.save(pdf_filename, pdf_file, save=False)
logger.info(f"self.file.name après save PDF dynamique: {self.file.name}")
super().save(update_fields=["file"])
except Exception as e:
logger.error(f"Erreur lors de la génération automatique du PDF pour le master {self.pk}: {e}")
# Pour les fichiers existants uploadés
elif self.file and hasattr(self.file, 'name') and self.name:
# On force le nom du fichier (nom du master + extension) si besoin
ext = os.path.splitext(self.file.name)[1]
clean_name = (self.name or 'document').replace(' ', '_').replace('/', '_')
final_file_name = f"{clean_name}{ext}"
# Si le nom ne correspond pas, on renomme le fichier physique et le FileField
if os.path.basename(self.file.name) != final_file_name:
current_path = self.file.path
abs_path = os.path.join(settings.MEDIA_ROOT, self.file.field.upload_to(self, final_file_name))
os.makedirs(os.path.dirname(abs_path), exist_ok=True)
if os.path.exists(abs_path):
try:
os.remove(abs_path)
logger.info(f"Suppression fichier existant: {abs_path}")
except Exception as e:
logger.error(f"Erreur suppression fichier existant avant renommage: {e}")
try:
os.rename(current_path, abs_path)
self.file.name = os.path.relpath(abs_path, settings.MEDIA_ROOT)
logger.info(f"Déplacement fichier existant: {current_path} -> {abs_path}")
super().save(update_fields=["file"])
except Exception as e:
logger.error(f"Erreur lors du déplacement du fichier existant: {e}")
# --- Traitement PDF dynamique AVANT le super().save() ---
if (
self.formMasterData
and isinstance(self.formMasterData, dict)
and self.formMasterData.get("fields")
):
from Subscriptions.util import generate_form_json_pdf
pdf_filename = f"{self.name}.pdf"
pdf_file = generate_form_json_pdf(self, self.formMasterData)
self.file.save(pdf_filename, pdf_file, save=False)
# 4. Synchronisation des templates pour tous les dossiers d'inscription concernés (création ou modification)
super().save(*args, **kwargs)
# Synchronisation des templates pour tous les dossiers d'inscription concernés (création ou modification)
try:
# Import local pour éviter le circular import
from Subscriptions.util import create_templates_for_registration_form
from Subscriptions.models import RegistrationForm
from Subscriptions.models import RegistrationForm, RegistrationSchoolFileTemplate
# Détermination des RF concernés
if is_new:
new_groups = set(self.groups.values_list('id', flat=True))
affected_rf_ids.update(
RegistrationForm.objects.filter(fileGroup__in=list(new_groups)).values_list('pk', flat=True)
)
else:
try:
old = RegistrationSchoolFileMaster.objects.get(pk=self.pk)
old_groups = set(old.groups.values_list('id', flat=True))
new_groups = set(self.groups.values_list('id', flat=True))
affected_rf_ids.update(
RegistrationForm.objects.filter(fileGroup__in=list(old_groups | new_groups)).values_list('pk', flat=True)
)
form_data_changed = (
old.formMasterData != self.formMasterData
and self.formMasterData
and isinstance(self.formMasterData, dict)
and self.formMasterData.get("fields")
)
name_changed = old.name != self.name
if form_data_changed or name_changed:
logger.info(f"[FormPerso] Modification du contenu du master '{self.name}' (id={self.pk})")
except RegistrationSchoolFileMaster.DoesNotExist:
pass
# Pour chaque RF concerné, régénérer les templates
for rf_id in affected_rf_ids:
try:
rf = RegistrationForm.objects.get(pk=rf_id)
logger.info(f"[FormPerso] Synchronisation template pour élève '{rf.student.last_name}_{rf.student.first_name}' (RF id={rf.pk}) suite à modification/ajout du master '{self.name}'")
create_templates_for_registration_form(rf)
except Exception as e:
logger.error(f"Erreur lors de la synchronisation des templates pour RF {rf_id} après modification du master {self.pk}: {e}")
except Exception as e:
logger.error(f"Erreur globale lors de la synchronisation des templates après modification du master {self.pk}: {e}")
def delete(self, *args, **kwargs):
# Supprimer le fichier physique du master si présent
logger.info(f"[FormPerso] Suppression master '{self.name}' (id={self.pk}) et tous ses templates")
# Import local pour éviter le circular import
from Subscriptions.models import RegistrationSchoolFileTemplate
templates = RegistrationSchoolFileTemplate.objects.filter(master=self)
for tmpl in templates:
logger.info(f"[FormPerso] Suppression template '{tmpl.name}' pour élève '{tmpl.registration_form.student.last_name}_{tmpl.registration_form.student.first_name}' (RF id={tmpl.registration_form.pk})")
if self.file and hasattr(self.file, 'path') and os.path.exists(self.file.path):
try:
self.file.delete(save=False)