diff --git a/sigi/apps/convenios/admin.py b/sigi/apps/convenios/admin.py
index dcaa156..2f5435f 100644
--- a/sigi/apps/convenios/admin.py
+++ b/sigi/apps/convenios/admin.py
@@ -6,7 +6,8 @@ from geraldo.generators import PDFGenerator
from sigi.apps.convenios.models import (Projeto, StatusConvenio,
TipoSolicitacao, Convenio,
- EquipamentoPrevisto, Anexo, Tramitacao)
+ EquipamentoPrevisto, Anexo, Tramitacao,
+ Gescon)
from sigi.apps.convenios.reports import ConvenioReport
from sigi.apps.convenios.views import adicionar_convenios_carrinho
from sigi.apps.utils import queryset_ascii
@@ -170,8 +171,13 @@ class EquipamentoPrevistoAdmin(BaseModelAdmin):
search_fields = ('convenio__id', 'equipamento__fabricante__nome',
'equipamento__modelo__modelo', 'equipamento__modelo__tipo__tipo')
+@admin.register(Gescon)
+class GesconAdmin(admin.ModelAdmin):
+ list_display = ('url_gescon', 'email', 'ultima_importacao')
+ readonly_fields = ('ultima_importacao',)
+
admin.site.register(Projeto)
admin.site.register(StatusConvenio)
admin.site.register(TipoSolicitacao)
admin.site.register(Convenio, ConvenioAdmin)
-admin.site.register(EquipamentoPrevisto, EquipamentoPrevistoAdmin)
+admin.site.register(EquipamentoPrevisto, EquipamentoPrevistoAdmin)
\ No newline at end of file
diff --git a/sigi/apps/convenios/migrations/0014_gescon.py b/sigi/apps/convenios/migrations/0014_gescon.py
new file mode 100644
index 0000000..7fe216c
--- /dev/null
+++ b/sigi/apps/convenios/migrations/0014_gescon.py
@@ -0,0 +1,30 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import models, migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('convenios', '0013_remove_convenio_duracao'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='Gescon',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('url_gescon', models.URLField(default='https://adm.senado.gov.br/gestao-contratos/api/contratos/busca?especie={s}', help_text='Informe o ponto de consulta do webservice do Gescon, inclusive com a querystring. No ponto onde deve ser inserida a sigla da subespecie do contrato, use a marca\xe7\xe3o {s}.
Por exemplo: https://adm.senado.gov.br/gestao-contratos/api/contratos/busca?especie={s}', verbose_name='Webservice Gescon')),
+ ('subespecies', models.TextField(default='AC=ACT\nPI=PI\nCN=PML\nTA=PML', help_text='Informe as siglas das subesp\xe9cies de contratos que devem ser pesquisados no Gescon com a sigla correspondente do projeto no SIGI. Coloque um par de siglas por linha, no formato SIGLA_GESTON=SIGLA_SIGI. As siglas n\xe3o encontradas ser\xe3o ignoradas.', verbose_name='Subesp\xe9cies')),
+ ('palavras', models.TextField(default='ILB\nINTERLEGIS', help_text='Palavras que devem aparecer no campo OBJETO dos dados do Gescon para identificar se o contrato pertence ao ILB.
- Informe uma palavra por linha.
- Ocorrendo qualquer uma das palavras, o contrato ser\xe1 importado.
', verbose_name='Palavras de filtro')),
+ ('email', models.EmailField(help_text='Caixa de e-mail para onde o relat\xf3rio di\xe1rio de importa\xe7\xe3o ser\xe1 enviado.', max_length=75, verbose_name='E-mail')),
+ ('ultima_importacao', models.TextField(verbose_name='Resultado da \xfaltima importa\xe7\xe3o', blank=True)),
+ ],
+ options={
+ 'verbose_name': 'Configura\xe7\xe3o do Gescon',
+ 'verbose_name_plural': 'Configura\xe7\xf5es do Gescon',
+ },
+ bases=(models.Model,),
+ ),
+ ]
diff --git a/sigi/apps/convenios/models.py b/sigi/apps/convenios/models.py
index 59f00fa..4982a5f 100644
--- a/sigi/apps/convenios/models.py
+++ b/sigi/apps/convenios/models.py
@@ -1,9 +1,14 @@
#-*- coding: utf-8 -*-
import re
+import requests
from datetime import datetime, date
from django.db import models
+from django.db.models import Q
+from django.core.mail import send_mail
+from django.core.urlresolvers import reverse
from django.utils.translation import ugettext as _
-from sigi.apps.utils import SearchField
+from sigi.apps.utils import SearchField, to_ascii
+from sigi.apps.casas.models import Orgao
from sigi.apps.servidores.models import Servidor, Servico
class Projeto(models.Model):
@@ -360,3 +365,383 @@ class Tramitacao(models.Model):
if self.observacao:
result = result + u" (%s)" % (self.observacao)
return unicode(result) # XXX is this unicode(...) really necessary???
+
+class Gescon(models.Model):
+ url_gescon = models.URLField(
+ _(u"Webservice Gescon"),
+ default=(u"https://adm.senado.gov.br/gestao-contratos/api/contratos"
+ u"/busca?especie={s}"),
+ help_text=_(u"Informe o ponto de consulta do webservice do Gescon, "
+ u"inclusive com a querystring. No ponto onde deve ser "
+ u"inserida a sigla da subespecie do contrato, use a "
+ u"marcação {s}.
Por exemplo: "
+ u"https://adm.senado.gov.br/gestao-contratos/api/contratos"
+ u"/busca?especie={s}")
+ )
+ subespecies = models.TextField(
+ _(u"Subespécies"),
+ default=u"AC=ACT\nPI=PI\nCN=PML\nTA=PML",
+ help_text=_(u"Informe as siglas das subespécies de contratos que "
+ u"devem ser pesquisados no Gescon com a sigla "
+ u"correspondente do projeto no SIGI. Coloque um par de "
+ u"siglas por linha, no formato SIGLA_GESTON=SIGLA_SIGI. "
+ u"As siglas não encontradas serão ignoradas.")
+ )
+ palavras = models.TextField(
+ _(u"Palavras de filtro"),
+ default=u"ILB\nINTERLEGIS",
+ help_text=_(u"Palavras que devem aparecer no campo OBJETO dos dados do "
+ u"Gescon para identificar se o contrato pertence ao ILB. "
+ u"- Informe uma palavra por linha.
"
+ u"- Ocorrendo qualquer uma das palavras, o contrato será "
+ u"importado.
")
+ )
+ email = models.EmailField(
+ _(u"E-mail"),
+ help_text=_(u"Caixa de e-mail para onde o relatório diário de "
+ u"importação será enviado.")
+ )
+ ultima_importacao = models.TextField(
+ _(u"Resultado da última importação"),
+ blank=True
+ )
+
+ class Meta:
+ verbose_name = _(u"Configuração do Gescon")
+ verbose_name_plural = _(u"Configurações do Gescon")
+
+ def __unicode__(self):
+ return self.url_gescon
+
+ def save(self, *args, **kwargs):
+ self.pk = 1 # Highlander (singleton pattern)
+ return super(Gescon, self).save(*args, **kwargs)
+
+ def delete(self, *args, **kwargs):
+ pass # Highlander is immortal
+
+ def add_message(self, msg, save=False):
+ self.ultima_importacao += msg + "\n"
+ if save:
+ self.save()
+ self.email_report()
+
+ def email_report(self):
+ if self.email:
+ send_mail(
+ subject=_(u"Relatório de importação GESCON"),
+ message=self.ultima_importacao,
+ recipient_list=self.email,
+ fail_silently=True
+ )
+ else:
+ self.ultima_importacao += _(
+ u"\n\n*Não foi definida uma caixa de e-mail nas configurações "
+ u"do Gescon*"
+ )
+ self.save()
+
+ def importa_contratos(self):
+ self.ultima_importacao = ""
+ self.add_message(
+ _(u"Importação iniciada em {:%d/%m/%Y %H:%M:%S}\n"
+ u"==========================================\n").format(
+ datetime.now()
+ )
+ )
+
+ if self.palavras == "":
+ self.add_message(_(u"Nenhuma palavra de pesquisa definida - "
+ u"processo abortado."), True)
+ return
+
+ if self.subespecies == "":
+ self.add_message(_(u"Nenhuma subespécie definida - processo "
+ u"abortado."), True)
+ return
+
+ if "{s}" not in self.url_gescon:
+ self.add_message(
+ _(
+ u"Falta a marcação {s} na URL para indicar o local onde "
+ u"inserir a sigla da subespécia na consulta ao webservice "
+ u"- processo abortado."
+ ),
+ True
+ )
+ return
+
+ palavras = self.palavras.split()
+ subespecies = {tuple(s.split("=")) for s in self.subespecies.split()}
+
+ for sigla_gescon, sigla_sigi in subespecies:
+ self.add_message(_(u"\nImportando subespécie {s}".format(
+ s=sigla_gescon)))
+ url = self.url_gescon.format(s=sigla_gescon)
+
+ projeto = Projeto.objects.get(sigla=sigla_sigi)
+
+ try:
+ response = requests.get(url)
+ except Exception as e:
+ self.add_message(
+ _(u"\tErro ao acessar {url}: {errmsg}").format(
+ url=url,
+ errmsg=str(e)
+ )
+ )
+ continue
+
+ if not response.ok:
+ self.add_message(
+ _(u"\tErro ao acessar {url}: {reason}").format(
+ url=url,
+ reason=response.reason
+ )
+ )
+ continue
+
+ if not 'application/json' in response.headers.get('Content-Type'):
+ self.add_message(_(u"\tResultado da consulta à {url} não "
+ u"retornou dados em formato json").format(
+ url=url
+ )
+ )
+ continue
+
+ contratos = response.json()
+
+ # Pegar só os contratos que possuem alguma das palavras-chave
+
+ nossos = [c for c in contratos
+ if any(palavra in c['objeto'] for palavra in palavras)]
+
+ self.add_message(
+ _(u"\t{count} contratos encontrados no Gescon").format(
+ count=len(nossos)
+ )
+ )
+
+ novos = 0
+ erros = 0
+ verificados = 0
+ atualizados = 0
+
+ for contrato in nossos:
+ numero = contrato['numero'].zfill(8)
+ numero = "{}/{}".format(numero[:4], numero[4:])
+ sigad = contrato['processo'].zfill(17)
+ sigad = "{}.{}/{}-{}".format(sigad[:5], sigad[5:11],
+ sigad[11:15], sigad[15:])
+
+
+ if contrato['cnpjCpfFornecedor']:
+ cnpj = contrato['cnpjCpfFornecedor'].zfill(14)
+ cnpj = "{}.{}.{}/{}-{}".format(cnpj[:2], cnpj[2:5],
+ cnpj[5:8], cnpj[8:12],
+ cnpj[12:])
+ else:
+ cnpj = None
+
+ if contrato['nomeFornecedor']:
+ nome = contrato['nomeFornecedor']
+ nome = nome.replace(u'VEREADORES DE', '')
+ nome = nome.split('-')[0]
+ nome = nome.split('/')[0]
+ nome = nome.strip()
+ nome = nome.replace(" ", " ")
+ nome = to_ascii(nome)
+ else:
+ nome = None
+
+ if (cnpj is None) and (nome is None):
+ self.add_message(
+ _(u"\tO contrato {numero} no Gescon não informa o CNPJ "
+ u"nem o nome do órgão.").format(numero=numero)
+ )
+ erros += 1
+ continue
+
+ orgao = None
+
+ if cnpj is not None:
+ try:
+ orgao = Orgao.objects.get(cnpj=cnpj)
+ except (
+ Orgao.DoesNotExist,
+ Orgao.MultipleObjectsReturned) as e:
+ orgao = None
+ pass
+
+ if (orgao is None) and (nome is not None):
+ try:
+ orgao = Orgao.objects.get(search_text__iexact=nome)
+ except (
+ Orgao.DoesNotExist,
+ Orgao.MultipleObjectsReturned) as e:
+ orgao = None
+ pass
+
+ if orgao is None:
+ self.add_message(
+ _(u"\tÓrgão não encontrado no SIGI ou mais de um órgão"
+ u"encontrado com o mesmo CNPJ ou nome. Favor "
+ u"regularizar o cadastro: CNPJ: {cnpj}, "
+ u"Nome: {nome}".format(
+ cnpj=contrato['cnpjCpfFornecedor'],
+ nome=contrato['nomeFornecedor']
+ )
+ )
+ )
+ erros += 1
+ continue
+
+ # O mais seguro é o NUP sigad
+ convenios = Convenio.objects.filter(num_processo_sf=sigad)
+ chk = convenios.count()
+
+ if chk == 0:
+ # NUP não encontrado, talvez exista apenas com o número
+ # do GESCON
+ convenios = Convenio.objects.filter(
+ Q(num_convenio=numero) |
+ Q(num_processo_sf=numero)
+ )
+ chk = convenios.count()
+
+ if chk == 0:
+ convenio = Convenio(
+ casa_legislativa=orgao,
+ projeto=projeto,
+ num_processo_sf=sigad,
+ num_convenio=numero,
+ data_sigi=date.today(),
+ data_sigad=contrato['assinatura'],
+ observacao=contrato['objeto'],
+ data_retorno_assinatura=contrato['inicioVigencia'],
+ data_termino_vigencia=contrato['terminoVigencia'],
+ data_pub_diario=contrato['publicacao']
+ )
+ convenio.save()
+ novos += 1
+ elif chk == 1:
+ convenio = convenios.get()
+ if convenio.casa_legislativa != orgao:
+ self.add_message(
+ _(u"\tO órgao no convênio {url} diverge do que "
+ u"consta no Gescon ({cnpj}, {nome})").format(
+ url=reverse('admin:%s_%s_change' % (
+ convenio._meta.app_label,
+ convenio._meta.model_name),
+ args=[convenio.id]),
+ cnpj=cnpj,
+ nome=contrato['nomeFornecedor']
+ )
+ )
+ erros += 1
+ continue
+
+ if convenio.num_processo_sf != sigad:
+ sigi_nums = filter(
+ type(convenio.num_processo_sf).isdigit,
+ convenio.num_processo_sf
+ ).zfill(17)
+ gesc_nums = filter(type(sigad).isdigit, sigad).zfill(17)
+ if ( sigi_nums == gesc_nums or
+ convenio.num_processo_sf == ""):
+ # Número SIGAD incorreto no SIGI, podemos corrigir
+ convenio.num_processso_sf = sigad
+ convenio.save()
+ else:
+ self.add_message(
+ _(u"\tO contrato Gescon nº {numero} corresponde"
+ u" ao convênio SIGI {url}, mas o NUP sigad "
+ u"diverge (Gescon: {sigad_gescon}, "
+ u"SIGI: {sigad_sigi})").format(
+ numero=numero,
+ url=reverse('admin:%s_%s_change' % (
+ convenio._meta.app_label,
+ convenio._meta.model_name),
+ args=[convenio.id]),
+ sigad_gescon=sigad,
+ sigad_sigi=convenio.num_processo_sf
+ )
+ )
+ erros += 1
+ continue
+
+ if convenio.num_convenio != numero:
+ sigi_nums = filter(type(convenio.num_convenio).isdigit,
+ convenio.num_convenio).zfill(8)
+ gesc_nums = filter(type(numero).isdigit,
+ numero).zfill(8)
+ if (sigi_nums == gesc_nums or
+ convenio.num_convenio == ""):
+ # Número gescon errado no SIGI mas podemos corrigir
+ convenio.num_convenio = numero
+ else:
+ self.add_message(
+ _(u"\tO contrato Gescon ID {id} corresponde ao "
+ u"convênio SIGI {url}, mas o número do convênio"
+ u" diverge (Gescon: {numero_gescon}, SIGI: "
+ u"{numero_sigi})").format(
+ id=contrato['id'],
+ url=reverse('admin:%s_%s_change' % (
+ convenio._meta.app_label,
+ convenio._meta.model_name),
+ args=[convenio.id]
+ ),
+ numero_gescon=numero,
+ numero_sigi=convenio.num_convenio
+ )
+ )
+ erros += 1
+ continue
+
+ if contrato['objeto'] not in convenio.observacao:
+ convenio.observacao += "\n" + contrato['objeto']
+
+ convenio.data_sigad=contrato['assinatura']
+ convenio.data_retorno_assinatura=contrato['inicioVigencia']
+ convenio.data_termino_vigencia=contrato['terminoVigencia']
+ convenio.data_pub_diario=contrato['publicacao']
+
+ try:
+ convenio.save()
+ except Exception:
+ import ipdb; ipdb.set_trace()
+ print contrato
+ raise
+
+ atualizados += 1
+ verificados += 1
+ else:
+ self.add_message(_(u"\tExistem {count} convênios no SIGI "
+ u"que correspondem ao mesmo contrato no "
+ u"Gescon (contrato {numero}, sigad "
+ u"{sigad})").format(
+ count=chk,
+ numero=numero,
+ sigad=sigad
+ )
+ )
+ erros += 1
+ continue
+
+ self.add_message(
+ _(u"\t{novos} novos convenios adicionados ao SIGI, "
+ u"{atualizados} atualizados, {verificados} confirmados e "
+ u"{erros} reportados com erro.").format(
+ novos=novos,
+ atualizados=atualizados,
+ verificados=verificados,
+ erros=erros
+ )
+ )
+
+ self.save()
+
+ @classmethod
+ def load(cls):
+ obj, created = cls.objects.get_or_create(pk=1)
+ return obj
\ No newline at end of file
diff --git a/sigi/apps/convenios/templates/convenios/importar_gescon.html b/sigi/apps/convenios/templates/convenios/importar_gescon.html
new file mode 100644
index 0000000..49f068a
--- /dev/null
+++ b/sigi/apps/convenios/templates/convenios/importar_gescon.html
@@ -0,0 +1,18 @@
+{% extends 'admin/base_site.html' %}
+{% load i18n %}
+
+{% block content_title %}{% trans 'Importar dados do Gescon' %}
{% endblock %}
+{% block object-tools-items %}
+Importar
+Configurações
+{% endblock %}
+{% block content %}
+ {% if gescon.ultima_importacao %}
+ {{ gescon.ultima_importacao }}
+ {% else %}
+ {% blocktrans %}
+ Nenhuma importação anterior foi realizada!
+ Configure a conexão com o Gescon para realizar a primeira importação.
+ {% endblocktrans %}
+ {% endif %}
+{% endblock %}
\ No newline at end of file
diff --git a/sigi/apps/convenios/urls.py b/sigi/apps/convenios/urls.py
index d3b753a..1986ecc 100644
--- a/sigi/apps/convenios/urls.py
+++ b/sigi/apps/convenios/urls.py
@@ -11,4 +11,5 @@ urlpatterns = patterns(
url(r'^convenio/carrinho/deleta_itens_carrinho$', 'deleta_itens_carrinho', name='deleta-itens-carrinho'), # tagerror
url(r'^convenio/csv/$', 'export_csv', name='convenios-csv'),
url(r'^reportsRegiao/(?P\w+)/$', 'report_regiao', name='convenios-report_regiao_pdf'),
+ url(r'^importar/$', 'importar_gescon', name='importar-gescon'),
)
diff --git a/sigi/apps/convenios/views.py b/sigi/apps/convenios/views.py
index 6760828..61aa3bf 100644
--- a/sigi/apps/convenios/views.py
+++ b/sigi/apps/convenios/views.py
@@ -13,7 +13,7 @@ from geraldo.generators import PDFGenerator
from sigi.apps.casas.models import Orgao
from sigi.apps.contatos.models import UnidadeFederativa
-from sigi.apps.convenios.models import Convenio, Projeto
+from sigi.apps.convenios.models import Convenio, Gescon, Projeto
from sigi.apps.convenios.reports import (ConvenioReport,
ConvenioReportSemAceite,
ConvenioPorCMReport,
@@ -372,3 +372,12 @@ def export_csv(request):
csv_writer.writerow(lista)
return response
+
+def importar_gescon(request):
+ action = request.GET.get('action', "")
+ gescon = Gescon.load()
+
+ if action == 'importar':
+ gescon.importa_contratos()
+
+ return render(request, "convenios/importar_gescon.html", {'gescon': gescon})
\ No newline at end of file
diff --git a/templates/admin/base_site.html b/templates/admin/base_site.html
index 6f61a15..f7d3120 100644
--- a/templates/admin/base_site.html
+++ b/templates/admin/base_site.html
@@ -41,6 +41,7 @@
{% trans 'Sites' %}
{% trans 'Diagnósticos' %}
{% trans 'Importar dados de Casas' %}
+ {% trans 'Importar convênios do Gescon' %}
{% endif %}