1 | # -*- coding: utf-8 -*- |
---|
2 | import os |
---|
3 | import logging |
---|
4 | import time |
---|
5 | import smtplib |
---|
6 | from datetime import datetime, date |
---|
7 | from django.contrib.auth.models import User |
---|
8 | from django.core.exceptions import ObjectDoesNotExist |
---|
9 | from django.db.models import Sum, Count |
---|
10 | from django.template.loader import get_template |
---|
11 | from django.template.context import Context |
---|
12 | from django.core.mail import send_mail |
---|
13 | from celery.task import task |
---|
14 | from django.utils import translation |
---|
15 | from django.utils.translation import ugettext_lazy as _l, ugettext as _ |
---|
16 | from comun.csv_unicode import fichero_unicode, unicode_csv_reader |
---|
17 | from comun.models import Error, CargaMasiva, Comercio, \ |
---|
18 | BienesComerciadosPrincipales, Pais, \ |
---|
19 | CodigoArmonizado, PaisesPrincipales |
---|
20 | from carga.models import ComercioExteriorNexo |
---|
21 | from comun.procesar_csv import ValidadorColumna, Expresion, ValidadorConjunto, ColumnaCSV, ConjuntoCSV, FlujoCSV, ErrorCSV |
---|
22 | from comun.utilidades import dictfetchall |
---|
23 | from comun.constantes import IMPORTACIONES, EXPORTACIONES, CMASIVA_MAX_SEC, \ |
---|
24 | EXPR_REG_ENTERO, EXPR_REG_CODIGO_ARANCELARIO, EXPR_REG_CIIU, \ |
---|
25 | EXPR_REG_PAIS, EXPR_REG_DECIMAL, EXPR_REG_STRING_150, \ |
---|
26 | EXPR_REG_ANYO, EXPR_REG_FECHA, EXPR_REG_MES, \ |
---|
27 | FLUJO_COMERCIAL_PRODUCCION_NACIONAL, FLUJO_COMERCIAL_COMERCIO_EXTERIOR, \ |
---|
28 | COMERCIO_EXTERIOR_INTRAALBA, COMERCIO_EXTERIOR_EXTRAALBA |
---|
29 | from django.conf import settings |
---|
30 | from django.db import connection, transaction |
---|
31 | |
---|
32 | |
---|
33 | logger = logging.getLogger("carga.procesarcsv") |
---|
34 | |
---|
35 | def enviar_email(correo, plantilla, titulo, variables): |
---|
36 | try: |
---|
37 | t = get_template(plantilla) |
---|
38 | c = Context(variables) |
---|
39 | |
---|
40 | send_mail(titulo, t.render(c), settings.EMAIL_FROM, [correo], fail_silently=False) |
---|
41 | except smtplib.SMTPException as e: |
---|
42 | logger.fatal('El correo no pudo ser enviado: smtpException ' + e) |
---|
43 | except Exception as e: |
---|
44 | logger.fatal('El correo no pudo ser enviado: ' + e) |
---|
45 | |
---|
46 | def prepare_carga_masiva(username, pais, flujo_comercial, mes, anyo): |
---|
47 | |
---|
48 | usuario = User.objects.get(username=username) |
---|
49 | |
---|
50 | try: |
---|
51 | proceso_carga_masiva = CargaMasiva.objects.get(usuario=usuario, pais=pais, flujo_comercial=flujo_comercial, mes=mes, anyo=anyo, procesando=True) |
---|
52 | |
---|
53 | if (datetime.now() - proceso_carga_masiva.fecha).seconds >= CMASIVA_MAX_SEC: |
---|
54 | proceso_carga_masiva.fecha = datetime.now() |
---|
55 | proceso_carga_masiva.procesando = False |
---|
56 | proceso_carga_masiva.save() |
---|
57 | |
---|
58 | carga_masiva = CargaMasiva(usuario=usuario, fecha=datetime.now(), pais=pais, flujo_comercial=flujo_comercial, mes=mes, anyo=anyo, procesando=True) |
---|
59 | carga_masiva.save() |
---|
60 | |
---|
61 | return usuario, False, carga_masiva |
---|
62 | else: |
---|
63 | return usuario, True, proceso_carga_masiva |
---|
64 | except ObjectDoesNotExist: |
---|
65 | carga_masiva = CargaMasiva(usuario=usuario, fecha=datetime.now(), pais=pais, flujo_comercial=flujo_comercial, mes=mes, anyo=anyo, procesando=True) |
---|
66 | carga_masiva.save() |
---|
67 | |
---|
68 | return usuario, False, carga_masiva |
---|
69 | |
---|
70 | def identificar_bienes_principales(cantidad): |
---|
71 | """ |
---|
72 | Algoritmo para identificar los bienes principales con relacion a <pais> dentro del flujo de comercio registrado |
---|
73 | """ |
---|
74 | comercio_exterior = [COMERCIO_EXTERIOR_INTRAALBA, COMERCIO_EXTERIOR_EXTRAALBA] |
---|
75 | flujo = [IMPORTACIONES, EXPORTACIONES] |
---|
76 | querys = {} |
---|
77 | cursor = connection.cursor() |
---|
78 | |
---|
79 | querys[COMERCIO_EXTERIOR_INTRAALBA] = """ |
---|
80 | SELECT * FROM |
---|
81 | (SELECT |
---|
82 | DISTINCT(codigo_armonizado_id) as codigo_armonizado, |
---|
83 | pais_id as pais, |
---|
84 | SUM(monto) as valor, |
---|
85 | anyo, |
---|
86 | rank() OVER (PARTITION BY anyo ORDER BY SUM(monto) DESC) as rango_monto |
---|
87 | FROM |
---|
88 | public.comun_comercio |
---|
89 | WHERE |
---|
90 | flujo_comercial = %s and |
---|
91 | pais_id in (SELECT pais_id FROM comun_pais WHERE anyo_inclusion > 0) |
---|
92 | GROUP BY |
---|
93 | pais_id, |
---|
94 | codigo_armonizado_id, |
---|
95 | anyo |
---|
96 | ORDER BY codigo_armonizado, rango_monto) as principales |
---|
97 | WHERE |
---|
98 | principales.rango_monto < %s; |
---|
99 | """ |
---|
100 | |
---|
101 | querys[COMERCIO_EXTERIOR_EXTRAALBA] = """ |
---|
102 | SELECT * FROM |
---|
103 | (SELECT |
---|
104 | DISTINCT(codigo_armonizado_id) as codigo_armonizado, |
---|
105 | pais_id as pais, |
---|
106 | SUM(monto) as valor, |
---|
107 | anyo, |
---|
108 | rank() OVER (PARTITION BY anyo ORDER BY SUM(monto) DESC) as rango_monto |
---|
109 | FROM |
---|
110 | public.comun_comercio |
---|
111 | WHERE |
---|
112 | flujo_comercial = %s and |
---|
113 | pais_id in (SELECT pais_id FROM comun_pais WHERE not anyo_inclusion > 0) |
---|
114 | GROUP BY |
---|
115 | pais_id, |
---|
116 | codigo_armonizado_id, |
---|
117 | anyo |
---|
118 | ORDER BY codigo_armonizado, rango_monto) as principales |
---|
119 | WHERE |
---|
120 | principales.rango_monto < %s; |
---|
121 | """ |
---|
122 | |
---|
123 | with transaction.commit_on_success(): |
---|
124 | |
---|
125 | try: |
---|
126 | cursor.execute("DELETE FROM comun_bienescomerciadosprincipales;") |
---|
127 | transaction.commit_unless_managed() |
---|
128 | |
---|
129 | for item_comercio_exterior in comercio_exterior: |
---|
130 | for item_flujo in flujo: |
---|
131 | cursor.execute(querys[item_comercio_exterior], [item_flujo, cantidad + 1]) |
---|
132 | filas = dictfetchall(cursor) |
---|
133 | |
---|
134 | bien_principal = BienesComerciadosPrincipales() |
---|
135 | for fila in filas: |
---|
136 | bien_principal.flujo_comercial = item_flujo |
---|
137 | bien_principal.comercio_exterior = item_comercio_exterior |
---|
138 | bien_principal.codigo_armonizado = CodigoArmonizado.objects.get(id = fila['codigo_armonizado']) |
---|
139 | bien_principal.pais = Pais.objects.get(id = fila['pais']) |
---|
140 | bien_principal.valor = fila['valor'] |
---|
141 | bien_principal.anyo = fila['anyo'] |
---|
142 | |
---|
143 | bien_principal.save() |
---|
144 | bien_principal = BienesComerciadosPrincipales() |
---|
145 | except Exception as e: |
---|
146 | logger.fatal('Exception ejecutando la carga de Bienes Principales: %s' % e) |
---|
147 | transaction.rollback() |
---|
148 | |
---|
149 | raise e |
---|
150 | |
---|
151 | def identificar_paises_principales(cantidad): |
---|
152 | """ |
---|
153 | Algoritmo para identificar los paises principales con relacion a <pais> dentro del flujo de comercio registrado |
---|
154 | """ |
---|
155 | comercio_exterior = [COMERCIO_EXTERIOR_INTRAALBA, COMERCIO_EXTERIOR_EXTRAALBA] |
---|
156 | flujo = [IMPORTACIONES, EXPORTACIONES] |
---|
157 | querys = {} |
---|
158 | cursor = connection.cursor() |
---|
159 | |
---|
160 | querys[COMERCIO_EXTERIOR_INTRAALBA] = """ |
---|
161 | SELECT * FROM |
---|
162 | (SELECT |
---|
163 | DISTINCT(pais_id) as pais, |
---|
164 | pais_relacion_id as pais_relacion, |
---|
165 | SUM(monto) as valor, |
---|
166 | anyo, |
---|
167 | rank() OVER (PARTITION BY anyo ORDER BY SUM(monto) DESC) |
---|
168 | FROM |
---|
169 | public.comun_comercio |
---|
170 | WHERE |
---|
171 | flujo_comercial = %s and |
---|
172 | pais_id in (SELECT pais_id FROM comun_pais WHERE anyo_inclusion > 0) |
---|
173 | GROUP BY |
---|
174 | pais_id, |
---|
175 | pais_relacion_id, |
---|
176 | anyo |
---|
177 | ORDER BY anyo, rank) as principales |
---|
178 | WHERE |
---|
179 | principales.rank < %s; |
---|
180 | """ |
---|
181 | |
---|
182 | querys[COMERCIO_EXTERIOR_EXTRAALBA] = """ |
---|
183 | SELECT * FROM |
---|
184 | (SELECT |
---|
185 | DISTINCT(pais_id) as pais, |
---|
186 | pais_relacion_id as pais_relacion, |
---|
187 | SUM(monto) as valor, |
---|
188 | anyo, |
---|
189 | rank() OVER (PARTITION BY anyo ORDER BY SUM(monto) DESC) |
---|
190 | FROM |
---|
191 | public.comun_comercio |
---|
192 | WHERE |
---|
193 | flujo_comercial = %s and |
---|
194 | pais_id in (SELECT pais_id FROM comun_pais WHERE not anyo_inclusion > 0) |
---|
195 | GROUP BY |
---|
196 | pais_id, |
---|
197 | pais_relacion_id, |
---|
198 | anyo |
---|
199 | ORDER BY anyo, rank) as principales |
---|
200 | WHERE |
---|
201 | principales.rank < %s; |
---|
202 | """ |
---|
203 | with transaction.commit_on_success(): |
---|
204 | |
---|
205 | try: |
---|
206 | cursor.execute("DELETE FROM comun_paisesprincipales;") |
---|
207 | transaction.commit_unless_managed() |
---|
208 | |
---|
209 | for item_comercio_exterior in comercio_exterior: |
---|
210 | for item_flujo in flujo: |
---|
211 | cursor.execute(querys[item_comercio_exterior], [item_flujo, cantidad + 1]) |
---|
212 | filas = dictfetchall(cursor) |
---|
213 | |
---|
214 | pais_principal = PaisesPrincipales() |
---|
215 | for fila in filas: |
---|
216 | pais_principal.flujo_comercial = item_flujo |
---|
217 | pais_principal.comercio_exterior = item_comercio_exterior |
---|
218 | pais_principal.pais = Pais.objects.get(id = fila['pais']) |
---|
219 | pais_principal.pais_relacion = Pais.objects.get(id = fila['pais_relacion']) |
---|
220 | pais_principal.valor = fila['valor'] |
---|
221 | pais_principal.anyo = fila['anyo'] |
---|
222 | |
---|
223 | pais_principal.save() |
---|
224 | pais_principal = PaisesPrincipales() |
---|
225 | except Exception as e: |
---|
226 | logger.fatal('Exception ejecutando la carga de Paises Principales: %s' % e) |
---|
227 | transaction.rollback() |
---|
228 | |
---|
229 | raise e |
---|
230 | |
---|
231 | @task |
---|
232 | def cargar_comercio_exterior(nombre_usuario, pais, anyo, mes, archivo): |
---|
233 | usuario, procesando, carga_masiva = prepare_carga_masiva(nombre_usuario, pais, FLUJO_COMERCIAL_COMERCIO_EXTERIOR, mes, anyo) |
---|
234 | |
---|
235 | variables = {'pais': _(pais), 'mes': mes, 'anyo': anyo} |
---|
236 | cur_language = translation.get_language() |
---|
237 | plantilla = 'carga/%s/notificacion_comercio_exterior.mail' % cur_language |
---|
238 | |
---|
239 | if procesando: |
---|
240 | variables['errores_csv'] = [_('archivo_procesando')] |
---|
241 | enviar_email(usuario.email, plantilla, _('titulo_correo_comercio_exterior'), variables) |
---|
242 | return None |
---|
243 | else: |
---|
244 | parametros = {'pais': pais, 'mes': mes, 'anyo': anyo} |
---|
245 | |
---|
246 | nexo = ComercioExteriorNexo(parametros) |
---|
247 | |
---|
248 | columnas = [] |
---|
249 | indice = 0 |
---|
250 | |
---|
251 | validador = ValidadorColumna(EXPR_REG_ENTERO, 'error_campo_no_entero') |
---|
252 | columnas.append(ColumnaCSV(indice, 'flujo_comercial', validador)) |
---|
253 | |
---|
254 | indice += 1 |
---|
255 | validador = ValidadorColumna(EXPR_REG_CODIGO_ARANCELARIO, 'error_campo_no_entero') |
---|
256 | columnas.append(ColumnaCSV(indice, 'codigo_armonizado', validador)) |
---|
257 | """ |
---|
258 | indice += 1 |
---|
259 | validador = ValidadorColumna(EXPR_REG_CIIU, _('error_campo_no_entero')) |
---|
260 | columnas.append(ColumnaCSV(indice, 'ciiu', validador)) |
---|
261 | """ |
---|
262 | indice += 1 |
---|
263 | validador = ValidadorColumna(EXPR_REG_PAIS, 'error_formato_pais') |
---|
264 | columna_pais_relacion = ColumnaCSV(indice, 'pais_relacion', validador) |
---|
265 | columnas.append(columna_pais_relacion) |
---|
266 | """ |
---|
267 | indice += 1 |
---|
268 | validador = ValidadorColumna(EXPR_REG_STRING_150, _('error_longitud_string')) |
---|
269 | columnas.append(ColumnaCSV(indice, 'identificacion_tributaria', validador)) |
---|
270 | |
---|
271 | indice += 1 |
---|
272 | validador = ValidadorColumna(EXPR_REG_STRING_150, _('error_longitud_string')) |
---|
273 | columnas.append(ColumnaCSV(indice, 'nombre_unidad_economica', validador)) |
---|
274 | """ |
---|
275 | indice += 1 |
---|
276 | validador = ValidadorColumna(EXPR_REG_STRING_150, 'error_longitud_string') |
---|
277 | columnas.append(ColumnaCSV(indice, 'unidad_medida', validador)) |
---|
278 | |
---|
279 | indice += 1 |
---|
280 | validador = ValidadorColumna(EXPR_REG_ENTERO, 'error_campo_no_entero') |
---|
281 | columnas.append(ColumnaCSV(indice, 'cantidad', validador)) |
---|
282 | |
---|
283 | indice += 1 |
---|
284 | validador = ValidadorColumna(EXPR_REG_DECIMAL, 'error_campo_no_decimal') |
---|
285 | columnas.append(ColumnaCSV(indice, 'monto', validador)) |
---|
286 | |
---|
287 | conjuntos = [] |
---|
288 | |
---|
289 | validador = ValidadorConjunto(Expresion.DISTINTO, 'error_pais_relacion') |
---|
290 | conjuntos.append(ConjuntoCSV(columna_pais_relacion.indice, pais, validador, segundo_es_valor = True)) |
---|
291 | |
---|
292 | csv_flujo = FlujoCSV() |
---|
293 | try: |
---|
294 | csv_flujo.cargar_fichero(archivo, columnas, nexo, conjuntos) |
---|
295 | except Exception: |
---|
296 | logger.critical('Error cargando el archivo') |
---|
297 | |
---|
298 | error = ErrorCSV(fila = None, columna = None, error = 'error_cargando_archivo') |
---|
299 | csv_flujo.errores_csv.append(error) |
---|
300 | csv_flujo.errores_trans.append(_('error_cargando_archivo')) |
---|
301 | |
---|
302 | variables['errores_csv'] = csv_flujo.errores_trans |
---|
303 | enviar_email(usuario.email, plantilla, _('titulo_correo_comercio_exterior'), variables) |
---|
304 | |
---|
305 | return None |
---|
306 | finally: |
---|
307 | if len(csv_flujo.errores_csv) > 0: |
---|
308 | for error_csv in csv_flujo.errores_csv: |
---|
309 | error = Error() |
---|
310 | error.carga_masiva = carga_masiva |
---|
311 | error.fila = error_csv.fila |
---|
312 | error.columna = error_csv.columna |
---|
313 | error.error = error_csv.error |
---|
314 | error.save() |
---|
315 | |
---|
316 | carga_masiva.errores = True |
---|
317 | |
---|
318 | variables['errores_csv'] = csv_flujo.errores_trans |
---|
319 | else: |
---|
320 | carga_masiva.errores = False |
---|
321 | |
---|
322 | try: |
---|
323 | identificar_bienes_principales(settings.NUMERO_MAXIMO_REGISTROS_FICHAS_PRINCIPALES) |
---|
324 | identificar_paises_principales(settings.NUMERO_MAXIMO_REGISTROS_FICHAS_PRINCIPALES) |
---|
325 | except Exception as e: |
---|
326 | error = ErrorCSV(fila = None, columna = None, error = 'error_actualizando_fichas') |
---|
327 | csv_flujo.errores_csv.append(error) |
---|
328 | csv_flujo.errores_trans.append(_('error_actualizando_fichas')) |
---|
329 | |
---|
330 | variables['errores_csv'] = csv_flujo.errores_trans |
---|
331 | enviar_email(usuario.email, plantilla, _('titulo_correo_comercio_exterior'), variables) |
---|
332 | |
---|
333 | carga_masiva.procesando = False |
---|
334 | carga_masiva.save() |
---|
335 | |
---|
336 | enviar_email(usuario.email, plantilla, _('titulo_correo_comercio_exterior'), variables) |
---|
337 | return 0 |
---|
338 | |
---|
339 | @task |
---|
340 | def cargar_produccion_nacional(nombre_usuario, pais, anyo, mes, archivo): |
---|
341 | """ |
---|
342 | Tarea para cargar la produccion nacional de un pais |
---|
343 | """ |
---|
344 | usuario, procesando, carga_masiva = prepare_carga_masiva(nombre_usuario, pais, FLUJO_COMERCIAL_PRODUCCION_NACIONAL, mes, anyo) |
---|
345 | |
---|
346 | variables = {'pais': pais, 'mes': mes, 'anyo': anyo} |
---|
347 | cur_language = translation.get_language() |
---|
348 | plantilla = 'carga/%s/notificacion_comercio_exterior.mail' % cur_language |
---|
349 | |
---|
350 | if procesando: |
---|
351 | variables['errores_csv'] = [_('archivo_procesando')] |
---|
352 | enviar_email(usuario.email, plantilla, _('titulo_correo_produccion_nacional'), variables) |
---|
353 | return None |
---|
354 | else: |
---|
355 | parametros = {'pais': pais, 'mes': mes, 'anyo': anyo, 'flujo': FLUJO_COMERCIAL_PRODUCCION_NACIONAL} |
---|
356 | |
---|
357 | nexo = ComercioExteriorNexo(parametros) |
---|
358 | |
---|
359 | columnas = [] |
---|
360 | indice = 0 |
---|
361 | |
---|
362 | validador = ValidadorColumna(EXPR_REG_CODIGO_ARANCELARIO, _('error_campo_no_entero')) |
---|
363 | columnas.append(ColumnaCSV(indice, 'codigo_armonizado', validador)) |
---|
364 | |
---|
365 | indice += 1 |
---|
366 | validador = ValidadorColumna(EXPR_REG_CIIU, _('error_campo_no_entero')) |
---|
367 | columnas.append(ColumnaCSV(indice, 'ciiu', validador)) |
---|
368 | |
---|
369 | indice += 1 |
---|
370 | validador = ValidadorColumna(EXPR_REG_STRING_150, _('error_longitud_string')) |
---|
371 | columnas.append(ColumnaCSV(indice, 'identificacion_tributaria', validador)) |
---|
372 | |
---|
373 | indice += 1 |
---|
374 | validador = ValidadorColumna(EXPR_REG_STRING_150, _('error_longitud_string')) |
---|
375 | columnas.append(ColumnaCSV(indice, 'nombre_unidad_economica', validador)) |
---|
376 | |
---|
377 | indice += 1 |
---|
378 | validador = ValidadorColumna(EXPR_REG_STRING_150, _('error_longitud_string')) |
---|
379 | columnas.append(ColumnaCSV(indice, 'unidad_medida', validador)) |
---|
380 | |
---|
381 | indice += 1 |
---|
382 | validador = ValidadorColumna(EXPR_REG_ENTERO, _('error_campo_no_entero')) |
---|
383 | columnas.append(ColumnaCSV(indice, 'cantidad', validador)) |
---|
384 | |
---|
385 | indice += 1 |
---|
386 | validador = ValidadorColumna(EXPR_REG_DECIMAL, _('error_campo_no_decimal')) |
---|
387 | columnas.append(ColumnaCSV(indice, 'monto', validador)) |
---|
388 | |
---|
389 | csv_flujo = FlujoCSV() |
---|
390 | try: |
---|
391 | csv_flujo.cargar_fichero(archivo, columnas, nexo) |
---|
392 | except Exception: |
---|
393 | logger.critical('Error cargando el archivo') |
---|
394 | csv_flujo.errores_csv.append(_('error_cargando_archivo')) |
---|
395 | |
---|
396 | variables['errores_csv'] = csv_flujo.errores_csv |
---|
397 | enviar_email(usuario.email, plantilla, _('titulo_correo_produccion_nacional'), variables) |
---|
398 | |
---|
399 | return None |
---|
400 | finally: |
---|
401 | if len(csv_flujo.errores_csv) > 0: |
---|
402 | for error in csv_flujo.errores_csv: |
---|
403 | error_csv = ErrorCSV() |
---|
404 | error_csv.carga_masiva = carga_masiva |
---|
405 | error_csv.error = error |
---|
406 | |
---|
407 | error_csv.save() |
---|
408 | |
---|
409 | carga_masiva.errores_csv = True |
---|
410 | |
---|
411 | variables['errores_csv'] = csv_flujo.errores_csv |
---|
412 | else: |
---|
413 | carga_masiva.errores_csv = False |
---|
414 | |
---|
415 | carga_masiva.procesando = False |
---|
416 | carga_masiva.save() |
---|
417 | |
---|
418 | enviar_email(usuario.email, plantilla, _('titulo_correo_produccion_nacional'), variables) |
---|
419 | return 0 |
---|