[pyar] Actualizando estado de carga de datos con Django y Celery

Marcelo Leiva Sandoval chelitoleiva en gmail.com
Sab Jul 23 14:42:25 ART 2016


Buenas tardes, tengo una vista donde de sube al servidor un archivo csv
(más de 50.000 registros) para realizar una carga masiva de datos.
Actualmente este archivo se guarda en /media/ y en con el signal
post_save() se ejecuta una tarea en Celery para realizar la carga de
información.
Esto funciona bastante bien, pero ahora necesito mostrar el estado de
avance de la carga de datos en el mismo modelo donde se encuentra el
archivo csv.

# models.py

class ArchivoCSV(models.Model):
    nombre = models.CharField(verbose_name='Nombre del archivo',
max_length=200, editable=False)
    archivo = models.FileField(upload_to=PATH_CSV_PREDIOS, max_length=800)
    porcentaje_carga =
models.PositiveIntegerField(verbose_name='Porcentaje de carga',
default=0,

validators=[MaxValueValidator(100), MinValueValidator(0)],
                                                   editable=False)
    estado = models.CharField(verbose_name='Estado de carga',
choices=ESTADOS_CARGA, default='S', max_length=1,
                              editable=False)

    creado_el = models.DateTimeField(auto_now_add=True)
    modificado_el = models.DateTimeField(auto_now=True)
    history = HistoricalRecords()

    class Meta:
        verbose_name_plural = "Cargar archivo CSV."
        ordering = ['-creado_el']

    def __str__(self):
        return '{0}'.format(self.nombre)

    def save(self, *args, **kwargs):
        if not self.pk:
            self.nombre = self.archivo
        super(ArchivoCSV, self).save(*args, **kwargs)

    def delete(self, using=None):
        archivo = self.archivo.name
        super(ArchivoCSV, self).delete()
        existe_archivo = os.path.exists(archivo)
        if existe_archivo:
            try:
                os.remove(archivo)
            except Exception as e:
                print(e)


@receiver(post_save, sender=PredioCSV)
def procesar_archivo_post_save(sender, **kwargs):
    instance = kwargs['instance']
    existe_archivo = os.path.exists(instance.archivo.name)
    if existe_archivo:
        from .tasks import procesar_csv_task
        procesar_csv_task.delay(instance)


En el task si agrego cantidad_registros = len(list(clientes)) la tarea
termina. Y si actualizo la instancia la tarea se multiplica infinitamente.

# tasks.py

@app.task(ignore_result=True, name="cargando-csv")
def procesar_csv_task(instance):
    nuevos_clientes = []
    listado_clientes = Cliente.objects.all()

    encoding = determinar_encoding(open(instance.archivo.name, "rb").read())
    if encoding:
        clientes = DictReader(open(instance.archivo.name, "rb"),
delimiter=';', encoding=encoding,
                              skipinitialspace=True)

        cantidad_registros = len(list(clientes))  # si realizo esto la
tarea termina

        try:
            for index, row in enumerate(clientes, start=1):
                try:
                    codigo = row['COD_CLIENTE']
                    ...

                    if listado_clientes.filter(codigo=codigo).first():
                    # se editan datos del cliente
                    else:
                        nuevo_cliente = Cliente()
                        ...

                        nuevos_clientes.append(nuevo_cliente)
                except Exception as e:
                    print(e)
                if index % 100 == 0:
                    instance.porcentaje_carga = index * 100 /
cantidad_registros  # con esto la tarea se multiplica
                    instance.save()

            Cliente.objects.bulk_create(nuevos_clientes)

        except Exception as e:
            print(e)
    else:
        print('No se puede definir el encodig del archivo csv.')


La idea es cada 100 registros trabajados actualizar el modelo con el
porcentaje de avance y luego de ejecutar el bulk_create cambiar el estado a
finalizado. Quedo atento a sus preguntas y sugerencias.

Saludos

-- 
Marcelo Leiva Sandoval
Django & Symfony2 Developer
Linux User #491264

"Los que aseguran que es imposible no deberían interrumpir a los que
estamos intentándolo". Thomas A. Edison.
------------ próxima parte ------------
Se ha borrado un adjunto en formato HTML...
URL: <http://listas.python.org.ar/pipermail/pyar/attachments/20160723/02243c76/attachment.html>


Más información sobre la lista de distribución pyar