Browse Source

Fix #54

pull/66/head
Sesostris Vieira 4 years ago
parent
commit
781dcc3bbd
  1. 6
      sigi/apps/casas/forms.py
  2. 72
      sigi/apps/casas/templates/casas/importar.html
  3. 18
      sigi/apps/casas/templates/casas/importar_result.html
  4. 4
      sigi/apps/casas/urls.py
  5. 236
      sigi/apps/casas/views.py
  6. 1
      templates/admin/base_site.html

6
sigi/apps/casas/forms.py

@ -6,6 +6,12 @@ from localflavor.br.forms import BRZipCodeField
from sigi.apps.casas.models import Orgao from sigi.apps.casas.models import Orgao
from sigi.apps.servidores.models import Servidor from sigi.apps.servidores.models import Servidor
class AtualizaCasaForm(forms.Form):
arquivo = forms.FileField(
required=True,
label=_(u"arquivo a importar"),
help_text=_(u"Envie um arquivo no formato CSV"),
)
class OrgaoForm(forms.ModelForm): class OrgaoForm(forms.ModelForm):
# cnpj = BRCNPJField( # cnpj = BRCNPJField(

72
sigi/apps/casas/templates/casas/importar.html

@ -0,0 +1,72 @@
{% extends "admin/base_site.html" %}
{% load i18n %}
{% block content_title %}
<h1 class="pull-left">{% trans 'Importar dados para atualização de órgãos' %}</h1>
{% endblock %}
{% block content %}
{% if error %}
<div class="alert alert-danger" role="alert">
{{ error }}
</div>
{% endif %}
<div id="content-main">
<form action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
<div class="form-group">
{{ form }}
</div>
<input type="submit" value="Importar" class="btn btn-primary"/>
</form>
<div id="data-hints">
<h3>Padrões do arquivo:</h3>
<ul class="list-group">
<li class="list-group-item">O arquivo deve ter o formato de texto, no padrão CSV</li>
<li class="list-group-item">Deve-se usar pt-br.UTF-8 como codificação do arquivo</li>
<li class="list-group-item">A primeira linha do arquivo teve conter o cabeçalho das colunas, conforme explicado a seguir</li>
<li class="list-group-item">Cada linha subsequente deve referir-se a um e somente um órgão</li>
<li class="list-group-item">Campos deixados em branco serão ignorados</li>
</ul>
<hr/>
<h3>Colunas do arquivo:</h3>
<ul class="list-group">
<li class="list-group-item">O arquivo deve possuir no mínimo as colunas marcadas como obrigatórias.
A primeira linha deve conter os cabeçalhos, que devem ser grafados
EXATAMENTE como descrito na tabela abaixo (minúsculas, sem acentos, sem espaços).</li>
<li class="list-group-item">É desejável que as colunas estejam na mesma ordem definida aqui.</li>
<li class="list-group-item">Colunas adicionais podem estar presentes, e serão ignoradas</li>
</ul>
<table class="table table-striped table-condensed">
<thead class="thead-light">
<tr>
<th>Obrigatório</th>
<th>Título do campo</th>
<th>Conteúdo esperado</th>
</tr>
</thead>
<tbody>
<tr><th>*</th><th>tipo</th><td>Deve conter CM para Câmara Municipal ou AL para Assembleia Legislativa</td></tr>
<tr><th>*</th><th>municipio</th><td>O nome do município. Não use abreviações!!!</td></tr>
<tr><th>*</th><th>uf</th><td>A sigla da Unidade da Federação (Estado) em letras maiúsculas (ex: MG, SP, PA)</td></tr>
<tr><th></th><th>orgao_endereco</th><td>O novo endereço do órgão.</td></tr>
<tr><th></th><th>orgao_bairro</th><td>O novo bairro do órgão</td></tr>
<tr><th></th><th>orgao_cep</th><td>O novo CEP do órgão</td></tr>
<tr><th></th><th>orgao_email</th><td>O novo e-mail institucional do órgão</td></tr>
<tr><th></th><th>orgao_portal</th><td>O novo endereço do portal institucional do órgão</td></tr>
<tr><th></th><th>orgao_telefones</th><td>Os telefones do órgão. Pode conter quantos telefones forem necessários, separando-os por espaço. Use a formatação padrão de telefones, mas não use espaço entre os dígitos de um telefone, senão o sistema interpretará como dois telefones diferentes. </td></tr>
<tr><th></th><th>presidente_nome</th><td>O nome completo do presidente</td></tr>
<tr><th></th><th>presidente_data_nascimento</th><td>A data de nascimento do presidente no formato DD/MM/AAAA</td></tr>
<tr><th></th><th>presidente_telefones</th><td>Os telefones ou whatsapp do presidente. Pode conter quantos telefones forem necessários, separando-os por espaço. Use a formatação padrão de telefones, mas não use espaço entre os dígitos de um telefone, senão o sistema interpretará como dois telefones diferentes.</td></tr>
<tr><th></th><th>presidente_emails</th><td>Os e-mails do presidente. Pode conter quantos e-mails forem necessários. Utilize espaço para separar um e-mail do outro</td></tr>
<tr><th></th><th>presidente_endereco</th><td>O novo endereço do presidente</td></tr>
<tr><th></th><th>presidente_municipio</th><td>O nome do município onde mora o presidente</td></tr>
<tr><th></th><th>presidente_bairro</th><td>O nome do bairro onde mora o presidente</td></tr>
<tr><th></th><th>presidente_cep</th><td>O novo CEP do presidente</td></tr>
<tr><th></th><th>presidente_redes_sociais</th><td>As redes sociais do presidente. Pode conter quantas redes sociais forem necessárias. Utilize espaço para separar uma rede da outra.</td></tr>
<tbody>
</table>
</div>
</div>
{% endblock %}

