Browse Source

Adicona parlamentares do TSE

pull/159/head
Sesostris Vieira 3 years ago
parent
commit
57d5a6cc38
  1. 225
      sigi/apps/casas/admin.py
  2. 16
      sigi/apps/casas/migrations/0024_delete_presidente.py
  3. 43
      sigi/apps/casas/models.py
  4. 10
      sigi/apps/casas/templates/admin/casas/anexo_convenio_snippet.html
  5. 75
      sigi/apps/casas/templates/admin/casas/orgao/change_form.html
  6. 10
      sigi/apps/casas/views.py
  7. 25
      sigi/apps/convenios/migrations/0025_alter_projeto_modelo_minuta_and_more.py
  8. 25
      sigi/apps/convenios/migrations/0026_alter_projeto_modelo_minuta_and_more.py
  9. 25
      sigi/apps/convenios/migrations/0027_alter_projeto_modelo_minuta_and_more.py
  10. 25
      sigi/apps/convenios/migrations/0028_alter_projeto_modelo_minuta_and_more.py
  11. 25
      sigi/apps/convenios/migrations/0029_alter_projeto_modelo_minuta_and_more.py
  12. 18
      sigi/apps/convenios/models.py
  13. 23
      sigi/apps/eventos/forms.py
  14. 23
      sigi/apps/eventos/static/css/convite.css
  15. 4
      sigi/apps/eventos/templates/eventos/alocacao_equipe.html
  16. 2
      sigi/apps/eventos/templates/eventos/calendario.html
  17. 194
      sigi/apps/eventos/templates/eventos/convida_casa.html
  18. 4
      sigi/apps/eventos/templates/eventos/oficio_padrao.html
  19. 11
      sigi/apps/eventos/templates/eventos/snippets/form_presidente_snippet.html
  20. 5
      sigi/apps/eventos/urls.py
  21. 69
      sigi/apps/eventos/views.py
  22. 2
      sigi/apps/ocorrencias/models.py
  23. 392
      sigi/apps/parlamentares/admin.py
  24. 7
      sigi/apps/parlamentares/apps.py
  25. 40
      sigi/apps/parlamentares/forms.py
  26. 4
      sigi/apps/parlamentares/jobs/__init__.py
  27. 0
      sigi/apps/parlamentares/jobs/daily/__init__.py
  28. 0
      sigi/apps/parlamentares/jobs/hourly/__init__.py
  29. 0
      sigi/apps/parlamentares/jobs/minutely/__init__.py
  30. 355
      sigi/apps/parlamentares/jobs/minutely/importa_parlamentar.py
  31. 0
      sigi/apps/parlamentares/jobs/monthly/__init__.py
  32. 9
      sigi/apps/parlamentares/jobs/sample.py
  33. 0
      sigi/apps/parlamentares/jobs/weekly/__init__.py
  34. 0
      sigi/apps/parlamentares/jobs/yearly/__init__.py
  35. 53
      sigi/apps/parlamentares/migrations/0001_initial.py
  36. 6
      sigi/apps/parlamentares/migrations/0002_auto_20210406_1945.py
  37. 98
      sigi/apps/parlamentares/migrations/0004_partido_legenda_alter_cargo_id_alter_coligacao_id_and_more.py
  38. 122
      sigi/apps/parlamentares/migrations/0005_remove_coligacao_legislatura_and_more.py
  39. 18
      sigi/apps/parlamentares/migrations/0006_alter_partido_sigla.py
  40. 17
      sigi/apps/parlamentares/migrations/0007_alter_parlamentar_options.py
  41. 18
      sigi/apps/parlamentares/migrations/0008_alter_parlamentar_foto.py
  42. 38
      sigi/apps/parlamentares/migrations/0009_parlamentar_ano_eleicao_parlamentar_flag_importa_and_more.py
  43. 17
      sigi/apps/parlamentares/migrations/0010_alter_parlamentar_options.py
  44. 245
      sigi/apps/parlamentares/models.py
  45. 11
      sigi/apps/parlamentares/templates/admin/parlamentares/parlamentar/cart/change_list_import_cart_export.html
  46. 1
      sigi/apps/parlamentares/templates/admin/parlamentares/parlamentar/change_list.html
  47. 94
      sigi/apps/parlamentares/templates/parlamentares/import.html
  48. 36
      sigi/apps/parlamentares/templates/parlamentares/import_email.html
  49. 37
      sigi/apps/parlamentares/templates/parlamentares/import_result.html
  50. 32
      sigi/apps/parlamentares/urls.py
  51. 154
      sigi/apps/parlamentares/views.py
  52. 8
      sigi/menu_conf.yaml
  53. 1
      sigi/settings.py
  54. 1
      sigi/urls.py

225
sigi/apps/casas/admin.py

