feature/pedimentos-correccion-partidas
This commit is contained in:
117
api/customs/management/commands/fix_archivo_case.py
Normal file
117
api/customs/management/commands/fix_archivo_case.py
Normal file
@@ -0,0 +1,117 @@
|
||||
"""
|
||||
Corrige el mismatch de case entre el campo `archivo` en BD y los nombres
|
||||
reales de los objetos en MinIO.
|
||||
|
||||
Causa habitual: transferencia de archivos de producción a local lowercaseó
|
||||
los filenames, pero la BD conserva los nombres originales con mayúsculas.
|
||||
|
||||
Estrategia: para cada Document cuyo `archivo` no exista en MinIO con el
|
||||
nombre exacto, intenta el filename en minúsculas. Si lo encuentra, actualiza
|
||||
el campo en BD. Los archivos que ya coinciden no se tocan.
|
||||
|
||||
Uso:
|
||||
python manage.py fix_archivo_case --pedimento <UUID> --dry-run
|
||||
python manage.py fix_archivo_case --pedimento <UUID>
|
||||
python manage.py fix_archivo_case --organizacion <UUID> --dry-run
|
||||
python manage.py fix_archivo_case --organizacion <UUID>
|
||||
"""
|
||||
import posixpath
|
||||
|
||||
from django.core.management.base import BaseCommand, CommandError
|
||||
|
||||
from api.customs.models import Pedimento
|
||||
from api.record.models import Document
|
||||
from api.utils.minio_client import minio_client
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = "Corrige mismatch de case entre campo archivo en BD y MinIO."
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
"--pedimento", metavar="UUID",
|
||||
help="UUID del pedimento a corregir.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--organizacion", metavar="UUID",
|
||||
help="UUID de la organización.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--dry-run", action="store_true",
|
||||
help="Solo diagnóstico, sin aplicar cambios.",
|
||||
)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
ped_id = options.get("pedimento")
|
||||
org_id = options.get("organizacion")
|
||||
dry_run = options["dry_run"]
|
||||
|
||||
if dry_run:
|
||||
self.stdout.write(self.style.WARNING(
|
||||
"=== MODO PRUEBA (--dry-run): Sin cambios en BD ===\n"
|
||||
))
|
||||
|
||||
qs = Document.objects.all()
|
||||
if ped_id:
|
||||
try:
|
||||
ped = Pedimento.objects.get(id=ped_id)
|
||||
except Pedimento.DoesNotExist:
|
||||
raise CommandError(f"Pedimento {ped_id!r} no encontrado.")
|
||||
qs = qs.filter(pedimento=ped)
|
||||
self.stdout.write(f"Pedimento: {ped.pedimento_app}\n")
|
||||
elif org_id:
|
||||
qs = qs.filter(organizacion_id=org_id)
|
||||
|
||||
total = qs.count()
|
||||
self.stdout.write(f"Documentos a revisar: {total}\n")
|
||||
|
||||
ok = mismatch = not_found = 0
|
||||
|
||||
for doc in qs.iterator(chunk_size=500):
|
||||
name = doc.archivo.name if doc.archivo else None
|
||||
if not name:
|
||||
continue
|
||||
|
||||
if minio_client.file_exists(name):
|
||||
ok += 1
|
||||
continue
|
||||
|
||||
lower_name = self._lower_filename(name)
|
||||
if lower_name == name:
|
||||
not_found += 1
|
||||
continue
|
||||
|
||||
if minio_client.file_exists(lower_name):
|
||||
mismatch += 1
|
||||
self.stdout.write(
|
||||
f" {'[DRY]' if dry_run else '[FIX]'} doc {doc.id}:\n"
|
||||
f" BD : {name}\n"
|
||||
f" MinIO : {lower_name}\n"
|
||||
)
|
||||
if not dry_run:
|
||||
doc.archivo.name = lower_name
|
||||
doc.save(update_fields=["archivo"])
|
||||
else:
|
||||
not_found += 1
|
||||
|
||||
self.stdout.write(
|
||||
f"\n{'─' * 60}\nRESUMEN\n"
|
||||
f" Coinciden exacto : {ok}\n"
|
||||
f" Mismatch de case : {mismatch}\n"
|
||||
f" No encontrados : {not_found}\n"
|
||||
)
|
||||
|
||||
if dry_run and mismatch:
|
||||
self.stdout.write(self.style.WARNING(
|
||||
"\nEjecuta sin --dry-run para aplicar los cambios."
|
||||
))
|
||||
elif not dry_run and mismatch:
|
||||
self.stdout.write(self.style.SUCCESS(
|
||||
f"\n{mismatch} registros actualizados en BD."
|
||||
))
|
||||
|
||||
def _lower_filename(self, name):
|
||||
"""Lowercase solo el filename, preserva el path del directorio."""
|
||||
dir_part = posixpath.dirname(name)
|
||||
filename = posixpath.basename(name)
|
||||
return posixpath.join(dir_part, filename.lower())
|
||||
Reference in New Issue
Block a user