18
sigi/apps/casas/templates/casas/importar_result.html

@ -0,0 +1,18 @@
{% extends "admin/base_site.html" %}
{% load i18n %}
{% block content_title %}
<h1 class="pull-left">Importação de órgãos concluída com {% if com_erros %}erros{% else %}sucesso!{% endif %}</h1>
{% endblock %}
{% block content %}
<div id="content-main">
<p>{{ total }} registros importados do arquivo {{ file_name }}</p>
{% if com_erros %}
<p>{{ com_erros }} registros apresentaram erros</p>
<p><a href="{{ MEDIA_URL }}temp/{{ result_file }}">Download do arquivo de erros</a></p>
{% endif %}
<p><a href="{% url 'importar-casas' %}">Importar outro arquivo</a></p>
</div>
{% endblock %}

4
sigi/apps/casas/urls.py

@ -1,5 +1,6 @@
# coding: utf-8 # coding: utf-8
from django.conf.urls import patterns, url from django.conf.urls import patterns, url
from sigi.apps.casas.views import importa_casas
urlpatterns = patterns( urlpatterns = patterns(
@ -35,4 +36,7 @@ urlpatterns = patterns(
url(r'^orgao/carrinho/deleta_itens_carrinho$', 'deleta_itens_carrinho', name='deleta-itens-carrinho'), # Error url(r'^orgao/carrinho/deleta_itens_carrinho$', 'deleta_itens_carrinho', name='deleta-itens-carrinho'), # Error
url(r'^portfolio/$', 'portfolio', name='casas-portfolio'), url(r'^portfolio/$', 'portfolio', name='casas-portfolio'),
url(r'^carteira/$', 'painel_relacionamento', name='casas-carteira'), url(r'^carteira/$', 'painel_relacionamento', name='casas-carteira'),
# Atualização por CSV
url(r'^orgao/importa/$', importa_casas.as_view(), name='importar-casas'),
) )

236
sigi/apps/casas/views.py

@ -1,26 +1,254 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import csv import csv
from datetime import datetime
from functools import reduce from functools import reduce
from geraldo.generators import PDFGenerator from geraldo.generators import PDFGenerator
from django.conf import settings
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.core.paginator import Paginator, InvalidPage, EmptyPage from django.core.paginator import Paginator, InvalidPage, EmptyPage
from django.db.models import Count, Q from django.db.models import Count, Q
from django.http import HttpResponse, HttpResponseRedirect from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidden
from django.shortcuts import render, get_object_or_404 from django.shortcuts import render, get_object_or_404
from django.utils.translation import ugettext as _, ungettext from django.utils.translation import ugettext as _, ungettext
from django.views.generic import View
from sigi.apps.casas.forms import PortfolioForm from sigi.apps.casas.forms import PortfolioForm, AtualizaCasaForm
from sigi.apps.casas.models import Orgao, TipoOrgao from sigi.apps.casas.models import Orgao, TipoOrgao, Funcionario
from sigi.apps.casas.reports import (CasasLegislativasLabels, from sigi.apps.casas.reports import (CasasLegislativasLabels,
CasasLegislativasLabelsSemPresidente) CasasLegislativasLabelsSemPresidente)
from sigi.apps.contatos.models import UnidadeFederativa, Mesorregiao, Microrregiao from sigi.apps.contatos.models import (UnidadeFederativa, Municipio,
Mesorregiao, Microrregiao)
from sigi.apps.ocorrencias.models import Ocorrencia from sigi.apps.ocorrencias.models import Ocorrencia
from sigi.apps.parlamentares.reports import ParlamentaresLabels from sigi.apps.parlamentares.reports import ParlamentaresLabels
from sigi.apps.servicos.models import TipoServico from sigi.apps.servicos.models import TipoServico
from sigi.apps.servidores.models import Servidor from sigi.apps.servidores.models import Servidor
from sigi.shortcuts import render_to_pdf from sigi.shortcuts import render_to_pdf
class importa_casas(View):
errors = []
total_registros = 0
TIPO = 'tipo'
MUNICIPIO = 'municipio'
UF = 'uf'
ORGAO_ENDERECO = 'orgao_endereco'
ORGAO_BAIRRO = 'orgao_bairro'
ORGAO_CEP = 'orgao_cep'
ORGAO_EMAIL = 'orgao_email'
ORGAO_PORTAL = 'orgao_portal'
ORGAO_TELEFONES = 'orgao_telefones'
PRESIDENTE_NOME = 'presidente_nome'
PRESIDENTE_DATA_NASCIMENTO = 'presidente_data_nascimento'
PRESIDENTE_TELEFONES = 'presidente_telefones'
PRESIDENTE_EMAILS = 'presidente_emails'
PRESIDENTE_ENDERECO = 'presidente_endereco'
PRESIDENTE_MUNICIPIO = 'presidente_municipio'
PRESIDENTE_BAIRRO = 'presidente_bairro'
PRESIDENTE_CEP = 'presidente_cep'
PRESIDENTE_REDES_SOCIAIS = 'presidente_redes_sociais'
ERROS = 'erros_importacao'
fieldnames = [TIPO, MUNICIPIO, UF, ORGAO_ENDERECO, ORGAO_BAIRRO, ORGAO_CEP,
ORGAO_EMAIL, ORGAO_PORTAL, ORGAO_TELEFONES, PRESIDENTE_NOME,
PRESIDENTE_DATA_NASCIMENTO, PRESIDENTE_TELEFONES,
PRESIDENTE_EMAILS, PRESIDENTE_ENDERECO, PRESIDENTE_MUNICIPIO,
PRESIDENTE_BAIRRO, PRESIDENTE_CEP, PRESIDENTE_REDES_SOCIAIS,
ERROS,]
ID_FIELDS = {TIPO, MUNICIPIO, UF}
ORGAO_FIELDS = {
ORGAO_ENDERECO: 'logradouro',
ORGAO_BAIRRO: 'bairro',
ORGAO_CEP: 'cep',
ORGAO_EMAIL: 'email',
ORGAO_PORTAL: 'pagina_web',
ORGAO_TELEFONES: 'telefones',
}
PRESIDENTE_FIELDS = {
PRESIDENTE_NOME: 'nome',
PRESIDENTE_DATA_NASCIMENTO: 'data_nascimento',
PRESIDENTE_TELEFONES: 'nota',
PRESIDENTE_EMAILS: 'email',
PRESIDENTE_ENDERECO: 'endereco',
PRESIDENTE_MUNICIPIO: 'municipio_id',
PRESIDENTE_BAIRRO: 'bairro',
PRESIDENTE_CEP: 'cep',
PRESIDENTE_REDES_SOCIAIS: 'redes_sociais',
}
def get(self, request):
form = AtualizaCasaForm()
return render(request, 'casas/importar.html', {'form': form})
def post(self, request):
form = AtualizaCasaForm(request.POST, request.FILES)
if form.is_valid():
file = form.cleaned_data['arquivo']
reader = csv.DictReader(file)
if not self.ID_FIELDS.issubset(reader.fieldnames):
return render(
request,
'casas/importar.html',
{'form': form, 'error': _(u"O arquivo não possui algum dos "
u"campos obrigatórios")}
)
if self.importa(reader):
# Importação concluída com êxito
return render(
request,
'casas/importar_result.html',
{'file_name': file.name, 'total': self.total_registros,
'com_erros': 0}
)
else:
# Importado com erros
file_name = "casas-erros-{:%Y-%m-%d-%H%M}.csv".format(
datetime.now())
fields = self.fieldnames
for f in reader.fieldnames:
if f not in fields:
fields.append(f)
with open(settings.MEDIA_ROOT+'/temp/'+file_name, "w+") as f:
writer = csv.DictWriter(f, fieldnames=fields)
writer.writeheader()
writer.writerows(self.errors)
return render(
request,
'casas/importar_result.html',
{'file_name': file.name, 'result_file': file_name,
'total': self.total_registros,
'com_erros': len(self.errors)}
)
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = (
'attachment; filename="somefilename.csv"')
return response
else:
return render(
request,
'casas/importar.html',
{'form': form, 'error': u"Erro no preenchimento do formulário."}
)
def importa(self, reader):
self.errors = []
self.total_registros = 0
for reg in reader:
self.total_registros += 1
reg[self.ERROS] = []
orgao = Orgao.objects.filter(
tipo__sigla=reg[self.TIPO],
municipio__nome=reg[self.MUNICIPIO],
municipio__uf__sigla=reg[self.UF]
)
if orgao.count() == 0:
reg[self.ERROS].append("Nao existe orgao com esta identificacao")
self.errors.append(reg)
continue
elif orgao.count() > 1:
reg[self.ERROS].append("Existem {count} orgaos com esta mesma "
"identificacao").format(count=orgao.count())
self.errors.append(reg)
continue
else:
orgao = orgao.get()
# Atualiza os dados do órgão
for key in self.ORGAO_FIELDS:
field_name = self.ORGAO_FIELDS[key]
if key in reg:
value = reg[key].strip()
if key == self.ORGAO_TELEFONES:
for numero in value.split(" "):
try:
orgao.telefones.update_or_create(numero=numero)
except:
reg[self.ERROS].append(
'Telefone {numero} não foi '
'atualizado'.format(numero=numero)
)
elif value != "" and value != getattr(orgao, field_name):
setattr(orgao, field_name, value)
try:
orgao.save()
except Exception as e:
reg[self.ERROS].append(
"Erro salvando o orgao: '{message}'".format(
message=e.message)
)
# Atualiza o presidente
presidente = orgao.presidente
if presidente is None:
presidente = Funcionario(
casa_legislativa=orgao,
setor="presidente"
)
for key in self.PRESIDENTE_FIELDS:
field_name = self.PRESIDENTE_FIELDS[key]
if key in reg:
value = reg[key].strip()
else:
value = ""
if value != "":
if key == self.PRESIDENTE_MUNICIPIO:
if ',' in value:
municipio, uf = value.split(',')
else:
municipio = value
uf = reg[self.UF]
try:
value = Municipio.objects.get(
nome__iexact=municipio.strip(),
uf__sigla=uf.strip()).pk
except:
value = None
reg[self.ERROS].append(
"Impossivel identificar o Municipio de "
"residencia do Presidente"
)
continue
if key == self.PRESIDENTE_REDES_SOCIAIS:
value = value.replace(" ", "\r")
if key == self.PRESIDENTE_DATA_NASCIMENTO:
sd = value.split('/')
if len(sd) < 3:
reg[self.ERROS].append(
"Data de nascimento do presidente esta em um "
"formato nao reconhecido. Use DD/MM/AAAA"
)
continue
else:
value = "{ano}-{mes}-{dia}".format(
ano=sd[2],
mes=sd[1],
dia=sd[0]
)
if value != getattr(presidente, field_name):
setattr(presidente, field_name, value)
try:
presidente.save()
except Exception as e:
reg[self.ERROS].append(
"Erro salvando presidente: '{message}'".format(
message=e.message)
)
if len(reg[self.ERROS]) > 0:
self.errors.append(reg)
return len(self.errors) == 0
# @param qs: queryset # @param qs: queryset
# @param o: (int) number of order field # @param o: (int) number of order field

1
templates/admin/base_site.html

@ -40,6 +40,7 @@
<li><a href="/auth/">{% trans 'Usuários' %} &amp; {% trans 'Grupos' %}</a></li> <li><a href="/auth/">{% trans 'Usuários' %} &amp; {% trans 'Grupos' %}</a></li>
<li><a href="/sites/site/">{% trans 'Sites' %}</a></li> <li><a href="/sites/site/">{% trans 'Sites' %}</a></li>
<li><a href="/diagnosticos/">{% trans 'Diagnósticos' %}</a></li> <li><a href="/diagnosticos/">{% trans 'Diagnósticos' %}</a></li>
<li><a href="{% url 'importar-casas' %}">{% trans 'Importar dados de Casas' %}</a></li>
</ul> </ul>
</li> </li>
{% endif %} {% endif %}

Loading…
Cancel
Save