@ -1,12 +1,13 @@
from django.contrib import admin from django.contrib import admin
from django.contrib.contenttypes.admin import GenericTabularInline from django.contrib.contenttypes.admin import GenericTabularInline
from django.template.loader import render_to_string
from django.urls import reverse from django.urls import reverse
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from django_weasyprint.views import WeasyTemplateResponse from django_weasyprint.views import WeasyTemplateResponse
from import_export.fields import Field from import_export.fields import Field
from sigi.apps.casas.forms import OrgaoForm from sigi.apps.casas.forms import OrgaoForm
from sigi.apps.casas.models import Orgao, Presidente, Funcionario, TipoOrgao from sigi.apps.casas.models import Orgao, Funcionario, TipoOrgao
from sigi.apps.casas.filters import ( from sigi.apps.casas.filters import (
GerentesInterlegisFilter, GerentesInterlegisFilter,
ConvenioFilter, ConvenioFilter,
@ -16,8 +17,10 @@ from sigi.apps.casas.filters import (
from sigi.apps.contatos.models import Telefone from sigi.apps.contatos.models import Telefone
from sigi.apps.convenios.models import Convenio from sigi.apps.convenios.models import Convenio
from sigi.apps.ocorrencias.models import Ocorrencia from sigi.apps.ocorrencias.models import Ocorrencia
from sigi.apps.parlamentares.models import Parlamentar
from sigi.apps.servicos.models import Servico from sigi.apps.servicos.models import Servico
from sigi.apps.servicos.filters import ServicoAtivoFilter from sigi.apps.servicos.filters import ServicoAtivoFilter
from sigi.apps.servidores.models import Servidor
from sigi.apps.utils import queryset_ascii from sigi.apps.utils import queryset_ascii
from sigi.apps.utils.mixins import CartExportReportMixin, LabeledResourse from sigi.apps.utils.mixins import CartExportReportMixin, LabeledResourse
@ -75,79 +78,30 @@ class TelefonesInline(GenericTabularInline):
extra = 1 extra = 1
class PresidenteInline(admin.StackedInline): class ParlamentarInline(admin.StackedInline):
model = Presidente model = Parlamentar
fields = ( fields = (
"nome", "foto",
"sexo", "nome_parlamentar",
"nome_completo",
"partido",
"presidente",
"data_nascimento", "data_nascimento",
"cpf", "cpf",
"identidade", "identidade",
"nota", "telefones",
"email", "email",
"tempo_de_servico",
"ult_alteracao",
"endereco",
"municipio",
"bairro",
"cep",
"redes_sociais", "redes_sociais",
)
autocomplete_fields = ("municipio",)
readonly_fields = ("ult_alteracao",)
extra = 1
max_num = 1
verbose_name_plural = _("Presidente")
def get_queryset(self, request):
return (
self.model.objects.exclude(desativado=True)
.extra(select={"ult_null": "ult_alteracao is null"})
.order_by("ult_null", "-ult_alteracao")
# A função extra foi usada para quando existir um registro com o
# campo igual a null não aparecer na frente dos mais novos
)
class ContatoInterlegisInline(admin.StackedInline):
model = Funcionario
fields = (
"nome",
"sexo",
"data_nascimento",
"cpf",
"identidade",
"nota",
"email",
"cargo",
"funcao",
"setor",
"tempo_de_servico",
"ult_alteracao", "ult_alteracao",
"endereco",
"municipio",
"bairro",
"cep",
"redes_sociais",
"desativado",
"observacoes",
) )
autocomplete_fields = ("municipio",)
readonly_fields = ("ult_alteracao",) readonly_fields = ("ult_alteracao",)
extra = 1 extra = 0
inlines = (TelefonesInline,)
verbose_name_plural = _("Contato(s) Interlegis Vigente(s)")
def get_queryset(self, request): def has_add_permission(self, request, *args, **kwargs):
return ( return False
self.model.objects.filter(setor="contato_interlegis")
.extra(select={"ult_null": "ult_alteracao is null"})
.order_by("-ult_alteracao")
)
def get_extra(self, request, obj=None, **kwargs): def has_delete_permission(self, request, *args, **kwargs):
extra = 0 return False
return extra
class FuncionariosInline(admin.StackedInline): class FuncionariosInline(admin.StackedInline):
@ -174,15 +128,11 @@ class FuncionariosInline(admin.StackedInline):
autocomplete_fields = ("municipio",) autocomplete_fields = ("municipio",)
readonly_fields = ("ult_alteracao",) readonly_fields = ("ult_alteracao",)
extra = 1 extra = 1
inlines = (TelefonesInline,) verbose_name_plural = _("Contatos da Casa")
verbose_name_plural = _("Outros Contatos da Casa")
def get_queryset(self, request): def get_queryset(self, request):
return ( return (
self.model.objects.exclude( self.model.objects.exclude(desativado=True)
cargo="Presidente",
)
.exclude(desativado=True)
.extra(select={"ult_null": "ult_alteracao is null"}) .extra(select={"ult_null": "ult_alteracao is null"})
.order_by("ult_null", "-ult_alteracao") .order_by("ult_null", "-ult_alteracao")
# A função extra foi usada para quando existir um registro com # A função extra foi usada para quando existir um registro com
@ -190,71 +140,46 @@ class FuncionariosInline(admin.StackedInline):
) )
class ConveniosInline(admin.TabularInline): class ConveniosInline(admin.StackedInline):
model = Convenio model = Convenio
fieldsets = ( fields = (
( "num_processo_sf",
None,
{
"fields": (
(
"link_sigad",
"status_convenio",
"num_convenio",
"projeto",
"observacao",
),
(
"data_retorno_assinatura",
"data_pub_diario",
),
("get_anexos",),
("link_convenio",),
)
},
),
)
readonly_fields = [
"link_convenio",
"link_sigad", "link_sigad",
"status_convenio", "status_convenio",
"num_convenio", "num_convenio",
"projeto", "projeto",
"observacao", "observacao",
"data_adesao",
"data_retorno_assinatura", "data_retorno_assinatura",
"data_termo_aceite", "data_termino_vigencia",
"data_pub_diario", "data_pub_diario",
"data_devolucao_via", "data_sigad",
"data_postagem_correio", "data_solicitacao",
"data_devolucao_sem_assinatura", "get_anexos",
"data_retorno_sem_assinatura", )
readonly_fields = [
"link_sigad",
"status_convenio",
"get_anexos", "get_anexos",
] ]
ordering = ("-data_retorno_assinatura",)
extra = 0 extra = 0
can_delete = False can_delete = False
ordering = ("-data_retorno_assinatura",) show_change_link = True
def has_add_permission(self, request, obj):
return False
@admin.display(description=_("Anexos"))
def get_anexos(self, obj): def get_anexos(self, obj):
return mark_safe( return mark_safe(
"<br/>".join( render_to_string(
[ "admin/casas/anexo_convenio_snippet.html",
f'<a href="{a.arquivo.url}" target="_blank">{a}</a>' context={"anexos": obj.anexo_set.all()},
for a in obj.anexo_set.all()
]
) )
) )
get_anexos.short_description = _("Anexos") @admin.display(description=_("Status do convênio"))
def status_convenio(self, obj): def status_convenio(self, obj):
if obj.pk is None: if obj.pk is None:
return "" return ""
status = obj.get_status() status = obj.get_status()
if status in ["Vencido", "Desistência", "Cancelado"]: if status in ["Vencido", "Desistência", "Cancelado"]:
label = r"danger" label = r"danger"
elif status == "Vigente": elif status == "Vigente":
@ -263,33 +188,16 @@ class ConveniosInline(admin.TabularInline):
label = r"warning" label = r"warning"
else: else:
label = r"info" label = r"info"
return mark_safe(f'<p class="label label-{label}">{status}</p>') return mark_safe(f'<p class="label label-{label}">{status}</p>')
status_convenio.short_description = _("Status do convênio") @admin.display(description=_("Ver no SIGAD"))
def link_convenio(self, obj):
if obj.pk is None:
return ""
opts = self.opts
url = reverse(
f"admin:{opts.app_label}_{opts.model_name}_change", args=[obj.pk]
)
return mark_safe(
f'<a href="{url}"><i class="material-icons Tiny">edit</i></a>'
)
link_convenio.short_description = _("Editar convenio")
def link_sigad(self, obj): def link_sigad(self, obj):
if obj.pk is None: if obj.pk is None:
return "" return ""
return mark_safe(obj.get_sigad_url()) return mark_safe(obj.get_sigad_url(display_type="icone"))
link_sigad.short_description = _("Processo no Senado")
class ServicoInline(admin.TabularInline): class ServicoInline(admin.StackedInline):
model = Servico model = Servico
fields = ( fields = (
"tipo_servico", "tipo_servico",
@ -301,45 +209,38 @@ class ServicoInline(admin.TabularInline):
"motivo_desativacao", "motivo_desativacao",
) )
readonly_fields = ["data_alteracao"] readonly_fields = ["data_alteracao"]
extra = 1
ordering = ("tipo_servico", "-data_alteracao") ordering = ("tipo_servico", "-data_alteracao")
extra = 0
show_change_link = True
class OcorrenciaInline(admin.TabularInline): class OcorrenciaInline(admin.StackedInline):
model = Ocorrencia model = Ocorrencia
fields = ( fields = (
"data_criacao", "data_criacao",
"categoria",
"tipo_contato",
"assunto", "assunto",
"prioridade", "prioridade",
"status", "status",
"descricao",
"resolucao",
"ticket",
"data_modificacao", "data_modificacao",
"link_editar",
) )
readonly_fields = ( readonly_fields = (
"data_criacao", "data_criacao",
"assunto",
"prioridade",
"status",
"data_modificacao", "data_modificacao",
"link_editar",
) )
ordering = ("-data_modificacao",)
extra = 0 extra = 0
max_num = 0
can_delete = False can_delete = False
ordering = ("-data_modificacao",) show_change_link = True
def link_editar(self, obj): def has_add_permission(self, request, obj):
if obj.pk is None: if Servidor.objects.filter(user=request.user).exists():
return "" return super().has_add_permission(request, obj)
opts = self.opts return False
url = reverse(
f"admin:{opts.app_label}_{opts.model_name}_change", args=[obj.pk]
)
return mark_safe(
f'<a href="{url}"><i class="material-icons Tiny">edit</i></a>'
)
link_editar.short_description = _("Editar")
@admin.register(Orgao) @admin.register(Orgao)
@ -348,8 +249,7 @@ class OrgaoAdmin(CartExportReportMixin, admin.ModelAdmin):
resource_class = OrgaoExportResourse resource_class = OrgaoExportResourse
inlines = ( inlines = (
TelefonesInline, TelefonesInline,
PresidenteInline, ParlamentarInline,
ContatoInterlegisInline,
FuncionariosInline, FuncionariosInline,
ConveniosInline, ConveniosInline,
ServicoInline, ServicoInline,
@ -365,6 +265,7 @@ class OrgaoAdmin(CartExportReportMixin, admin.ModelAdmin):
"get_servicos", "get_servicos",
) )
list_display_links = ( list_display_links = (
"id",
"sigla", "sigla",
"nome", "nome",
) )
@ -464,6 +365,20 @@ class OrgaoAdmin(CartExportReportMixin, admin.ModelAdmin):
queryset = super(OrgaoAdmin, self).get_queryset(request) queryset = super(OrgaoAdmin, self).get_queryset(request)
return queryset.prefetch_related("gerentes_interlegis", "convenio_set") return queryset.prefetch_related("gerentes_interlegis", "convenio_set")
def save_related(self, request, form, formsets, change):
for formset in formsets:
if formset.model == Ocorrencia:
formset.save(commit=False)
for obj in formset.new_objects:
if (
not hasattr(obj, "servidor_registro")
or obj.servidor_registro is None
):
obj.servidor_registro = Servidor.objects.get(
user=request.user
)
return super().save_related(request, form, formsets, change)
def get_uf(self, obj): def get_uf(self, obj):
return obj.municipio.uf.nome return obj.municipio.uf.nome

16
sigi/apps/casas/migrations/0024_delete_presidente.py

@ -0,0 +1,16 @@
# Generated by Django 4.0.4 on 2022-06-18 13:14
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('casas', '0023_funcionario_cpf_funcionario_identidade'),
]
operations = [
migrations.DeleteModel(
name='Presidente',
),
]

43
sigi/apps/casas/models.py

@ -149,7 +149,7 @@ class Orgao(models.Model):
@property @property
def num_parlamentares(self): def num_parlamentares(self):
return 0 return self.parlamentar_set.count()
@property @property
def telefone(self): def telefone(self):
@ -160,28 +160,11 @@ class Orgao(models.Model):
@property @property
def presidente(self): def presidente(self):
try: return self.parlamentar_set.filter(presidente=True).first()
if self.funcionario_set.filter(setor="presidente").count() > 1:
return self.funcionario_set.filter(setor="presidente")[0]
else:
return self.funcionario_set.get(setor="presidente")
except Funcionario.DoesNotExist:
return None
@property @property
def contato_interlegis(self): def contato_interlegis(self):
try: return self.funcionario_set.filter(setor="contato_interlegis").first()
if (
self.funcionario_set.filter(setor="contato_interlegis").count()
> 1
):
return self.funcionario_set.filter(setor="contato_interlegis")[
0
]
else:
return self.funcionario_set.get(setor="contato_interlegis")
except Funcionario.DoesNotExist:
return None
def __str__(self): def __str__(self):
return self.nome return self.nome
@ -308,23 +291,3 @@ class Funcionario(models.Model):
def __str__(self): def __str__(self):
return self.nome return self.nome
class PresidenteManager(models.Manager):
def get_queryset(self):
qs = super(PresidenteManager, self).get_queryset()
qs = qs.filter(setor="presidente")
return qs
class Presidente(Funcionario):
class Meta:
proxy = True
objects = PresidenteManager()
def save(self, *args, **kwargs):
self.setor = "presidente"
self.cargo = "Presidente"
self.funcao = "Presidente"
return super(Presidente, self).save(*args, **kwargs)

10
sigi/apps/casas/templates/admin/casas/anexo_convenio_snippet.html

@ -0,0 +1,10 @@
{% load i18n %}
{% if not anexos %}
<p>{% trans "Nenhum anexo no convênio" %}</p>
{% else %}
<div class="collection">
{% for anexo in anexos %}
<a href="{{ anexo.arquivo.url }}" target="_blank" class="collection-item">{{ anexo }}</a>
{% endfor %}
</div>
{% endif %}

75
sigi/apps/casas/templates/admin/casas/orgao/change_form.html

@ -0,0 +1,75 @@
{% extends "admin/change_form.html" %}
{% load i18n %}
{% block extrastyle %}
{{ block.super }}
{% endblock %}
{% block form_top %}
<div class="row">
<div class="col s12">
<ul class="tabs">
{% for fieldset in adminform %}
<li class="tab">
<a href="#{{ fieldset.name|default:'geral'|slugify }}">
{{ fieldset.name|default:_("Geral") }}
</a>
</li>
{% endfor %}
{% for inline_admin_formset in inline_admin_formsets %}
<li class="tab">
<a href="#{{ inline_admin_formset.opts.verbose_name_plural|slugify }}">
{{ inline_admin_formset.opts.verbose_name_plural|default:_("inline") }}
</a>
</li>
{% endfor %}
</ul>
</div>
</div>
{% endblock %}
{% block field_sets %}
{% for fieldset in adminform %}
<div id="{{ fieldset.name|default:'geral'|slugify }}" class="col s12" style="height: auto;">
{% include "admin/includes/fieldset.html" %}
</div>
{% endfor %}
{% endblock %}
{% block inline_field_sets %}
{% for inline_admin_formset in inline_admin_formsets %}
<div id="{{ inline_admin_formset.opts.verbose_name_plural|default:'inline'|slugify }}" class="col s12">
{% include inline_admin_formset.opts.template %}
</div>
{% endfor %}
{% endblock %}
{% block footer %}
{{ block.super }}
<script type="text/javascript">
const resize_tab_content = function($tab_parent, $tab) {
if ($tab_parent.height() != $tab.height()+24) {
$tab_parent.height($tab.height()+24);
}
}
$(document).ready(function(){
M.Tabs.init($('.tabs'),{swipeable: true, onShow: function(tab) {
var $tab = $(tab);
resize_tab_content($tab.parent(), $tab);
}});
$(".tabs-content").on("DOMSubtreeModified", function() {
var $tab_parent = $(this);
resize_tab_content($tab_parent, $tab_parent.children(".active"));
})
})
</script>
<style>
/* .tabs-content {
overflow-y: auto !important;
} */
.carousel-item {
height: auto !important;
}
</style>
{% endblock %}

10
sigi/apps/casas/views.py

@ -1,23 +1,15 @@
import csv import csv
from functools import reduce from functools import reduce
from django.db.models import Count, Q
from django.contrib import messages
from sigi.apps.utils import to_ascii
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.http import ( from django.http import (
HttpResponse, HttpResponse,
HttpResponseRedirect, 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 gettext as _, ngettext from django.utils.translation import gettext as _, ngettext
from django.views.generic import View
from django_weasyprint.views import WeasyTemplateView from django_weasyprint.views import WeasyTemplateView
from sigi.apps.casas.forms import PortfolioForm, AtualizaCasaForm from sigi.apps.casas.forms import PortfolioForm, AtualizaCasaForm
from sigi.apps.casas.models import Orgao, TipoOrgao, Funcionario from sigi.apps.casas.models import Orgao, TipoOrgao, Funcionario
from sigi.apps.servidores.models import Servidor from sigi.apps.servidores.models import Servidor

25
sigi/apps/convenios/migrations/0025_alter_projeto_modelo_minuta_and_more.py

File diff suppressed because one or more lines are too long

25
sigi/apps/convenios/migrations/0026_alter_projeto_modelo_minuta_and_more.py

File diff suppressed because one or more lines are too long

25
sigi/apps/convenios/migrations/0027_alter_projeto_modelo_minuta_and_more.py

File diff suppressed because one or more lines are too long

25
sigi/apps/convenios/migrations/0028_alter_projeto_modelo_minuta_and_more.py

File diff suppressed because one or more lines are too long

25
sigi/apps/convenios/migrations/0029_alter_projeto_modelo_minuta_and_more.py

File diff suppressed because one or more lines are too long

18
sigi/apps/convenios/models.py

@ -11,6 +11,7 @@ from django.utils.translation import gettext as _
from tinymce.models import HTMLField from tinymce.models import HTMLField
from sigi.apps.contatos.models import Municipio, UnidadeFederativa from sigi.apps.contatos.models import Municipio, UnidadeFederativa
from sigi.apps.eventos.models import Evento from sigi.apps.eventos.models import Evento
from sigi.apps.parlamentares.models import Parlamentar
from sigi.apps.utils import to_ascii from sigi.apps.utils import to_ascii
from sigi.apps.casas.models import Funcionario, Orgao from sigi.apps.casas.models import Funcionario, Orgao
from sigi.apps.servidores.models import Servidor, Servico from sigi.apps.servidores.models import Servidor, Servico
@ -23,7 +24,7 @@ class Projeto(models.Model):
[ [
("evento", Evento), ("evento", Evento),
("casa", Orgao), ("casa", Orgao),
("presidente", Funcionario), ("presidente", Parlamentar),
("contato", Funcionario), ("contato", Funcionario),
("casa.municipio", Municipio), ("casa.municipio", Municipio),
("casa.municipio.uf", UnidadeFederativa), ("casa.municipio.uf", UnidadeFederativa),
@ -36,7 +37,7 @@ class Projeto(models.Model):
[ [
("evento", Evento), ("evento", Evento),
("casa", Orgao), ("casa", Orgao),
("presidente", Funcionario), ("presidente", Parlamentar),
("contato", Funcionario), ("contato", Funcionario),
("casa.municipio", Municipio), ("casa.municipio", Municipio),
("casa.municipio.uf", UnidadeFederativa), ("casa.municipio.uf", UnidadeFederativa),
@ -263,20 +264,27 @@ class Convenio(models.Model):
return "" return ""
return obj.get_sigad_url() return obj.get_sigad_url()
def get_sigad_url(self): def get_sigad_url(self, display_type="numero"):
m = re.match( m = re.match(
r"(?P<orgao>00100|00200)\.(?P<sequencial>\d{6})/(?P<ano>\d{4})-\d{2}", r"(?P<orgao>00100|00200)\.(?P<sequencial>\d{6})/(?P<ano>\d{4})-\d{2}",
self.num_processo_sf, self.num_processo_sf,
) )
if m: if m:
orgao, sequencial, ano = m.groups() orgao, sequencial, ano = m.groups()
if display_type == "numero":
display = self.num_processo_sf
else:
display = "<i class='material-icons'>visibility</i>"
return ( return (
f'<a href="https://intra.senado.leg.br/sigad/novo/protocolo/' f'<a href="https://intra.senado.leg.br/sigad/novo/protocolo/'
f"impressao.asp?area=processo&txt_numero_orgao={orgao}" f"impressao.asp?area=processo&txt_numero_orgao={orgao}"
f'&txt_numero_sequencial={sequencial}&txt_numero_ano={ano}" ' f'&txt_numero_sequencial={sequencial}&txt_numero_ano={ano}" '
f'target="_blank">{self.num_processo_sf}</a>' f'target="_blank">{display}</a>'
) )
return self.num_processo_sf if display_type == "numero":
return self.num_processo_sf
else:
return "<i class='material-icons'>visibility_off</i>"
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
self.conveniada = self.data_retorno_assinatura is not None self.conveniada = self.data_retorno_assinatura is not None

23
sigi/apps/eventos/forms.py

@ -3,6 +3,7 @@ from django.utils.translation import gettext as _
from material.admin.widgets import MaterialAdminTextareaWidget from material.admin.widgets import MaterialAdminTextareaWidget
from sigi.apps.casas.models import Funcionario, Orgao from sigi.apps.casas.models import Funcionario, Orgao
from sigi.apps.eventos.models import Convite, ModeloDeclaracao, Evento from sigi.apps.eventos.models import Convite, ModeloDeclaracao, Evento
from sigi.apps.parlamentares.models import Parlamentar
class EventoAdminForm(forms.ModelForm): class EventoAdminForm(forms.ModelForm):
@ -79,3 +80,25 @@ class FuncionarioForm(forms.ModelForm):
"nota": MaterialAdminTextareaWidget, "nota": MaterialAdminTextareaWidget,
"redes_sociais": MaterialAdminTextareaWidget, "redes_sociais": MaterialAdminTextareaWidget,
} }
class ParlamentarForm(forms.ModelForm):
class Meta:
model = Parlamentar
fields = [
"nome_completo",
"nome_parlamentar",
"data_nascimento",
"cpf",
"identidade",
"telefones",
"email",
"redes_sociais",
"observacoes",
]
widgets = {
"nome_completo": forms.HiddenInput,
"redes_sociais": MaterialAdminTextareaWidget,
"observacoes": MaterialAdminTextareaWidget,
"status_mandato": forms.RadioSelect,
}

23
sigi/apps/eventos/static/css/convite.css

@ -0,0 +1,23 @@
.lista_parlamentares {
width: 100%;
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .14), 0 3px 1px -2px rgba(0, 0, 0, .12), 0 1px 5px 0 rgba(0, 0, 0, .2);
margin: .5rem 0 1rem 0 !important;
}
.input-field>label {
position: relative;
font-size: 0.8rem;
}
.suplente {
color: #a5d6a7;
}
.inativo {
color: #9e9e9e;
}
.presidente-foto .card-title {
position: relative;
bottom: 2em;
}

4
sigi/apps/eventos/templates/eventos/alocacao_equipe.html

@ -57,8 +57,8 @@
<i class="large material-icons">print</i> <i class="large material-icons">print</i>
</a> </a>
<ul> <ul>
<li><a class="btn-floating" href="?ano={{ ano_pesquisa|safe }}{% if mes_pesquisa %}&mes={{ mes_pesquisa|safe }}{% endif %}{% if semana_pesquisa %}&semana={{ semana_pesquisa|safe }}{% endif %}&fmt=pdf" title="{% trans "Exportar para PDF" %}"><i class="material-icons">picture_as_pdf</i></a></li> <li><a class="btn-floating" href="?ano={{ ano_pesquisa|safe }}{% if mes_pesquisa %}&mes={{ mes_pesquisa|safe }}{% endif %}{% if semana_pesquisa %}&semana={{ semana_pesquisa|safe }}{% endif %}&fmt=pdf" title="{% trans 'Exportar para PDF' %}" target="_blank"><i class="material-icons">picture_as_pdf</i></a></li>
<li><a class="btn-floating" href="?ano={{ ano_pesquisa|safe }}{% if mes_pesquisa %}&mes={{ mes_pesquisa|safe }}{% endif %}{% if semana_pesquisa %}&semana={{ semana_pesquisa|safe }}{% endif %}&fmt=csv" title="{% trans "Exportar para CSV" %}"><i class="material-icons">file_download</i></a></li> <li><a class="btn-floating" href="?ano={{ ano_pesquisa|safe }}{% if mes_pesquisa %}&mes={{ mes_pesquisa|safe }}{% endif %}{% if semana_pesquisa %}&semana={{ semana_pesquisa|safe }}{% endif %}&fmt=csv" title="{% trans 'Exportar para CSV' %}" target="_blank"><i class="material-icons">file_download</i></a></li>
</ul> </ul>
</div> </div>
{% include "eventos/snippets/alocacao_equipe_snippet.html" with mode="html" %} {% include "eventos/snippets/alocacao_equipe_snippet.html" with mode="html" %}

2
sigi/apps/eventos/templates/eventos/calendario.html

@ -23,7 +23,7 @@
<a class="btn-floating btn-small" href="?ano={{ ano_pesquisa|safe }}&mes={{ mes_pesquisa|safe }}&fmt=cal" title="{% trans 'Formato de calendário' %}"><i class="material-icons">date_range</i></a> <a class="btn-floating btn-small" href="?ano={{ ano_pesquisa|safe }}&mes={{ mes_pesquisa|safe }}&fmt=cal" title="{% trans 'Formato de calendário' %}"><i class="material-icons">date_range</i></a>
{% endif %} {% endif %}
<li> <li>
<a class="btn-floating btn-small" href="?ano={{ ano_pesquisa|safe }}&mes={{ mes_pesquisa|safe }}&pdf=1"><i class="material-icons">picture_as_pdf</i></a> <a class="btn-floating btn-small" href="?ano={{ ano_pesquisa|safe }}&mes={{ mes_pesquisa|safe }}&pdf=1" target="_blank"><i class="material-icons">picture_as_pdf</i></a>
</li> </li>
</ul> </ul>
</div> </div>

194
sigi/apps/eventos/templates/eventos/convida_casa.html

@ -1,98 +1,152 @@
{% extends "admin/base_site.html" %} {% extends "admin/base_site.html" %}
{% load i18n static %} {% load i18n static %}
{% block extrastyle %}
{{ block.super }}
<link rel="stylesheet" type="text/css" href="{% static 'css/convite.css' %}">
{% endblock %}
{% block content %} {% block content %}
{{ block.super }} {{ block.super }}
<form action="" method="post" name="convite" enctype="multipart/form-data">{% csrf_token %} <form action="" method="post" name="convite" enctype="multipart/form-data">
<div class="row"> {% csrf_token %}
<div class="col s12"> <div class="row">
<h5> <div class="col s12">
{% blocktranslate with casa_nome=casa.nome evento_nome=evento.nome %} <h5>
Convidar {{ casa_nome }} para {{ evento_nome }} {% blocktranslate with casa_nome=casa.nome evento_nome=evento.nome %}
{% endblocktranslate %} Convidar {{ casa_nome }} para {{ evento_nome }}
</h5> {% endblocktranslate %}
</h5>
</div>
</div> </div>
</div> <div class="row">
<div class="row"> <div class="col s12">
<div class="col s12"> <div class="card">
<div class="card"> <div class="card-content">
<div class="card-content"> <span class="card-title">{% trans "Convite" %}</span>
<span class="card-title">{% trans "Convite" %}</span> {{ form_convite }}
{{ form_convite }} </div>
</div> </div>
</div> </div>
</div> </div>
</div> <div class="row">
<div class="row"> <div class="col s12">
<div class="col s12"> <div class="card">
<div class="card"> <div class="card-content">
<div class="card-content"> <span class="card-title">{% trans "Casa" %}</span>
<span class="card-title">{% trans "Casa" %}</span> {{ form_casa }}
{{ form_casa }} </div>
</div> </div>
</div> </div>
</div> </div>
</div> <div class="row">
<div class="row"> <div class="col s12">
<div class="col s12"> <div class="card">
<div class="card"> <div class="card-content">
<div class="card-content"> <span class="card-title">{% trans "Identifique o presidente" %}</span>
<span class="card-title">{% trans "Dados do presidente" %}</span> <div class="row">
{{ form_presidente }} <div class="col s12">
<div class="row">
<div class="input-field col s12">
<i class="material-icons prefix">search</i>
<input type="text" id="busca_parlamentar" class="autocomplete"{% if presidente %} value="{{ presidente.nome_completo }}"{% endif %}>
</div>
</div>
</div>
</div>
<div class="center-align hide" id="load_presidente_form">
<div class="preloader-wrapper small active">
<div class="spinner-layer spinner-green-only">
<div class="circle-clipper left">
<div class="circle"></div>
</div><div class="gap-patch">
<div class="circle"></div>
</div><div class="circle-clipper right">
<div class="circle"></div>
</div>
</div>
</div>
<p>{% trans "Carregando dados do parlamentar..." %}</p>
</div>
<div id="presidente-form">
{% if form_presidente %}
{% include "eventos/snippets/form_presidente_snippet.html" %}
{% endif %}
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> <div class="row">
<div class="row"> <div class="col s12">
<div class="col s12"> <div class="card">
<div class="card"> <div class="card-content">
<div class="card-content"> <span class="card-title">
<span class="card-title"> {% trans "Dados do contato Interlegis" %}
{% trans "Dados do contato Interlegis" %} <a href="#" id="copiar-presidente" class="waves-effect waves-light btn-small light-green accent-1 right">
<a href="#" id="copiar-presidente" class="waves-effect waves-light btn-small light-green accent-1 right"> <i class="material-icons left">content_copy</i>
<i class="material-icons left">content_copy</i> {% trans "Copiar dados do presidente" %}
{% trans "Copiar dados do presidente" %} </a>
</a> </span>
</span> {{ form_contato }}
{{ form_contato }} </div>
</div> </div>
</div> </div>
</div> </div>
</div> <div class="row">
<div class="row"> <div class="col s12">
<div class="col s12"> <div class="card">
<div class="card"> <div class="card-action">
<div class="card-action"> <button class="btn waves-effect waves-light" type="submit" name="save">
<button class="btn waves-effect waves-light" type="submit" name="save"> {% trans "Save" %}
{% trans "Save" %} <i class="material-icons right">send</i>
<i class="material-icons right">send</i> </button>
</button> {% for proj in projetos %}
{% for proj in projetos %} <button class="btn waves-effect waves-light" type="submit" name="save" value="{{ proj.id }}">
<button class="btn waves-effect waves-light" type="submit" name="save" value="{{ proj.id }}"> {% blocktrans with sigla=proj.sigla %}Salvar e gerar minuta de {{ sigla}}{% endblocktrans %}
{% blocktrans with sigla=proj.sigla %}Salvar e gerar minuta de {{ sigla}}{% endblocktrans %} <i class="material-icons right">picture_as_pdf</i>
<i class="material-icons right">picture_as_pdf</i> </button>
</button> {% endfor %}
{% endfor %} </div>
</div> </div>
</div> </div>
</div> </div>
</div> </form>
</form>
{% endblock %} {% endblock %}
{% block footer %} {% block footer %}
{{ block.super }} {{ block.super }}
<script> <script>
$(document).ready(function() { $(document).ready(function() {
$("#copiar-presidente").click(function(event) { $("#copiar-presidente").click(function(event) {
event.preventDefault(); event.preventDefault();
var dados = {{% for field in form_presidente %} var campos = [['nome_completo', 'nome'], ['cpf', 'cpf'], ['identidade', 'identidade'], ['telefones', 'nota'], ['email', 'email']];
{{ field.name }}: $("#{{ field.id_for_label }}").val(),{% endfor %} for (var x=0; x<campos.length; x++) {
}; $(`#id_contato-${campos[x][1]}`).val($(`#id_presidente-${campos[x][0]}`).val());
{% for field in form_contato %} }
$("#{{ field.id_for_label }}").val(dados.{{ field.name }}); });
{% endfor %} $.ajax("{% url 'parlamentar-json' casa.id %}", {
dataType: 'json',
success: function(data) {
function complete(nome) {
$("#load_presidente_form").removeClass("hide")
$("div#presidente-form").html("");
$.get(`/eventos/evento/presidente/${data[nome]["id"]}/`, function(html) {
$("div#presidente-form").html(html);
$("#load_presidente_form").addClass("hide")
})
}
var options = {};
for (nome in data) {options[nome] = data[nome]["foto"]};
M.Autocomplete.init(
$('input#busca_parlamentar'),
{data: options,
onAutocomplete: complete });
},
error: function(jqXHR, textStatus) {
alert(`Erro ao carregar parlamentares, com status ${textStatus}`);
}
})
}) })
})
</script> </script>
{% endblock %} {% endblock %}

4
sigi/apps/eventos/templates/eventos/oficio_padrao.html

@ -55,7 +55,9 @@
{% block body_content %} {% block body_content %}
<div id="logo" class="logo"> <div id="logo" class="logo">
<img src="{{ casa.brasao.url }}" class="logo-image"> {% if casa.brasao %}
<img src="{{ casa.brasao.url }}" class="logo-image">
{% endif %}
</div> </div>
{{ block.super }} {{ block.super }}
{% endblock %} {% endblock %}

11
sigi/apps/eventos/templates/eventos/snippets/form_presidente_snippet.html

@ -0,0 +1,11 @@
{% load i18n %}
{% if presidente.foto %}
<div class="presidente-foto">
<img class="materialboxed" width="120" src="{{ presidente.foto.url }}" alt="{{ presidente.nome_completo }}">
<script>$(document).ready(function(){M.Materialbox.init($('.materialboxed'));})</script>
{% endif %}
<span class="card-title">{% trans "Complemente os dados do presidente" %}</span>
{% if presidente.foto %}</div>{% endif %}
{{ form_presidente }}
<input type="hidden" name="id_presidente" value="{{ presidente.id|safe }}">

5
sigi/apps/eventos/urls.py

@ -17,6 +17,11 @@ urlpatterns = [
views.declaracao, views.declaracao,
name="evento-declaracao", name="evento-declaracao",
), ),
path(
"evento/presidente/<int:presidente_id>/",
views.presidente_form,
name="presidente-form-snippet",
),
] ]
# from django.conf.urls import patterns, url # from django.conf.urls import patterns, url

69
sigi/apps/eventos/views.py

@ -1,13 +1,13 @@
import calendar import calendar
import csv import csv
import locale import locale
from datetime import datetime
from functools import reduce from functools import reduce
from typing import OrderedDict from typing import OrderedDict
from django import forms
from django.contrib import messages from django.contrib import messages
from django.contrib.admin.sites import site from django.contrib.admin.sites import site
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.http import HttpResponse, JsonResponse from django.http import HttpResponse
from django.shortcuts import redirect, render, get_object_or_404 from django.shortcuts import redirect, render, get_object_or_404
from django.template import Template, Context from django.template import Template, Context
from django.template.exceptions import TemplateSyntaxError from django.template.exceptions import TemplateSyntaxError
@ -24,15 +24,17 @@ from django_weasyprint.utils import django_url_fetcher
from django_weasyprint.views import WeasyTemplateResponse from django_weasyprint.views import WeasyTemplateResponse
from docx import Document from docx import Document
from weasyprint import HTML from weasyprint import HTML
from sigi.apps.casas.models import Funcionario, Orgao, Presidente from sigi.apps.casas.models import Funcionario, Orgao
from sigi.apps.convenios.models import Projeto from sigi.apps.convenios.models import Projeto
from sigi.apps.eventos.models import Evento, Equipe, Convite, Modulo, Anexo from sigi.apps.eventos.models import Evento, Convite, Anexo
from sigi.apps.eventos.forms import ( from sigi.apps.eventos.forms import (
SelecionaModeloForm, SelecionaModeloForm,
ConviteForm, ConviteForm,
CasaForm, CasaForm,
FuncionarioForm, FuncionarioForm,
ParlamentarForm,
) )
from sigi.apps.parlamentares.models import Parlamentar
from sigi.apps.servidores.models import Servidor from sigi.apps.servidores.models import Servidor
@ -215,6 +217,8 @@ def convida_casa(request, evento_id, casa_id):
else: else:
run_final.text += run.text run_final.text += run.text
run.text = "" run.text = ""
if run_final.text.count("{{") != run_final.text.count("}}"):
continue
try: try:
run_final.text = Template(run_final.text).render(context) run_final.text = Template(run_final.text).render(context)
run_final = None run_final = None
@ -242,17 +246,23 @@ def convida_casa(request, evento_id, casa_id):
data_convite=timezone.localdate(), data_convite=timezone.localdate(),
) )
presidente = casa.presidente or Funcionario(
casa_legislativa=casa, setor="presidente"
)
contato = casa.contato_interlegis or Funcionario( contato = casa.contato_interlegis or Funcionario(
casa_legislativa=casa, setor="contato_interlegis" casa_legislativa=casa, setor="contato_interlegis"
) )
presidente = casa.presidente
parlamentares = casa.parlamentar_set.all()
if request.method == "POST": if request.method == "POST":
id_presidente = request.POST.get("id_presidente", None)
if id_presidente is None:
messages.error(request, _("Presidente não foi identificado"))
return redirect(".")
presidente = get_object_or_404(Parlamentar, id=id_presidente)
form_convite = ConviteForm(request.POST, instance=convite) form_convite = ConviteForm(request.POST, instance=convite)
form_casa = CasaForm(request.POST, request.FILES, instance=casa) form_casa = CasaForm(request.POST, request.FILES, instance=casa)
form_presidente = FuncionarioForm( form_presidente = ParlamentarForm(
request.POST, instance=presidente, prefix="presidente" request.POST, instance=presidente, prefix="presidente"
) )
form_contato = FuncionarioForm( form_contato = FuncionarioForm(
@ -267,8 +277,13 @@ def convida_casa(request, evento_id, casa_id):
form_contato.is_valid(), form_contato.is_valid(),
] ]
): ):
contato = form_contato.save() contato = form_contato.save(commit=False)
presidente = form_presidente.save() contato.setor = "contato_interlegis"
contato.save()
presidente = form_presidente.save(commit=False)
presidente.status_mandato = "E"
presidente.presidente = True
presidente.save()
casa = form_casa.save() casa = form_casa.save()
convite = form_convite.save() convite = form_convite.save()
@ -321,7 +336,9 @@ def convida_casa(request, evento_id, casa_id):
) )
nome = f"Minuta de {projeto.sigla} da {casa.nome}"[:70] nome = f"Minuta de {projeto.sigla} da {casa.nome}"[:70]
minuta = Anexo(descricao=nome, evento=evento) minuta = Anexo(descricao=nome, evento=evento)
minuta.arquivo.name = slugify(nome) + ".docx" minuta.arquivo.name = (
f"{Anexo.arquivo.field.upload_to}/{slugify(nome)}.docx"
)
doc.save(minuta.arquivo.path) doc.save(minuta.arquivo.path)
minuta.save() minuta.save()
query_str += f"anexo_id={minuta.id}" query_str += f"anexo_id={minuta.id}"
@ -330,34 +347,52 @@ def convida_casa(request, evento_id, casa_id):
else: else:
return redirect(evento.get_absolute_url()) return redirect(evento.get_absolute_url())
else: else:
messages.error(_("Preencha corretamente o convite")) messages.error(request, _("Preencha corretamente o convite"))
else: else:
form_convite = ConviteForm(instance=convite) form_convite = ConviteForm(instance=convite)
form_casa = CasaForm(instance=casa) form_casa = CasaForm(instance=casa)
form_presidente = FuncionarioForm(
instance=presidente, prefix="presidente"
)
form_contato = FuncionarioForm(instance=contato, prefix="contato") form_contato = FuncionarioForm(instance=contato, prefix="contato")
if presidente:
form_presidente = ParlamentarForm(
instance=presidente, prefix="presidente"
)
else:
form_presidente = ""
context = site.each_context(request) context = site.each_context(request) or {}
context.update( context.update(
{ {
"form_convite": form_convite, "form_convite": form_convite,
"form_casa": form_casa, "form_casa": form_casa,
"form_presidente": form_presidente,
"form_contato": form_contato, "form_contato": form_contato,
"form_presidente": form_presidente,
"evento": evento, "evento": evento,
"convite": convite, "convite": convite,
"casa": casa, "casa": casa,
"presidente": presidente, "presidente": presidente,
"contato": contato, "contato": contato,
"projetos": projetos, "projetos": projetos,
"parlamentares": parlamentares,
} }
) )
return render(request, "eventos/convida_casa.html", context) return render(request, "eventos/convida_casa.html", context)
@login_required
def presidente_form(request, presidente_id):
presidente = get_object_or_404(Parlamentar, pk=presidente_id)
form = ParlamentarForm(instance=presidente, prefix="presidente")
return render(
request,
"eventos/snippets/form_presidente_snippet.html",
{
"form_presidente": form,
"presidente": presidente,
},
)
def gerar_anexo(casa, presidente, contato, path, modelo, nome, texto): def gerar_anexo(casa, presidente, contato, path, modelo, nome, texto):
template_string = ( template_string = (
f'{{% extends "eventos/{modelo}" %}}' f'{{% extends "eventos/{modelo}" %}}'

2
sigi/apps/ocorrencias/models.py

@ -116,7 +116,7 @@ class Ocorrencia(models.Model):
raise ValidationError( raise ValidationError(
{ {
"ticket": _( "ticket": _(
"Já existe ocorrência " "registrada para este ticket" "Já existe ocorrência registrada para este ticket"
) )
} }
) )

392
sigi/apps/parlamentares/admin.py

@ -1,43 +1,51 @@
# -*- coding: utf-8 -*- import csv
import json
from django.db import transaction
from django.contrib import admin from django.contrib import admin
from django.contrib.contenttypes import generic from django.contrib import messages
from django.contrib.contenttypes.admin import GenericTabularInline
from django.core.files.temp import NamedTemporaryFile
from django.http import HttpResponseRedirect, HttpResponse from django.http import HttpResponseRedirect, HttpResponse
from django.shortcuts import redirect, render
from django.urls import path, reverse
from django.utils import timezone
from django.utils.html import escape, escapejs from django.utils.html import escape, escapejs
from django.utils.safestring import mark_safe
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from sigi.apps.casas.models import Orgao
from sigi.apps.contatos.models import Telefone from sigi.apps.parlamentares.jobs import import_path, json_path
from sigi.apps.parlamentares.models import ( from sigi.apps.parlamentares.models import Partido, Parlamentar
Partido, from sigi.apps.parlamentares.forms import ImportForm
Parlamentar,
Mandato,
Legislatura,
Coligacao,
ComposicaoColigacao,
SessaoLegislativa,
MesaDiretora,
Cargo,
MembroMesaDiretora,
)
from sigi.apps.parlamentares.views import adicionar_parlamentar_carrinho
from sigi.apps.utils.base_admin import BaseModelAdmin
from sigi.apps.utils.filters import AlphabeticFilter from sigi.apps.utils.filters import AlphabeticFilter
from sigi.apps.utils.mixins import (
ImportCartExportMixin,
CartExportMixin,
LabeledResourse,
)
class MandatosInline(admin.TabularInline): class ParlamentarResource(LabeledResourse):
model = Mandato class Meta:
extra = 1 model = Parlamentar
raw_id_fields = ("legislatura", "partido") fields = (
"casa_legislativa__nome",
"casa_legislativa__municipio__uf__sigla",
class TelefonesInline(generic.GenericTabularInline): "partido__legenda",
model = Telefone "partido__sigla",
extra = 2 "partido__nome",
"presidente",
"nome_completo",
class PartidoAdmin(BaseModelAdmin): "nome_parlamentar",
list_display = ("nome", "sigla") "data_nascimento",
list_display_links = ("nome", "sigla") "cpf",
search_fields = ("nome", "sigla") "identidade",
"telefones",
"email",
"redes_sociais",
"ult_alteracao",
"observacoes",
)
export_order = fields
class ParlamentarNomeCompletoFilter(AlphabeticFilter): class ParlamentarNomeCompletoFilter(AlphabeticFilter):
@ -45,221 +53,145 @@ class ParlamentarNomeCompletoFilter(AlphabeticFilter):
parameter_name = "nome_completo" parameter_name = "nome_completo"
class ParlamentarAdmin(BaseModelAdmin): @admin.register(Partido)
inlines = (TelefonesInline, MandatosInline) class PartidoAdmin(ImportCartExportMixin, admin.ModelAdmin):
list_display = ("nome_completo", "nome_parlamentar", "sexo") list_display = ("legenda", "nome", "sigla")
list_display_links = ("nome_completo", "nome_parlamentar") search_fields = ("legenda", "nome", "sigla")
list_filter = (ParlamentarNomeCompletoFilter,)
actions = [
"adiciona_parlamentar", @admin.register(Parlamentar)
] class ParlamentarAdmin(CartExportMixin, admin.ModelAdmin):
resource_class = ParlamentarResource
change_list_template = (
"admin/parlamentares/parlamentar/cart/"
"change_list_import_cart_export.html"
)
list_display = (
"get_foto",
"nome_completo",
"casa_legislativa",
"status_mandato",
"get_uf",
"partido",
)
list_filter = (
"casa_legislativa__municipio__uf",
("casa_legislativa__tipo", admin.RelatedOnlyFieldListFilter),
"partido",
"status_mandato",
"presidente",
ParlamentarNomeCompletoFilter,
)
fieldsets = ( fieldsets = (
( (
None, _("mandato"),
{ {
"fields": ("nome_completo", "nome_parlamentar", "sexo"), "fields": (
"casa_legislativa",
"ano_eleicao",
"partido",
"presidente",
)
}, },
), ),
# (_('Endereço'), {
# 'fields': ('logradouro', 'bairro', 'municipio', 'cep'),
# }),
( (
_("Outras informações"), _("dados pessoais"),
{ {
"fields": ("data_nascimento", "email", "pagina_web", "foto"), "fields": (
"nome_completo",
"nome_parlamentar",
"foto",
"data_nascimento",
"cpf",
"identidade",
),
}, },
), ),
(
_("contatos"),
{"fields": ("telefones", "email", "redes_sociais")},
),
) )
radio_fields = {"sexo": admin.VERTICAL} autocomplete_fields = ("casa_legislativa",)
# raw_id_fields = ('municipio',)
search_fields = ( search_fields = (
"nome_completo", "nome_completo",
"nome_parlamentar", "nome_parlamentar",
"email", "email",
"pagina_web", "casa_legislativa__search_text",
) )
def adiciona_parlamentar(self, request, queryset): def get_urls(self):
if "carrinho_parlametar" in request.session: urls = super().get_urls()
q1 = len(request.session["carrinho_parlamentar"]) info = self.get_model_info()
else: my_urls = [
q1 = 0 path(
adicionar_parlamentar_carrinho(request, queryset=queryset) "import/",
q2 = len(request.session["carrinho_parlamentar"]) self.admin_site.admin_view(self.import_action),
quant = q2 - q1 name="%s_%s_import" % info,
if quant: ),
self.message_user( path(
request, _("%s Parlamentares adicionados no carrinho") % (quant) "import_result/",
) self.admin_site.admin_view(self.result_import_action),
name="%s_%s_import_result" % info,
),
]
return my_urls + urls
@admin.display(
description=_("UF"), ordering="casa_legislativa__municipio__uf__nome"
)
def get_uf(self, obj):
return obj.casa_legislativa.municipio.uf.nome
@mark_safe
@admin.display(description=_("Foto"))
def get_foto(self, obj):
if obj.foto:
return f'<img class="circle" src="{obj.foto.url}" style="width: 58px; height: 58px;"/>'
else: else:
self.message_user( return (
request, '<i class="material-icons medium grey-text">account_circle</i>'
_(
"Os parlamentares selecionadas já foram adicionadas anteriormente"
),
) )
return HttpResponseRedirect(".")
adiciona_parlamentar.short_description = _(
"Armazenar parlamentar no carrinho para exportar"
)
class MandatoAdmin(BaseModelAdmin):
list_display = (
"parlamentar",
"legislatura",
"partido",
"inicio_mandato",
"fim_mandato",
"is_afastado",
)
list_filter = ("is_afastado", "partido")
search_fields = (
"legislatura__numero",
"parlamentar__nome_completo",
"parlamentar__nome_parlamentar",
"partido__nome",
"partido__sigla",
)
raw_id_fields = ("parlamentar", "legislatura", "partido")
# radio_fields = {'suplencia': admin.VERTICAL}
class MandatoInline(admin.TabularInline): def import_action(self, request, *args, **kwargs):
model = Mandato def save_file(uploaded, destination_path):
raw_id_fields = [ with open(destination_path / uploaded.name, "wb") as dst_file:
"parlamentar", for chunck in uploaded:
] dst_file.write(chunck)
dst_file.flush()
class LegislaturaAdmin(BaseModelAdmin): form = ImportForm(request.POST, request.FILES)
date_hierarchy = "data_inicio" context = admin.site.each_context(request) or {}
list_display = ( context["opts"] = self.model._meta
"numero", context["form"] = form
"casa_legislativa", context["last_result"] = import_path / "result.html"
"uf", if request.method == "POST" and form.is_valid():
"data_inicio", json_data = {
"data_fim", "upload_time": timezone.localtime(),
"data_eleicao", "user_id": request.user.id,
"total_parlamentares", "tipo_candidatos": form.cleaned_data["tipo_candidatos"],
) "suplentes": form.cleaned_data["suplentes"],
raw_id_fields = ("casa_legislativa",) "codificacao": form.cleaned_data["codificacao"],
list_display_links = ("numero",) "sigla_uf": form.cleaned_data["uf_importar"],
list_filter = ("casa_legislativa__municipio__uf",) }
search_fields = ( if form.cleaned_data["arquivo_tse"]:
"casa_legislativa__nome", save_file(form.cleaned_data["arquivo_tse"], import_path)
"casa_legislativa__municipio__nome", json_data["resultados"] = form.cleaned_data["arquivo_tse"].name
) if form.cleaned_data["arquivo_redes"]:
inlines = (MandatoInline,) save_file(form.cleaned_data["arquivo_redes"], import_path)
json_data["redes_sociais"] = form.cleaned_data[
def uf(self, obj): "arquivo_redes"
return obj.casa_legislativa.municipio.uf.sigla ].name
if form.cleaned_data["arquivo_fotos"]:
uf.short_description = _("UF") save_file(form.cleaned_data["arquivo_fotos"], import_path)
uf.admin_order_field = "casa_legislativa__municipio__uf" json_data["fotos"] = form.cleaned_data["arquivo_fotos"].name
json_path.write_text(json.dumps(json_data, default=str))
def lookup_allowed(self, lookup, value): return redirect(
return super(LegislaturaAdmin, self).lookup_allowed( reverse("admin:%s_%s_import_result" % self.get_model_info())
lookup, value
) or lookup in ["casa_legislativa__municipio__uf__codigo_ibge__exact"]
def response_change(self, request, obj):
response = super(LegislaturaAdmin, self).response_change(request, obj)
if "_popup" in request.POST:
response = HttpResponse(
'<script type="text/javascript">opener.dismissAddAnotherPopup(window, "%s", "%s");</script>'
%
# escape() calls force_unicode.
(escape(obj.pk), escapejs(obj))
) )
return response return render(request, "parlamentares/import.html", context)
class ColigacaoAdmin(BaseModelAdmin):
list_display = ("nome", "legislatura", "numero_votos")
list_display_links = ("nome",)
raw_id_fields = ("legislatura",)
search_fields = ("nome", "legislatura__numero")
class ComposicaoColigacaoAdmin(BaseModelAdmin):
list_display = ("coligacao", "partido")
list_display_links = ("coligacao", "partido")
list_filter = ("partido",)
raw_id_fields = ("coligacao", "partido")
search_fields = ("coligacao__nome", "partido__nome", "partido__sigla")
class SessaoLegislativaAdmin(BaseModelAdmin):
list_display = (
"numero",
"mesa_diretora",
"legislatura",
"tipo",
"data_inicio",
"data_fim",
)
list_display_links = ("numero",)
list_filter = ("tipo",)
fieldsets = (
(None, {"fields": ("numero", "mesa_diretora", "legislatura", "tipo")}),
(
None,
{
"fields": (
("data_inicio", "data_fim"),
("data_inicio_intervalo", "data_fim_intervalo"),
)
},
),
)
radio_fields = {"tipo": admin.VERTICAL}
raw_id_fields = ("mesa_diretora", "legislatura")
search_fields = ("numero", "mesa_diretora__casa_legislativa__nome")
class CargoAdmin(BaseModelAdmin):
list_display = ("descricao",)
search_fields = ("descricao",)
class MembroMesaDiretoraInline(admin.TabularInline):
model = MembroMesaDiretora
max_num = 11
extra = 4
raw_id_fields = ("parlamentar", "cargo")
class MembroMesaDiretoraAdmin(BaseModelAdmin):
list_display = ("parlamentar", "cargo", "mesa_diretora")
list_display_links = ("parlamentar",)
list_filter = ("cargo",)
raw_id_fields = ("parlamentar", "cargo", "mesa_diretora")
search_fields = (
"cargo__descricao",
"parlamentar__nome_completo",
"parlamentar__nome_parlamentar",
"mesa_diretora__casa_legislativa__nome",
)
class MesaDiretoraAdmin(BaseModelAdmin):
inlines = (MembroMesaDiretoraInline,)
raw_id_fields = ("casa_legislativa",)
list_display = ("id", "casa_legislativa")
search_fields = ("casa_legislativa__nome",)
admin.site.register(Partido, PartidoAdmin) def result_import_action(self, request, *args, **kwargs):
admin.site.register(Parlamentar, ParlamentarAdmin) context = admin.site.each_context(request) or {}
admin.site.register(Mandato, MandatoAdmin) context["opts"] = self.model._meta
admin.site.register(Legislatura, LegislaturaAdmin) return render(request, "parlamentares/import_result.html", context)
admin.site.register(Coligacao, ColigacaoAdmin)
admin.site.register(ComposicaoColigacao, ComposicaoColigacaoAdmin)
admin.site.register(SessaoLegislativa, SessaoLegislativaAdmin)
admin.site.register(MesaDiretora, MesaDiretoraAdmin)
admin.site.register(Cargo, CargoAdmin)
admin.site.register(MembroMesaDiretora, MembroMesaDiretoraAdmin)

7
sigi/apps/parlamentares/apps.py

@ -0,0 +1,7 @@
from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _
class CasasConfig(AppConfig):
name = "sigi.apps.parlamentares"
verbose_name = _("parlamentares")

40
sigi/apps/parlamentares/forms.py

@ -0,0 +1,40 @@
from requests import options
from django import forms
from django.utils.translation import gettext as _
from sigi.apps.contatos.models import UnidadeFederativa
class ImportForm(forms.Form):
CODIFICACAO_CHOICES = (
("iso8859-1", _("LATIN 1 (iso-8859-1)")),
("utf-8", _("UTF-8")),
)
UF_CHOICES = [
("BR", _("Todo o Brasil")),
] + [(uf.sigla, uf.nome) for uf in UnidadeFederativa.objects.all()]
TIPO_CHOICES = [
("V", _("Vereador")),
("D", _("Deputado Estadual")),
]
codificacao = forms.ChoiceField(
label=_("Codificação de caracteres"),
choices=CODIFICACAO_CHOICES,
help_text=_(
"Verifique no PDF do TSE a codificação de caracteres dos "
"arquivos"
),
)
arquivo_tse = forms.FileField(label=_("Arquivo do TSE"), required=False)
arquivo_redes = forms.FileField(
label=_("Arquivo de redes sociais"), required=False
)
arquivo_fotos = forms.FileField(label=_("Zip das fotos"), required=False)
tipo_candidatos = forms.ChoiceField(
label=_("Tipo de candidatos"), choices=TIPO_CHOICES
)
suplentes = forms.BooleanField(
label=_("Importar suplentes"), required=False
)
uf_importar = forms.ChoiceField(
label=_("Unidade Federativa"), choices=UF_CHOICES
)

4
sigi/apps/parlamentares/jobs/__init__.py

@ -0,0 +1,4 @@
from django.conf import settings
import_path = settings.MEDIA_ROOT / "parlamentares/parlamentar/import"
json_path = import_path / "config.json"

0
sigi/apps/parlamentares/jobs/daily/__init__.py

0
sigi/apps/parlamentares/jobs/hourly/__init__.py

0
sigi/apps/parlamentares/jobs/minutely/__init__.py

355
sigi/apps/parlamentares/jobs/minutely/importa_parlamentar.py

@ -0,0 +1,355 @@
import csv
import zipfile
from datetime import datetime
import json
import logging
from django.contrib.auth import get_user_model
from django.conf import settings
from django.db import transaction
from django.core.mail import send_mail
from django.template.loader import render_to_string
from django.utils.translation import gettext as _
from django_extensions.management.jobs import MinutelyJob
from sigi.apps.casas.models import Orgao
from sigi.apps.parlamentares.jobs import import_path, json_path
from sigi.apps.parlamentares.models import Parlamentar, Partido
class Job(MinutelyJob):
help = "Importa parlamentares de arquivo do TSE"
def execute(self):
json_data = self.get_json_data()
if json_data is None:
return
json_data["inicio_processamento"] = str(datetime.now())
result_final = []
# Importa parlamentares #
if "resultados" in json_data:
result = self.importa_parlamentares(
import_path / json_data["resultados"], json_data
)
if result["erros"]:
self.remove_files(json_data)
self.send_mail(result["erros"], json_data)
return
result_final.append(_("* IMPORTAÇÃO DOS PARLAMENTARES *"))
result_final.extend(result["infos"])
if "redes_sociais" in json_data:
result = self.importa_redes(
import_path / json_data["redes_sociais"],
json_data["codificacao"],
)
result_final.append(_("* IMPORTAÇÃO DAS REDES SOCIAIS *"))
result_final.extend(result["infos"])
result_final.append(_("* IMPORTAÇÃO DAS REDES SOCIAIS - ERROS *"))
result_final.extend(result["erros"])
if "fotos" in json_data:
result = self.importa_fotos(import_path / json_data["fotos"])
result_final.append(_("* IMPORTAÇÃO DAS FOTOS *"))
result_final.extend(result["infos"])
result_final.append(_("* IMPORTAÇÃO DAS FOTOS - ERROS *"))
result_final.extend(result["erros"])
self.remove_files(json_data)
self.send_mail(result_final, json_data)
return
def get_json_data(self):
if json_path.is_file():
data = json.loads(json_path.read_text())
json_path.unlink(missing_ok=True)
return data
return None
def remove_files(self, json_data):
if "resultados" in json_data:
(import_path / json_data["resultados"]).unlink(missing_ok=True)
if "redes_sociais" in json_data:
(import_path / json_data["redes_sociais"]).unlink(missing_ok=True)
if "fotos" in json_data:
(import_path / json_data["fotos"]).unlink(missing_ok=True)
def send_mail(self, result, json_data):
user = get_user_model().objects.get(id=int(json_data["user_id"]))
json_data["fim_processamento"] = str(datetime.now())
json_data["user"] = user.get_full_name()
del json_data["user_id"]
result = list(dict.fromkeys(result))
txt_message = "\n".join(result)
html_message = render_to_string(
"parlamentares/import_email.html",
{"result": result, "json_data": json_data},
)
recipient_list = [a[1] for a in settings.ADMINS].append(user.email)
result_file = import_path / "result.html"
result_file.write_text(html_message, encoding="utf-8")
send_mail(
subject="Resultados da importação de dados de parlamentares",
message=txt_message,
recipient_list=recipient_list,
html_message=html_message,
)
def importa_parlamentares(self, file_name, json_data):
def limpa_flag():
# Limpa o flag de importação para garantir que nada seja apagado #
# indevidamente #
Parlamentar.objects.all().update(flag_importa="")
def marcar_antigos():
if json_data["sigla_uf"] == "BR":
Parlamentar.objects.filter(
casa_legislativa__tipo__sigla__in=tipo_casa
).update(flag_importa="E")
else:
Parlamentar.objects.filter(
casa_legislativa__municipio__uf__sigla=json_data[
"sigla_uf"
],
casa_legislativa__tipo__sigla__in=tipo_casa,
).update(flag_importa="E")
def apagar_antigos():
Parlamentar.objects.filter(flag_importa="E").delete()
def apagar_novos():
Parlamentar.objects.filter(flag_importa="N").delete()
if json_data["tipo_candidatos"] == "D":
tipo_casa = ["AL", "CT"]
cargos = ["7", "8"] # Deputado Estadual e Distrital
else:
tipo_casa = ["CM"]
cargos = ["13"]
cod_situacao = ["1", "2", "3"] # Eleito, por qp, por média
if json_data["suplentes"]:
cod_situacao.append("5") # suplente
result = {"infos": [], "erros": []}
with open(file_name, "r", encoding=json_data["codificacao"]) as f:
if f.encoding != json_data["codificacao"]:
result["erros"].append(
f"Codificação de caracteres do arquivo {file_name} é "
f"{f.encoding}. Precisa converter para "
f"{json_data['codificacao']}."
)
reader = csv.DictReader(f, delimiter=";")
fields = {
"ANO_ELEICAO",
"SG_UE",
"NM_UE",
"CD_CARGO",
"SQ_CANDIDATO",
"NM_CANDIDATO",
"NM_URNA_CANDIDATO",
"NR_PARTIDO",
"NM_PARTIDO",
"CD_SIT_TOT_TURNO",
}
try:
fieldnames = reader.fieldnames
except Exception as e:
result["erros"].append(str(e))
fieldnames = []
if not fields.issubset(set(fieldnames)):
result["erros"].append(
"Nao foram encontrados todos os campos necessários no "
"arquivo. São esperados os seguintes campos: "
+ ", ".join(fields)
)
if result["erros"]:
return result
limpa_flag()
marcar_antigos()
skiped = 0
imported = 0
total = 0
apenas_verificar = False
for row in reader:
total += 1
if (total % 1000) == 0:
print(total)
if not (
row["CD_CARGO"] in cargos
and row["CD_SIT_TOT_TURNO"] in cod_situacao
):
skiped += 1
continue
cod_tse = row["SG_UE"]
legenda = int(row["NR_PARTIDO"])
# Hack para 2022 - fusão de partidos #
if legenda in [17, 25]:
legenda = 44
try:
if json_data["tipo_candidatos"] == "V":
casa = Orgao.objects.get(
municipio__codigo_tse=int(cod_tse),
tipo__sigla__in=tipo_casa,
)
else:
casa = Orgao.objects.get(
municipio__uf__sigla=cod_tse,
tipo__sigla__in=tipo_casa,
)
except:
# De agora em diante apenas procura erros, sem criar
# novos parlamentares, para agilizar o processo
apenas_verificar = True
result["erros"].append(
"Não foi encontrada a Casa Legislativa com "
f"o código TSE {cod_tse}. O nome do "
f"ente da federação é {row['NM_UE']}. "
"Corrija o cadastro do SIGI e tente novamente."
)
try:
partido = Partido.objects.get(legenda=legenda)
except:
# De agora em diante apenas procura erros, sem criar
# novos parlamentares, para agilizar o processo
apenas_verificar = True
result["erros"].append(
f"O partido {row['NM_PARTIDO']} de legenda "
f"{legenda} não foi encontrado no SIGI."
)
if not apenas_verificar:
Parlamentar.objects.update_or_create(
flag_importa="N",
sequencial_tse=row["SQ_CANDIDATO"],
ano_eleicao=row["ANO_ELEICAO"],
nome_completo=row["NM_CANDIDATO"],
nome_parlamentar=row["NM_URNA_CANDIDATO"],
partido=partido,
casa_legislativa=casa,
status_mandato="S"
if row["CD_SIT_TOT_TURNO"] == "5"
else "E",
)
imported += 1
if result["erros"]:
apagar_novos()
result["infos"] = []
else:
apagar_antigos()
limpa_flag()
result["infos"].append(f"Total de registros lidos: {total}")
result["infos"].append(f"Total de registros ignorados: {skiped}")
result["infos"].append(f"Total de registros importados: {imported}")
return result
def importa_redes(self, file_name, codificacao):
result = {"infos": [], "erros": []}
with open(file_name, "r", encoding=codificacao) as f:
if f.encoding != codificacao:
result["erros"].append(
f"Codificação de caracteres do arquivo {file_name} é "
f"{f.encoding}. Precisa converter para {codificacao}."
)
reader = csv.DictReader(f, delimiter=";")
fields = {
"SQ_CANDIDATO",
"DS_URL",
}
try:
fieldnames = reader.fieldnames
except Exception as e:
result["erros"].append(str(e))
fieldnames = []
if not fields.issubset(set(fieldnames)):
result["erros"].append(
"Nao foram encontrados todos os campos necessários no "
"arquivo. São esperados os seguintes campos: "
+ ", ".join(fields)
)
if result["erros"]:
return result
skiped = 0
imported = 0
total = 0
for row in reader:
total += 1
try:
parlamentar = Parlamentar.objects.get(
sequencial_tse=row["SQ_CANDIDATO"]
)
if (
row["DS_URL"]
not in parlamentar.redes_sociais.splitlines()
):
parlamentar.redes_sociais += "\n" + row["DS_URL"]
parlamentar.save()
imported += 1
else:
skiped += 1
except Parlamentar.DoesNotExist:
skiped += 1
result["infos"].append(f"Total de registros lidos: {total}")
result["infos"].append(f"Total de registros ignorados: {skiped}")
result["infos"].append(f"Total de registros importados: {imported}")
return result
def importa_fotos(self, file_name):
result = {"erros": [], "infos": []}
if not zipfile.is_zipfile(file_name):
result["erros"].append("Arquivo de fotos deve ser um ZIP")
return result
with zipfile.ZipFile(file_name, mode="r") as zip_file:
if zip_file.testzip() is not None:
result["erros"].append("Arquivo de fotos está corrompido")
return result
sequenciais = {n[3:14]: n for n in zip_file.namelist()}
print(f"Importar {len(sequenciais)} fotos")
parlamentares = Parlamentar.objects.filter(
sequencial_tse__in=sequenciais.keys()
)
total = len(zip_file.namelist())
imported = parlamentares.count()
skiped = total - imported
print(f"{imported} parlamentares encontrados")
if imported <= 0:
result["erros"].append(
"Nenhuma das fotos corresponde a algum parlamentar"
)
return result
relative_path = Parlamentar.foto.field.upload_to
foto_folder = settings.MEDIA_ROOT / relative_path
for parlamentar in parlamentares:
foto_nome = sequenciais[parlamentar.sequencial_tse]
try:
zip_file.extract(foto_nome, foto_folder)
parlamentar.foto.name = str(f"{relative_path}/{foto_nome}")
parlamentar.save()
except Exception as e:
result["erros"].append(str(e))
result["infos"].extend(
[
f"Total de fotos no arquivo: {total}",
f"Número de fotos importadas: {imported}",
f"Número de fotos ignoradas: {skiped}",
]
)
return result

0
sigi/apps/parlamentares/jobs/monthly/__init__.py

9
sigi/apps/parlamentares/jobs/sample.py

@ -0,0 +1,9 @@
from django_extensions.management.jobs import BaseJob
class Job(BaseJob):
help = "My sample job."
def execute(self):
# executing empty sample job
pass

0
sigi/apps/parlamentares/jobs/weekly/__init__.py

0
sigi/apps/parlamentares/jobs/yearly/__init__.py

53
sigi/apps/parlamentares/migrations/0001_initial.py

@ -79,6 +79,7 @@ class Migration(migrations.Migration):
models.ForeignKey( models.ForeignKey(
verbose_name="coliga\xe7\xe3o", verbose_name="coliga\xe7\xe3o",
to="parlamentares.Coligacao", to="parlamentares.Coligacao",
on_delete=models.CASCADE,
), ),
), ),
], ],
@ -120,7 +121,9 @@ class Migration(migrations.Migration):
), ),
( (
"casa_legislativa", "casa_legislativa",
models.ForeignKey(to="casas.CasaLegislativa"), models.ForeignKey(
to="casas.CasaLegislativa", on_delete=models.CASCADE
),
), ),
], ],
options={ options={
@ -159,10 +162,17 @@ class Migration(migrations.Migration):
verbose_name="afastado", verbose_name="afastado",
), ),
), ),
("cargo", models.ForeignKey(to="parlamentares.Cargo")), (
"cargo",
models.ForeignKey(
to="parlamentares.Cargo", on_delete=models.CASCADE
),
),
( (
"legislatura", "legislatura",
models.ForeignKey(to="parlamentares.Legislatura"), models.ForeignKey(
to="parlamentares.Legislatura", on_delete=models.CASCADE
),
), ),
], ],
options={}, options={},
@ -180,7 +190,12 @@ class Migration(migrations.Migration):
primary_key=True, primary_key=True,
), ),
), ),
("cargo", models.ForeignKey(to="parlamentares.Cargo")), (
"cargo",
models.ForeignKey(
to="parlamentares.Cargo", on_delete=models.CASCADE
),
),
], ],
options={ options={
"ordering": ("parlamentar",), "ordering": ("parlamentar",),
@ -206,6 +221,7 @@ class Migration(migrations.Migration):
models.ForeignKey( models.ForeignKey(
verbose_name="Casa Legislativa", verbose_name="Casa Legislativa",
to="casas.CasaLegislativa", to="casas.CasaLegislativa",
on_delete=models.CASCADE,
), ),
), ),
], ],
@ -346,13 +362,16 @@ class Migration(migrations.Migration):
), ),
( (
"legislatura", "legislatura",
models.ForeignKey(to="parlamentares.Legislatura"), models.ForeignKey(
to="parlamentares.Legislatura", on_delete=models.CASCADE
),
), ),
( (
"mesa_diretora", "mesa_diretora",
models.ForeignKey( models.ForeignKey(
verbose_name="Mesa Diretora", verbose_name="Mesa Diretora",
to="parlamentares.MesaDiretora", to="parlamentares.MesaDiretora",
on_delete=models.CASCADE,
), ),
), ),
], ],
@ -366,13 +385,17 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name="membromesadiretora", model_name="membromesadiretora",
name="mesa_diretora", name="mesa_diretora",
field=models.ForeignKey(to="parlamentares.MesaDiretora"), field=models.ForeignKey(
to="parlamentares.MesaDiretora", on_delete=models.CASCADE
),
preserve_default=True, preserve_default=True,
), ),
migrations.AddField( migrations.AddField(
model_name="membromesadiretora", model_name="membromesadiretora",
name="parlamentar", name="parlamentar",
field=models.ForeignKey(to="parlamentares.Parlamentar"), field=models.ForeignKey(
to="parlamentares.Parlamentar", on_delete=models.CASCADE
),
preserve_default=True, preserve_default=True,
), ),
migrations.AlterUniqueTogether( migrations.AlterUniqueTogether(
@ -382,13 +405,17 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name="mandato", model_name="mandato",
name="parlamentar", name="parlamentar",
field=models.ForeignKey(to="parlamentares.Parlamentar"), field=models.ForeignKey(
to="parlamentares.Parlamentar", on_delete=models.CASCADE
),
preserve_default=True, preserve_default=True,
), ),
migrations.AddField( migrations.AddField(
model_name="mandato", model_name="mandato",
name="partido", name="partido",
field=models.ForeignKey(to="parlamentares.Partido"), field=models.ForeignKey(
to="parlamentares.Partido", on_delete=models.CASCADE
),
preserve_default=True, preserve_default=True,
), ),
migrations.AlterUniqueTogether( migrations.AlterUniqueTogether(
@ -398,13 +425,17 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name="composicaocoligacao", model_name="composicaocoligacao",
name="partido", name="partido",
field=models.ForeignKey(to="parlamentares.Partido"), field=models.ForeignKey(
to="parlamentares.Partido", on_delete=models.CASCADE
),
preserve_default=True, preserve_default=True,
), ),
migrations.AddField( migrations.AddField(
model_name="coligacao", model_name="coligacao",
name="legislatura", name="legislatura",
field=models.ForeignKey(to="parlamentares.Legislatura"), field=models.ForeignKey(
to="parlamentares.Legislatura", on_delete=models.CASCADE
),
preserve_default=True, preserve_default=True,
), ),
] ]

