Browse Source

Merge branch '3.1.x' into impl_params_expand_include_exclude

pull/3809/head
Edward 2 weeks ago
committed by GitHub
parent
commit
7799e1ab97
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 12
      docker/config/nginx/sapl.conf
  2. 28
      sapl/audiencia/migrations/0020_auto_20251201_1450.py
  3. 4
      sapl/audiencia/models.py
  4. 21
      sapl/logging/filters.py
  5. 0
      sapl/middleware/__init__.py
  6. 0
      sapl/middleware/check_password.py
  7. 0
      sapl/middleware/endpoint_restriction.py
  8. 24
      sapl/middleware/request_id.py
  9. 10
      sapl/painel/views.py
  10. 23
      sapl/parlamentares/migrations/0045_auto_20251201_1531.py
  11. 4
      sapl/parlamentares/models.py
  12. 6
      sapl/relatorios/forms.py
  13. 19
      sapl/sessao/forms.py
  14. 4
      sapl/sessao/views.py
  15. 18
      sapl/settings.py
  16. 2
      sapl/templates/norma/layouts.yaml
  17. 11
      sapl/templates/painel/voto_individual.html
  18. 22
      sapl/templates/sessao/adicionar_varias_materias_expediente.html
  19. 2
      sapl/templates/sessao/blocos_ata/materias_expediente.html
  20. 2
      sapl/templates/sessao/blocos_ata/materias_ordem_dia.html

12
docker/config/nginx/sapl.conf

@ -4,6 +4,13 @@ upstream sapl_server {
}
# Se o cliente já manda X-Request-ID, reaproveita; senão, usa $request_id (nginx)
map $http_x_request_id $req_id {
default $http_x_request_id;
"" $request_id;
}
server {
listen 80;
@ -30,7 +37,9 @@ server {
return 204;
}
proxy_set_header X-Request-ID $req_id;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://sapl_server;
@ -45,10 +54,11 @@ server {
}
location / {
proxy_set_header X-Request-ID $req_id;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://sapl_server;
}

28
sapl/audiencia/migrations/0020_auto_20251201_1450.py

@ -0,0 +1,28 @@
# Generated by Django 2.2.28 on 2025-12-01 17:50
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('audiencia', '0019_auto_20240711_1400'),
]
operations = [
migrations.AlterField(
model_name='audienciapublica',
name='ano',
field=models.PositiveSmallIntegerField(choices=[(2026, 2026), (2025, 2025), (2024, 2024), (2023, 2023), (2022, 2022), (2021, 2021), (2020, 2020), (2019, 2019), (2018, 2018), (2017, 2017), (2016, 2016), (2015, 2015), (2014, 2014), (2013, 2013), (2012, 2012), (2011, 2011), (2010, 2010), (2009, 2009), (2008, 2008), (2007, 2007), (2006, 2006), (2005, 2005), (2004, 2004), (2003, 2003), (2002, 2002), (2001, 2001), (2000, 2000), (1999, 1999), (1998, 1998), (1997, 1997), (1996, 1996), (1995, 1995), (1994, 1994), (1993, 1993), (1992, 1992), (1991, 1991), (1990, 1990), (1989, 1989), (1988, 1988), (1987, 1987), (1986, 1986), (1985, 1985), (1984, 1984), (1983, 1983), (1982, 1982), (1981, 1981), (1980, 1980), (1979, 1979), (1978, 1978), (1977, 1977), (1976, 1976), (1975, 1975), (1974, 1974), (1973, 1973), (1972, 1972), (1971, 1971), (1970, 1970), (1969, 1969), (1968, 1968), (1967, 1967), (1966, 1966), (1965, 1965), (1964, 1964), (1963, 1963), (1962, 1962), (1961, 1961), (1960, 1960), (1959, 1959), (1958, 1958), (1957, 1957), (1956, 1956), (1955, 1955), (1954, 1954), (1953, 1953), (1952, 1952), (1951, 1951), (1950, 1950), (1949, 1949), (1948, 1948), (1947, 1947), (1946, 1946), (1945, 1945), (1944, 1944), (1943, 1943), (1942, 1942), (1941, 1941), (1940, 1940), (1939, 1939), (1938, 1938), (1937, 1937), (1936, 1936), (1935, 1935), (1934, 1934), (1933, 1933), (1932, 1932), (1931, 1931), (1930, 1930), (1929, 1929), (1928, 1928), (1927, 1927), (1926, 1926), (1925, 1925), (1924, 1924), (1923, 1923), (1922, 1922), (1921, 1921), (1920, 1920), (1919, 1919), (1918, 1918), (1917, 1917), (1916, 1916), (1915, 1915), (1914, 1914), (1913, 1913), (1912, 1912), (1911, 1911), (1910, 1910), (1909, 1909), (1908, 1908), (1907, 1907), (1906, 1906), (1905, 1905), (1904, 1904), (1903, 1903), (1902, 1902), (1901, 1901), (1900, 1900), (1899, 1899), (1898, 1898), (1897, 1897), (1896, 1896), (1895, 1895), (1894, 1894), (1893, 1893), (1892, 1892), (1891, 1891), (1890, 1890)], verbose_name='Ano'),
),
migrations.AlterField(
model_name='audienciapublica',
name='nome',
field=models.CharField(max_length=250, verbose_name='Nome da Audiência Pública'),
),
migrations.AlterField(
model_name='audienciapublica',
name='tema',
field=models.CharField(max_length=250, verbose_name='Tema da Audiência Pública'),
),
]

