Browse Source

1413-Audiência Pública (#1743)

* Adicionando estrutura inicial de audiência

* Corrige erro em test_urls.py

* Modelo de dados inicial de audiência

* Adições e correções

* Nova adições em audiência

Criação preliminar do form de audiência
Correções na model
Adição das urls do Crud
Adição da view inicial de audiência

* Correções para o funcionamento inicial de audiência

* Corrige erros nos testes

* Layout inicial de audiência

* Pequenas correções

* Modificação em Tipo Audiência e correções

* Corrige o update, delete e list view.

* Adição de valores padrão ao tipo no form

* Adição de matéria por meio de dados chave

* Corrige erro ao mostrar detalhes de audiência
pull/1807/head
João Pedro Sconetto 7 years ago
committed by Edward
parent
commit
44db001655
  1. 1
      sapl/audiencia/__init__.py
  2. 3
      sapl/audiencia/admin.py
  3. 8
      sapl/audiencia/apps.py
  4. 85
      sapl/audiencia/forms.py
  5. 62
      sapl/audiencia/migrations/0001_initial.py
  6. 20
      sapl/audiencia/migrations/0002_auto_20180302_0926.py
  7. 36
      sapl/audiencia/migrations/0003_auto_20180302_1111.py
  8. 31
      sapl/audiencia/migrations/0004_auto_20180305_1006.py
  9. 0
      sapl/audiencia/migrations/__init__.py
  10. 149
      sapl/audiencia/models.py
  11. 3
      sapl/audiencia/tests/test_audiencia.py
  12. 10
      sapl/audiencia/urls.py
  13. 55
      sapl/audiencia/views.py
  14. 3
      sapl/redireciona_urls/views.py
  15. 15
      sapl/rules/map_rules.py
  16. 1
      sapl/settings.py
  17. 24
      sapl/templates/audiencia/layouts.yaml
  18. 2
      sapl/templates/navbar.yaml
  19. 4
      sapl/test_urls.py
  20. 2
      sapl/urls.py

1
sapl/audiencia/__init__.py

@ -0,0 +1 @@
default_app_config = 'sapl.audiencia.apps.AppConfig'

3
sapl/audiencia/admin.py

@ -0,0 +1,3 @@
from sapl.utils import register_all_models_in_admin
register_all_models_in_admin(__name__)

8
sapl/audiencia/apps.py

@ -0,0 +1,8 @@
from django import apps
from django.utils.translation import ugettext_lazy as _
class AppConfig(apps.AppConfig):
name = 'sapl.audiencia'
label = 'audiencia'
verbose_name = _('Audiência Pública')

85
sapl/audiencia/forms.py

@ -0,0 +1,85 @@
from django import forms
from django.core.exceptions import ObjectDoesNotExist, ValidationError
from django.db import transaction
from django.utils.translation import ugettext_lazy as _
from sapl.audiencia.models import AudienciaPublica, TipoAudienciaPublica
from sapl.materia.models import MateriaLegislativa, TipoMateriaLegislativa
from sapl.utils import timezone
class AudienciaForm(forms.ModelForm):
data_atual = timezone.now()
tipo = forms.ModelChoiceField(required=True,
label='Tipo de Audiência Pública',
queryset=TipoAudienciaPublica.objects.all().order_by('nome'))
tipo_materia = forms.ModelChoiceField(
label=_('Tipo Matéria'),
required=True,
queryset=TipoMateriaLegislativa.objects.all(),
empty_label='Selecione',
)
numero_materia = forms.CharField(
label='Número Matéria', required=True)
ano_materia = forms.CharField(
label='Ano Matéria',
initial=int(data_atual.year),
required=True)
class Meta:
model = AudienciaPublica
fields = ['tipo', 'numero', 'nome',
'tema', 'data', 'hora_inicio', 'hora_fim',
'observacao', 'audiencia_cancelada', 'url_audio',
'url_video', 'upload_pauta', 'upload_ata',
'upload_anexo', 'tipo_materia', 'numero_materia',
'ano_materia']
def __init__(self, **kwargs):
super(AudienciaForm, self).__init__(**kwargs)
tipos = []
if not self.fields['tipo'].queryset:
tipos.append(TipoAudienciaPublica.objects.create(nome='Audiência Pública', tipo='A'))
tipos.append(TipoAudienciaPublica.objects.create(nome='Plebiscito', tipo='P'))
tipos.append(TipoAudienciaPublica.objects.create(nome='Referendo', tipo='R'))
tipos.append(TipoAudienciaPublica.objects.create(nome='Iniciativa Popular', tipo='I'))
for t in tipos:
t.save()
def clean(self):
cleaned_data = super(AudienciaForm, self).clean()
if not self.is_valid():
return cleaned_data
try:
materia = MateriaLegislativa.objects.get(
numero=self.cleaned_data['numero_materia'],
ano=self.cleaned_data['ano_materia'],
tipo=self.cleaned_data['tipo_materia'])
except ObjectDoesNotExist:
msg = _('A matéria a ser inclusa não existe no cadastro'
' de matérias legislativas.')
raise ValidationError(msg)
else:
cleaned_data['materia'] = materia
if self.cleaned_data['hora_inicio'] and self.cleaned_data['hora_fim']:
if (self.cleaned_data['hora_fim'] <
self.cleaned_data['hora_inicio']):
msg = _('A hora de fim não pode ser anterior a hora de ínicio')
raise ValidationError(msg)
return self.cleaned_data
@transaction.atomic()
def save(self, commit=True):
audiencia = super(AudienciaForm, self).save(commit)
return audiencia

62
sapl/audiencia/migrations/0001_initial.py

@ -0,0 +1,62 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.13 on 2018-03-01 17:01
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
import sapl.audiencia.models
import sapl.utils
class Migration(migrations.Migration):
initial = True
dependencies = [
('materia', '0025_auto_20180221_1649'),
]
operations = [
migrations.CreateModel(
name='AudienciaPublica',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('numero', models.PositiveIntegerField(blank=True, verbose_name='Número')),
('nome', models.CharField(max_length=100, verbose_name='Nome da Audiência Pública')),
('tema', models.CharField(max_length=100, verbose_name='Tema da Audiência Pública')),
('data', models.DateField(verbose_name='Data')),
('hora_inicio', models.CharField(max_length=5, verbose_name='Horário (hh:mm)')),
('hora_fim', models.CharField(max_length=5, verbose_name='Horário (hh:mm)')),
('observacao', models.CharField(blank=True, max_length=200, verbose_name='Observação')),
('audiencia_cancelada', models.BooleanField(choices=[(True, 'Sim'), (False, 'Não')], default=False, verbose_name='Audiência Cancela?')),
('url_audio', models.URLField(blank=True, max_length=150, verbose_name='URL Arquivo Áudio (Formatos MP3 / AAC)')),
('url_video', models.URLField(blank=True, max_length=150, verbose_name='URL Arquivo Vídeo (Formatos MP4 / FLV / WebM)')),
('upload_pauta', models.FileField(blank=True, null=True, upload_to=sapl.audiencia.models.pauta_upload_path, validators=[sapl.utils.restringe_tipos_de_arquivo_txt], verbose_name='Pauta da Audiência Pública')),
('upload_ata', models.FileField(blank=True, null=True, upload_to=sapl.audiencia.models.ata_upload_path, validators=[sapl.utils.restringe_tipos_de_arquivo_txt], verbose_name='Ata da Audiência Pública')),
('upload_anexo', models.FileField(blank=True, null=True, upload_to=sapl.audiencia.models.anexo_upload_path, verbose_name='Anexo da Audiência Pública')),
('materia', models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='materia.MateriaLegislativa', verbose_name='Matéria Legislativa')),
],
options={
'verbose_name': 'Audiência Pública',
'verbose_name_plural': 'Audiências Públicas',
'ordering': ['nome', 'numero', 'tipo'],
},
),
migrations.CreateModel(
name='TipoAudienciaPublica',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('nome', models.CharField(choices=[('A', 'Audiência Pública'), ('P', 'Plebiscito'), ('R', 'Referendo'), ('I', 'Iniciativa Popular')], max_length=1, verbose_name='Tipo de Audiência Pública')),
],
options={
'verbose_name': 'Tipo de Audiência Pública',
'verbose_name_plural': 'Tipos de Audiência Pública',
'ordering': ['nome'],
},
),
migrations.AddField(
model_name='audienciapublica',
name='tipo',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='audiencia.TipoAudienciaPublica', verbose_name='Tipo'),
),
]

