From deee281954fae13049f40656bbebfc812dd7a8d2 Mon Sep 17 00:00:00 2001 From: Edward Oliveira Date: Tue, 23 Jun 2026 17:35:17 -0300 Subject: [PATCH 1/7] Fix ValueError in audiencia form and AttributeError on HEAD requests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - audiencia/forms.py: also catch ValueError when querying MateriaLegislativa by numero, so a non-numeric input (e.g. '36 e 61') surfaces as a form validation error instead of an unhandled 500. - utils.py: MultiFormatOutputMixin.render_to_response was doing getattr(request, request.method) which fails for HEAD requests because Django exposes request.GET/POST but not request.HEAD; remap HEAD → GET. Co-Authored-By: Claude Sonnet 4.6 --- sapl/audiencia/forms.py | 2 +- sapl/utils.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/sapl/audiencia/forms.py b/sapl/audiencia/forms.py index 211db9855..f1978032f 100755 --- a/sapl/audiencia/forms.py +++ b/sapl/audiencia/forms.py @@ -115,7 +115,7 @@ class AudienciaForm(FileFieldCheckMixin, forms.ModelForm): numero=materia, ano=ano_materia, tipo=tipo_materia) - except ObjectDoesNotExist: + except (ObjectDoesNotExist, ValueError): msg = _('A matéria %s nº %s/%s não existe no cadastro' ' de matérias legislativas.' % (tipo_materia, materia, ano_materia)) self.logger.warning( diff --git a/sapl/utils.py b/sapl/utils.py index ee97094aa..42d4b2c6e 100644 --- a/sapl/utils.py +++ b/sapl/utils.py @@ -1395,8 +1395,9 @@ class MultiFormatOutputMixin: return {fmt: fields for fmt in self.formats_impl} def render_to_response(self, context, **response_kwargs): - format_result = getattr(self.request, self.request.method).get( - 'format', None) + # HEAD shares query params with GET; Django has no request.HEAD attribute. + _method = 'GET' if self.request.method == 'HEAD' else self.request.method + format_result = getattr(self.request, _method).get('format', None) if format_result: if format_result not in self.formats_impl: From 3b5e3747b7d7b5223e03f2fde2fa0fb2bdb91d10 Mon Sep 17 00:00:00 2001 From: Edward Oliveira Date: Fri, 10 Jul 2026 23:28:27 -0300 Subject: [PATCH 2/7] =?UTF-8?q?Fix=20duplicate=20normas/mat=C3=A9rias=20in?= =?UTF-8?q?=20author=20reports=20and=20prevent=20unfiltered=20queries?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RelatorioNormasPorAutorFilterSet: chaining .filter(autorianorma__primeiro_autor=True) after the filterset's own autorianorma__autor filter created a second JOIN, causing each norma to appear once per Autoria with primeiro_autor=True. Removing that filter leaves a single JOIN so DISTINCT works correctly. Both filtersets now return none() when no author is selected, preventing a full-table scan with an unguarded ORDER BY on a related field. Both views pass the Autor object (not str) into context so the templates can compare autoria.autor != autor by PK. Both templates now show only co-authors (those different from the filtered author) in the Coautor(es) column, mirroring each other. RelatorioMateriasPorAutorView: removed the OrderedDict.fromkeys() deduplication workaround that was masking the same underlying JOIN issue. Co-Authored-By: Claude Sonnet 4.6 --- sapl/relatorios/forms.py | 6 +++++- sapl/relatorios/views.py | 9 ++++----- .../RelatorioMateriasPorAutor_filter.html | 14 ++++++-------- .../relatorios/RelatorioNormasPorAutor_filter.html | 8 +++----- 4 files changed, 18 insertions(+), 19 deletions(-) diff --git a/sapl/relatorios/forms.py b/sapl/relatorios/forms.py index 23145f899..f659c254c 100644 --- a/sapl/relatorios/forms.py +++ b/sapl/relatorios/forms.py @@ -633,6 +633,8 @@ class RelatorioMateriasPorAutorFilterSet(django_filters.FilterSet): @property def qs(self): parent = super().qs + if not self.data.get('autoria__autor'): + return parent.none() return parent.distinct().order_by('-ano', '-numero', 'tipo', 'autoria__autor', '-autoria__primeiro_autor') class Meta(FilterOverridesMetaMixin): @@ -744,7 +746,9 @@ class RelatorioNormasPorAutorFilterSet(django_filters.FilterSet): @property def qs(self): parent = super().qs - return parent.distinct().filter(autorianorma__primeiro_autor=True) \ + if not self.data.get('autorianorma__autor'): + return parent.none() + return parent.distinct() \ .order_by('autorianorma__autor', '-autorianorma__primeiro_autor', 'tipo', '-ano', '-numero') class Meta(FilterOverridesMetaMixin): diff --git a/sapl/relatorios/views.py b/sapl/relatorios/views.py index bc28b3ffc..60ba1635e 100755 --- a/sapl/relatorios/views.py +++ b/sapl/relatorios/views.py @@ -2660,7 +2660,6 @@ class RelatorioMateriasPorAutorView(RelatorioMixin, FilterView): return context qs = context['object_list'] - context['materias_resultado'] = list(collections.OrderedDict.fromkeys(qs)) context['qtdes'] = num_materias_por_tipo(qs) qr = self.request.GET.copy() @@ -2675,9 +2674,9 @@ class RelatorioMateriasPorAutorView(RelatorioMixin, FilterView): context['tipo'] = '' if self.request.GET['autoria__autor']: autor = int(self.request.GET['autoria__autor']) - context['autor'] = (str(Autor.objects.get(id=autor))) + context['autor'] = Autor.objects.get(id=autor) else: - context['autor'] = '' + context['autor'] = None context['periodo'] = ( self.request.GET['data_apresentacao_0'] + ' - ' + self.request.GET['data_apresentacao_1']) @@ -2921,9 +2920,9 @@ class RelatorioNormasPorAutorView(RelatorioMixin, FilterView): if self.request.GET['autorianorma__autor']: autor = int(self.request.GET['autorianorma__autor']) - context['autor'] = (str(Autor.objects.get(id=autor))) + context['autor'] = Autor.objects.get(id=autor) else: - context['autor'] = '' + context['autor'] = None context['periodo'] = ( self.request.GET['data_0'] + ' - ' + self.request.GET['data_1']) diff --git a/sapl/templates/relatorios/RelatorioMateriasPorAutor_filter.html b/sapl/templates/relatorios/RelatorioMateriasPorAutor_filter.html index 5acb303c1..dcd7287e0 100644 --- a/sapl/templates/relatorios/RelatorioMateriasPorAutor_filter.html +++ b/sapl/templates/relatorios/RelatorioMateriasPorAutor_filter.html @@ -17,7 +17,7 @@  Tipo de matéria: {{ tipo }}
 Data de apresentação: {{ periodo }}


- {% if materias_resultado %} + {% if object_list %} @@ -43,10 +43,10 @@ - + - {% for materia in materias_resultado %} + {% for materia in object_list %} diff --git a/sapl/templates/relatorios/RelatorioNormasPorAutor_filter.html b/sapl/templates/relatorios/RelatorioNormasPorAutor_filter.html index c3f045301..51c987c34 100644 --- a/sapl/templates/relatorios/RelatorioNormasPorAutor_filter.html +++ b/sapl/templates/relatorios/RelatorioNormasPorAutor_filter.html @@ -54,13 +54,11 @@ From 0caaf4d402b23c17ba142dd3a57278d386b1c740 Mon Sep 17 00:00:00 2001 From: Edward Oliveira Date: Sat, 11 Jul 2026 15:01:52 -0300 Subject: [PATCH 3/7] =?UTF-8?q?hot-fix:=20imprime=20somente=20cont=C3=A9ud?= =?UTF-8?q?o=20da=20ocorr=C3=AAncia?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../blocos_sessao_plenaria/ocorrencias_da_sessao.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sapl/templates/relatorios/blocos_sessao_plenaria/ocorrencias_da_sessao.html b/sapl/templates/relatorios/blocos_sessao_plenaria/ocorrencias_da_sessao.html index f37c899be..fc2016b83 100644 --- a/sapl/templates/relatorios/blocos_sessao_plenaria/ocorrencias_da_sessao.html +++ b/sapl/templates/relatorios/blocos_sessao_plenaria/ocorrencias_da_sessao.html @@ -1,4 +1,4 @@

Ocorrências da Sessão

{% for o in lst_ocorrencias%} -

{{o|striptags|safe}}

+

{{o.conteudo|striptags|safe}}

{% endfor %} From 96ee98d414d5efc60538fd80d751889aaf250308 Mon Sep 17 00:00:00 2001 From: joaohortsenado <100957576+joaohortsenado@users.noreply.github.com> Date: Thu, 16 Jul 2026 12:58:47 -0300 Subject: [PATCH 4/7] =?UTF-8?q?Corrige=20ordem=20dos=20expedientes=20no=20?= =?UTF-8?q?PDF=20do=20Resumo=20da=20Sess=C3=A3o=20(#3844)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit O PDF do Resumo da Sessão ordenava os expedientes alfabeticamente pelo nome do tipo, ignorando o campo `ordenacao` de TipoExpediente. Numa Casa que cadastre vários tipos, isso embaralha o documento: "Grande Expediente" sai antes de "Leitura e Aprovação da Ata" e o encerramento pode ser impresso logo após a abertura. A tela do Resumo e o Extrato passam por sapl.sessao.views.get_expedientes, que já ordena por `tipo__ordenacao`; apenas o relatório em PDF, que usa get_sessao_plenaria, ficou para trás. O order_by('tipo__nome') havia sido introduzido em 25be37eb justamente para acompanhar o Resumo, mas quando 2eb0b796 (#2920) criou o campo `ordenacao` somente get_expedientes foi atualizado, e os dois caminhos divergiram em silêncio. Alinha get_sessao_plenaria à mesma ordenação e adiciona testes que travam a equivalência entre o PDF e o Resumo exibido em tela. OSTicket #125461 --- sapl/relatorios/tests.py | 53 ++++++++++++++++++++++++++++++++++++++-- sapl/relatorios/views.py | 4 ++- 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/sapl/relatorios/tests.py b/sapl/relatorios/tests.py index a79ca8be5..fe46e86c4 100644 --- a/sapl/relatorios/tests.py +++ b/sapl/relatorios/tests.py @@ -1,3 +1,52 @@ -# from django.test import TestCase +import pytest +from model_bakery import baker -# Create your tests here. +from sapl.base.models import CasaLegislativa +from sapl.relatorios.views import get_sessao_plenaria +from sapl.sessao.models import ExpedienteSessao, SessaoPlenaria, TipoExpediente +from sapl.sessao.views import get_expedientes + + +def cria_sessao_com_expedientes(): + """Cria uma sessão cujos expedientes têm `ordenacao` inversa à ordem + alfabética dos nomes -- o caso que expõe o OSTicket #125461.""" + sessao = baker.make(SessaoPlenaria) + tipo_leitura = baker.make( + TipoExpediente, nome='Leitura e Aprovação da Ata', ordenacao=1) + tipo_grande = baker.make( + TipoExpediente, nome='Grande Expediente', ordenacao=2) + + for tipo in (tipo_grande, tipo_leitura): + baker.make(ExpedienteSessao, sessao_plenaria=sessao, tipo=tipo, + conteudo='

Conteúdo de {}.

'.format(tipo.nome)) + + return sessao + + +@pytest.mark.django_db(transaction=False) +def test_relatorio_sessao_respeita_ordenacao_do_tipo_expediente(): + """O PDF da Sessão Plenária deve seguir o campo `ordenacao` de + TipoExpediente, e não a ordem alfabética do nome.""" + sessao = cria_sessao_com_expedientes() + casa = baker.make(CasaLegislativa) + user = baker.make('auth.User') + + lst_expedientes = get_sessao_plenaria(sessao, casa, user)[6] + nomes = [e['nom_expediente'] for e in lst_expedientes] + + assert nomes == ['Leitura e Aprovação da Ata', 'Grande Expediente'] + + +@pytest.mark.django_db(transaction=False) +def test_relatorio_sessao_tem_mesma_ordem_de_expedientes_do_resumo(): + """O PDF e o Resumo exibido em tela não podem divergir: foi essa + divergência que originou o OSTicket #125461.""" + sessao = cria_sessao_com_expedientes() + casa = baker.make(CasaLegislativa) + user = baker.make('auth.User') + + do_pdf = [e['nom_expediente'] + for e in get_sessao_plenaria(sessao, casa, user)[6]] + da_tela = [e['tipo'].nome for e in get_expedientes(sessao)['expedientes']] + + assert do_pdf == da_tela diff --git a/sapl/relatorios/views.py b/sapl/relatorios/views.py index 60ba1635e..8b89342fd 100755 --- a/sapl/relatorios/views.py +++ b/sapl/relatorios/views.py @@ -609,8 +609,10 @@ def get_sessao_plenaria(sessao, casa, user): # Exibe os Expedientes lst_expedientes = [] + # A ordenação deve ser a mesma de sapl.sessao.views.get_expedientes, para + # que o PDF confira com o Resumo exibido em tela. OSTicket #125461 expedientes = ExpedienteSessao.objects.filter( - sessao_plenaria=sessao).order_by('tipo__nome') + sessao_plenaria=sessao).order_by('tipo__ordenacao', 'tipo__nome') for e in expedientes: conteudo = e.conteudo if not is_empty(conteudo): From 9da03c1f083e7920530a0bc864757e7728ed3ff5 Mon Sep 17 00:00:00 2001 From: Edward Oliveira Date: Mon, 3 Aug 2026 14:19:15 -0300 Subject: [PATCH 5/7] hot-fix: Remove alerta errado --- sapl/materia/forms.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/sapl/materia/forms.py b/sapl/materia/forms.py index 3615fee47..9a0d5007c 100644 --- a/sapl/materia/forms.py +++ b/sapl/materia/forms.py @@ -1944,10 +1944,6 @@ class ProposicaoForm(FileFieldCheckMixin, forms.ModelForm): fields = [ to_column((Fieldset( TipoProposicao._meta.verbose_name, Field('tipo')), 12)), - to_column( - (Alert('teste', - css_class="ementa_materia hidden alert-info", - dismiss=False), 12)), to_column(('descricao', 12)), to_column(('observacao', 12)), From a258c0a232013e6663865a22578fbef454086a17 Mon Sep 17 00:00:00 2001 From: Edward Oliveira Date: Mon, 3 Aug 2026 15:48:02 -0300 Subject: [PATCH 6/7] hot-fix: Adiciona MIME Media Types opcionais para CSV. --- sapl/utils.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sapl/utils.py b/sapl/utils.py index 42d4b2c6e..9bd6231dc 100644 --- a/sapl/utils.py +++ b/sapl/utils.py @@ -611,6 +611,9 @@ TIPOS_TEXTO_PERMITIDOS = ( 'text/pdf', 'text/x-pdf', 'text/plain', + 'text/csv', + 'application/vnd.ms-excel', + 'application/csv', 'application/txt', 'browser/internal', 'text/anytext', From 85079b492d691ed6ae256c7d383a996be02ca3e2 Mon Sep 17 00:00:00 2001 From: joaohortsenado <100957576+joaohortsenado@users.noreply.github.com> Date: Tue, 4 Aug 2026 15:04:19 -0300 Subject: [PATCH 7/7] =?UTF-8?q?Oculta=20sess=C3=B5es=20plen=C3=A1rias=20n?= =?UTF-8?q?=C3=A3o=20iniciadas=20da=20consulta=20p=C3=BAblica=20(#3845)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Oculta sessões plenárias não iniciadas da consulta pública Sessões cadastradas com antecedência ficavam visíveis no portal mesmo com "Publicar pauta?", "Sessão iniciada?" e "Sessão finalizada?" em Não: nenhum desses campos filtrava queryset público. Só publicar_pauta filtrava, e apenas nos dois pontos de entrada da pauta. Passa a esconder do usuário anônimo as sessões marcadas explicitamente como não iniciadas e sem pauta publicada. Usuário autenticado continua vendo todas. A regra fica centralizada em SessaoPlenaria.objects.visiveis_para(user) e é aplicada na pesquisa de sessões, no detalhe, no resumo e no resumo/ata. iniciada nulo conta como visível: o campo nasceu sem default e o default=True só chegou na migração 0027, que não preencheu as linhas existentes, então bases anteriores a out/2018 têm sessões realizadas com o campo em NULL. Também restringe PautaSessaoDetailView, que não filtrava publicar_pauta e expunha por URL a pauta de sessões não publicadas. * Troca custom manager por função utilitária de visibilidade Atende à revisão do PR: substitui a subclasse SessaoPlenariaQuerySet e o método .visiveis_para() por uma função restringe_sessoes_visiveis(qs, user), chamada como wrapper nos pontos de acesso público. Mantém a mesma regra de filtragem e o comportamento de fundir as cadeias de filter em um único SQL. --------- Co-authored-by: Edward <9326037+edwardoliveira@users.noreply.github.com> --- sapl/sessao/models.py | 20 ++++++ sapl/sessao/tests/test_sessao_view.py | 92 ++++++++++++++++++++++++++- sapl/sessao/views.py | 21 +++++- 3 files changed, 130 insertions(+), 3 deletions(-) diff --git a/sapl/sessao/models.py b/sapl/sessao/models.py index 613068afe..ad71be072 100644 --- a/sapl/sessao/models.py +++ b/sapl/sessao/models.py @@ -153,6 +153,26 @@ def anexo_upload_path(instance, filename): # return get_sessao_media_path(instance, 'anexo', filename) +def restringe_sessoes_visiveis(qs, user): + """Restringe o queryset às sessões visíveis na consulta pública. + + Usuário autenticado enxerga todas. Para o público, ficam ocultas as + sessões cadastradas com antecedência, ou seja, as marcadas como não + iniciadas e sem pauta publicada. + + `iniciada` nulo conta como visível: o default do campo só surgiu na + migração 0027, que não preencheu as linhas já existentes, então bases + antigas têm sessões realizadas com o campo em NULL. + """ + if user.is_authenticated: + return qs + + return qs.filter( + Q(publicar_pauta=True) | + Q(iniciada=True) | + Q(iniciada__isnull=True)) + + class SessaoPlenaria(models.Model): # TODO trash??? Seems to have been a FK in the past. Would be: # andamento_sessao = models.ForeignKey( diff --git a/sapl/sessao/tests/test_sessao_view.py b/sapl/sessao/tests/test_sessao_view.py index 00284465b..70bb920a9 100644 --- a/sapl/sessao/tests/test_sessao_view.py +++ b/sapl/sessao/tests/test_sessao_view.py @@ -1,4 +1,7 @@ import pytest +from datetime import date +from django.contrib.auth import get_user_model +from django.contrib.auth.models import AnonymousUser from django.urls import reverse from django.utils.translation import ugettext_lazy as _ from model_bakery import baker @@ -8,7 +11,8 @@ from sapl.sessao.models import (SessaoPlenaria, TipoSessaoPlenaria, IntegranteMesa, SessaoPlenariaPresenca, JustificativaAusencia, ExpedienteSessao, TipoExpediente, ExpedienteMateria, - Orador, OcorrenciaSessao) + Orador, OcorrenciaSessao, + restringe_sessoes_visiveis) from sapl.parlamentares.models import Parlamentar, CargoMesa, Filiacao @@ -146,4 +150,88 @@ class TestResumoView(): ocorrencia = baker.make(OcorrenciaSessao, sessao_plenaria=self.sessao_plenaria) resultado_get_ocorrencia = get_ocorrencias_da_sessao(self.sessao_plenaria) - assert resultado_get_ocorrencia['ocorrencias_da_sessao'][0] == ocorrencia \ No newline at end of file + assert resultado_get_ocorrencia['ocorrencias_da_sessao'][0] == ocorrencia + + +@pytest.mark.django_db(transaction=False) +def test_visiveis_para_oculta_do_anonimo_apenas_a_sessao_previa(): + previa = baker.make(SessaoPlenaria, iniciada=False, publicar_pauta=False) + com_pauta = baker.make(SessaoPlenaria, iniciada=False, publicar_pauta=True) + iniciada = baker.make(SessaoPlenaria, iniciada=True, publicar_pauta=False) + # Sessões anteriores à migração 0027 ficaram com `iniciada` em NULL. + legada = baker.make(SessaoPlenaria, iniciada=None, publicar_pauta=False) + + visiveis = restringe_sessoes_visiveis( + SessaoPlenaria.objects.all(), AnonymousUser()) + + assert previa not in visiveis + assert com_pauta in visiveis + assert iniciada in visiveis + assert legada in visiveis + + +@pytest.mark.django_db(transaction=False) +def test_visiveis_para_nao_oculta_nada_de_usuario_autenticado(): + previa = baker.make(SessaoPlenaria, iniciada=False, publicar_pauta=False) + + operador = baker.make(get_user_model()) + + assert previa in restringe_sessoes_visiveis( + SessaoPlenaria.objects.all(), operador) + + +@pytest.mark.django_db(transaction=False) +def test_pesquisar_sessao_nao_lista_sessao_previa_para_anonimo(client): + previa = baker.make(SessaoPlenaria, iniciada=False, publicar_pauta=False, + data_inicio=date(2025, 11, 5)) + iniciada = baker.make(SessaoPlenaria, iniciada=True, publicar_pauta=False, + data_inicio=date(2025, 11, 5)) + + response = client.get(reverse('sapl.sessao:pesquisar_sessao'), + {'data_inicio__year': '2025'}) + + assert response.status_code == 200 + assert previa not in response.context['object_list'] + assert iniciada in response.context['object_list'] + + +@pytest.mark.django_db(transaction=False) +def test_pesquisar_sessao_lista_sessao_previa_para_autenticado(admin_client): + previa = baker.make(SessaoPlenaria, iniciada=False, publicar_pauta=False, + data_inicio=date(2025, 11, 5)) + + response = admin_client.get(reverse('sapl.sessao:pesquisar_sessao'), + {'data_inicio__year': '2025'}) + + assert response.status_code == 200 + assert previa in response.context['object_list'] + + +@pytest.mark.django_db(transaction=False) +def test_detail_sessao_previa_indisponivel_para_anonimo(client): + previa = baker.make(SessaoPlenaria, iniciada=False, publicar_pauta=False) + + response = client.get(reverse('sapl.sessao:sessaoplenaria_detail', + kwargs={'pk': previa.pk})) + + assert response.status_code == 404 + + +@pytest.mark.django_db(transaction=False) +def test_resumo_de_sessao_previa_indisponivel_para_anonimo(client): + previa = baker.make(SessaoPlenaria, iniciada=False, publicar_pauta=False) + + response = client.get(reverse('sapl.sessao:resumo', + kwargs={'pk': previa.pk})) + + assert response.status_code == 404 + + +@pytest.mark.django_db(transaction=False) +def test_pauta_nao_publicada_indisponivel_para_anonimo(client): + sem_pauta = baker.make(SessaoPlenaria, iniciada=True, publicar_pauta=False) + + response = client.get(reverse('sapl.sessao:pauta_sessao_detail', + kwargs={'pk': sem_pauta.pk})) + + assert response.status_code == 404 diff --git a/sapl/sessao/views.py b/sapl/sessao/views.py index ab30c2c6a..64f7ce7c1 100755 --- a/sapl/sessao/views.py +++ b/sapl/sessao/views.py @@ -64,7 +64,7 @@ from .models import (Bancada, CargoBancada, CargoMesa, SessaoPlenaria, SessaoPlenariaPresenca, TipoExpediente, TipoResultadoVotacao, TipoSessaoPlenaria, VotoParlamentar, TipoRetiradaPauta, RetiradaPauta, TipoJustificativa, JustificativaAusencia, OradorOrdemDia, - ORDENACAO_RESUMO, RegistroLeitura) + ORDENACAO_RESUMO, RegistroLeitura, restringe_sessoes_visiveis) TipoSessaoCrud = CrudAux.build(TipoSessaoPlenaria, 'tipo_sessao_plenaria') TipoJustificativaCrud = CrudAux.build(TipoJustificativa, 'tipo_justificativa') @@ -1348,6 +1348,13 @@ class SessaoCrud(Crud): class DetailView(Crud.DetailView): + def get(self, request, *args, **kwargs): + if not restringe_sessoes_visiveis( + SessaoPlenaria.objects.filter(pk=kwargs.get('pk')), + request.user).exists(): + raise Http404() + return super().get(request, *args, **kwargs) + @property def layout_key(self): sessao = self.object @@ -2327,6 +2334,10 @@ class ResumoView(DetailView): model = SessaoPlenaria logger = logging.getLogger(__name__) + def get_queryset(self): + return restringe_sessoes_visiveis( + SessaoPlenaria.objects.all(), self.request.user) + def get_context(self, *args, **kwargs): self.object = self.get_object() context = self.get_context_data(object=self.object) @@ -3833,6 +3844,12 @@ class PautaSessaoDetailView(PautaMultiFormatOutputMixin, DetailView): ('situacao', 'Situação') ) + def get_queryset(self): + qs = SessaoPlenaria.objects.all() + if not self.request.user.is_authenticated: + qs = qs.filter(publicar_pauta=True) + return qs + def hook_autor(self, obj): return ','.join(obj['autor']) @@ -4028,6 +4045,8 @@ class PesquisarSessaoPlenariaView(MultiFormatOutputMixin, FilterView): qs = self.get_queryset().select_related( 'tipo', 'sessao_legislativa', 'legislatura') + qs = restringe_sessoes_visiveis(qs, self.request.user) + qs = qs.distinct().order_by( '-legislatura__numero', '-data_inicio', '-hora_inicio')
Matéria EmentaAutor(es)Coautor(es)
@@ -56,11 +56,9 @@ {% autoescape off %}{{materia.ementa}}
{{materia.observacao}}{% endautoescape %}
- {% for autor in materia.autoria_set.all %} - {% if not autor.primeiro_autor %} - {{ autor.autor }}
- {% else %} - {{ autor.autor }}
+ {% for autoria in materia.autoria_set.all %} + {% if autoria.autor != autor %} + {{ autoria.autor }}
{% endif %} {% endfor %}
{% autoescape off %}{{norma.ementa}}
{{norma.observacao}}{% endautoescape %}
- {% if norma.autorianorma_set.first != norma.autorianorma_set.last %} - {% for autor in norma.autorianorma_set.all %} - {% if not autor.primeiro_autor %} - {{ autor.autor }}
+ {% for autoria in norma.autorianorma_set.all %} + {% if autoria.autor != autor %} + {{ autoria.autor }}
{% endif %} {% endfor %} - {% endif %}