Browse Source

Fix(IntegracaoHub): materia pela generic FK e autor_nome de volta AB#1480

Dois achados do teste manual de integracao (13/08, ambiente real):

1. select_related('materia_gerada') estourava FieldError — o campo NAO
   existe no modelo: o bloco `materia_gerada = models.ForeignKey(...)` do
   materia/models.py esta DENTRO de uma docstring (codigo morto). O vinculo
   real e a generic FK conteudo_gerado_related (content_type + object_id),
   que aponta para MateriaLegislativa OU DocumentoAcessorio — so a materia
   interessa ao contrato. Sem isso, ProtocoloGerado nunca sairia.

2. O commit do autor_nome (ebf0fac) ficou fora do merge do PR #4 e o campo
   sumiu do poll. Sem ele o acervo perde o nome do autor nao mapeado, que e
   a regra do refinamento do historico §3.2. Reaplicado.

Verificado no ambiente real: poll devolve as proposicoes do acervo com
autor_nome e rascunho corretos.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
pull/3858/head
kemuel 1 month ago
parent
commit
81153f00b8
  1. 19
      sapl/integracao_hub/serializacao.py
  2. 4
      sapl/integracao_hub/views.py

19
sapl/integracao_hub/serializacao.py

@ -14,13 +14,17 @@ def _iso(valor):
def _materia_da_proposicao(proposicao): def _materia_da_proposicao(proposicao):
materia = proposicao.materia_gerada """A matéria gerada vem da generic FK, não de um campo direto.
if materia is None:
conteudo = getattr(proposicao, 'conteudo_gerado_related', None) O `materia_gerada` que aparece no models.py está DENTRO de uma docstring
if isinstance(conteudo, MateriaLegislativa): (código morto) o vínculo real é `conteudo_gerado_related`
materia = conteudo (content_type + object_id), que aponta para MateriaLegislativa OU para
if materia is None: DocumentoAcessorio. a matéria interessa ao contrato canônico.
"""
conteudo = proposicao.conteudo_gerado_related
if not isinstance(conteudo, MateriaLegislativa):
return None return None
materia = conteudo
return { return {
'id': materia.pk, 'id': materia.pk,
'tipo': { 'tipo': {
@ -45,6 +49,9 @@ def serializar_proposicao(proposicao):
'descricao': proposicao.tipo.descricao if proposicao.tipo else None, 'descricao': proposicao.tipo.descricao if proposicao.tipo else None,
}, },
'autor': proposicao.autor_id, 'autor': proposicao.autor_id,
# Nome para exibição no acervo do consumidor (refinamento do histórico §3.2):
# nunca usado para resolver identidade — contrato §3.2.
'autor_nome': proposicao.autor.nome if proposicao.autor else None,
'ementa': proposicao.descricao, 'ementa': proposicao.descricao,
'rascunho': proposicao.data_envio is None, 'rascunho': proposicao.data_envio is None,
'cancelado': proposicao.cancelado, 'cancelado': proposicao.cancelado,

4
sapl/integracao_hub/views.py

@ -180,7 +180,7 @@ class ProposicoesCadastradasPollView(PollView):
status=status.HTTP_400_BAD_REQUEST) status=status.HTTP_400_BAD_REQUEST)
itens = (Proposicao.objects itens = (Proposicao.objects
.filter(id__gt=id_gt, cancelado=False) .filter(id__gt=id_gt, cancelado=False)
.select_related('tipo', 'materia_gerada__tipo', 'content_type') .select_related('tipo', 'autor', 'content_type')
.order_by('id')[:self._limite(request)]) .order_by('id')[:self._limite(request)])
return self._resposta(itens, serializar_proposicao) return self._resposta(itens, serializar_proposicao)
@ -197,7 +197,7 @@ class PollPorDataView(PollView):
filtro = {'%s__gte' % self.campo_cursor: desde, 'cancelado': False} filtro = {'%s__gte' % self.campo_cursor: desde, 'cancelado': False}
itens = (Proposicao.objects itens = (Proposicao.objects
.filter(**filtro) .filter(**filtro)
.select_related('tipo', 'materia_gerada__tipo', 'content_type') .select_related('tipo', 'autor', 'content_type')
.order_by(self.campo_cursor, 'id')[:self._limite(request)]) .order_by(self.campo_cursor, 'id')[:self._limite(request)])
return self._resposta(itens, serializar_proposicao) return self._resposta(itens, serializar_proposicao)

Loading…
Cancel
Save