4
sapl/audiencia/models.py

@ -65,9 +65,9 @@ class AudienciaPublica(models.Model):
ano = models.PositiveSmallIntegerField(verbose_name=_('Ano'),
choices=RANGE_ANOS)
nome = models.CharField(
max_length=100, verbose_name=_('Nome da Audiência Pública'))
max_length=250, verbose_name=_('Nome da Audiência Pública'))
tema = models.CharField(
max_length=100, verbose_name=_('Tema da Audiência Pública'))
max_length=250, 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)'))

21
sapl/logging/filters.py

@ -0,0 +1,21 @@
# sapl/logging/filters.py
import logging
import contextvars
_request_id = contextvars.ContextVar("request_id", default="-")
def set_request_id(value: str):
_request_id.set(value)
def get_request_id() -> str:
return _request_id.get()
class RequestIdFilter(logging.Filter):
def filter(self, record: logging.LogRecord) -> bool:
# garante que SEMPRE existe
if not hasattr(record, "request_id"):
record.request_id = get_request_id()
return True

0
sapl/middleware/__init__.py

0
sapl/middleware.py → sapl/middleware/check_password.py

0
sapl/endpoint_restriction_middleware.py → sapl/middleware/endpoint_restriction.py

24
sapl/middleware/request_id.py

@ -0,0 +1,24 @@
import uuid
from sapl.logging.filters import set_request_id
HEADER_NAME = "HTTP_X_REQUEST_ID"
RESPONSE_HEADER = "X-Request-ID"
def _new_id():
return uuid.uuid4().hex
class RequestIdMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
# recebe `request_id` do nginx ou do cliente senão cria um
request_id = request.META.get(HEADER_NAME) or _new_id()
request_id = str(request_id)[:64]
request.request_id = request_id
set_request_id(request_id)
response = self.get_response(request)
response[RESPONSE_HEADER] = request_id
return response

10
sapl/painel/views.py

