mirror of https://github.com/interlegis/sapl.git
Browse Source
* Impede presenças duplicadas de parlamentar na mesma sessão
O relatório de presença exibia percentuais acima de 100% — em Capanema/PR,
um vereador aparecia com 46 sessões em um período de 45. A contagem não
estava errada: existiam mesmo duas linhas de presença dele na sessão nº 22
de 30/06/2025, e o relatório contava linhas com Count('id').
A origem é PresencaView/PresencaOrdemDiaView. `presentes_banco` vem do banco
como inteiros e `marcados` vem do POST como strings, então
`set(presentes_banco) - set(marcados)` nunca casava e resultava em apagar
todas as presenças da sessão a cada salvamento, recriando-as em seguida.
Sequencialmente o resultado é correto, mas duas submissões concorrentes do
formulário — um duplo clique em Salvar — passam ambas pela janela entre o
apagar e o recriar e gravam duas linhas por parlamentar. Os ids gravados em
Capanema confirmam: pares adjacentes intercalados na ordem alfabética da
tela, assinatura de dois laços concorrentes.
Nada no banco impedia isso. Não é um caso isolado: Capanema tinha 625 linhas
excedentes em 313 sessões, e a base de Agudo/RS, 9 linhas em 1 sessão.
Corrige em quatro frentes:
- compara os ids com o mesmo tipo e passa a criar apenas quem ainda não tem
presença, sem apagar e recriar quem permanece marcado;
- usa bulk_create(ignore_conflicts=True), para que a submissão concorrente
descarte a inserção repetida em vez de estourar IntegrityError;
- adiciona unique_together (sessao_plenaria, parlamentar) nos dois modelos,
que é a proteção efetiva contra concorrência, com migração que remove as
duplicatas existentes antes de criar a restrição;
- conta sessões distintas no relatório, em vez de linhas, para que bases
ainda não migradas não exibam percentuais impossíveis.
OSTicket #212222
* Remove presenças duplicadas com RunSQL na migração 0070
Substitui o RunPython por um DELETE ... USING por tabela, conforme
sugestão da revisão: mantém a presença de menor id de cada par
(sessão, parlamentar), agora também em sessao_presencaordemdia.
A preferência por data_sessao preenchida foi abandonada, já que o
campo não é lido nem escrito em nenhum ponto do código.
pull/3864/head
committed by
GitHub
5 changed files with 188 additions and 27 deletions
@ -0,0 +1,40 @@ |
|||||
|
# Generated by Django 2.2.28 on 2026-08-05 16:00 |
||||
|
|
||||
|
from django.db import migrations |
||||
|
|
||||
|
|
||||
|
# Mantém uma única presença por (sessão, parlamentar). Presença é um sim/não, |
||||
|
# então as linhas repetidas não carregam informação adicional: preserva-se a |
||||
|
# de menor id e descartam-se as demais. |
||||
|
REMOVE_DUPLICADAS_SQL = """ |
||||
|
DELETE FROM {tabela} a |
||||
|
USING {tabela} b |
||||
|
WHERE a.sessao_plenaria_id = b.sessao_plenaria_id |
||||
|
AND a.parlamentar_id = b.parlamentar_id |
||||
|
AND a.id > b.id; |
||||
|
""" |
||||
|
|
||||
|
|
||||
|
class Migration(migrations.Migration): |
||||
|
|
||||
|
dependencies = [ |
||||
|
('sessao', '0069_auto_20220919_1705'), |
||||
|
] |
||||
|
|
||||
|
operations = [ |
||||
|
migrations.RunSQL( |
||||
|
REMOVE_DUPLICADAS_SQL.format( |
||||
|
tabela='sessao_sessaoplenariapresenca'), |
||||
|
migrations.RunSQL.noop), |
||||
|
migrations.RunSQL( |
||||
|
REMOVE_DUPLICADAS_SQL.format(tabela='sessao_presencaordemdia'), |
||||
|
migrations.RunSQL.noop), |
||||
|
migrations.AlterUniqueTogether( |
||||
|
name='sessaoplenariapresenca', |
||||
|
unique_together={('sessao_plenaria', 'parlamentar')}, |
||||
|
), |
||||
|
migrations.AlterUniqueTogether( |
||||
|
name='presencaordemdia', |
||||
|
unique_together={('sessao_plenaria', 'parlamentar')}, |
||||
|
), |
||||
|
] |
||||
Loading…
Reference in new issue