20
sapl/audiencia/migrations/0002_auto_20180302_0926.py

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.13 on 2018-03-02 12:26
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('audiencia', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='audienciapublica',
name='observacao',
field=models.TextField(blank=True, max_length=500, verbose_name='Observação'),
),
]

36
sapl/audiencia/migrations/0003_auto_20180302_1111.py

@ -0,0 +1,36 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.13 on 2018-03-02 14:11
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('audiencia', '0002_auto_20180302_0926'),
]
operations = [
migrations.AlterField(
model_name='audienciapublica',
name='audiencia_cancelada',
field=models.BooleanField(choices=[(True, 'Sim'), (False, 'Não')], default=False, verbose_name='Audiência Cancelada?'),
),
migrations.AlterField(
model_name='audienciapublica',
name='hora_fim',
field=models.CharField(max_length=5, verbose_name='Horário Fim(hh:mm)'),
),
migrations.AlterField(
model_name='audienciapublica',
name='hora_inicio',
field=models.CharField(max_length=5, verbose_name='Horário Início(hh:mm)'),
),
migrations.AlterField(
model_name='audienciapublica',
name='materia',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='materia.MateriaLegislativa', verbose_name='Matéria Legislativa'),
),
]

31
sapl/audiencia/migrations/0004_auto_20180305_1006.py