@ -93,7 +93,7 @@ def votacao_aberta(request):
return votacoes_abertas.first(), None
def votacao(context,context_vars):
def votacao(context, context_vars):
logger = logging.getLogger(__name__)
parlamentar = context_vars['votante'].parlamentar
parlamentar_presente = False
@ -204,7 +204,7 @@ def votante_view(request):
username = request.user.username if request.user.is_authenticated else 'AnonymousUser'
# Pega o votante relacionado ao usuário
template_name = 'painel/voto_nominal.html'
template_name = 'painel/voto_individual.html'
context = {}
context_vars = {}
@ -237,13 +237,15 @@ def votante_view(request):
if request.method == 'POST':
if context_vars['ordem_dia']:
try:
logger.info("user=" + username + ". Tentando obter objeto VotoParlamentar para parlamentar={} e ordem={}."
logger.info("user=" + username + ". Tentando obter objeto VotoParlamentar para parlamentar={} e "
"ordem={}. "
.format(context_vars['parlamentar'], context_vars['ordem_dia']))
voto = VotoParlamentar.objects.get(
parlamentar=context_vars['parlamentar'],
ordem=context_vars['ordem_dia'])
except ObjectDoesNotExist:
logger.error("user=" + username + ". Erro ao obter VotoParlamentar para parlamentar={} e ordem={}. Criando objeto."
logger.error("user=" + username + ". Erro ao obter VotoParlamentar para parlamentar={} e ordem={}. "
"Criando objeto. "
.format(context_vars['parlamentar'], context_vars['ordem_dia']))
voto = VotoParlamentar.objects.create(
parlamentar=context_vars['parlamentar'],

23
sapl/parlamentares/migrations/0045_auto_20251201_1531.py

@ -0,0 +1,23 @@
# Generated by Django 2.2.28 on 2025-12-01 18:31
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('parlamentares', '0044_adiciona_cargos_mesa'),
]
operations = [
migrations.AlterField(
model_name='parlamentar',
name='nome_completo',
field=models.CharField(max_length=80, verbose_name='Nome Completo'),
),
migrations.AlterField(
model_name='parlamentar',
name='nome_parlamentar',
field=models.CharField(max_length=80, verbose_name='Nome Parlamentar'),
),
]

4
sapl/parlamentares/models.py

@ -206,9 +206,9 @@ class Parlamentar(models.Model):
on_delete=models.PROTECT,
verbose_name=_('Situação Militar'))
nome_completo = models.CharField(
max_length=50, verbose_name=_('Nome Completo'))
max_length=80, verbose_name=_('Nome Completo'))
nome_parlamentar = models.CharField(
max_length=50,
max_length=80,
verbose_name=_('Nome Parlamentar'))
sexo = models.CharField(
max_length=1, verbose_name=_('Sexo'), choices=SEXO_CHOICE)

6
sapl/relatorios/forms.py