6
sigi/apps/parlamentares/migrations/0002_auto_20210406_1945.py

@ -15,14 +15,16 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name="legislatura", model_name="legislatura",
name="casa_legislativa", name="casa_legislativa",
field=models.ForeignKey(to="casas.Orgao"), field=models.ForeignKey(to="casas.Orgao", on_delete=models.CASCADE),
preserve_default=True, preserve_default=True,
), ),
migrations.AlterField( migrations.AlterField(
model_name="mesadiretora", model_name="mesadiretora",
name="casa_legislativa", name="casa_legislativa",
field=models.ForeignKey( field=models.ForeignKey(
verbose_name="Casa Legislativa", to="casas.Orgao" verbose_name="Casa Legislativa",
to="casas.Orgao",
on_delete=models.CASCADE,
), ),
preserve_default=True, preserve_default=True,
), ),

98
sigi/apps/parlamentares/migrations/0004_partido_legenda_alter_cargo_id_alter_coligacao_id_and_more.py

@ -0,0 +1,98 @@
# Generated by Django 4.0.4 on 2022-05-28 13:03
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('parlamentares', '0003_auto_20210416_0841'),
]
operations = [
migrations.AddField(
model_name='partido',
name='legenda',
field=models.PositiveIntegerField(default=0, verbose_name='nº da legenda'),
),
migrations.AlterField(
model_name='cargo',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
migrations.AlterField(
model_name='coligacao',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
migrations.AlterField(
model_name='composicaocoligacao',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
migrations.AlterField(
model_name='legislatura',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
migrations.AlterField(
model_name='mandato',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
migrations.AlterField(
model_name='membromesadiretora',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
migrations.AlterField(
model_name='mesadiretora',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
migrations.AlterField(
model_name='parlamentar',
name='email',
field=models.EmailField(blank=True, max_length=254, verbose_name='e-mail'),
),
migrations.AlterField(
model_name='parlamentar',
name='foto',
field=models.ImageField(blank=True, height_field='foto_altura', upload_to='fotos/parlamentares', width_field='foto_largura'),
),
migrations.AlterField(
model_name='parlamentar',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
migrations.AlterField(
model_name='parlamentar',
name='sexo',
field=models.CharField(choices=[('M', 'Masculino'), ('F', 'Feminino')], max_length=1),
),
migrations.AlterField(
model_name='partido',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
migrations.AlterField(
model_name='partido',
name='nome',
field=models.CharField(max_length=50, verbose_name='nome'),
),
migrations.AlterField(
model_name='partido',
name='sigla',
field=models.CharField(max_length=20, verbose_name='silga'),
),
migrations.AlterField(
model_name='sessaolegislativa',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
migrations.AlterField(
model_name='sessaolegislativa',
name='tipo',
field=models.CharField(choices=[('O', 'Ordinária'), ('E', 'Extraordinária')], default='O', max_length=1),
),
]

122
sigi/apps/parlamentares/migrations/0005_remove_coligacao_legislatura_and_more.py

@ -0,0 +1,122 @@
# Generated by Django 4.0.4 on 2022-05-28 13:44
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
("casas", "0023_funcionario_cpf_funcionario_identidade"),
(
"parlamentares",
"0004_partido_legenda_alter_cargo_id_alter_coligacao_id_and_more",
),
]
operations = [
migrations.RemoveField(
model_name="parlamentar",
name="pagina_web",
),
migrations.RemoveField(
model_name="parlamentar",
name="sexo",
),
migrations.AddField(
model_name="parlamentar",
name="casa_legislativa",
field=models.ForeignKey(
default=1,
on_delete=django.db.models.deletion.CASCADE,
to="casas.orgao",
verbose_name="casa legislativa",
),
preserve_default=False,
),
migrations.AddField(
model_name="parlamentar",
name="cpf",
field=models.CharField(
blank=True, max_length=20, verbose_name="CPF"
),
),
migrations.AddField(
model_name="parlamentar",
name="identidade",
field=models.CharField(
blank=True,
help_text="Informe o RG e o órgão emissor.",
max_length=30,
verbose_name="Identidade (RG)",
),
),
migrations.AddField(
model_name="parlamentar",
name="observacoes",
field=models.TextField(blank=True, verbose_name="observações"),
),
migrations.AddField(
model_name="parlamentar",
name="partido",
field=models.ForeignKey(
default=1,
on_delete=django.db.models.deletion.CASCADE,
to="parlamentares.partido",
verbose_name="partido",
),
preserve_default=False,
),
migrations.AddField(
model_name="parlamentar",
name="presidente",
field=models.BooleanField(default=False, verbose_name="presidente"),
),
migrations.AddField(
model_name="parlamentar",
name="redes_sociais",
field=models.TextField(
blank=True,
help_text="Colocar um por linha",
verbose_name="redes sociais",
),
),
migrations.AddField(
model_name="parlamentar",
name="telefones",
field=models.CharField(
blank=True, max_length=250, null=True, verbose_name="telefones"
),
),
migrations.AddField(
model_name="parlamentar",
name="ult_alteracao",
field=models.DateTimeField(
auto_now=True, null=True, verbose_name="última alteração"
),
),
migrations.DeleteModel(
name="Mandato",
),
migrations.DeleteModel(
name="MembroMesaDiretora",
),
migrations.DeleteModel(
name="Cargo",
),
migrations.DeleteModel(
name="SessaoLegislativa",
),
migrations.DeleteModel(
name="MesaDiretora",
),
migrations.DeleteModel(
name="ComposicaoColigacao",
),
migrations.DeleteModel(
name="Coligacao",
),
migrations.DeleteModel(
name="Legislatura",
),
]

18
sigi/apps/parlamentares/migrations/0006_alter_partido_sigla.py

@ -0,0 +1,18 @@
# Generated by Django 4.0.4 on 2022-05-29 13:43
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('parlamentares', '0005_remove_coligacao_legislatura_and_more'),
]
operations = [
migrations.AlterField(
model_name='partido',
name='sigla',
field=models.CharField(max_length=20, verbose_name='sigla'),
),
]

17
sigi/apps/parlamentares/migrations/0007_alter_parlamentar_options.py

@ -0,0 +1,17 @@
# Generated by Django 4.0.4 on 2022-06-18 13:23
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('parlamentares', '0006_alter_partido_sigla'),
]
operations = [
migrations.AlterModelOptions(
name='parlamentar',
options={'ordering': ('presidente', 'nome_completo'), 'verbose_name_plural': 'parlamentares'},
),
]

18
sigi/apps/parlamentares/migrations/0008_alter_parlamentar_foto.py

@ -0,0 +1,18 @@
# Generated by Django 4.0.4 on 2022-06-20 23:56
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('parlamentares', '0007_alter_parlamentar_options'),
]
operations = [
migrations.AlterField(
model_name='parlamentar',
name='foto',
field=models.ImageField(blank=True, height_field='foto_altura', upload_to='parlamentares/parlamentar/fotos', width_field='foto_largura'),
),
]

38
sigi/apps/parlamentares/migrations/0009_parlamentar_ano_eleicao_parlamentar_flag_importa_and_more.py

@ -0,0 +1,38 @@
# Generated by Django 4.0.4 on 2022-06-24 01:39
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('parlamentares', '0008_alter_parlamentar_foto'),
]
operations = [
migrations.AddField(
model_name='parlamentar',
name='ano_eleicao',
field=models.PositiveIntegerField(blank=True, null=True, verbose_name='Ano de eleição'),
),
migrations.AddField(
model_name='parlamentar',
name='flag_importa',
field=models.CharField(blank=True, default='', editable=False, max_length=1),
),
migrations.AddField(
model_name='parlamentar',
name='sequencial_tse',
field=models.CharField(blank=True, default='', editable=False, max_length=20, verbose_name='Sequencial TSE'),
),
migrations.AddField(
model_name='parlamentar',
name='status_mandato',
field=models.CharField(choices=[('E', 'Em exercício'), ('S', 'Suplente'), ('I', 'Inativo')], default='E', max_length=1, verbose_name='status do mandato'),
),
migrations.AlterField(
model_name='parlamentar',
name='foto',
field=models.ImageField(blank=True, height_field='foto_altura', max_length=200, upload_to='parlamentares/parlamentar/fotos', width_field='foto_largura'),
),
]

17
sigi/apps/parlamentares/migrations/0010_alter_parlamentar_options.py

@ -0,0 +1,17 @@
# Generated by Django 4.0.4 on 2022-06-29 02:10
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('parlamentares', '0009_parlamentar_ano_eleicao_parlamentar_flag_importa_and_more'),
]
operations = [
migrations.AlterModelOptions(
name='parlamentar',
options={'ordering': ('status_mandato', 'presidente', 'nome_completo'), 'verbose_name_plural': 'parlamentares'},
),
]

245
sigi/apps/parlamentares/models.py

@ -1,219 +1,104 @@
# -*- coding: utf-8 -*-
from django.db import models from django.db import models
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from sigi.apps.casas.models import Orgao from sigi.apps.casas.models import Orgao
class Partido(models.Model): class Partido(models.Model):
nome = models.CharField(max_length=50) nome = models.CharField(_("nome"), max_length=50)
sigla = models.CharField(max_length=10) sigla = models.CharField(_("sigla"), max_length=20)
legenda = models.PositiveIntegerField(_("nº da legenda"), default=0)
class Meta: class Meta:
ordering = ("nome",) ordering = ("nome",)
def __unicode__(self): def __str__(self):
return "%s (%s)" % (unicode(self.nome), unicode(self.sigla)) return _(f"{self.sigla} - {self.nome}")
class Parlamentar(models.Model): class Parlamentar(models.Model):
SEXO_CHOICES = ( STATUS_CHOICE = (
("M", _("Masculino")), ("E", _("Em exercício")),
("F", _("Feminino")), ("S", _("Suplente")),
("I", _("Inativo")),
)
casa_legislativa = models.ForeignKey(
Orgao, verbose_name=_("casa legislativa"), on_delete=models.CASCADE
)
partido = models.ForeignKey(
Partido, verbose_name=_("partido"), on_delete=models.CASCADE
)
ano_eleicao = models.PositiveIntegerField(
_("Ano de eleição"), blank=True, null=True
)
status_mandato = models.CharField(
_("status do mandato"), max_length=1, choices=STATUS_CHOICE, default="E"
) )
presidente = models.BooleanField(_("presidente"), default=False)
nome_completo = models.CharField(max_length=128) nome_completo = models.CharField(max_length=128)
nome_parlamentar = models.CharField(max_length=35, blank=True) nome_parlamentar = models.CharField(max_length=35, blank=True)
foto = models.ImageField( foto = models.ImageField(
upload_to="fotos/parlamentares", max_length=200,
upload_to="parlamentares/parlamentar/fotos",
width_field="foto_largura", width_field="foto_largura",
height_field="foto_altura", height_field="foto_altura",
blank=True, blank=True,
) )
foto_largura = models.SmallIntegerField(editable=False, null=True) foto_largura = models.SmallIntegerField(editable=False, null=True)
foto_altura = models.SmallIntegerField(editable=False, null=True) foto_altura = models.SmallIntegerField(editable=False, null=True)
sexo = models.CharField(
max_length=1,
choices=SEXO_CHOICES,
)
data_nascimento = models.DateField( data_nascimento = models.DateField(
_("data de nascimento"), _("data de nascimento"),
blank=True, blank=True,
null=True, null=True,
) )
email = models.EmailField(_("e-mail"), blank=True) cpf = models.CharField(_("CPF"), max_length=20, blank=True)
pagina_web = models.URLField(_("página web"), blank=True) identidade = models.CharField(
_("Identidade (RG)"),
class Meta: max_length=30,
ordering = ("nome_completo",)
verbose_name_plural = _("parlamentares")
def __unicode__(self):
if self.nome_parlamentar:
return self.nome_parlamentar
return self.nome_completo
class Mandato(models.Model):
SUPLENCIA_CHOICES = (
("T", _("Titular")),
("S", _("Suplente")),
)
parlamentar = models.ForeignKey(Parlamentar, on_delete=models.CASCADE)
legislatura = models.ForeignKey(
"parlamentares.Legislatura", on_delete=models.CASCADE
)
partido = models.ForeignKey(Partido, on_delete=models.CASCADE)
cargo = models.ForeignKey("parlamentares.Cargo", on_delete=models.PROTECT)
inicio_mandato = models.DateField(_("início de mandato"))
fim_mandato = models.DateField(_("fim de mandato"))
is_afastado = models.BooleanField(
_("afastado"),
default=False,
help_text=_("Marque caso parlamentar não esteja ativo."),
)
# suplencia = models.CharField(
# _('suplência'),
# max_length=1,
# choices=SUPLENCIA_CHOICES,
# )
def __unicode__(self):
return str(self.id)
class Legislatura(models.Model):
casa_legislativa = models.ForeignKey(Orgao, on_delete=models.CASCADE)
numero = models.PositiveSmallIntegerField(_("número legislatura"))
data_inicio = models.DateField(_("início"))
data_fim = models.DateField(_("fim"))
data_eleicao = models.DateField(_("data da eleição"))
total_parlamentares = models.PositiveIntegerField(
_("Total de parlamentares")
)
casa_legislativa.convenio_uf_filter = True
casa_legislativa.convenio_cl_tipo_filter = True
class Meta:
unique_together = ("casa_legislativa", "numero")
ordering = ["casa_legislativa__municipio__uf__sigla", "-data_inicio"]
def __unicode__(self):
return _(
"%(number)sª legislatura da %(parliament)s (%(initial_year)s-%(final_year)s)"
) % dict(
number=self.numero,
parliament=self.casa_legislativa.__unicode__(),
initial_year=self.data_inicio.year,
final_year=self.data_fim.year,
)
class Coligacao(models.Model):
nome = models.CharField(max_length=50)
legislatura = models.ForeignKey(Legislatura, on_delete=models.CASCADE)
numero_votos = models.PositiveIntegerField(
_("número de votos"),
blank=True, blank=True,
null=True, help_text=_("Informe o RG e o órgão emissor."),
) )
telefones = models.CharField(
class Meta: _("telefones"), max_length=250, null=True, blank=True
ordering = ("legislatura", "nome")
verbose_name = _("coligação")
verbose_name_plural = _("coligações")
def __unicode__(self):
return self.nome
class ComposicaoColigacao(models.Model):
coligacao = models.ForeignKey(
Coligacao, on_delete=models.CASCADE, verbose_name=_("coligação")
)
partido = models.ForeignKey(
"parlamentares.Partido", on_delete=models.CASCADE
) )
email = models.EmailField(_("e-mail"), blank=True)
class Meta: redes_sociais = models.TextField(
verbose_name = _("composição da coligação") _("redes sociais"), help_text=_("Colocar um por linha"), blank=True
verbose_name_plural = _("composições das coligações")
def __unicode__(self):
return str(self.id)
class SessaoLegislativa(models.Model):
SESSAO_CHOICES = (
("O", _("Ordinária")),
("E", _("Extraordinária")),
)
numero = models.PositiveSmallIntegerField(
_("número da sessão"), unique=True
) )
mesa_diretora = models.ForeignKey( ult_alteracao = models.DateTimeField(
"MesaDiretora", _("última alteração"),
on_delete=models.PROTECT, null=True,
verbose_name=_("Mesa Diretora"), blank=True,
editable=True,
auto_now=True,
) )
legislatura = models.ForeignKey(Legislatura, on_delete=models.CASCADE) observacoes = models.TextField(_("observações"), blank=True)
tipo = models.CharField(max_length=1, choices=SESSAO_CHOICES, default="O") sequencial_tse = models.CharField(
data_inicio = models.DateField(_("início")) _("Sequencial TSE"),
data_fim = models.DateField(_("fim")) max_length=20,
data_inicio_intervalo = models.DateField( blank=True,
_("início de intervalo"), blank=True, null=True default="",
editable=False,
) )
data_fim_intervalo = models.DateField( flag_importa = models.CharField(
_("fim de intervalo"), blank=True, null=True max_length=1, blank=True, default="", editable=False
) )
class Meta: class Meta:
ordering = ("legislatura", "numero") ordering = (
verbose_name = _("Sessão Legislativa") "status_mandato",
verbose_name_plural = _("Sessões Legislativas") "presidente",
"nome_completo",
def __unicode__(self): )
return str(self.numero) verbose_name_plural = _("parlamentares")
class MesaDiretora(models.Model):
casa_legislativa = models.ForeignKey(
"casas.Orgao",
on_delete=models.CASCADE,
verbose_name=_("Casa Legislativa"),
)
class Meta:
verbose_name = _("Mesa Diretora")
verbose_name_plural = _("Mesas Diretoras")
def __unicode__(self):
return _("Mesa Diretora da %s") % unicode(self.casa_legislativa)
class Cargo(models.Model):
descricao = models.CharField(_("descrição"), max_length=30)
class Meta:
ordering = ("descricao",)
def __unicode__(self):
return self.descricao
class MembroMesaDiretora(models.Model):
parlamentar = models.ForeignKey(
"parlamentares.Parlamentar", on_delete=models.CASCADE
)
cargo = models.ForeignKey(Cargo, on_delete=models.PROTECT)
mesa_diretora = models.ForeignKey(MesaDiretora, on_delete=models.CASCADE)
class Meta: def __str__(self):
ordering = ("parlamentar",) if self.nome_parlamentar:
unique_together = ("cargo", "mesa_diretora") return self.nome_parlamentar
verbose_name = _("membro de Mesa Diretora") return self.nome_completo
verbose_name_plural = _("membros de Mesa Diretora")
def __unicode__(self): def save(self, *args, **kwargs):
return "%s (%s)" % (unicode(self.parlamentar), unicode(self.cargo)) if self.presidente:
self.casa_legislativa.parlamentar_set.filter(
presidente=True
).update(presidente=False)
return super().save(*args, **kwargs)

11
sigi/apps/parlamentares/templates/admin/parlamentares/parlamentar/cart/change_list_import_cart_export.html

@ -0,0 +1,11 @@
{% extends "admin/cart/change_list_cart_export.html" %}
{% load admin_urls i18n %}
{% block object-tools-items %}
<li>
<a class="btn-floating tooltipped waves-effect waves-light" href="{% url opts|admin_urlname:'import' %}{{cl.get_query_string}}" data-position="left" data-tooltip="{% trans "Importar dados do TSE" %}">
<i class="material-icons">file_upload</i>
</a>
</li>
{{ block.super }}
{% endblock %}

1
sigi/apps/parlamentares/templates/admin/parlamentares/parlamentar/change_list.html

@ -1 +0,0 @@
{% extends "change_list_with_cart.html" %}

94
sigi/apps/parlamentares/templates/parlamentares/import.html

@ -0,0 +1,94 @@
{% extends "admin/base_site.html" %}
{% load i18n admin_urls %}
{% block extrastyle %}
{{ block.super }}
<style>
#content {
display: block;
}
.submit-row>a {
color: #fff;
}
</style>
{% endblock %}
{% block breadcrumbs %}{% endblock %}
{% block content_title %}
<h4>{% trans "Importar dados do TSE" %}</h4>
{% endblock %}
{% block content %}
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<div class="container">
{% if last_result.exists %}
<div class="breadcrumbs">
<a href="{{ MEDIA_URL }}parlamentares/parlamentar/import/result.html" target="_blank">
<i class="material-icons left">visibility</i>
{% trans "Visualizar o resultado da última importação" %}
</a>
</div>
{% endif %}
{{ form }}
<div class="submit-row">
<button class="btn waves-effect waves-light" type="submit" name="import">
<i class="material-icons left">done</i>
{% trans "Agendar importação" %}
</button>
<a class="btn waves-effect waves-light" href="{% url opts|admin_urlname:'changelist' %}">
<i class="material-icons left">navigate_before</i>
{% trans "Voltar" %}
</a>
</div>
</form>
<div class="container">
<h6>{% trans "ATENÇÃO" %}</h6>
<ul class="collection">
<li class="collection-item">
{% blocktrans %}
A importação é um processo demorado, principalmente se o arquivo for
muito grande. Por isso a importação não será realizada imediatamente
agora, mas de forma assíncrona. Quando o sistema concluir o processo de
importação, um e-mail com um resumo dos resultados será enviado para
você e para os administradores do SIGI.{% endblocktrans %}
</li>
{% if last_result.exists %}
<li class="collection-item">
{% blocktrans %}
O resumo dos resultados da última importação realizada pelo sistema
pode ser{% endblocktrans %}
<a href="{{ MEDIA_URL }}parlamentares/parlamentar/import/result.html" target="_blank">
{% trans "visualizado aqui" %}
</a>
</li>
{% endif %}
<li class="collection-item">
{% trans "Os arquivos de resultados das eleiçoes podem ser encontrados no" %}
<a href="https://dadosabertos.tse.jus.br/" target="_blank">{% trans "Portal de dados abertos do TSE" %}</a>
</li>
<li class="collection-item">
{% blocktrans %}
Os arquivos no formato CSV devem ser
<em>EXTRAÍDOS</em>
do ZIP baixado do TSE.
Não envie o ZIP inteiro, pois o SIGI não consegue descompactar.
Já os arquivos que contêm as fotos
<em>DEVEM</em>
ser enviados em formato ZIP.{% endblocktrans %}
</li>
<li class="collection-item">
{% blocktrans %}
O CSV do TSE vem com os caracteres codificados em 'LATIN 1' (iso-8859-1).
O SIGI trabalha com codificação UTF-8. O SIGI tenta fazer a conversão
se você selecionar LATIN 1, mas o ideal é que você faça a conversão
manualmente antes de importar. Para isso, pode usar o Excel ou
Libreoffice-calc.{% endblocktrans %}
<blockquote>
{% trans "Se estiver usando Linux, comande:" %}
<br>
<code>iconv -f iso8859-1 -t utf-8 arquivo_tse.csv -o arquivo_convertido.csv</code>
</blockquote>
</li>
</ul>
</div>
{% endblock %}

36
sigi/apps/parlamentares/templates/parlamentares/import_email.html

@ -0,0 +1,36 @@
{% load i18n %}
<!DOCTYPE html>
{% get_current_language as LANGUAGE_CODE %}{% get_current_language_bidi as LANGUAGE_BIDI %}
<html
lang="{{ LANGUAGE_CODE|default:"
en-us
" }}"
dir="{{ LANGUAGE_BIDI|yesno:'rtl,ltr,auto' }}"
>
<head>
<meta charset="utf-8">
</head>
<body>
<h4>Resultado da importação de dados de parlamentares</h4>
<table>
{% for key, value in json_data.items %}
<tr>
<th>{{ key }}</th>
<td>{{ value }}</td>
</tr>
{% endfor %}
</table>
<br>
<ul>
{% for row in result %}
{% if row|first == '*' %}
<li>
<h5>{{ row }}</h5>
</li>
{% else %}
<li>{{ row }}</li>
{% endif %}
{% endfor %}
</ul>
</body>
</html>

37
sigi/apps/parlamentares/templates/parlamentares/import_result.html

@ -0,0 +1,37 @@
{% extends "admin/base_site.html" %}
{% load i18n admin_urls %}
{% block extrastyle %}
{{ block.super }}
<style>
#content {
display: block;
}
.submit-row>a {
color: #fff;
}
</style>
{% endblock %}
{% block breadcrumbs %}{% endblock %}
{% block content_title %}
<h4>{% trans "Importação de dados do TSE agendada" %}</h4>
{% endblock %}
{% block content %}
<div class="container">
<p>
{% blocktrans %}
A importação foi agendada com sucesso e o arquivo do TSE
será processado em breve. Ao concluir a importação, o SIGI
enviará um e-mail para você e para os administradores do
sistema, avisando dos resultados do processo.
{% endblocktrans %}
</p>
</div>
<div class="submit-row">
<a class="btn waves-effect waves-light" href="{% url opts|admin_urlname:'changelist' %}">
<i class="material-icons left">navigate_before</i>
{% trans "Voltar" %}
</a>
</div>
{% endblock %}

32
sigi/apps/parlamentares/urls.py

@ -1,26 +1,10 @@
# coding: utf-8 from django.urls import path, include
from django.conf.urls import patterns, url from sigi.apps.parlamentares import views
urlpatterns = patterns( urlpatterns = [
"sigi.apps.parlamentares.views", path(
# Reports labels parlamentares "parlamentar_json/<int:casa_id>/",
url(r"^parlamentar/labels/$", "labels_report", name="labels-report-all"), views.parlamentar_json,
url( name="parlamentar-json",
r"^parlamentar/(?P<id>\w+)/labels/$",
"labels_report",
name="labels-report-id",
), ),
# Carrinho ]
url(
r"^parlamentar/carrinho/$",
"visualizar_carrinho",
name="visualizar-carrinho",
),
url(
r"^parlamentar/carrinho/deleta_itens_carrinho$",
"deleta_itens_carrinho",
name="deleta-itens-carrinho",
),
# A view excluir_carrinho n existe ainda.
# url(r'^parlamentar/carrinho/exluir_carrinho$', 'excluir_carrinho', name='excluir-carrinho'),
)

154
sigi/apps/parlamentares/views.py

@ -1,152 +1,22 @@
# coding: utf-8
import datetime
import csv
from django.template import Context, loader
from django.core.paginator import Paginator, InvalidPage, EmptyPage
from django.conf import settings from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.core.paginator import Paginator, InvalidPage, EmptyPage
from django.http import HttpResponse, HttpResponseRedirect, JsonResponse
from django.shortcuts import render, get_list_or_404 from django.shortcuts import render, get_list_or_404
from django.http import HttpResponse, HttpResponseRedirect from django.template import Context, loader, RequestContext
from django.views.decorators.csrf import csrf_protect from django.views.decorators.csrf import csrf_protect
from django.template import RequestContext
from sigi.apps.casas.models import Orgao from sigi.apps.casas.models import Orgao
from sigi.apps.parlamentares.models import Parlamentar from sigi.apps.parlamentares.models import Parlamentar
from sigi.apps.parlamentares.reports import ParlamentaresLabels
from geraldo.generators import PDFGenerator
from django.contrib.auth.decorators import login_required
def adicionar_parlamentar_carrinho(request, queryset=None, id=None):
if request.method == "POST":
ids_selecionados = request.POST.getlist("_selected_action")
if "carrinho_parlametar" not in request.session:
request.session["carrinho_parlamentar"] = ids_selecionados
else:
lista = request.session["carrinho_parlamentar"]
# Verifica se id já não está adicionado
for id in ids_selecionados:
if id not in lista:
lista.append(id)
request.session["carrinho_parlamentar"] = lista
@login_required @login_required
@csrf_protect def parlamentar_json(request, casa_id):
def visualizar_carrinho(request): return JsonResponse(
qs = carrinhoOrGet_for_qs(request)
paginator = Paginator(qs, 100)
# Make sure page request is an int. If not, deliver first page.
# Esteja certo de que o `page request` é um inteiro. Se não, mostre a primeira página.
try:
page = int(request.GET.get("page", "1"))
except ValueError:
page = 1
# Se o page request (9999) está fora da lista, mostre a última página.
try:
paginas = paginator.page(page)
except (EmptyPage, InvalidPage):
paginas = paginator.page(paginator.num_pages)
carrinhoIsEmpty = not ("carrinho_parlamentares" in request.session)
return render(
request,
"parlamentares/carrinho.html",
{ {
"carIsEmpty": carrinhoIsEmpty, p.nome_completo: {
"paginas": paginas, "foto": p.foto.url if p.foto else None,
"query_str": "?" + request.META["QUERY_STRING"], "id": p.id,
}, }
for p in Parlamentar.objects.filter(casa_legislativa_id=casa_id)
}
) )
def carrinhoOrGet_for_qs(request):
"""
Verifica se existe parlamentares na sessão se não verifica get e retorna qs correspondente.
"""
if "carrinho_parlamentar" in request.session:
ids = request.session["carrinho_parlamentar"]
qs = Parlamentar.objects.filter(pk__in=ids)
else:
qs = Parlamentar.objects.all()
if request.GET:
qs = get_for_qs(request.GET, qs)
return qs
def query_ordena(qs, o, ot):
list_display = ("nome_completo",)
aux = list_display[(int(o) - 1)]
if ot == "asc":
qs = qs.order_by(aux)
else:
qs = qs.order_by("-" + aux)
return qs
def get_for_qs(get, qs):
"""
Verifica atributos do GET e retorna queryset correspondente
"""
kwargs = {}
for k, v in get.iteritems():
if not (k == "page" or k == "pop" or k == "q"):
if not k == "o":
if k == "ot":
qs = query_ordena(qs, get["o"], get["ot"])
else:
kwargs[str(k)] = v
qs = qs.filter(**kwargs)
return qs
@login_required
def deleta_itens_carrinho(request):
"""
Deleta itens selecionados do carrinho
"""
if request.method == "POST":
ids_selecionados = request.POST.getlist("_selected_action")
if "carrinho_parlamentar" in request.session:
lista = request.session["carrinho_parlamentar"]
for item in ids_selecionados:
lista.remove(item)
if lista:
request.session["carrinho_parlamentar"] = lista
else:
del lista
del request.session["carrinho_parlamentar"]
return HttpResponseRedirect(".")
@login_required
def labels_report(request, id=None, formato="3x9_etiqueta"):
"""TODO: adicionar suporte para resultado de pesquisa do admin."""
if request.POST:
if "tipo_etiqueta" in request.POST:
tipo = request.POST["tipo_etiqueta"]
if id:
qs = Parlamentar.objects.filter(pk=id)
else:
qs = carrinhoOrGet_for_qs(request)
if not qs:
return HttpResponseRedirect("../")
response = HttpResponse(content_type="application/pdf")
response["Content-Disposition"] = "attachment; filename=casas.pdf"
report = ParlamentaresLabels(queryset=qs, formato=formato)
report.generate_by(PDFGenerator, filename=response)
return response

8
sigi/menu_conf.yaml

@ -31,8 +31,8 @@ main_menu:
- title: Assembléias Legislativas - title: Assembléias Legislativas
view_name: admin:casas_orgao_changelist view_name: admin:casas_orgao_changelist
querystr: tipo__sigla__exact=AL querystr: tipo__sigla__exact=AL
# - title: Parlamentares - title: Parlamentares
# view_name: admin:parlamentares_parlamentar_changelist view_name: admin:parlamentares_parlamentar_changelist
- title: Demais órgãos - title: Demais órgãos
view_name: admin:casas_orgao_changelist view_name: admin:casas_orgao_changelist
querystr: tipo__legislativo__exact=0 querystr: tipo__legislativo__exact=0
@ -115,5 +115,5 @@ main_menu:
view_name: admin:eventos_funcao_changelist view_name: admin:eventos_funcao_changelist
- title: Modelos de declaração - title: Modelos de declaração
view_name: admin:eventos_modelodeclaracao_changelist view_name: admin:eventos_modelodeclaracao_changelist
# - title: Partidos políticos - title: Partidos políticos
# view_name: admin:parlamentares_partido_changelist view_name: admin:parlamentares_partido_changelist

1
sigi/settings.py

@ -44,6 +44,7 @@ INSTALLED_APPS = [
"sigi.apps.home", "sigi.apps.home",
"sigi.apps.inventario", "sigi.apps.inventario",
"sigi.apps.ocorrencias", "sigi.apps.ocorrencias",
"sigi.apps.parlamentares",
"sigi.apps.servicos", "sigi.apps.servicos",
"sigi.apps.servidores", "sigi.apps.servidores",
"sigi.apps.utils", "sigi.apps.utils",

1
sigi/urls.py

@ -23,6 +23,7 @@ urlpatterns = [
path("admin/convenios/", include("sigi.apps.convenios.urls")), path("admin/convenios/", include("sigi.apps.convenios.urls")),
path("admin/ocorrencias/", include("sigi.apps.ocorrencias.urls")), path("admin/ocorrencias/", include("sigi.apps.ocorrencias.urls")),
path("eventos/", include("sigi.apps.eventos.urls")), path("eventos/", include("sigi.apps.eventos.urls")),
path("parlamentares/", include("sigi.apps.parlamentares.urls")),
path("admin/", admin.site.urls), path("admin/", admin.site.urls),
path("tinymce/", include("tinymce.urls")), path("tinymce/", include("tinymce.urls")),
path("", include("sigi.apps.home.urls")), path("", include("sigi.apps.home.urls")),

Loading…
Cancel
Save