@ -0,0 +1,31 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.13 on 2018-03-05 13:06
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('audiencia', '0003_auto_20180302_1111'),
]
operations = [
migrations.AddField(
model_name='tipoaudienciapublica',
name='tipo',
field=models.CharField(choices=[('A', 'Audiência Pública'), ('P', 'Plebiscito'), ('R', 'Referendo'), ('I', 'Iniciativa Popular')], default='A', max_length=1, verbose_name='Tipo de Audiência Pública'),
),
migrations.AlterField(
model_name='audienciapublica',
name='tipo',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='audiencia.TipoAudienciaPublica', verbose_name='Tipo de Audiência Pública'),
),
migrations.AlterField(
model_name='tipoaudienciapublica',
name='nome',
field=models.CharField(default='Audiência Pública', max_length=50, verbose_name='Nome do Tipo de Audiência Pública'),
),
]

0
sapl/audiencia/migrations/__init__.py

149
sapl/audiencia/models.py

@ -0,0 +1,149 @@
import reversion
from django.db import models
from django.utils.translation import ugettext_lazy as _
from model_utils import Choices
from sapl.materia.models import MateriaLegislativa
from sapl.parlamentares.models import (CargoMesa, Parlamentar)
from sapl.utils import (YES_NO_CHOICES, SaplGenericRelation,
restringe_tipos_de_arquivo_txt, texto_upload_path)
def get_audiencia_media_path(instance, subpath, filename):
return './sapl/audiencia/%s/%s/%s' % (instance.numero, subpath, filename)
def pauta_upload_path(instance, filename):
return texto_upload_path(
instance, filename, subpath='pauta', pk_first=True)
def ata_upload_path(instance, filename):
return texto_upload_path(instance, filename, subpath='ata', pk_first=True)
def anexo_upload_path(instance, filename):
return texto_upload_path(
instance, filename, subpath='anexo', pk_first=True)
@reversion.register()
class TipoAudienciaPublica(models.Model):
TIPO_AUDIENCIA_CHOICES = Choices(('A', 'audiencia', _('Audiência Pública')),
('P', 'plebiscito', _('Plebiscito')),
('R', 'referendo', _('Referendo')),
('I', 'iniciativa', _('Iniciativa Popular')))
nome = models.CharField(
max_length=50, verbose_name=_('Nome do Tipo de Audiência Pública'), default='Audiência Pública')
tipo = models.CharField(
max_length=1, verbose_name=_('Tipo de Audiência Pública'), choices=TIPO_AUDIENCIA_CHOICES, default='A')
class Meta:
verbose_name = _('Tipo de Audiência Pública')
verbose_name_plural = _('Tipos de Audiência Pública')
ordering = ['nome']
def __str__(self):
return self.nome
@reversion.register()
class AudienciaPublica(models.Model):
materia = models.ForeignKey(
MateriaLegislativa,
on_delete=models.PROTECT,
null=True,
blank=True,
verbose_name=_('Matéria Legislativa'))
tipo = models.ForeignKey(TipoAudienciaPublica,
on_delete=models.PROTECT,
null=True,
blank=True,
verbose_name=_('Tipo de Audiência Pública'))
numero = models.PositiveIntegerField(blank=True, verbose_name=_('Número'))
nome = models.CharField(
max_length=100, verbose_name=_('Nome da Audiência Pública'))
tema = models.CharField(
max_length=100, verbose_name=_('Tema da Audiência Pública'))
data = models.DateField(verbose_name=_('Data'))
hora_inicio = models.CharField(
max_length=5, verbose_name=_('Horário Início(hh:mm)'))
hora_fim = models.CharField(
max_length=5, verbose_name=_('Horário Fim(hh:mm)'))
observacao = models.TextField(
max_length=500, blank=True, verbose_name=_('Observação'))
audiencia_cancelada = models.BooleanField(
default=False,
choices=YES_NO_CHOICES,
verbose_name=_('Audiência Cancelada?'))
url_audio = models.URLField(
max_length=150, blank=True,
verbose_name=_('URL Arquivo Áudio (Formatos MP3 / AAC)'))
url_video = models.URLField(
max_length=150, blank=True,
verbose_name=_('URL Arquivo Vídeo (Formatos MP4 / FLV / WebM)'))
upload_pauta = models.FileField(
blank=True,
null=True,
upload_to=pauta_upload_path,
verbose_name=_('Pauta da Audiência Pública'),
validators=[restringe_tipos_de_arquivo_txt])
upload_ata = models.FileField(
blank=True,
null=True,
upload_to=ata_upload_path,
verbose_name=_('Ata da Audiência Pública'),
validators=[restringe_tipos_de_arquivo_txt])
upload_anexo = models.FileField(
blank=True,
null=True,
upload_to=anexo_upload_path,
verbose_name=_('Anexo da Audiência Pública'))
class Meta:
verbose_name = _('Audiência Pública')
verbose_name_plural = _('Audiências Públicas')
ordering = ['nome', 'numero', 'tipo']
def __str__(self):
return self.nome
def delete(self, using=None, keep_parents=False):
if self.upload_pauta:
self.upload_pauta.delete()
if self.upload_ata:
self.upload_ata.delete()
if self.upload_anexo:
self.upload_anexo.delete()
return models.Model.delete(
self, using=using, keep_parents=keep_parents)
def save(self, force_insert=False, force_update=False, using=None,
update_fields=None):
if not self.pk and (self.upload_pauta or self.upload_ata or
self.upload_anexo):
upload_pauta = self.upload_pauta
upload_ata = self.upload_ata
upload_anexo = self.upload_anexo
self.upload_pauta = None
self.upload_ata = None
self.upload_anexo = None
models.Model.save(self, force_insert=force_insert,
force_update=force_update,
using=using,
update_fields=update_fields)
self.upload_pauta = upload_pauta
self.upload_ata = upload_ata
self.upload_anexo = upload_anexo
return models.Model.save(self, force_insert=force_insert,
force_update=force_update,
using=using,
update_fields=update_fields)