@ -70,7 +70,9 @@ class RelatorioDocumentosAcessoriosFilterSet(django_filters.FilterSet):
)
def ordem_or_expediente(queryset, name, value):
class RelatorioVotacoesNominaisFilterSet(django_filters.FilterSet):
def ordem_or_expediente(self, queryset, name, value):
if value is None:
return queryset
value = getattr(value, "pk", value)
@ -78,8 +80,6 @@ def ordem_or_expediente(queryset, name, value):
expediente_q = f"expediente__materia__{name}"
return queryset.filter(Q(**{ordem_q: value}) | Q(**{expediente_q: value}))
class RelatorioVotacoesNominaisFilterSet(django_filters.FilterSet):
tipo_id = django_filters.ModelChoiceFilter(
queryset=TipoMateriaLegislativa.objects.all(),
method='ordem_or_expediente',

19
sapl/sessao/forms.py

@ -22,7 +22,8 @@ from sapl.materia.models import (MateriaLegislativa, StatusTramitacao,
from sapl.parlamentares.models import Mandato, Parlamentar
from sapl.protocoloadm.models import TipoDocumentoAdministrativo,\
DocumentoAdministrativo
from sapl.sessao.models import Correspondencia
from sapl.sessao.models import Correspondencia, AbstractOrdemDia
from sapl.utils import (autor_label, autor_modal,
choice_anos_com_sessaoplenaria,
FileFieldCheckMixin,
@ -563,6 +564,22 @@ class SessaoPlenariaFilterSet(django_filters.FilterSet):
)
class AdicionarVariasMateriasForm(forms.Form):
check_all = forms.BooleanField(
label='Marcar/Desmarcar Todos',
required=False,
widget=forms.CheckboxInput(
attrs={'onchange':'checkAll(this)'}),
)
tipo_votacao = forms.ChoiceField(required=False,
choices= AbstractOrdemDia.TIPO_VOTACAO_CHOICES,
initial=False,
widget=forms.RadioSelect(
attrs={'onchange':'marcaTipoVotacao()'}),
)
class AdicionarVariasMateriasFilterSet(MateriaLegislativaFilterSet):
o = MateriaPesquisaOrderingFilter()

4
sapl/sessao/views.py

@ -51,7 +51,7 @@ from sapl.settings import TIME_ZONE, RATE_LIMITER_RATE
from sapl.utils import show_results_filter_set, remover_acentos, get_client_ip, \
MultiFormatOutputMixin, PautaMultiFormatOutputMixin, ratelimit_ip
from .forms import (AdicionarVariasMateriasFilterSet, BancadaForm,
from .forms import (AdicionarVariasMateriasFilterSet, AdicionarVariasMateriasForm, BancadaForm,
ExpedienteForm, JustificativaAusenciaForm, OcorrenciaSessaoForm, ListMateriaForm,
MesaForm, OradorExpedienteForm, OradorForm, PautaSessaoFilterSet,
PresencaForm, ResumoOrdenacaoForm, SessaoPlenariaFilterSet,
@ -4190,6 +4190,8 @@ class AdicionarVariasMateriasExpediente(PermissionRequiredForAppCrudMixin,
qr = self.request.GET.copy()
form = AdicionarVariasMateriasForm
context['form'] = form
context['filter_url'] = ('&' + qr.urlencode()) if len(qr) > 0 else ''
context['pk_sessao'] = self.kwargs['pk']

18
sapl/settings.py

@ -138,11 +138,12 @@ HAYSTACK_CONNECTIONS = {
}
MIDDLEWARE = [
'sapl.middleware.request_id.RequestIdMiddleware',
'django_prometheus.middleware.PrometheusBeforeMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.middleware.common.CommonMiddleware',
'sapl.endpoint_restriction_middleware.EndpointRestrictionMiddleware',
'sapl.middleware.endpoint_restriction.EndpointRestrictionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
@ -150,7 +151,7 @@ MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'whitenoise.middleware.WhiteNoiseMiddleware',
'waffle.middleware.WaffleMiddleware',
'sapl.middleware.CheckWeakPasswordMiddleware',
'sapl.middleware.check_password.CheckWeakPasswordMiddleware',
'django_prometheus.middleware.PrometheusAfterMiddleware',
]
if DEBUG:
@ -421,32 +422,37 @@ LOGGING = {
'require_debug_true': {
'()': 'django.utils.log.RequireDebugTrue',
},
'request_id': {
"()": 'sapl.logging.filters.RequestIdFilter',
},
},
'formatters': {
'verbose': {
'format': '%(levelname)s %(asctime)s ' + host + ' %(pathname)s %(name)s:%(funcName)s:%(lineno)d %(message)s'
'format': '%(levelname)s %(asctime)s [%(request_id)s] ' + host + '%(pathname)s %(name)s:%(funcName)s:%('
'lineno)d %(message)s '
},
'simple': {
'format': '%(levelname)s %(asctime)s - %(message)s'
'format': '%(levelname)s %(asctime)s [%(request_id)s] - %(message)s'
},
},
'handlers': {
'console': {
'level': 'INFO',
'class': 'logging.StreamHandler',
'filters': ['require_debug_true'],
'filters': ['request_id', 'require_debug_true'],
'formatter': 'simple',
},
'console_verbose': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'filters': ['require_debug_true'],
'filters': ['request_id', 'require_debug_true'],
'formatter': 'verbose',
},
'applogfile': {
'level': 'INFO',
'class': 'logging.handlers.RotatingFileHandler',
'filename': 'sapl.log',
'filters': ['request_id'],
'maxBytes': 1024 * 1024 * 15, # 15MB
'backupCount': 10,
'formatter': 'verbose',

2
sapl/templates/norma/layouts.yaml

@ -32,7 +32,7 @@ AnexoNormaJuridica:
NormaJuridicaCreate:
{% trans 'Identificação Básica' %}:
- orgao tipo:5 numero:2 ano:2
- orgao tipo:5 ano:2 numero:2
- data esfera_federacao complemento
- tipo_materia numero_materia ano_materia
- data_publicacao veiculo_publicacao data_vigencia pagina_inicio_publicacao pagina_fim_publicacao

11
sapl/templates/painel/voto_nominal.html → sapl/templates/painel/voto_individual.html

@ -132,9 +132,6 @@
{% render_bundle 'painel' 'js' %}
<script type="text/javascript">
var d = new Date();
var n = d.toLocaleDateString();
document.getElementById("date").innerHTML = n;
$(window).on('beforeunload', function () {
$("input[type=submit], input[type=button]").prop("disabled", "disabled");
@ -177,12 +174,14 @@
m = checkTime(m);
s = checkTime(s);
$("#relogio").text(h+":"+m+":"+s)
var t = setTimeout(function(){
startTime()
}, 500);
var t = setInterval(() => startTime(), 500);
}
$(document).ready(function(){
var d = new Date();
var n = d.toLocaleDateString();
$("#date").text(n);
startTime();
setTimeout(function() {

22
sapl/templates/sessao/adicionar_varias_materias_expediente.html

@ -38,6 +38,16 @@
{% elif paginator.count == 1 %}
<h3>{% trans 'Pesquisa concluída com sucesso! Foi encontrada 1 matéria.'%}</h3>
{% endif %}
<tr>
<td>
{{ form.check_all }} {{ form.check_all.label }}
</td>
<td>
{% for tipo in form.tipo_votacao %}
{{ tipo }}<br />
{% endfor %}
</td>
</tr>
{% for m in page_obj %}
<tr>
<td>
@ -126,5 +136,17 @@
$(window).on('beforeunload', function() {
$("input[type=submit], input[type=button]").prop("disabled", "disabled")
});
function checkAll(elem) {
$('input[name="materia_id"]:checkbox').prop('checked', elem.checked);
}
function marcaTipoVotacao() {
$('input[name="materia_id"]:checked').each(function() {
var tipoVotacao = $('input[name="tipo_votacao"]:checked').val();
var idMateria = "#tipo_votacao_" + $(this).val() + "_" + tipoVotacao;
$(idMateria).prop('checked', true);
});
}
</script>
{% endblock extra_js %}

2
sapl/templates/sessao/blocos_ata/materias_expediente.html

@ -24,7 +24,7 @@
Abstenções: {{ m.voto_abstencoes }},
{% endif %}
{% endif %}
Resultado:</b> {{ m.resultado }}
<strong>Resultado:</b> {{ m.resultado }}</strong>
{% if m.resultado_observacao %} - Obs.: {{ m.resultado_observacao }} {% endif %}
{% if m.voto_nominal %}
<b>Votos Nominais :</b>

2
sapl/templates/sessao/blocos_ata/materias_ordem_dia.html

@ -25,7 +25,7 @@
Abstenções: {{ m.voto_abstencoes }},
{% endif %}
{% endif %}
Resultado:</b> {{ m.resultado }}
<strong>Resultado:</b> {{ m.resultado }}</strong>
{% if m.resultado_observacao %} - Obs.: {{ m.resultado_observacao }} {% endif %}
{% if m.voto_nominal %}
<b>Votos Nominais :</b>

Loading…
Cancel
Save