mirror of
https://git.v0id.ovh/n3wt-innov/n3wt-school.git
synced 2026-01-28 23:43:22 +00:00
feat: Gestion de l'arborescence des documents d'école en fonction des requêtes CRUD [N3WTS-17]
This commit is contained in:
@ -2,11 +2,9 @@ from django.db import models
|
|||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
import os
|
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
logger = logging.getLogger("SubscriptionModels")
|
logger = logging.getLogger("SubscriptionModels")
|
||||||
|
|
||||||
@ -350,123 +348,124 @@ class RegistrationSchoolFileMaster(models.Model):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
import os
|
|
||||||
affected_rf_ids = set()
|
affected_rf_ids = set()
|
||||||
is_new = self.pk is None
|
is_new = self.pk is None
|
||||||
|
|
||||||
super().save(*args, **kwargs)
|
# Log création ou modification du master
|
||||||
|
|
||||||
# 2. Gestion des groupes pour la synchro des templates
|
|
||||||
if is_new:
|
if is_new:
|
||||||
from Subscriptions.models import RegistrationForm
|
logger.info(f"[FormPerso] Création master '{self.name}' pour établissement '{self.establishment}'")
|
||||||
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:
|
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:
|
try:
|
||||||
old = RegistrationSchoolFileMaster.objects.get(pk=self.pk)
|
old = RegistrationSchoolFileMaster.objects.get(pk=self.pk)
|
||||||
old_groups = set(old.groups.values_list('id', flat=True))
|
if old.file and old.file.name:
|
||||||
new_groups = set(self.groups.values_list('id', flat=True))
|
old_filename = os.path.basename(old.file.name)
|
||||||
from Subscriptions.models import RegistrationForm
|
# Nouveau nom selon le type (dynamique ou existant)
|
||||||
affected_rf_ids.update(
|
if (
|
||||||
RegistrationForm.objects.filter(fileGroup__in=list(old_groups | new_groups)).values_list('pk', flat=True)
|
self.formMasterData
|
||||||
)
|
and isinstance(self.formMasterData, dict)
|
||||||
form_data_changed = (
|
and self.formMasterData.get("fields")
|
||||||
old.formMasterData != self.formMasterData
|
):
|
||||||
and self.formMasterData
|
new_filename = f"{self.name}.pdf"
|
||||||
and isinstance(self.formMasterData, dict)
|
else:
|
||||||
and self.formMasterData.get("fields")
|
# Pour les forms existants, le nom attendu est self.name + extension du fichier existant
|
||||||
)
|
extension = os.path.splitext(old_filename)[1]
|
||||||
name_changed = old.name != self.name
|
new_filename = f"{self.name}{extension}" if extension else self.name
|
||||||
# --- Correction spécifique pour les fichiers existants (PDF, DOC, etc.) ---
|
if new_filename and old_filename != new_filename:
|
||||||
# Si le nom change et qu'on a un fichier existant, on doit renommer physiquement le fichier
|
old_file_path = old.file.path
|
||||||
if old.file and not self.file and name_changed:
|
if os.path.exists(old_file_path):
|
||||||
old_file_path = old.file.path
|
try:
|
||||||
ext = os.path.splitext(old_file_path)[1]
|
os.remove(old_file_path)
|
||||||
clean_name = (self.name or 'document').replace(' ', '_').replace('/', '_')
|
logger.info(f"[FormPerso] Suppression de l'ancien fichier master: {old_file_path}")
|
||||||
new_filename = f"{clean_name}{ext}"
|
except Exception as e:
|
||||||
new_rel_path = os.path.join(os.path.dirname(old.file.name), new_filename)
|
logger.error(f"[FormPerso] Erreur suppression ancien fichier master: {e}")
|
||||||
new_abs_path = os.path.join(os.path.dirname(old_file_path), new_filename)
|
# Correction du nom du fichier pour éviter le suffixe random
|
||||||
logger.info(f"Renommage fichier: {old_file_path} -> {new_abs_path}")
|
if (
|
||||||
try:
|
not self.formMasterData
|
||||||
if not os.path.exists(new_abs_path):
|
or not (isinstance(self.formMasterData, dict) and self.formMasterData.get("fields"))
|
||||||
os.rename(old_file_path, new_abs_path)
|
):
|
||||||
self.file.name = new_rel_path
|
# Si le fichier existe et le nom ne correspond pas, renommer le fichier physique et mettre à jour le FileField
|
||||||
logger.info(f"self.file.name après renommage: {self.file.name}")
|
if self.file and self.file.name:
|
||||||
super().save(update_fields=["file"])
|
current_filename = os.path.basename(self.file.name)
|
||||||
else:
|
current_path = self.file.path
|
||||||
logger.info(f"Le fichier cible existe déjà: {new_abs_path}")
|
expected_filename = new_filename
|
||||||
except Exception as e:
|
expected_path = os.path.join(os.path.dirname(current_path), expected_filename)
|
||||||
logger.error(f"Erreur lors du renommage du fichier master: {e}")
|
if current_filename != expected_filename:
|
||||||
elif old.file and self.file and self.file != old.file:
|
try:
|
||||||
old.file.delete(save=False)
|
if os.path.exists(current_path):
|
||||||
elif old.file and form_data_changed:
|
os.rename(current_path, expected_path)
|
||||||
old.file.delete(save=False)
|
self.file.name = os.path.join(os.path.dirname(self.file.name), expected_filename).replace("\\", "/")
|
||||||
self.file = None
|
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:
|
except RegistrationSchoolFileMaster.DoesNotExist:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Harmonisation : gestion du fichier (dynamique ou existant) sans appeler registration_school_file_master_upload_to
|
# --- Traitement PDF dynamique AVANT le super().save() ---
|
||||||
# Pour les formulaires dynamiques (PDF à générer)
|
if (
|
||||||
if not self.file and self.formMasterData and isinstance(self.formMasterData, dict) and self.formMasterData.get("fields"):
|
self.formMasterData
|
||||||
try:
|
and isinstance(self.formMasterData, dict)
|
||||||
from Subscriptions.util import generate_form_json_pdf
|
and self.formMasterData.get("fields")
|
||||||
pdf_filename = f"{self.name or 'formulaire'}.pdf"
|
):
|
||||||
abs_path = os.path.join(settings.MEDIA_ROOT, self.file.field.upload_to(self, pdf_filename))
|
from Subscriptions.util import generate_form_json_pdf
|
||||||
os.makedirs(os.path.dirname(abs_path), exist_ok=True)
|
pdf_filename = f"{self.name}.pdf"
|
||||||
if os.path.exists(abs_path):
|
pdf_file = generate_form_json_pdf(self, self.formMasterData)
|
||||||
try:
|
self.file.save(pdf_filename, pdf_file, save=False)
|
||||||
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}")
|
|
||||||
|
|
||||||
# 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:
|
try:
|
||||||
|
# Import local pour éviter le circular import
|
||||||
from Subscriptions.util import create_templates_for_registration_form
|
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:
|
for rf_id in affected_rf_ids:
|
||||||
try:
|
try:
|
||||||
rf = RegistrationForm.objects.get(pk=rf_id)
|
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)
|
create_templates_for_registration_form(rf)
|
||||||
except Exception as e:
|
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}")
|
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:
|
except Exception as e:
|
||||||
logger.error(f"Erreur globale lors de la synchronisation des templates après modification du master {self.pk}: {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):
|
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):
|
if self.file and hasattr(self.file, 'path') and os.path.exists(self.file.path):
|
||||||
try:
|
try:
|
||||||
self.file.delete(save=False)
|
self.file.delete(save=False)
|
||||||
|
|||||||
@ -136,7 +136,6 @@ def create_templates_for_registration_form(register_form):
|
|||||||
|
|
||||||
school_masters = RegistrationSchoolFileMaster.objects.filter(groups=current_group).distinct()
|
school_masters = RegistrationSchoolFileMaster.objects.filter(groups=current_group).distinct()
|
||||||
parent_masters = RegistrationParentFileMaster.objects.filter(groups=current_group).distinct()
|
parent_masters = RegistrationParentFileMaster.objects.filter(groups=current_group).distinct()
|
||||||
logger.info("util.create_templates_for_registration_form - school_masters récupérés")
|
|
||||||
|
|
||||||
school_master_ids = {m.pk for m in school_masters}
|
school_master_ids = {m.pk for m in school_masters}
|
||||||
parent_master_ids = {m.pk for m in parent_masters}
|
parent_master_ids = {m.pk for m in parent_masters}
|
||||||
@ -163,25 +162,20 @@ def create_templates_for_registration_form(register_form):
|
|||||||
tmpl.delete()
|
tmpl.delete()
|
||||||
logger.info("Deleted obsolete parent template %s for RF %s", getattr(tmpl, "pk", None), register_form.pk)
|
logger.info("Deleted obsolete parent template %s for RF %s", getattr(tmpl, "pk", None), register_form.pk)
|
||||||
|
|
||||||
# Créer les school templates manquants
|
# Créer les school templates manquants ou mettre à jour les existants si le master a changé
|
||||||
logger.info("util.create_templates_for_registration_form - Créer les school templates manquants")
|
|
||||||
for m in school_masters:
|
for m in school_masters:
|
||||||
exists = RegistrationSchoolFileTemplate.objects.filter(master=m, registration_form=register_form).exists()
|
tmpl_qs = RegistrationSchoolFileTemplate.objects.filter(master=m, registration_form=register_form)
|
||||||
if exists:
|
tmpl = tmpl_qs.first() if tmpl_qs.exists() else None
|
||||||
continue
|
|
||||||
base_slug = (m.name or "master").strip().replace(" ", "_")[:40]
|
base_slug = (m.name or "master").strip().replace(" ", "_")[:40]
|
||||||
slug = f"{base_slug}_{register_form.pk}_{m.pk}"
|
slug = f"{base_slug}_{register_form.pk}_{m.pk}"
|
||||||
|
|
||||||
# --- Correction : Générer un nom de fichier unique uniquement si le master n'a pas de fichier ---
|
|
||||||
file_name = None
|
file_name = None
|
||||||
if m.file and hasattr(m.file, 'name') and m.file.name:
|
if m.file and hasattr(m.file, 'name') and m.file.name:
|
||||||
# Utiliser le nom du fichier tel qu'il est stocké dans le master (pas de suffixe aléatoire ici)
|
|
||||||
file_name = os.path.basename(m.file.name)
|
file_name = os.path.basename(m.file.name)
|
||||||
logger.info(f"util.create_templates_for_registration_form - file_name 1 : {file_name}")
|
|
||||||
elif m.file:
|
elif m.file:
|
||||||
file_name = str(m.file)
|
file_name = str(m.file)
|
||||||
else:
|
else:
|
||||||
# Générer le PDF si besoin (rare ici)
|
|
||||||
try:
|
try:
|
||||||
pdf_file = generate_form_json_pdf(register_form, m.formMasterData)
|
pdf_file = generate_form_json_pdf(register_form, m.formMasterData)
|
||||||
file_name = os.path.basename(pdf_file.name)
|
file_name = os.path.basename(pdf_file.name)
|
||||||
@ -189,8 +183,54 @@ def create_templates_for_registration_form(register_form):
|
|||||||
logger.error(f"Erreur lors de la génération du PDF pour le template: {e}")
|
logger.error(f"Erreur lors de la génération du PDF pour le template: {e}")
|
||||||
file_name = None
|
file_name = None
|
||||||
|
|
||||||
logger.info(f"util.create_templates_for_registration_form - file_name : {file_name}")
|
from django.core.files.base import ContentFile
|
||||||
|
upload_rel_path = registration_school_file_upload_to(
|
||||||
|
type("Tmp", (), {
|
||||||
|
"registration_form": register_form,
|
||||||
|
"establishment": getattr(register_form, "establishment", None),
|
||||||
|
"student": getattr(register_form, "student", None)
|
||||||
|
})(),
|
||||||
|
file_name
|
||||||
|
)
|
||||||
|
abs_path = os.path.join(settings.MEDIA_ROOT, upload_rel_path)
|
||||||
|
master_file_path = m.file.path if m.file and hasattr(m.file, 'path') else None
|
||||||
|
|
||||||
|
if tmpl:
|
||||||
|
template_file_name = os.path.basename(tmpl.file.name) if tmpl.file and tmpl.file.name else None
|
||||||
|
master_file_changed = template_file_name != file_name
|
||||||
|
# --- GESTION FORM EXISTANT : suppression ancien template si nom ou contenu master changé ---
|
||||||
|
if master_file_changed or (
|
||||||
|
master_file_path and os.path.exists(master_file_path) and
|
||||||
|
(not tmpl.file or not os.path.exists(abs_path) or os.path.getmtime(master_file_path) > os.path.getmtime(abs_path))
|
||||||
|
):
|
||||||
|
# Supprimer l'ancien fichier du template (même si le nom change)
|
||||||
|
if tmpl.file and tmpl.file.name:
|
||||||
|
old_template_path = os.path.join(settings.MEDIA_ROOT, tmpl.file.name)
|
||||||
|
if os.path.exists(old_template_path):
|
||||||
|
try:
|
||||||
|
os.remove(old_template_path)
|
||||||
|
logger.info(f"util.create_templates_for_registration_form - Suppression ancien fichier template: {old_template_path}")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Erreur suppression ancien fichier template: {e}")
|
||||||
|
# Copier le nouveau fichier du master (form existant)
|
||||||
|
if master_file_path and os.path.exists(master_file_path):
|
||||||
|
try:
|
||||||
|
os.makedirs(os.path.dirname(abs_path), exist_ok=True)
|
||||||
|
import shutil
|
||||||
|
shutil.copy2(master_file_path, abs_path)
|
||||||
|
logger.info(f"util.create_templates_for_registration_form - Copie du fichier master {master_file_path} -> {abs_path}")
|
||||||
|
tmpl.file.name = upload_rel_path
|
||||||
|
tmpl.name = m.name or ""
|
||||||
|
tmpl.slug = slug
|
||||||
|
tmpl.formTemplateData = m.formMasterData or []
|
||||||
|
tmpl.save()
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Erreur lors de la copie du fichier master pour mise à jour du template: {e}")
|
||||||
|
created.append(tmpl)
|
||||||
|
logger.info("util.create_templates_for_registration_form - Mise à jour school template %s from master %s for RF %s", tmpl.pk, m.pk, register_form.pk)
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Sinon, création du template comme avant
|
||||||
tmpl = RegistrationSchoolFileTemplate(
|
tmpl = RegistrationSchoolFileTemplate(
|
||||||
master=m,
|
master=m,
|
||||||
registration_form=register_form,
|
registration_form=register_form,
|
||||||
@ -199,20 +239,7 @@ def create_templates_for_registration_form(register_form):
|
|||||||
slug=slug,
|
slug=slug,
|
||||||
)
|
)
|
||||||
if file_name:
|
if file_name:
|
||||||
from django.core.files.base import ContentFile
|
# Copier le fichier du master si besoin (form existant)
|
||||||
# Vérifier si le fichier existe déjà dans MEDIA_ROOT (copie du master)
|
|
||||||
upload_rel_path = registration_school_file_upload_to(
|
|
||||||
type("Tmp", (), {
|
|
||||||
"registration_form": register_form,
|
|
||||||
"establishment": getattr(register_form, "establishment", None),
|
|
||||||
"student": getattr(register_form, "student", None)
|
|
||||||
})(),
|
|
||||||
file_name
|
|
||||||
)
|
|
||||||
abs_path = os.path.join(settings.MEDIA_ROOT, upload_rel_path)
|
|
||||||
master_file_path = m.file.path if m.file and hasattr(m.file, 'path') else None
|
|
||||||
|
|
||||||
# Si le fichier n'existe pas dans le dossier cible, le copier depuis le master
|
|
||||||
if master_file_path and not os.path.exists(abs_path):
|
if master_file_path and not os.path.exists(abs_path):
|
||||||
try:
|
try:
|
||||||
import shutil
|
import shutil
|
||||||
@ -221,8 +248,6 @@ def create_templates_for_registration_form(register_form):
|
|||||||
logger.info(f"util.create_templates_for_registration_form - Copie du fichier master {master_file_path} -> {abs_path}")
|
logger.info(f"util.create_templates_for_registration_form - Copie du fichier master {master_file_path} -> {abs_path}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Erreur lors de la copie du fichier master pour le template: {e}")
|
logger.error(f"Erreur lors de la copie du fichier master pour le template: {e}")
|
||||||
|
|
||||||
# Associer le fichier existant (ou copié) au template
|
|
||||||
tmpl.file.name = upload_rel_path
|
tmpl.file.name = upload_rel_path
|
||||||
tmpl.save()
|
tmpl.save()
|
||||||
created.append(tmpl)
|
created.append(tmpl)
|
||||||
|
|||||||
Reference in New Issue
Block a user