3
sapl/audiencia/tests/test_audiencia.py

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

10
sapl/audiencia/urls.py

@ -0,0 +1,10 @@
from django.conf.urls import include, url
from sapl.audiencia.views import (index, AudienciaCrud)
from .apps import AppConfig
app_name = AppConfig.name
urlpatterns = [
url(r'^audiencia/', include(AudienciaCrud.get_urls())),
]

55
sapl/audiencia/views.py

@ -0,0 +1,55 @@
from django.shortcuts import render
from django.http import HttpResponse
from django.core.urlresolvers import reverse
from django.db.models import F
from django.views.decorators.clickjacking import xframe_options_exempt
from django.views.generic import ListView
from sapl.comissoes.forms import ParticipacaoCreateForm, ParticipacaoEditForm
from sapl.crud.base import RP_DETAIL, RP_LIST, Crud, CrudAux, MasterDetailCrud
from sapl.materia.models import MateriaLegislativa
from .forms import AudienciaForm
from .models import (AudienciaPublica, TipoAudienciaPublica)
def index(request):
return HttpResponse("Audiência Pública")
class AudienciaCrud(Crud):
model = AudienciaPublica
public = [RP_LIST, RP_DETAIL, ]
class BaseMixin(Crud.BaseMixin):
list_field_names = ['materia', 'tipo', 'numero', 'nome',
'data']
ordering = 'nome', 'numero', 'tipo', 'data'
class ListView(Crud.ListView):
paginate_by = 10
class CreateView(Crud.CreateView):
form_class = AudienciaForm
def form_valid(self, form):
return super(Crud.CreateView, self).form_valid(form)
class UpdateView(Crud.UpdateView):
form_class = AudienciaForm
def get_initial(self):
self.initial['tipo_materia'] = self.object.materia.tipo.id
self.initial['numero_materia'] = self.object.materia.numero
self.initial['ano_materia'] = self.object.materia.ano
return self.initial
class DeleteView(Crud.DeleteView):
pass
class DetailView(Crud.DetailView):
layout_key = 'AudienciaPublicaDetail'
@xframe_options_exempt
def get(self, request, *args, **kwargs):
return super().get(request, *args, **kwargs)

