mirror of https://github.com/interlegis/sapl.git
Browse Source
* 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ênciapull/1807/head
João Pedro Sconetto
7 years ago
committed by
Edward
20 changed files with 513 additions and 1 deletions
@ -0,0 +1 @@ |
|||||
|
default_app_config = 'sapl.audiencia.apps.AppConfig' |
@ -0,0 +1,3 @@ |
|||||
|
from sapl.utils import register_all_models_in_admin |
||||
|
|
||||
|
register_all_models_in_admin(__name__) |
@ -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') |
@ -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 |
@ -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'), |
||||
|
), |
||||
|
] |
@ -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'), |
||||
|
), |
||||
|
] |
@ -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'), |
||||
|
), |
||||
|
] |
@ -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,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) |
@ -0,0 +1,3 @@ |
|||||
|
from django.test import TestCase |
||||
|
|
||||
|
# Create your tests here. |
@ -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())), |
||||
|
] |
@ -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) |
||||
|
|
||||
|
|
@ -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 |
Loading…
Reference in new issue