3
sapl/redireciona_urls/views.py

@ -7,6 +7,7 @@ from sapl.materia.apps import AppConfig as materiaConfig
from sapl.norma.apps import AppConfig as normaConfig
from sapl.parlamentares.apps import AppConfig as parlamentaresConfig
from sapl.sessao.apps import AppConfig as sessaoConfig
from sapl.audiencia.apps import AppConfig as audienciaConfig
from .exceptions import UnknownUrlNameError
@ -22,6 +23,7 @@ app_materia = materiaConfig.name
app_sessao = sessaoConfig.name
app_norma = normaConfig.name
app_relatorios = relatoriosConfig.name
app_audiencia = audienciaConfig.name
pesquisar_atas = (app_atas + ':atas')
presenca_sessao = (app_presenca_sessao + ':presenca_sessao')
@ -32,6 +34,7 @@ parlamentar_mesa_diretora = (app_parlamentares + ':mesa_diretora')
comissao_list = (app_comissoes + ':comissao_list')
comissao_detail = (app_comissoes + ':comissao_detail')
audiencia = (app_audiencia + ':audiencia')
reuniao_detail = (app_comissoes + ':reuniao_detail')
materialegislativa_detail = (app_materia + ':materialegislativa_detail')

15
sapl/rules/map_rules.py

@ -7,6 +7,7 @@ from sapl.norma import models as norma
from sapl.painel import models as painel
from sapl.parlamentares import models as parlamentares
from sapl.protocoloadm import models as protocoloadm
from sapl.audiencia import models as audiencia
from sapl.rules import (RP_ADD, RP_CHANGE, RP_DELETE, RP_DETAIL, RP_LIST,
SAPL_GROUP_ADMINISTRATIVO, SAPL_GROUP_ANONYMOUS,
SAPL_GROUP_AUTOR, SAPL_GROUP_COMISSOES,
@ -60,6 +61,15 @@ rules_group_administrativo = {
]
}
rules_group_audiencia = {
'group': SAPL_GROUP_GERAL,
'rules': [
(audiencia.AudienciaPublica, __base__),
(audiencia.TipoAudienciaPublica, __base__),
]
}
rules_group_protocolo = {
'group': SAPL_GROUP_PROTOCOLO,
'rules': [
@ -272,7 +282,8 @@ rules_group_geral = {
(compilacao.TipoDispositivoRelationship, []),
(compilacao.PerfilEstruturalTextoArticulado, []),
(audiencia.AudienciaPublica, __base__),
(audiencia.TipoAudienciaPublica, __base__),
@ -299,6 +310,7 @@ rules_group_geral['rules'] = (rules_group_geral['rules'] +
rules_group_administrativo['rules'] +
rules_group_protocolo['rules'] +
rules_group_comissoes['rules'] +
rules_group_audiencia['rules'] +
rules_group_materia['rules'] +
rules_group_norma['rules'] +
rules_group_sessao['rules'] +
@ -307,6 +319,7 @@ rules_group_geral['rules'] = (rules_group_geral['rules'] +
rules_patterns = [
rules_group_audiencia,
rules_group_administrativo,
rules_group_protocolo,
rules_group_comissoes,

1
sapl/settings.py

@ -47,6 +47,7 @@ else:
# SAPL business apps in dependency order
SAPL_APPS = (
'sapl.audiencia',
'sapl.base',
'sapl.crud',
'sapl.parlamentares',

24
sapl/templates/audiencia/layouts.yaml

@ -0,0 +1,24 @@
{% load i18n %}
AudienciaPublica:
{% trans 'Audiência Pública' %}:
- nome:10 numero
- tema
{% trans 'Dados' %}:
- tipo_materia numero_materia ano_materia
- tipo:4 data:2 hora_inicio:3 hora_fim:3
- url_audio url_video
- upload_pauta upload_ata upload_anexo
- observacao
- audiencia_cancelada
AudienciaPublicaDetail:
{% trans 'Audiência Pública' %}:
- nome:10 numero
- tema
{% trans 'Dados' %}:
- materia tipo
- data hora_inicio hora_fim
- url_audio url_video
- upload_pauta upload_ata upload_anexo
- observacao
- audiencia_cancelada

2
sapl/templates/navbar.yaml

@ -13,6 +13,8 @@
url: sapl.parlamentares:frente_list
- title: {% trans 'Parlamentares' %}
url: sapl.parlamentares:parlamentar_list
- title: {% trans 'Audiências Públicas' %}
url: sapl.audiencia:audienciapublica_list
- title: {% trans 'Protocolo' %}
check_permission: protocoloadm.list_protocolo

4
sapl/test_urls.py

@ -148,6 +148,10 @@ def test_crudaux_list_do_crud_esta_na_pagina_sistema(url_item, admin_client):
apps_url_patterns_prefixs_and_users = {
'audiencia': {
'prefixs': [
'/audiencia',
]},
'api': {
'prefixs': [
'/api/',

2
sapl/urls.py

@ -33,6 +33,7 @@ import sapl.protocoloadm.urls
import sapl.redireciona_urls.urls
import sapl.relatorios.urls
import sapl.sessao.urls
import sapl.audiencia.urls
urlpatterns = [
url(r'^$', TemplateView.as_view(template_name='index.html'),
@ -50,6 +51,7 @@ urlpatterns = [
url(r'', include(sapl.protocoloadm.urls)),
url(r'', include(sapl.compilacao.urls)),
url(r'', include(sapl.relatorios.urls)),
url(r'', include(sapl.audiencia.urls)),
# must come at the end
# so that base /sistema/ url doesn't capture its children

Loading…
Cancel
Save