Browse Source

Merge branch '3.1.x' into doc_restrito

pull/3613/head
cristian-longhi 1 month ago
committed by GitHub
parent
commit
090d18e26c
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 90
      CHANGES.md
  2. 2
      docker/Dockerfile
  3. 4
      docker/docker-compose.yaml
  4. 17
      docker/start.sh
  5. 45
      docs/feature-flags.md
  6. 74
      frontend/src/__apps/compilacao/js/old/compilacao_edit.js
  7. 6
      frontend/src/__global/js/tinymce/index.js
  8. 754
      frontend/webpack-stats.json
  9. 14525
      package-lock.json
  10. 4
      package.json
  11. 22
      release.sh
  12. 4
      requirements/dev-requirements.txt
  13. 9
      requirements/requirements.txt
  14. 17
      sapl/api/views_comissoes.py
  15. 18
      sapl/audiencia/migrations/0019_auto_20240711_1400.py
  16. 35
      sapl/base/forms.py
  17. 17
      sapl/base/migrations/0059_remove_appconfig_sapl_as_sapn.py
  18. 18
      sapl/base/migrations/0060_auto_20240812_1628.py
  19. 6
      sapl/base/models.py
  20. 14
      sapl/base/receivers.py
  21. 10
      sapl/base/templatetags/common_tags.py
  22. 26
      sapl/base/templatetags/menus.py
  23. 25
      sapl/base/views.py
  24. 19
      sapl/compilacao/models.py
  25. 2
      sapl/compilacao/urls.py
  26. 27
      sapl/context_processors.py
  27. 10
      sapl/crispy_layout_mixin.py
  28. 96
      sapl/crud/base.py
  29. 9
      sapl/materia/forms.py
  30. 19
      sapl/materia/migrations/0085_auto_20231028_1838.py
  31. 33
      sapl/materia/migrations/0086_auto_20240711_1400.py
  32. 26
      sapl/materia/migrations/0087_update_viewdb_materiaemtramitacao.py
  33. 29
      sapl/materia/models.py
  34. 57
      sapl/materia/views.py
  35. 21
      sapl/middleware.py
  36. 22
      sapl/norma/forms.py
  37. 46
      sapl/norma/migrations/0045_auto_20240711_1405.py
  38. 35
      sapl/norma/models.py
  39. 24
      sapl/norma/views.py
  40. 5
      sapl/parlamentares/forms.py
  41. 31
      sapl/parlamentares/migrations/0008_adiciona_cargos_mesa.py
  42. 41
      sapl/parlamentares/migrations/0044_adiciona_cargos_mesa.py
  43. 30
      sapl/parlamentares/urls.py
  44. 12
      sapl/parlamentares/views.py
  45. 23
      sapl/protocoloadm/migrations/0045_auto_20240711_1405.py
  46. 14
      sapl/protocoloadm/views.py
  47. 2
      sapl/rules/group_geral.py
  48. 157
      sapl/sessao/views.py
  49. 34
      sapl/settings.py
  50. 13
      sapl/static/sapl/frontend/css/chunk-vendors.045ec640.css
  51. BIN
      sapl/static/sapl/frontend/css/chunk-vendors.045ec640.css.gz
  52. 13
      sapl/static/sapl/frontend/css/chunk-vendors.299c587b.css
  53. BIN
      sapl/static/sapl/frontend/css/chunk-vendors.299c587b.css.gz
  54. BIN
      sapl/static/sapl/frontend/css/compilacao.0baf3580.css.gz
  55. BIN
      sapl/static/sapl/frontend/css/global.45591136.css.gz
  56. BIN
      sapl/static/sapl/frontend/css/painel.e2b9504e.css.gz
  57. BIN
      sapl/static/sapl/frontend/fonts/fa-brands-400.5d18d427.ttf
  58. BIN
      sapl/static/sapl/frontend/fonts/fa-brands-400.5d18d427.ttf.gz
  59. BIN
      sapl/static/sapl/frontend/fonts/fa-brands-400.87587a68.woff2
  60. BIN
      sapl/static/sapl/frontend/fonts/fa-brands-400.9a905705.ttf
  61. BIN
      sapl/static/sapl/frontend/fonts/fa-brands-400.9a905705.ttf.gz
  62. BIN
      sapl/static/sapl/frontend/fonts/fa-brands-400.b6033b54.woff2
  63. BIN
      sapl/static/sapl/frontend/fonts/fa-regular-400.3580b4a9.woff2
  64. BIN
      sapl/static/sapl/frontend/fonts/fa-regular-400.3ccdbd3d.woff2
  65. BIN
      sapl/static/sapl/frontend/fonts/fa-regular-400.67a0fb74.ttf
  66. BIN
      sapl/static/sapl/frontend/fonts/fa-regular-400.67a0fb74.ttf.gz
  67. BIN
      sapl/static/sapl/frontend/fonts/fa-regular-400.81482cd4.ttf
  68. BIN
      sapl/static/sapl/frontend/fonts/fa-regular-400.81482cd4.ttf.gz
  69. BIN
      sapl/static/sapl/frontend/fonts/fa-solid-900.0b0cc8a6.woff2
  70. BIN
      sapl/static/sapl/frontend/fonts/fa-solid-900.69d3141a.ttf
  71. BIN
      sapl/static/sapl/frontend/fonts/fa-solid-900.69d3141a.ttf.gz
  72. BIN
      sapl/static/sapl/frontend/fonts/fa-solid-900.6a8db53d.ttf
  73. BIN
      sapl/static/sapl/frontend/fonts/fa-solid-900.6a8db53d.ttf.gz
  74. BIN
      sapl/static/sapl/frontend/fonts/fa-solid-900.fd0b155c.woff2
  75. BIN
      sapl/static/sapl/frontend/fonts/fa-v4compatibility.2c070fd2.ttf
  76. BIN
      sapl/static/sapl/frontend/fonts/fa-v4compatibility.2c070fd2.ttf.gz
  77. BIN
      sapl/static/sapl/frontend/fonts/fa-v4compatibility.e4efb16c.ttf
  78. BIN
      sapl/static/sapl/frontend/fonts/fa-v4compatibility.e4efb16c.ttf.gz
  79. BIN
      sapl/static/sapl/frontend/img/down_arrow_select.jpg.gz
  80. 19
      sapl/static/sapl/frontend/js/chunk-vendors.5e41c7a6.js
  81. BIN
      sapl/static/sapl/frontend/js/chunk-vendors.5e41c7a6.js.LICENSE.txt.gz
  82. BIN
      sapl/static/sapl/frontend/js/chunk-vendors.5e41c7a6.js.gz
  83. 19
      sapl/static/sapl/frontend/js/chunk-vendors.e8ab4373.js
  84. 244
      sapl/static/sapl/frontend/js/chunk-vendors.e8ab4373.js.LICENSE.txt
  85. BIN
      sapl/static/sapl/frontend/js/chunk-vendors.e8ab4373.js.LICENSE.txt.gz
  86. BIN
      sapl/static/sapl/frontend/js/chunk-vendors.e8ab4373.js.gz
  87. 1
      sapl/static/sapl/frontend/js/compilacao.57574b17.js
  88. BIN
      sapl/static/sapl/frontend/js/compilacao.57574b17.js.gz
  89. 1
      sapl/static/sapl/frontend/js/compilacao.d3adb1c1.js
  90. BIN
      sapl/static/sapl/frontend/js/compilacao.d3adb1c1.js.gz
  91. 2
      sapl/static/sapl/frontend/js/global.2f5aff92.js
  92. BIN
      sapl/static/sapl/frontend/js/global.2f5aff92.js.gz
  93. 2
      sapl/static/sapl/frontend/js/global.8ef6e932.js
  94. 0
      sapl/static/sapl/frontend/js/global.8ef6e932.js.LICENSE.txt
  95. BIN
      sapl/static/sapl/frontend/js/global.8ef6e932.js.gz
  96. 2
      sapl/static/sapl/frontend/js/painel.73a4fb17.js
  97. BIN
      sapl/static/sapl/frontend/js/painel.73a4fb17.js.gz
  98. BIN
      sapl/static/sapl/frontend/js/painel.aa1df64a.js.gz
  99. 1
      sapl/static/sapl/frontend/js/parlamentar.f0710dca.js
  100. BIN
      sapl/static/sapl/frontend/js/parlamentar.f0710dca.js.gz

90
CHANGES.md

@ -1,4 +1,94 @@
3.1.164-RC0 / 2025-07-09
========================
* Fix: nome casa duplicado
* feat: #3771 (#3772)
* Adiciona metadados para Google crawler
* Remove version e monta mais um volume
* Fix: log de aplicação
* feat: #3769 (#3770)
* fix: #3767 (#3768)
* Release: 3.1.163
3.1.163 / 2025-05-21
====================
* Fix fancycompleter incompatibility when calling shell_plus
3.1.163-RC24 / 2025-05-19
=========================
* Implementa relatórios CSV, XLSX e JSON para Pauta de Sessão (#3744)
* feat: adiciona pydevd-pycharm para facilitar debug usando pycharm ide (#3717)
* Ajusta link do texto integral da Norma Jurídica nos formatos CSV, XLSX e JSON. (#3743)
* Força mudança de senha para senhas fracas
* Fix Chamado #643964 - Multiple definitions in dictionary at byte 0xc854c for key /Info
* Ajusta link do texto original da Matéria nos formatos CSV, XLSX e JSON. (#3742)
* fix: aplica um lazy loading no sortable do jquery ui pelo evento mouseenter. (#3737)
* fix: corrige condição para mostrar btn para TAs.
* fix: add migrate resultante do commit anterior
* fix: torna cep opcional no model CasaLegislativa
3.1.163-RC23 / 2024-07-31
=========================
* Otimiza recuperação de normas relacionadas (#3734)
* hot-fix: resolve imcompatibilidade de regex com versão de produção do postgresql 9.6
3.1.163-RC21 / 2024-06-25
=========================
* fix: descomenta código pertencente ao commit anterior
* fix: elimina dupla extração de assinaturas eletrônicas
* fix: remove listagem pública de OperadorAutor
* fix: altera campo de 'resultado' ao registrar leitura em bloco
* bump version gunicorn in setup.py
* rebuild frontend
* fix dependabot alerts #61 - bump version axios
* fix: ajuste no conjunto inicial de permissões na construção de classe crud
* Impl Mixin para gerar arquivos de pesq em diversos formatos (#3710)
* Fix estatistica norma view (#3707)
* bump version gunicorn, pillow
* Build(deps): Bump pillow from 10.0.1 to 10.3.0 in /requirements (#3709)
* fix: corrige display de data e hora de protocolor manual
* fix: corrige carga de permissões públicas do crud
* Add blank space when SAPN
* fix: corrige bug na pesquisa de impressos etiquetas (#3695)
* feat: adiciona o turno na info de materias na pauta de sessao (#3694)
* fix: invert lógica do sapln_switch
* fix: Update docker-compose.yaml
* Update CHANGES.md
3.1.163-RC20 / 2023-12-04
=========================
* HOT-FIX: Bump easy-thumbnails
3.1.163-RC19 / 2023-11-29
=========================
* Bump Pillow
3.1.163-RC18 / 2023-11-29
=========================
* Bump Pillow
* Adiciona feature flag lib
* Conserta relatoria de matérias
* Bump Pillow version
* feat: adiciona script para ajuste to tamanho de fontes de seçoes do painel eletronico
* Conserta bug na paginação do Relatório de Matérias por Tramitação
* fix: ajusta ordenacao de votos nominais por ordem alfabetica no extrato da sessao
* fix: corrige erro de loaddata cargomesa
* feat: adiciona check de presenca e sessao aberta na leitura em bloco da ordem do dia
* fix: padroniza nome_parlamentar para lista de presenca e votacoes nominais no resumo da ata
* Apaga Numeração se TipoMateriaLegislativa é apagado
* Adiciona opção de remover formatação
* Adiciona ordenacao em cargo mesa
* Remove tags de considerações finais e ocorrências de sessão
* fix: padroniza nome_parlamentar para lista de presenca e votacoes nominais no resumo da ata
3.1.163-RC17 / 2023-09-30
=========================

2
docker/Dockerfile

@ -68,6 +68,6 @@ ENV DEBIAN_FRONTEND teletype
EXPOSE 80/tcp 443/tcp
VOLUME ["/var/interlegis/sapl/data", "/var/interlegis/sapl/media"]
VOLUME ["/var/interlegis/sapl/data", "/var/interlegis/sapl/media", "/var/log/sapl/"]
CMD ["/var/interlegis/sapl/start.sh"]

4
docker/docker-compose.yaml

@ -1,4 +1,3 @@
version: "3.7"
services:
sapldb:
image: postgres:10.5-alpine
@ -32,7 +31,7 @@ services:
networks:
- sapl-net
sapl:
image: interlegis/sapl:3.1.163-RC17
image: interlegis/sapl:3.1.164-RC0
# build:
# context: ../
# dockerfile: ./docker/Dockerfile
@ -54,6 +53,7 @@ services:
SOLR_COLLECTION: sapl
SOLR_URL: http://solr:solr@saplsolr:8983
IS_ZK_EMBEDDED: 'True'
ENABLE_SAPN: 'False'
TZ: America/Sao_Paulo
volumes:
- sapl_data:/var/interlegis/sapl/data

17
docker/start.sh

@ -39,6 +39,7 @@ create_env() {
echo "SOLR_COLLECTION = ""${SOLR_COLLECTION-sapl}" >> $FILENAME
echo "SOLR_URL = ""${SOLR_URL-http://localhost:8983}" >> $FILENAME
echo "IS_ZK_EMBEDDED = ""${IS_ZK_EMBEDDED-False}" >> $FILENAME
echo "ENABLE_SAPN = ""${ENABLE_SAPN-False}" >> $FILENAME
echo "[ENV FILE] done."
}
@ -85,14 +86,30 @@ if [ "${USE_SOLR-False}" == "True" ] || [ "${USE_SOLR-False}" == "true" ]; then
fi
python3 solr_cli.py -u $SOLR_URL -c $SOLR_COLLECTION -s $NUM_SHARDS -rf $RF -ms $MAX_SHARDS_PER_NODE $ZK_EMBEDDED &
# Enable SOLR switch on, creating if it doesn't exist on database
./manage.py waffle_switch SOLR_SWITCH on --create
else
echo "Solr is offline, not possible to connect."
# Disable Solr switch off, creating if it doesn't exist on database
./manage.py waffle_switch SOLR_SWITCH off --create
fi
else
echo "Solr support is not initialized."
# Disable Solr switch off, creating if it doesn't exist on database
./manage.py waffle_switch SOLR_SWITCH off --create
fi
## Enable/Disable SAPN
if [ "${ENABLE_SAPN-False}" == "True" ] || [ "${ENABLE_SAPN-False}" == "true" ]; then
echo "Enabling SAPN"
./manage.py waffle_switch SAPLN_SWITCH on --create
else
echo "Enabling SAPL"
./manage.py waffle_switch SAPLN_SWITCH off --create
fi
echo "Creating admin user..."
user_created=$(python3 create_admin.py 2>&1)

45
docs/feature-flags.md

@ -0,0 +1,45 @@
### Types
Waffle supports three types of feature flippers:
### Flags
_"Flags can be used to enable a feature for specific users, groups, users meeting
certain criteria (such as being authenticated, or superusers) or a certain percentage
of visitors."_
https://waffle.readthedocs.io/en/stable/types/flag.html
### Switches
_"Switches are simple booleans: they are on or off, for everyone, all the time.
They do not require a request object and can be used in other contexts, such
as management commands and tasks."_
Enabling/Disabling via CLI (example):
./manage.py waffle_switch SOLR_SWITCH on --create
./manage.py waffle_switch SOLR_SWITCH off --create
https://waffle.readthedocs.io/en/stable/types/switch.html
### Samples
_"Samples are on a given percentage of the time. They do not require a request
object and can be used in other contexts, such as management commands and tasks."_
Liga e desliga baseado em amostragem aleatória.
https://waffle.readthedocs.io/en/stable/types/sample.html
### Reference
* Documentation: https://waffle.readthedocs.io/
* Managing from CLI: https://waffle.readthedocs.io/en/stable/usage/cli.html
* Templates: https://waffle.readthedocs.io/en/stable/usage/templates.html
* Views: https://waffle.readthedocs.io/en/stable/usage/views.html
* Decorators: https://waffle.readthedocs.io/en/stable/usage/decorators.html

74
frontend/src/__apps/compilacao/js/old/compilacao_edit.js

@ -1,4 +1,3 @@
const _$ = window.$
window.DispositivoEdit = function () {
@ -500,38 +499,47 @@ window.DispositivoEdit = function () {
}
instance.reloadFunctionsDraggables = function () {
_$('.dpt-alts').sortable({
revert: true,
distance: 15,
start: function (event, ui) {
},
stop: function (event, ui) {
const pk = ui.item.attr('pk')
const bloco_pk = ui.item.closest('.dpt-alts').closest('.dpt').attr('pk')
const url = pk + '/refresh?action=json_drag_move_dpt_alterado&index=' + ui.item.index() + '&bloco_pk=' + bloco_pk
_$.get(url).done(function (data) {
// console.log(pk + ' - ' + bloco_pk)
// reloadFunctionsForObjectsOfCompilacao();
})
}
})
const dptAlts = _$('.dpt-alts')
if (dptAlts.length > 0) {
dptAlts.sortable({
connectWith: '.dpt-alts',
items: '.sorting-initialize',
revert: true,
distance: 15,
start: function (event, ui) {
},
stop: function (event, ui) {
const pk = ui.item.attr('pk')
const bloco_pk = ui.item.closest('.dpt-alts').closest('.dpt').attr('pk')
const url = pk + '/refresh?action=json_drag_move_dpt_alterado&index=' + ui.item.index() + '&bloco_pk=' + bloco_pk
_$.get(url).done(function (data) {
// handle data if needed
})
}
})
dptAlts.find('.dpt').one('mouseenter', function () {
$(this).addClass('sorting-initialize')
dptAlts.sortable('refresh')
})
_$('.dpt-alts .dpt').draggable({
connectToSortable: '.dpt-alts',
revert: 'invalid',
zIndex: 1,
distance: 15,
drag: function (event, ui) {
// _$('.dpt-comp-selected').removeClass('dpt-comp-selected');
_$('.dpt-alts').addClass('drag')
},
stop: function (event, ui) {
_$('.dpt-alts').removeClass('drag')
}
})
_$('.dpt-alts .dpt').draggable({
connectToSortable: '.dpt-alts',
revert: 'invalid',
zIndex: 1,
distance: 15,
drag: function (event, ui) {
_$('.dpt-alts').addClass('drag')
},
stop: function (event, ui) {
_$('.dpt-alts').removeClass('drag')
}
})
_$('.dpt-alts').disableSelection()
dptAlts.disableSelection()
} else {
console.warn("No '.dpt-alts' elements found to make sortable/draggable.")
}
}
instance.scrollTo = function (dpt) {
try {
@ -570,7 +578,9 @@ window.DispositivoEdit = function () {
instance.triggerBtnDptEdit(href[1])
}
_$('main').click(function (event) {
if (event.target === this || event.target === this.firstElementChild) { instance.clearEditSelected() }
if (event.target === this || event.target === this.firstElementChild) {
instance.clearEditSelected()
}
})
instance.waitHide()
}

6
frontend/src/__global/js/tinymce/index.js

@ -20,11 +20,11 @@ window.initTextRichEditor = function (elements, readonly = false, paste_as_text
language: 'pt_BR',
branding: false,
forced_root_block: 'p',
toolbar: "...| removeformat | ...",
paste_as_text,
plugins: 'table lists advlist link code',
toolbar: 'undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link | code',
menubar: 'file edit view insert format table'
toolbar: 'undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link | code | removeformat ',
menubar: 'file edit view insert format table',
license_key: 'gpl'
}
if (readonly) {
configTinymce.readonly = 1

754
frontend/webpack-stats.json

File diff suppressed because it is too large

14525
package-lock.json

File diff suppressed because it is too large

4
package.json

@ -9,7 +9,7 @@
},
"dependencies": {
"@fortawesome/fontawesome-free": "^6.1.2",
"axios": "^0.27.2",
"axios": "^1.7.2",
"bootstrap": "^4.6.2",
"bootstrap-vue": "^2.22.0",
"diff": "^5.1.0",
@ -21,7 +21,7 @@
"moment": "^2.29.4",
"moment-locales-webpack-plugin": "^1.2.0",
"popper.js": "^1.16.1",
"tinymce": "^6.1.2",
"tinymce": "^7.2.0",
"vue": "^2.7.9"
},
"devDependencies": {

22
release.sh

@ -5,7 +5,6 @@
##
## IMPORTANT: requires gh and git-extras commands installed
## Currently only runs on MacOS because of sed issue on lines 41 to 47 (see double quotes after -i)
##
# TODO: verificar porque só pega versões superiores (3.1.200 ao invés de 3.1.200-RC9)
@ -44,14 +43,19 @@ function change_files {
echo "Updating from "$OLD_VERSION" to "$FINAL_VERSION""
sed -E -i "" "s|$OLD_VERSION|$FINAL_VERSION|g" docker/docker-compose.yaml
sed -E -i "" "s|$OLD_VERSION|$FINAL_VERSION|g" setup.py
sed -E -i "" "s|$OLD_VERSION|$FINAL_VERSION|g" sapl/templates/base.html
sed -E -i "" "s|$OLD_VERSION|$FINAL_VERSION|g" sapl/settings.py
if [[ "$OSTYPE" == "darwin"* ]]; then
# MacOS (BSD sed)
sed -E -i "" "s|$OLD_VERSION|$FINAL_VERSION|g" docker/docker-compose.yaml
sed -E -i "" "s|$OLD_VERSION|$FINAL_VERSION|g" setup.py
sed -E -i "" "s|$OLD_VERSION|$FINAL_VERSION|g" sapl/templates/base.html
sed -E -i "" "s|$OLD_VERSION|$FINAL_VERSION|g" sapl/settings.py
else
# Linux (GNU sed)
sed -i -E "s|$OLD_VERSION|$FINAL_VERSION|g" docker/docker-compose.yaml
sed -i -E "s|$OLD_VERSION|$FINAL_VERSION|g" setup.py
sed -i -E "s|$OLD_VERSION|$FINAL_VERSION|g" sapl/templates/base.html
sed -i -E "s|$OLD_VERSION|$FINAL_VERSION|g" sapl/settings.py
fi
}
function set_major_version {

4
requirements/dev-requirements.txt

@ -4,6 +4,8 @@ autopep8==1.2.4
beautifulsoup4==4.9.1
django-debug-toolbar==1.11.1
ipdb==0.13.3
pdbpp==0.9.2
fancycompleter==0.9.1
pdbpp==0.10.3
pip-review==0.4
pipdeptree==0.10.1
pydevd-pycharm~=203.7148.7

9
requirements/requirements.txt

@ -6,15 +6,15 @@ dj-database-url==0.5.0
django-braces==1.14.0
django-crispy-forms==1.7.2
django-contrib-postgres==0.0.1
django-floppyforms==1.8.0
django-extra-views==0.12.0
django-model-utils==3.1.2
django-extensions==2.1.4
django-image-cropping==1.2
django-waffle==3.0.0
django-webpack-loader==1.6.0
drf-spectacular==0.18.2
django-ratelimit==3.0.1
easy-thumbnails==2.5
easy-thumbnails==2.8.5
python-decouple==3.1
psycopg2-binary==2.8.6
pyyaml==6.0.1
@ -22,8 +22,8 @@ pytz==2019.3
python-magic==0.4.15
unipath==1.1
WeasyPrint==51
Pillow==9.3.0
gunicorn==19.9.0
Pillow==10.3.0
gunicorn==22.0.0
more-itertools==8.2.0
pysolr==3.6.0
PyPDF4==1.27.0
@ -35,6 +35,7 @@ kazoo==2.8.0
django-prometheus==2.2.0
asn1crypto==1.5.1
XlsxWriter==3.2.0
git+https://github.com/interlegis/trml2pdf
git+https://github.com/interlegis/django-admin-bootstrapped

17
sapl/api/views_comissoes.py

@ -3,6 +3,10 @@ from django.apps.registry import apps
from drfautoapi.drfautoapi import ApiViewSetConstrutor, \
customize, wrapper_queryset_response_for_drf_action
from sapl.comissoes.models import Comissao
from rest_framework.decorators import action
from sapl.materia.models import MateriaEmTramitacao
ApiViewSetConstrutor.build_class(
@ -10,3 +14,16 @@ ApiViewSetConstrutor.build_class(
apps.get_app_config('comissoes')
]
)
@customize(Comissao)
class _ComissaoViewSet:
@action(detail=True)
def materiaemtramitacao(self, request, *args, **kwargs):
return self.get_materiaemtramitacao(**kwargs)
@wrapper_queryset_response_for_drf_action(model=MateriaEmTramitacao)
def get_materiaemtramitacao(self, **kwargs):
return self.get_queryset().filter(
unidade_tramitacao_atual__comissao=kwargs['pk'],
)

18
sapl/audiencia/migrations/0019_auto_20240711_1400.py

@ -0,0 +1,18 @@
# Generated by Django 2.2.28 on 2024-07-11 17:00
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('audiencia', '0018_auto_20230529_1641'),
]
operations = [
migrations.AlterField(
model_name='audienciapublica',
name='ano',
field=models.PositiveSmallIntegerField(choices=[(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'),
),
]

35
sapl/base/forms.py

@ -40,7 +40,7 @@ from sapl.utils import (autor_label, autor_modal, ChoiceWithoutValidationField,
FilterOverridesMetaMixin, FileFieldCheckMixin,
ImageThumbnailFileInput, qs_override_django_filter,
RANGE_ANOS, YES_NO_CHOICES, choice_tipos_normas,
GoogleRecapthaMixin, parlamentares_ativos, RANGE_MESES)
GoogleRecapthaMixin, parlamentares_ativos, RANGE_MESES, is_weak_password, delete_cached_entry)
from .models import AppConfig, CasaLegislativa
@ -288,8 +288,13 @@ class UserAdminForm(ModelForm):
)
else:
if new_password1 and new_password2:
if is_weak_password(new_password1):
raise forms.ValidationError(_(
'A senha deve ter pelo menos 8 caracteres e incluir uma combinação '
'de letras maiúsculas e minúsculas, números e caracteres especiais.'
))
password_validation.validate_password(
new_password2, self.instance)
new_password1, self.instance)
parlamentar = data.get('parlamentar', None)
if parlamentar and parlamentar.votante_set.exists() and \
@ -916,22 +921,26 @@ class CasaLegislativaForm(FileFieldCheckMixin, ModelForm):
# chama __clean de FileFieldCheckMixin
# por estar em clean de campo
super(CasaLegislativaForm, self)._check()
logotipo = self.cleaned_data.get('logotipo')
if logotipo:
if logotipo.size > MAX_IMAGE_UPLOAD_SIZE:
raise ValidationError("Imagem muito grande. ( > 2MB )")
if logotipo and logotipo.size > MAX_IMAGE_UPLOAD_SIZE:
raise ValidationError("Imagem muito grande. ( > 2MB )")
return logotipo
def save(self, commit=True):
casa = super(CasaLegislativaForm, self).save(commit=commit)
delete_cached_entry("site-title")
return casa
class LoginForm(AuthenticationForm):
username = forms.CharField(
label="Username", max_length=30,
label="Usuário", max_length=30,
widget=forms.TextInput(
attrs={
'class': 'form-control', 'name': 'username'}))
password = forms.CharField(
label="Password", max_length=30,
label="Senha", max_length=30,
widget=forms.PasswordInput(
attrs={
'class': 'form-control', 'name': 'password'}))
@ -1009,7 +1018,6 @@ class ConfiguracoesAppForm(ModelForm):
'google_recaptcha_site_key',
'google_recaptcha_secret_key',
'google_analytics_id_metrica',
'sapl_as_sapn',
'identificacao_de_documentos',
]
@ -1140,12 +1148,15 @@ class AlterarSenhaForm(Form):
# TODO: caracteres alfanuméricos, maiúsculas (?),
# TODO: senha atual igual a senha anterior, etc
if len(new_password1) < 6:
if is_weak_password(new_password1):
self.logger.warning(
'A senha informada não tem o mínimo de 6 caracteres.'
'A senha deve ter pelo menos 8 caracteres e incluir uma combinação '
'de letras maiúsculas e minúsculas, números e caracteres especiais.'
)
raise ValidationError(
"A senha informada deve ter no mínimo 6 caracteres")
'A senha deve ter pelo menos 8 caracteres e incluir uma combinação '
'de letras maiúsculas e minúsculas, números e caracteres especiais.'
)
username = data['username']
old_password = data['old_password']

17
sapl/base/migrations/0059_remove_appconfig_sapl_as_sapn.py

@ -0,0 +1,17 @@
# Generated by Django 2.2.28 on 2023-11-27 00:23
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('base', '0058_appconfig_ordenacao_pesquisa_materia'),
]
operations = [
migrations.RemoveField(
model_name='appconfig',
name='sapl_as_sapn',
),
]

18
sapl/base/migrations/0060_auto_20240812_1628.py

@ -0,0 +1,18 @@
# Generated by Django 2.2.28 on 2024-08-12 19:28
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('base', '0059_remove_appconfig_sapl_as_sapn'),
]
operations = [
migrations.AlterField(
model_name='casalegislativa',
name='cep',
field=models.CharField(blank=True, max_length=100, verbose_name='CEP'),
),
]

6
sapl/base/models.py

@ -56,7 +56,7 @@ class CasaLegislativa(models.Model):
nome = models.CharField(max_length=100, verbose_name=_('Nome'))
sigla = models.CharField(max_length=100, verbose_name=_('Sigla'))
endereco = models.CharField(max_length=100, verbose_name=_('Endereço'))
cep = models.CharField(max_length=100, verbose_name=_('CEP'))
cep = models.CharField(max_length=100, blank=True, verbose_name=_('CEP'))
municipio = models.CharField(max_length=50, verbose_name=_('Município'))
uf = models.CharField(max_length=2,
choices=LISTA_DE_UFS,
@ -107,10 +107,6 @@ class AppConfig(models.Model):
default="",
verbose_name=_('Esfera Federação'),
choices=ESFERA_FEDERACAO_CHOICES)
sapl_as_sapn = models.BooleanField(
verbose_name=_(
'Utilizar SAPL apenas como SAPL-Normas?'),
choices=YES_NO_CHOICES, default=False)
# MÓDULO PARLAMENTARES

14
sapl/base/receivers.py

@ -283,13 +283,18 @@ def signed_files_extraction_function(sender, instance, **kwargs):
return signs
# tenta extrair via /Fields
fields_br = []
try:
pdf = PdfFileReader(file)
fields = pdf.getFields()
fields_br = list(
map(lambda x: x.get('/V', {}).get('/ByteRange', []), fields.values()))
except Exception as e:
try:
pdf = PdfFileReader(file, strict=False)
fields = pdf.getFields()
fields_br = list(
map(lambda x: x.get('/V', {}).get('/ByteRange', []), fields.values()))
except Exception as ee:
fields = ee
@ -309,13 +314,17 @@ def signed_files_extraction_function(sender, instance, **kwargs):
n += 1
br = [int(i, 10) for i in pdfdata[start + 1: stop].split()]
if br in fields_br:
continue
contents = pdfdata[br[0] + br[1] + 1: br[2] - 1]
bcontents = bytes.fromhex(contents.decode("utf8"))
data1 = pdfdata[br[0]: br[0] + br[1]]
data2 = pdfdata[br[2]: br[2] + br[3]]
#signedData = data1 + data2
nome = 'Nome do assinante não localizado.'
not_nome = nome = 'Nome do assinante não localizado.'
oname = ''
try:
info = cms.ContentInfo.load(bcontents)
@ -348,7 +357,8 @@ def signed_files_extraction_function(sender, instance, **kwargs):
pass
fd = None
signs.append((nome, [fd, oname]))
if nome != not_nome:
signs.append((nome, [fd, oname]))
except Exception as e:
pass

10
sapl/base/templatetags/common_tags.py

@ -84,6 +84,9 @@ def desc_operation(value):
@register.filter
def format_user(user):
if not user:
return ""
if user.first_name:
return user.first_name + " " + user.last_name + " (" + user.username + ")"
else:
@ -408,4 +411,9 @@ def parse_datetime(value):
@register.filter
def is_report_visible(request, url_path=None):
return is_report_allowed(request, url_path)
return is_report_allowed(request, url_path)
@register.filter
def sort_by_index(queryset, index):
return sorted(queryset, key=lambda x: x[index])

26
sapl/base/templatetags/menus.py

@ -183,17 +183,21 @@ def resolve_urls_inplace(menu, pk, rm, context):
as funcionalidades diretas do MasterDetailCrud, como:
- visualização de detalhes, adição, edição, remoção.
"""
if 'view' in context:
view = context['view']
if hasattr(view, '__class__') and\
hasattr(view.__class__, 'crud'):
urls = view.__class__.crud.get_urls()
for u in urls:
if (u.name == url_name or
'urls_extras' in menu and
u.name in menu['urls_extras']):
menu['active'] = 'active'
break
try:
if 'view' in context:
view = context['view']
if hasattr(view, 'crud'):
urls = view.crud.get_urls()
for u in urls:
if (u.name == url_name or
'urls_extras' in menu and
u.name in menu['urls_extras']):
menu['active'] = 'active'
break
except:
url_active = menu.get('url', '')
logger.warning(
f'Não foi possível definir se url {url_active} é a url ativa.')
elif 'check_permission' in menu and not context[
'request'].user.has_perm(menu['check_permission']):
menu['active'] = ''

25
sapl/base/views.py

@ -6,7 +6,7 @@ import os
from django.apps.registry import apps
from django.contrib import messages
from django.contrib.auth import get_user_model, views
from django.contrib.auth import authenticate, login, get_user_model, views
from django.contrib.auth.mixins import PermissionRequiredMixin
from django.contrib.auth.models import Group
from django.contrib.auth.tokens import default_token_generator
@ -50,8 +50,8 @@ from sapl.relatorios.views import (relatorio_estatisticas_acesso_normas)
from sapl.sessao.models import (Bancada, SessaoPlenaria)
from sapl.settings import EMAIL_SEND_USER
from sapl.utils import (gerar_hash_arquivo, intervalos_tem_intersecao, mail_service_configured,
SEPARADOR_HASH_PROPOSICAO, show_results_filter_set, google_recaptcha_configured, sapl_as_sapn,
get_client_ip)
SEPARADOR_HASH_PROPOSICAO, show_results_filter_set, google_recaptcha_configured,
get_client_ip, sapn_is_enabled, is_weak_password)
from .forms import (AlterarSenhaForm, CasaLegislativaForm, ConfiguracoesAppForm, EstatisticasAcessoNormasForm)
from .models import AppConfig, CasaLegislativa
@ -62,7 +62,7 @@ def get_casalegislativa():
class IndexView(TemplateView):
def get(self, request, *args, **kwargs):
if sapl_as_sapn():
if sapn_is_enabled():
return redirect('/norma/pesquisar')
return TemplateView.get(self, request, *args, **kwargs)
@ -75,6 +75,21 @@ class LoginSapl(views.LoginView):
template_name = 'base/login.html'
authentication_form = LoginForm
def form_valid(self, form):
"""Override do comportamento padrão para verificar senha fraca"""
username = form.cleaned_data.get('username')
password = form.cleaned_data.get('password')
user = authenticate(self.request, username=username, password=password)
if user is not None:
login(self.request, user)
if is_weak_password(password):
self.request.session['weak_password'] = True
return redirect(self.get_success_url())
# Fallback se falhar a autenticação (tecnicamente não devia chegar aqui)
return super().form_invalid(form)
class ConfirmarEmailView(TemplateView):
template_name = "email/confirma.html"
@ -1481,6 +1496,8 @@ class AlterarSenha(FormView):
user.set_password(new_password)
user.save()
self.request.session.pop('weak_password', None)
return super().form_valid(form)

19
sapl/compilacao/models.py

@ -390,7 +390,6 @@ class TextoArticulado(TimestampedMixin):
@classonlymethod
def update_or_create(cls, view_integracao, obj):
map_fields = view_integracao.map_fields
ta_values = getattr(view_integracao, 'ta_values', {})
@ -1182,6 +1181,24 @@ class Dispositivo(BaseModel, TimestampedMixin):
help_text=_('O recorte de imagem '
'é possível após a atualização.'))
# define custom manager
class SelectRelatedManager(models.Manager):
def get_queryset(self):
return super().get_queryset().select_related('tipo_dispositivo',
'publicacao',
'ta',
'ta_publicado',
'dispositivo_subsequente',
'dispositivo_substituido',
'dispositivo_pai',
'dispositivo_pai__tipo_dispositivo',
'dispositivo_raiz',
'dispositivo_vigencia',
'dispositivo_atualizador'
)
# Replace the default manager with custom manager
objects = SelectRelatedManager()
class Meta:
verbose_name = _('Dispositivo')
verbose_name_plural = _('Dispositivos')

2
sapl/compilacao/urls.py

@ -23,7 +23,7 @@ urlpatterns_compilacao = [
url(r'^(?P<ta_id>[0-9]+)/text$',
views.TextView.as_view(), name='ta_text'),
url(r'^(?P<ta_id>[0-9]+)/text/vigencia/(?P<sign>.+)/$',
url(r'^(?P<ta_id>[0-9]+)/text/vigencia/(?P<sign>.*:[A-Za-z0-9_-]+)/$',
views.TextView.as_view(), name='ta_vigencia'),
url(r'^(?P<ta_id>[0-9]+)/text/edit',

27
sapl/context_processors.py

@ -1,15 +1,13 @@
import logging
from django.conf import settings
from django.utils.translation import ugettext_lazy as _
from sapl.utils import google_recaptcha_configured as google_recaptcha_configured_utils,\
sapl_as_sapn as sapl_as_sapn_utils
from sapl.utils import google_recaptcha_configured as \
google_recaptcha_configured_utils, sapn_is_enabled, cached_call, get_base_url
from sapl.utils import mail_service_configured as mail_service_configured_utils
def parliament_info(request):
from sapl.base.views import get_casalegislativa
casa = get_casalegislativa()
if casa:
@ -19,7 +17,6 @@ def parliament_info(request):
def mail_service_configured(request):
if not mail_service_configured_utils(request):
logger = logging.getLogger(__name__)
logger.warning(_('Servidor de email não configurado.'))
@ -28,7 +25,6 @@ def mail_service_configured(request):
def google_recaptcha_configured(request):
if not google_recaptcha_configured_utils():
logger = logging.getLogger(__name__)
logger.warning(_('Google Recaptcha não configurado.'))
@ -36,10 +32,19 @@ def google_recaptcha_configured(request):
return {'google_recaptcha_configured': True}
def sapl_as_sapn(request):
return {
'sapl_as_sapn': sapl_as_sapn_utils(),
'nome_sistema': _('Sistema de Apoio ao Processo Legislativo')
if not sapl_as_sapn_utils()
@cached_call("site-title", timeout=60 * 2)
def enable_sapn(request):
verbose_name = _('Sistema de Apoio ao Processo Legislativo') \
if not sapn_is_enabled() \
else _('Sistema de Apoio à Publicação de Leis e Normas')
from sapl.base.models import CasaLegislativa
casa_legislativa = CasaLegislativa.objects.first()
nome_casa = casa_legislativa.nome if casa_legislativa and casa_legislativa.nome else ''
return {
'sapl_as_sapn': sapn_is_enabled(),
'nome_sistema': verbose_name,
'nome_casa': nome_casa,
'base_url': get_base_url(request),
}

10
sapl/crispy_layout_mixin.py

@ -284,7 +284,7 @@ class CrispyLayoutFormMixin:
'text': field_display,
}
def fk_urlize_for_detail(self, obj, fieldname):
def fk_urlify_for_detail(self, obj, fieldname):
field = obj._meta.get_field(fieldname)
value = getattr(obj, fieldname)
@ -298,6 +298,14 @@ class CrispyLayoutFormMixin:
return field.verbose_name, display
def fk_urlify_for_list(self, obj, field):
value = getattr(obj, field)
return reverse(
'%s:%s_detail' % (
value._meta.app_config.name,
value._meta.model_name),
kwargs={'pk': value.id}),
def m2m_urlize_for_detail(self, obj, fieldname):
manager, fieldname = tuple(fieldname.split('__'))

96
sapl/crud/base.py

@ -263,18 +263,18 @@ class CrudBaseMixin(CrispyLayoutFormMixin):
self.model_name_set = getattr(
obj.model, obj.model_set).field.model._meta.model_name
if hasattr(self, 'permission_required') and self.permission_required:
if hasattr(obj, 'public'):
self.permission_required = list(
set(self.permission_required) - set(obj.public))
else:
obj.public = []
if not hasattr(obj, 'public'):
obj.public = []
self.permission_required = tuple((
self.permission(pr) for pr in self.permission_required))
if hasattr(self, 'permission_required') and self.permission_required:
else:
obj.public = []
self.permission_required = tuple(
(
self.permission(pr) for pr in (
set(self.permission_required) - set(obj.public)
)
)
)
@classmethod
def url_name(cls, suffix):
@ -413,7 +413,7 @@ class CrudListView(PermissionRequiredContainerCrudMixin, ListView):
'composicao__periodo__data_inicio', 'composicao__periodo__data_fim')]
"""
r = []
for fieldname in self.list_field_names:
for (fieldname, _) in self._parse_field_names(self.list_field_names):
if not isinstance(fieldname, tuple):
fieldname = fieldname,
s = []
@ -446,14 +446,27 @@ class CrudListView(PermissionRequiredContainerCrudMixin, ListView):
r.append(s)
return r
def _parse_field_names(self, field_name_list):
parsed_list = []
for field_name in field_name_list:
field_tuple = tuple(field_name.split('|')) \
if '|' in field_name else (field_name, None)
parsed_list.append(field_tuple)
return parsed_list
def _as_row(self, obj):
r = []
for i, name in enumerate(self.list_field_names):
for i, (name, func) in enumerate(self._parse_field_names(self.list_field_names)):
# URL padrão para primeira coluna da listagem
url = self.resolve_url(
ACTION_DETAIL, args=(obj.id,)) if i == 0 else None
# gera URL para matéria a partir de fk_urlify_for_list em
# layouts.yaml
if i > 0 and func is not None:
url = getattr(self, func)(obj, name)[0]
"""Caso o crud list seja para uma relação ManyToManyField"""
if url and hasattr(self, 'crud') and\
if url and hasattr(self, 'crud') and \
hasattr(self.crud, 'is_m2m') and self.crud.is_m2m:
url = url + ('?pkk=' + self.kwargs['pk']
if 'pk' in self.kwargs else '')
@ -483,7 +496,7 @@ class CrudListView(PermissionRequiredContainerCrudMixin, ListView):
if m:
ss = get_field_display(m, n[-1])[1]
ss = (
('<br>' if '<ul>' in ss else ' - ') + ss)\
('<br>' if '<ul>' in ss else ' - ') + ss) \
if ss and j != 0 and s else ss
except:
pass
@ -935,6 +948,8 @@ class CrudDeleteView(PermissionRequiredContainerCrudMixin,
class Crud:
__abstract__ = True
BaseMixin = CrudBaseMixin
ListView = CrudListView
CreateView = CrudCreateView
@ -943,22 +958,38 @@ class Crud:
DeleteView = CrudDeleteView
help_topic = ''
class PublicMixin:
permission_required = []
@classonlymethod
def get_urls(cls):
def _add_base(view):
if view:
class CrudViewWithBase(cls.BaseMixin, view):
model = cls.model
help_topic = cls.help_topic
crud = cls
if not view:
return
CrudViewWithBase.__name__ = view.__name__
return CrudViewWithBase
if not cls.__abstract__:
return view
pr = set(view.permission_required) if hasattr(
view, 'permission_required') else set()
if hasattr(view, 'permission_required') and \
view.permission_required and \
hasattr(cls, 'public') and \
cls.public:
#print(view.permission_required, view)
#print(cls.public, cls)
pr = pr - set(cls.public)
class CrudViewWithBase(cls.BaseMixin, view):
permission_required = tuple(pr)
model = cls.model
help_topic = cls.help_topic
crud = cls
CrudViewWithBase.__name__ = view.__name__
return CrudViewWithBase
CrudListView = _add_base(cls.ListView)
CrudCreateView = _add_base(cls.CreateView)
@ -966,6 +997,21 @@ class Crud:
CrudUpdateView = _add_base(cls.UpdateView)
CrudDeleteView = _add_base(cls.DeleteView)
cruds = CrudListView, CrudCreateView, CrudDetailView, CrudUpdateView, CrudDeleteView
if cls.__abstract__:
class CRUD(cls):
__abstract__ = False
ListView = CrudListView
CreateView = CrudCreateView
DetailView = CrudDetailView
UpdateView = CrudUpdateView
DeleteView = CrudDeleteView
for c in cruds:
if c:
c.crud = CRUD
cruds_base = [
(CrudListView.get_url_regex()
if CrudListView else None, CrudListView, ACTION_LIST),
@ -1525,7 +1571,7 @@ class MasterDetailCrud(Crud):
class CrudBaseForListAndDetailExternalAppView(MasterDetailCrud):
CreateView, UpdateView, DeleteView = None, None, None
class BaseMixin(Crud.PublicMixin, MasterDetailCrud.BaseMixin):
class BaseMixin(MasterDetailCrud.BaseMixin):
def resolve_url(self, suffix, args=None):
obj = self.crud if hasattr(self, 'crud') else self

9
sapl/materia/forms.py

@ -42,7 +42,7 @@ from sapl.utils import (autor_label, autor_modal, timing,
models_with_gr_for_model, qs_override_django_filter,
SEPARADOR_HASH_PROPOSICAO,
validar_arquivo, YES_NO_CHOICES,
GoogleRecapthaMixin)
GoogleRecapthaMixin, get_client_ip)
from .models import (AcompanhamentoMateria, Anexada, Autoria,
DespachoInicial, DocumentoAcessorio, Numeracao,
@ -1045,6 +1045,7 @@ class MateriaLegislativaFilterSet(django_filters.FilterSet):
'ano_origem_externa',
'data_origem_externa',
'local_origem_externa',
'regime_tramitacao',
]
def filter_ementa(self, queryset, name, value):
@ -1105,7 +1106,7 @@ class MateriaLegislativaFilterSet(django_filters.FilterSet):
('tramitacao__status', 6),
])
row9 = to_row(
[('materiaassunto__assunto', 6), ('indexacao', 6)])
[('materiaassunto__assunto', 4), ('indexacao', 4), ('regime_tramitacao', 4)])
row8 = to_row(
[
@ -2659,7 +2660,9 @@ class ConfirmarProposicaoForm(ProposicaoForm):
protocolo.save()
HistoricoProposicao.objects.create(proposicao=proposicao,
status='E')
status='E',
user=self.initial['user'],
ip=self.initial['ip'])
self.instance.results['messages']['success'].append(_(
'Protocolo realizado com sucesso'))

19
sapl/materia/migrations/0085_auto_20231028_1838.py

@ -0,0 +1,19 @@
# Generated by Django 2.2.28 on 2023-10-28 21:38
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('materia', '0084_auto_20231007_2149'),
]
operations = [
migrations.AlterField(
model_name='numeracao',
name='tipo_materia',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='materia.TipoMateriaLegislativa', verbose_name='Tipo de Matéria'),
),
]

33
sapl/materia/migrations/0086_auto_20240711_1400.py

@ -0,0 +1,33 @@
# Generated by Django 2.2.28 on 2024-07-11 17:00
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('materia', '0085_auto_20231028_1838'),
]
operations = [
migrations.AlterField(
model_name='materialegislativa',
name='ano',
field=models.PositiveSmallIntegerField(choices=[(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='materialegislativa',
name='ano_origem_externa',
field=models.PositiveSmallIntegerField(blank=True, choices=[(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)], null=True, verbose_name='Ano'),
),
migrations.AlterField(
model_name='numeracao',
name='ano_materia',
field=models.PositiveSmallIntegerField(choices=[(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='proposicao',
name='ano',
field=models.PositiveSmallIntegerField(blank=True, choices=[(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)], default=None, null=True, verbose_name='Ano'),
),
]

26
sapl/materia/migrations/0087_update_viewdb_materiaemtramitacao.py

@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.20 on 2019-08-27 20:13
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('materia', '0086_auto_20240711_1400'),
]
operations = [
migrations.RunSQL("""
create or replace view materia_materiaemtramitacao as
select m.id as id,
m.id as materia_id,
t.id as tramitacao_id,
t.unidade_tramitacao_destino_id as unidade_tramitacao_atual_id
from materia_materialegislativa m
inner join materia_tramitacao t on (m.id = t.materia_id)
where t.id = (select max(id) from materia_tramitacao where materia_id = m.id)
order by m.id DESC
"""),
]

29
sapl/materia/models.py

@ -1,4 +1,6 @@
from datetime import datetime
from django.contrib.auth.models import Group
from django.contrib.contenttypes.fields import GenericRelation
from django.contrib.contenttypes.models import ContentType
@ -14,13 +16,13 @@ from sapl.comissoes.models import Comissao, Reuniao
from sapl.compilacao.models import (PerfilEstruturalTextoArticulado,
TextoArticulado)
from sapl.parlamentares.models import Parlamentar
#from sapl.protocoloadm.models import Protocolo
from sapl.utils import (RANGE_ANOS, YES_NO_CHOICES, SaplGenericForeignKey,
SaplGenericRelation, restringe_tipos_de_arquivo_txt,
texto_upload_path, get_settings_auth_user_model,
OverwriteStorage)
#from sapl.protocoloadm.models import Protocolo
EM_TRAMITACAO = [(1, 'Sim'),
(0, 'Não')]
@ -340,7 +342,13 @@ class MateriaLegislativa(models.Model):
if protocolo.timestamp:
return protocolo.timestamp
elif protocolo.timestamp_data_hora_manual:
return protocolo.timestamp_data_hora_manual
tz = timezone.localtime().tzinfo
return tz.localize(
datetime.combine(
protocolo.data,
protocolo.hora
)
)
elif protocolo.data:
return protocolo.data
@ -629,7 +637,7 @@ class Numeracao(models.Model):
materia = models.ForeignKey(MateriaLegislativa, on_delete=models.CASCADE)
tipo_materia = models.ForeignKey(
TipoMateriaLegislativa,
on_delete=models.PROTECT,
on_delete=models.CASCADE,
verbose_name=_('Tipo de Matéria'))
numero_materia = models.CharField(max_length=5,
verbose_name=_('Número'))
@ -931,7 +939,7 @@ class Proposicao(models.Model):
"""
Ao ser recebida, irá gerar uma nova matéria ou um documento acessorio de uma existente
materia_gerada = models.ForeignKey(
MateriaLegislativa,
blank=True,
@ -1027,7 +1035,8 @@ class Proposicao(models.Model):
def save(self, force_insert=False, force_update=False, using=None,
update_fields=None):
# atualiza o usuario baseado no status da proposição (que esta sendo calculado pela data)
# atualiza o usuario baseado no status da proposição (que esta sendo
# calculado pela data)
if self.data_envio is not None and not self.usuario_envio:
self.usuario_envio = self.user
elif self.data_recebimento is not None and not self.usuario_recebimento:
@ -1259,6 +1268,16 @@ class MateriaEmTramitacao(models.Model):
MateriaLegislativa, on_delete=models.DO_NOTHING)
tramitacao = models.ForeignKey(Tramitacao, on_delete=models.DO_NOTHING)
unidade_tramitacao_atual = models.ForeignKey(
UnidadeTramitacao,
related_name='materiaemtramitacao_set',
on_delete=models.DO_NOTHING,
verbose_name=_('Unidade de Tramitação Atual'),
db_column='unidade_tramitacao_atual_id',
null=True,
blank=True
)
class Meta:
managed = False
db_table = "materia_materiaemtramitacao"

57
sapl/materia/views.py

@ -24,6 +24,7 @@ from django.shortcuts import render
from django.template import loader
from django.urls import reverse
from django.utils import formats, timezone
from django.utils.encoding import force_text
from django.utils.translation import ugettext_lazy as _
from django.views.generic import CreateView, ListView, TemplateView, UpdateView
from django.views.generic.base import RedirectView
@ -53,7 +54,7 @@ from sapl.utils import (autor_label, autor_modal, gerar_hash_arquivo, get_base_u
get_client_ip, get_mime_type_from_file_extension, lista_anexados,
mail_service_configured, montar_row_autor, SEPARADOR_HASH_PROPOSICAO,
show_results_filter_set, get_tempfile_dir,
google_recaptcha_configured)
google_recaptcha_configured, MultiFormatOutputMixin)
from .forms import (AcessorioEmLoteFilterSet, AcompanhamentoMateriaForm,
AnexadaEmLoteFilterSet, AdicionarVariasAutoriasFilterSet,
@ -104,9 +105,9 @@ def proposicao_texto(request, pk):
if proposicao.texto_original:
if (not proposicao.data_recebimento and
not proposicao.autor.operadores.filter(
id=request.user.id
).exists()
not proposicao.autor.operadores.filter(
id=request.user.id
).exists()
):
logger.error("user=" + username + ". Usuário ({}) não tem permissão para acessar o texto original."
.format(request.user.id))
@ -1261,11 +1262,11 @@ class HistoricoProposicaoView(PermissionRequiredMixin, ListView):
paginate_by = 10
model = HistoricoProposicao
permission_required = (
'materia.list_historicoproposicao',
'materia.add_historicoproposicao',
'materia.change_historicoproposicao',
'materia.delete_historicoproposicao',
'materia.detail_historicoproposicao',
'materia.list_historicoproposicao',
'materia.add_historicoproposicao',
'materia.change_historicoproposicao',
'materia.delete_historicoproposicao',
'materia.detail_historicoproposicao',
)
def get_queryset(self):
@ -1337,6 +1338,9 @@ class RelatoriaCrud(MasterDetailCrud):
return {'comissao': localizacao}
class ListView(MasterDetailCrud.ListView):
layout_key = 'RelatoriaList'
class UpdateView(MasterDetailCrud.UpdateView):
form_class = RelatoriaForm
layout_key = None
@ -2076,11 +2080,27 @@ class AcompanhamentoExcluirView(TemplateView):
return HttpResponseRedirect(self.get_success_url())
class MateriaLegislativaPesquisaView(FilterView):
class MateriaLegislativaPesquisaView(MultiFormatOutputMixin, FilterView):
model = MateriaLegislativa
filterset_class = MateriaLegislativaFilterSet
paginate_by = 50
fields_base_report = [
'id', 'ano', 'numero', 'tipo__sigla', 'tipo__descricao', 'autoria__autor__nome', 'texto_original', 'ementa'
]
fields_report = {
'csv': fields_base_report,
'xlsx': fields_base_report,
'json': fields_base_report,
}
def hook_texto_original(self, obj):
url = self.request.build_absolute_uri('/')[:-1]
texto_original = obj.texto_original if not isinstance(
obj, dict) else obj["texto_original"]
return f'{url}/media/{texto_original}'
def get_filterset_kwargs(self, filterset_class):
super().get_filterset_kwargs(filterset_class)
@ -2115,6 +2135,9 @@ class MateriaLegislativaPesquisaView(FilterView):
"anexadas",
"tipo",
"texto_articulado",
"relatoria_set",
"relatoria_set__comissao",
"relatoria_set__parlamentar",
"tramitacao_set",
"tramitacao_set__status",
"tramitacao_set__unidade_tramitacao_local",
@ -2136,8 +2159,9 @@ class MateriaLegislativaPesquisaView(FilterView):
qs = qs.filter(materiaassunto__isnull=True)
if 'o' in self.request.GET and not self.request.GET['o']:
args = ['-ano', 'tipo__sequencia_regimental', '-numero'] if BaseAppConfig.attr('ordenacao_pesquisa_materia') == 'R' else ['-ano', 'tipo__sigla', '-numero']
args = ['-ano', 'tipo__sequencia_regimental', '-numero'] if BaseAppConfig.attr(
'ordenacao_pesquisa_materia') == 'R' else ['-ano', 'tipo__sigla', '-numero']
qs = qs.order_by(*args)
kwargs.update({
@ -2169,9 +2193,6 @@ class MateriaLegislativaPesquisaView(FilterView):
context['show_results'] = show_results_filter_set(qr)
context['USE_SOLR'] = settings.USE_SOLR if hasattr(
settings, 'USE_SOLR') else False
return context
@ -2700,9 +2721,9 @@ class EtiquetaPesquisaView(PermissionRequiredMixin, FormView):
if form.cleaned_data['processo_inicial']:
materias = materias.filter(
numeracao__numero_materia__gte=form.cleaned_data[
numero__gte=form.cleaned_data[
'processo_inicial'],
numeracao__numero_materia__lte=form.cleaned_data[
numero__lte=form.cleaned_data[
'processo_final'])
context['quantidade'] = len(materias)
@ -3012,7 +3033,7 @@ def create_pdf_docacessorios(materia,excluir_restritos):
materia.pk,
time.mktime(datetime.now().timetuple()))
merger = PdfFileMerger()
merger = PdfFileMerger(strict=False)
for f in docs_path:
merger.append(fileobj=f)

21
sapl/middleware.py

@ -0,0 +1,21 @@
import logging
from django.shortcuts import redirect
from django.urls import reverse
class CheckWeakPasswordMiddleware:
logger = logging.getLogger(__name__)
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
if request.user.is_authenticated and \
request.session.get('weak_password', False) and \
request.path != reverse('sapl.base:alterar_senha') and \
request.path != reverse('sapl.base:logout'):
logging.warning(f"Usuário {request.user.username} possui senha fraca.")
return redirect('sapl.base:alterar_senha')
return self.get_response(request)

22
sapl/norma/forms.py

@ -1,10 +1,11 @@
import logging
import re
from crispy_forms.layout import (Button, Fieldset, HTML, Layout)
from django import forms
from django.contrib.postgres.search import SearchVector
from django.core.exceptions import ObjectDoesNotExist, ValidationError
from django.db.models import Q
from django.db.models import Q, F, Func, Value
from django.forms import ModelChoiceField, ModelForm, widgets
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
@ -69,6 +70,10 @@ class NormaFilterSet(django_filters.FilterSet):
label='Ano',
choices=choice_anos_com_normas)
numero = django_filters.CharFilter(
method='filter_numero',
label=_('Número'))
ementa = django_filters.CharFilter(
method='filter_ementa',
label=_('Pesquisar expressões na ementa da norma'))
@ -129,6 +134,19 @@ class NormaFilterSet(django_filters.FilterSet):
form_actions(label='Pesquisar'))
)
def filter_numero(self, queryset, name, value):
p = r'(\W|_)'
value = re.sub(p, '', value, flags=re.IGNORECASE)
return queryset.annotate(
numero_clean=Func(
F('numero'),
Value(p),
Value(''),
Value('g'),
function='REGEXP_REPLACE'
)
).filter(numero_clean=value)
def filter_ementa(self, queryset, name, value):
return queryset.annotate(search=SearchVector('ementa',
config='portuguese')).filter(search=value)
@ -264,7 +282,7 @@ class NormaJuridicaForm(FileFieldCheckMixin, ModelForm):
texto_integral = self.cleaned_data.get('texto_integral', False)
if texto_integral:
validar_arquivo(texto_integral, "Texto Integral")
validar_arquivo(texto_integral, "Texto Original")
return texto_integral

46
sapl/norma/migrations/0045_auto_20240711_1405.py

@ -0,0 +1,46 @@
# Generated by Django 2.2.28 on 2024-07-11 17:05
from django.db import migrations, models
import sapl.norma.models
import sapl.utils
class Migration(migrations.Migration):
dependencies = [
('norma', '0044_auto_20230529_1641'),
]
operations = [
migrations.AlterField(
model_name='anexonormajuridica',
name='ano',
field=models.PositiveSmallIntegerField(choices=[(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='normaestatisticas',
name='ano',
field=models.PositiveSmallIntegerField(choices=[(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)], default=sapl.norma.models.get_ano_atual, verbose_name='Ano'),
),
migrations.AlterField(
model_name='normajuridica',
name='ano',
field=models.PositiveSmallIntegerField(choices=[(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='normajuridica',
name='texto_integral',
field=models.FileField(blank=True,
max_length=300,
null=True,
storage=sapl.utils.OverwriteStorage(),
upload_to=sapl.norma.models.norma_upload_path,
validators=[
sapl.utils.restringe_tipos_de_arquivo_txt
],
verbose_name='Texto Original'),
),
]

35
sapl/norma/models.py

@ -138,7 +138,7 @@ class NormaJuridica(models.Model):
blank=True,
null=True,
upload_to=norma_upload_path,
verbose_name=_('Texto Integral'),
verbose_name=_('Texto Original'),
storage=OverwriteStorage(),
validators=[restringe_tipos_de_arquivo_txt])
tipo = models.ForeignKey(
@ -231,12 +231,22 @@ class NormaJuridica(models.Model):
ordering = ['-data', '-numero']
def get_normas_relacionadas(self):
principais = NormaRelacionada.objects.filter(
norma_principal=self.id).order_by('norma_principal__data',
'norma_relacionada__data')
relacionadas = NormaRelacionada.objects.filter(
norma_relacionada=self.id).order_by('norma_principal__data',
'norma_relacionada__data')
principais = NormaRelacionada.objects.\
select_related('tipo_vinculo',
'norma_principal',
'norma_relacionada',
'norma_principal__tipo',
'norma_relacionada__tipo').\
filter(norma_principal=self.id).order_by('norma_principal__data',
'norma_relacionada__data')
relacionadas = NormaRelacionada.objects.\
select_related('tipo_vinculo',
'norma_principal',
'norma_relacionada',
'norma_principal__tipo',
'norma_relacionada__tipo').\
filter(norma_relacionada=self.id).order_by('norma_principal__data',
'norma_relacionada__data')
return (principais, relacionadas)
def get_anexos_norma_juridica(self):
@ -259,6 +269,17 @@ class NormaJuridica(models.Model):
def epigrafe(self):
return self.__str__()
@property
def epigrafe_simplificada(self):
numero_norma = self.numero
if numero_norma.isnumeric():
numero_norma = '{0:,}'.format(int(self.numero)).replace(',', '.')
return _('%(tipo)s%(numero)s, de %(data)s') % {
'tipo': self.tipo,
'numero': numero_norma,
'data': defaultfilters.date(self.data, "d \d\e F \d\e Y").lower()}
def delete(self, using=None, keep_parents=False):
texto_integral = self.texto_integral
result = super().delete(using=using, keep_parents=keep_parents)

24
sapl/norma/views.py

@ -28,7 +28,7 @@ from sapl.crud.base import (RP_DETAIL, RP_LIST, Crud, CrudAux,
MasterDetailCrud, make_pagination)
from sapl.materia.models import Orgao
from sapl.utils import show_results_filter_set, get_client_ip,\
sapl_as_sapn
sapn_is_enabled, MultiFormatOutputMixin
from .forms import (AnexoNormaJuridicaForm, NormaFilterSet, NormaJuridicaForm,
NormaPesquisaSimplesForm, NormaRelacionadaForm,
@ -104,7 +104,7 @@ class PesquisarAssuntoNormaView(FilterView):
if 'assunto' in self.request.META['QUERY_STRING'] or\
'page' in self.request.META['QUERY_STRING']:
resultados = self.object_list
resultados = self.object_list
else:
resultados = []
@ -147,11 +147,27 @@ class NormaRelacionadaCrud(MasterDetailCrud):
layout_key = 'NormaRelacionadaDetail'
class NormaPesquisaView(FilterView):
class NormaPesquisaView(MultiFormatOutputMixin, FilterView):
model = NormaJuridica
filterset_class = NormaFilterSet
paginate_by = 50
fields_base_report = [
'id', 'ano', 'numero', 'tipo__sigla', 'tipo__descricao', 'texto_integral', 'ementa'
]
fields_report = {
'csv': fields_base_report,
'xlsx': fields_base_report,
'json': fields_base_report,
}
def hook_texto_integral(self, obj):
url = self.request.build_absolute_uri('/')[:-1]
texto_integral = obj.texto_integral if not isinstance(
obj, dict) else obj["texto_integral"]
return f'{url}/media/{texto_integral}'
def get_queryset(self):
qs = super().get_queryset()
@ -187,8 +203,6 @@ class NormaPesquisaView(FilterView):
context['filter_url'] = ('&' + qr.urlencode()) if len(qr) > 0 else ''
context['show_results'] = show_results_filter_set(qr)
context['USE_SOLR'] = settings.USE_SOLR if hasattr(
settings, 'USE_SOLR') else False
return context

5
sapl/parlamentares/forms.py

@ -13,7 +13,6 @@ from django.forms import ModelForm
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
import django_filters
from floppyforms.widgets import ClearableFileInput
from image_cropping.widgets import CropWidget, ImageCropWidget
from sapl.base.models import Autor, TipoAutor
@ -26,10 +25,6 @@ from .models import (Coligacao, ComposicaoColigacao, Filiacao, Frente, Legislatu
Mandato, Parlamentar, Partido, Votante, Bloco, FrenteParlamentar, BlocoMembro)
class ImageThumbnailFileInput(ClearableFileInput):
template_name = 'floppyforms/image_thumbnail.html'
class CustomImageCropWidget(ImageCropWidget):
"""
Custom ImageCropWidget that doesn't show the initial value of the field.

31
sapl/parlamentares/migrations/0008_adiciona_cargos_mesa.py

@ -1,41 +1,20 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations
import json
import os
from django.core.management import call_command
from django.db import migrations
def gera_cargos_mesa(apps, schema_editor):
CargoMesa = apps.get_model("parlamentares", "CargoMesa")
db_alias = schema_editor.connection.alias
cargos_mesa = CargoMesa.objects.all().exists()
if cargos_mesa:
# Caso haja algum CargoMesa cadastrado na base de dados,
# a migração não deve ser carregada para evitar duplicações de dados.
print("Carga de {} não efetuada. Já Existem {} cadastrados...".format(
CargoMesa._meta.verbose_name, CargoMesa._meta.verbose_name_plural))
else:
fixture_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '../fixtures'))
# pega partidos listados em fixtures/pre_popula_partidos.json
fixture_filename = 'pre_popula_cargosmesa.json'
fixture_file = os.path.join(fixture_dir, fixture_filename)
call_command('loaddata', fixture_file)
class Migration(migrations.Migration):
dependencies = [
# A dependencia real desse script é o arquivo 0001_initial.py, mas
# isso gera um erro (Conflicting migrations detected; multiple leaf
# nodes in the migration graph). para não ocasionar problemas de migração,
# vamos manter a ordem padrão do django.
# nodes in the migration graph). para não ocasionar problemas de migração,
# vamos manter a ordem padrão do django.
('parlamentares', '0007_adiciona_partidos'),
]
operations = [
migrations.RunPython(gera_cargos_mesa),
# obsoleto, migrado para 0044 devido a dependência gerada por 0043
# migrations.RunPython(gera_cargos_mesa),
]

41
sapl/parlamentares/migrations/0044_adiciona_cargos_mesa.py

@ -0,0 +1,41 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import os
from django.core.management import call_command
from django.db import migrations
def gera_cargos_mesa(apps, schema_editor):
CargoMesa = apps.get_model("parlamentares", "CargoMesa")
db_alias = schema_editor.connection.alias
cargos_mesa = CargoMesa.objects.all().exists()
if cargos_mesa:
# Caso haja algum CargoMesa cadastrado na base de dados,
# a migração não deve ser carregada para evitar duplicações de dados.
print("Carga de {} não efetuada. Já Existem {} cadastrados...".format(
CargoMesa._meta.verbose_name, CargoMesa._meta.verbose_name_plural))
else:
fixture_dir = os.path.abspath(os.path.join(
os.path.dirname(__file__), '../fixtures'))
# pega partidos listados em fixtures/pre_popula_partidos.json
fixture_filename = 'pre_popula_cargosmesa.json'
fixture_file = os.path.join(fixture_dir, fixture_filename)
call_command('loaddata', fixture_file)
class Migration(migrations.Migration):
dependencies = [
# A dependencia real desse script é o arquivo 0001_initial.py, mas
# isso gera um erro (Conflicting migrations detected; multiple leaf
# nodes in the migration graph). para não ocasionar problemas de migração,
# vamos manter a ordem padrão do django.
('parlamentares', '0043_auto_20231007_2149'),
]
operations = [
migrations.RunPython(gera_cargos_mesa),
]

30
sapl/parlamentares/urls.py

@ -37,6 +37,8 @@ urlpatterns = [
ProposicaoParlamentarCrud.get_urls() +
RelatoriaParlamentarCrud.get_urls() +
VotanteView.get_urls()
)),
url(r'^parlamentar/pesquisar-parlamentar/',
@ -48,16 +50,21 @@ urlpatterns = [
url(r'^parlamentar/(?P<pk>\d+)/normas$',
ParlamentarNormasView.as_view(), name='parlamentar_normas'),
url(r'^parlamentar/(?P<pk>\d+)/frentes/$', get_parlamentar_frentes, name='parlamentar_frentes'),
url(r'^parlamentar/(?P<pk>\d+)/frentes/$',
get_parlamentar_frentes, name='parlamentar_frentes'),
url(r'^parlamentar/vincular-parlamentar/$',
VincularParlamentarView.as_view(), name='vincular_parlamentar'),
url(r'^parlamentar/coligacao-legislatura/', coligacao_legislatura, name="coligacao_legislatura"),
url(r'^sistema/coligacao/', include(ColigacaoCrud.get_urls() + ComposicaoColigacaoCrud.get_urls())),
url(r'^sistema/pesquisar-coligacao/', PesquisarColigacaoView.as_view(), name='pesquisar_coligacao'),
url(r'^parlamentar/coligacao-legislatura/',
coligacao_legislatura, name="coligacao_legislatura"),
url(r'^sistema/coligacao/', include(ColigacaoCrud.get_urls() +
ComposicaoColigacaoCrud.get_urls())),
url(r'^sistema/pesquisar-coligacao/',
PesquisarColigacaoView.as_view(), name='pesquisar_coligacao'),
url(r'^sistema/coligacao/', include(ColigacaoCrud.get_urls() + ComposicaoColigacaoCrud.get_urls())),
url(r'^sistema/coligacao/', include(ColigacaoCrud.get_urls() +
ComposicaoColigacaoCrud.get_urls())),
url(r'^sistema/bloco/', include(BlocoCrud.get_urls())),
url(r'^sistema/bloco-cargo/', include(BlocoCargoCrud.get_urls())),
@ -65,7 +72,8 @@ urlpatterns = [
url(r'^sistema/frente/', include(FrenteCrud.get_urls())),
url(r'^sistema/frente-cargo/', include(FrenteCargoCrud.get_urls())),
url(r'^sistema/frente-parlamentares/', include(FrenteParlamentarCrud.get_urls())),
url(r'^sistema/frente-parlamentares/',
include(FrenteParlamentarCrud.get_urls())),
url(r'^sistema/frente/atualiza-lista-parlamentares',
frente_atualiza_lista_parlamentares,
@ -86,8 +94,10 @@ urlpatterns = [
include(TipoMilitarCrud.get_urls())),
url(r'^sistema/parlamentar/partido/', include(PartidoCrud.get_urls())),
url(r'^sistema/parlamentar/pesquisar-partido/', PesquisarPartidoView.as_view(), name='pesquisar_partido'),
url(r'^sistema/parlamentar/partido/(?P<pk>\d+)/filiados$', parlamentares_filiados, name='parlamentares_filiados'),
url(r'^sistema/parlamentar/pesquisar-partido/',
PesquisarPartidoView.as_view(), name='pesquisar_partido'),
url(r'^sistema/parlamentar/partido/(?P<pk>\d+)/filiados$',
parlamentares_filiados, name='parlamentares_filiados'),
url(r'^sistema/mesa-diretora/sessao-legislativa/',
include(SessaoLegislativaCrud.get_urls())),
@ -109,7 +119,7 @@ urlpatterns = [
url(r'^mesa-diretora/remove-parlamentar-composicao/$',
remove_parlamentar_composicao, name='remove_parlamentar_composicao'),
url(r'^parlamentar/get-sessoes-legislatura/$',
url(r'^parlamentar/get-sessoes-legislatura/$',
get_sessoes_legislatura, name='get_sessoes_legislatura'),
]

12
sapl/parlamentares/views.py

@ -113,12 +113,13 @@ class VotanteView(MasterDetailCrud):
class FrenteList(MasterDetailCrud):
public = [RP_DETAIL, RP_LIST]
model = Frente
is_m2m = True
parent_field = 'parlamentares'
CreateView, UpdateView, DeleteView = None, None, None
class BaseMixin(Crud.PublicMixin, MasterDetailCrud.BaseMixin):
class BaseMixin(MasterDetailCrud.BaseMixin):
list_field_names = ['nome', 'data_criacao', 'data_extincao']
@classmethod
@ -127,6 +128,7 @@ class FrenteList(MasterDetailCrud):
class RelatoriaParlamentarCrud(CrudBaseForListAndDetailExternalAppView):
public = [RP_DETAIL, RP_LIST]
model = Relatoria
parent_field = 'parlamentar'
help_topic = 'tramitacao_relatoria'
@ -355,6 +357,7 @@ class PesquisarPartidoView(FilterView):
class ParticipacaoParlamentarCrud(CrudBaseForListAndDetailExternalAppView):
public = [RP_DETAIL, RP_LIST]
model = Participacao
parent_field = 'parlamentar'
namespace = AppConfig.name
@ -589,6 +592,7 @@ def get_parlamentar_frentes(request, pk):
context = {
'subnav_template_name': 'parlamentares/subnav.yaml',
'root_pk': pk,
'sexo_parlamentar': Parlamentar.objects.get(id=pk).sexo,
'nome_parlamentar': Parlamentar.objects.get(id=pk).nome_parlamentar,
'frentes': frentes,
'num_frentes': len(frentes)
@ -1388,7 +1392,8 @@ def altera_field_mesa_public_view(request):
partido_parlamentar_sessao_legislativa(sessao, parlamentar))
if parlamentar.fotografia:
try:
logger.warning(f"Iniciando cropping da imagem {parlamentar.fotografia}")
logger.warning(
f"Iniciando cropping da imagem {parlamentar.fotografia}")
thumbnail_url = get_backend().get_thumbnail_url(
parlamentar.fotografia,
{
@ -1398,7 +1403,8 @@ def altera_field_mesa_public_view(request):
'detail': True,
}
)
logger.warning(f"Cropping da imagem {parlamentar.fotografia} realizado com sucesso")
logger.warning(
f"Cropping da imagem {parlamentar.fotografia} realizado com sucesso")
lista_fotos.append(thumbnail_url)
except Exception as e:
logger.error(e)

23
sapl/protocoloadm/migrations/0045_auto_20240711_1405.py

@ -0,0 +1,23 @@
# Generated by Django 2.2.28 on 2024-07-11 17:05
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('protocoloadm', '0044_auto_20230529_1641'),
]
operations = [
migrations.AlterField(
model_name='documentoadministrativo',
name='ano',
field=models.PositiveSmallIntegerField(choices=[(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='protocolo',
name='ano',
field=models.PositiveSmallIntegerField(choices=[(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 do Protocolo'),
),
]

14
sapl/protocoloadm/views.py

@ -47,7 +47,7 @@ from sapl.relatorios.views import relatorio_doc_administrativos
from sapl.utils import (create_barcode, get_base_url, get_client_ip,
get_mime_type_from_file_extension, lista_anexados,
show_results_filter_set, mail_service_configured, from_date_to_datetime_utc,
google_recaptcha_configured, get_tempfile_dir)
google_recaptcha_configured, get_tempfile_dir, MultiFormatOutputMixin)
from .forms import (AcompanhamentoDocumentoForm, AnexadoEmLoteFilterSet, AnexadoForm,
AnularProtocoloAdmForm, compara_tramitacoes_doc,
@ -174,7 +174,7 @@ def create_pdf_docacessorios(docadministrativo):
logger.info("Gerando compilado PDF de documentos acessorios com {} documentos"
.format(docs_path))
merger = PdfFileMerger()
merger = PdfFileMerger(strict=False)
for f in docs_path:
merger.append(fileobj=f)
@ -1039,6 +1039,7 @@ class ProtocoloMateriaTemplateView(PermissionRequiredMixin, TemplateView):
class PesquisarDocumentoAdministrativoView(DocumentoAdministrativoMixin,
MultiFormatOutputMixin,
PermissionRequiredMixin,
FilterView):
model = DocumentoAdministrativo
@ -1046,6 +1047,15 @@ class PesquisarDocumentoAdministrativoView(DocumentoAdministrativoMixin,
paginate_by = 10
permission_required = ('protocoloadm.list_documentoadministrativo', )
fields_base_report = [
'id', 'ano', 'numero', 'tipo__sigla', 'tipo__descricao', 'assunto'
]
fields_report = {
'csv': fields_base_report,
'xlsx': fields_base_report,
'json': fields_base_report,
}
def get_filterset_kwargs(self, filterset_class):
super(PesquisarDocumentoAdministrativoView,
self).get_filterset_kwargs(filterset_class)

2
sapl/rules/group_geral.py

@ -26,7 +26,7 @@ rules_group_geral = {
[RP_ADD], __perms_publicas__),
(base.TipoAutor, __base__, __perms_publicas__),
(base.Autor, __base__, __perms_publicas__),
(base.OperadorAutor, __base__, __perms_publicas__),
(base.OperadorAutor, __base__, set()),
(base.AuditLog, __base__, set()),
(base.Metadata, __base__, set()),

157
sapl/sessao/views.py

@ -45,7 +45,8 @@ from sapl.sessao.forms import ExpedienteMateriaForm, OrdemDiaForm, OrdemExpedien
CorrespondenciaForm, CorrespondenciaEmLoteFilterSet
from sapl.sessao.models import Correspondencia
from sapl.settings import TIME_ZONE
from sapl.utils import show_results_filter_set, remover_acentos, get_client_ip
from sapl.utils import show_results_filter_set, remover_acentos, get_client_ip,\
MultiFormatOutputMixin, PautaMultiFormatOutputMixin
from .forms import (AdicionarVariasMateriasFilterSet, BancadaForm,
ExpedienteForm, JustificativaAusenciaForm, OcorrenciaSessaoForm, ListMateriaForm,
@ -147,7 +148,8 @@ def verifica_votacoes_abertas(request):
for sessao in votacoes_abertas:
ordens = sessao.ordemdia_set.filter(votacao_aberta=True)
expediente = sessao.expedientemateria_set.filter(votacao_aberta=True)
expediente = sessao.expedientemateria_set.filter(
votacao_aberta=True)
for o in ordens:
o.votacao_aberta = False
o.save()
@ -167,7 +169,7 @@ def verifica_sessao_iniciada(request, spk, is_leitura=False):
aux_text = 'leitura' if is_leitura else 'votação'
logger.info('user=' + username + '. Não é possível abrir matérias para {}. '
'Esta SessaoPlenaria (id={}) não foi iniciada ou está finalizada.'.format(
aux_text, spk))
aux_text, spk))
msg = _('Não é possível abrir matérias para {}. '
'Esta Sessão Plenária não foi iniciada ou está finalizada.'
' Vá em "Abertura"->"Dados Básicos" e altere os valores dos campos necessários.'.format(aux_text))
@ -481,11 +483,11 @@ def customize_link_materia(context, pk, has_permission, is_expediente):
'mid': obj.materia_id})
resultado = (
'<a href="%s?page=%s">%s<br/><br/>%s</a>' % (
url,
context.get('page', 1),
resultado_descricao,
resultado_observacao))
'<a href="%s?page=%s">%s<br/><br/>%s</a>' % (
url,
context.get('page', 1),
resultado_descricao,
resultado_observacao))
else:
if obj.tipo_votacao == NOMINAL:
@ -496,7 +498,7 @@ def customize_link_materia(context, pk, has_permission, is_expediente):
'pk': obj.sessao_plenaria_id,
'oid': obj.pk,
'mid': obj.materia_id}) + \
'?&materia=expediente'
'?&materia=expediente'
else:
url = reverse(
'sapl.sessao:votacao_nominal_transparencia',
@ -504,7 +506,7 @@ def customize_link_materia(context, pk, has_permission, is_expediente):
'pk': obj.sessao_plenaria_id,
'oid': obj.pk,
'mid': obj.materia_id}) + \
'?&materia=ordem'
'?&materia=ordem'
resultado = ('<a href="%s">%s<br/>%s</a>' %
(url,
@ -519,7 +521,7 @@ def customize_link_materia(context, pk, has_permission, is_expediente):
'pk': obj.sessao_plenaria_id,
'oid': obj.pk,
'mid': obj.materia_id}) + \
'?&materia=expediente'
'?&materia=expediente'
else:
url = reverse(
'sapl.sessao:votacao_simbolica_transparencia',
@ -527,7 +529,7 @@ def customize_link_materia(context, pk, has_permission, is_expediente):
'pk': obj.sessao_plenaria_id,
'oid': obj.pk,
'mid': obj.materia_id}) + \
'?&materia=ordem'
'?&materia=ordem'
resultado = ('<a href="%s">%s<br/>%s</a>' %
(url,
@ -791,7 +793,7 @@ class MateriaOrdemDiaCrud(MasterDetailCrud):
sessao_plenaria=self.kwargs['pk']).aggregate(
Max('numero_ordem'))['numero_ordem__max']
self.initial['numero_ordem'] = (
max_numero_ordem if max_numero_ordem else 0) + 1
max_numero_ordem if max_numero_ordem else 0) + 1
return self.initial
def get_success_url(self):
@ -934,7 +936,7 @@ class ExpedienteMateriaCrud(MasterDetailCrud):
sessao_plenaria=self.kwargs['pk']).aggregate(
Max('numero_ordem'))['numero_ordem__max']
initial['numero_ordem'] = (
max_numero_ordem if max_numero_ordem else 0) + 1
max_numero_ordem if max_numero_ordem else 0) + 1
return initial
def get_success_url(self):
@ -1414,7 +1416,7 @@ class PresencaView(FormMixin, PresencaMixin, DetailView):
# Id dos parlamentares presentes
marcados = request.POST.getlist('presenca_ativos') \
+ request.POST.getlist('presenca_inativos')
+ request.POST.getlist('presenca_inativos')
# Deletar os que foram desmarcados
deletar = set(presentes_banco) - set(marcados)
@ -1529,7 +1531,7 @@ class PresencaOrdemDiaView(FormMixin, PresencaMixin, DetailView):
# Id dos parlamentares presentes
marcados = request.POST.getlist('presenca_ativos') \
+ request.POST.getlist('presenca_inativos')
+ request.POST.getlist('presenca_inativos')
# Deletar os que foram desmarcados
deletar = set(presentes_banco) - set(marcados)
@ -1791,7 +1793,7 @@ def insere_parlamentar_composicao(request):
username = request.user.username
if request.user.has_perm(
'%s.add_%s' % (
AppConfig.label, IntegranteMesa._meta.model_name)):
AppConfig.label, IntegranteMesa._meta.model_name)):
composicao = IntegranteMesa()
@ -1825,7 +1827,8 @@ def insere_parlamentar_composicao(request):
if parlamentar_ja_inserido:
logger.debug(
"user=" + username + ". Parlamentar (id={}) já inserido na sessao_plenaria(id={}) e cargo(ìd={})."
"user=" + username +
". Parlamentar (id={}) já inserido na sessao_plenaria(id={}) e cargo(ìd={})."
.format(request.POST['parlamentar'], composicao.sessao_plenaria.id, composicao.cargo.id))
return JsonResponse({'msg': ('Parlamentar já inserido!', 0)})
@ -1854,7 +1857,7 @@ def remove_parlamentar_composicao(request):
username = request.user.username
if request.POST and request.user.has_perm(
'%s.delete_%s' % (
AppConfig.label, IntegranteMesa._meta.model_name)):
AppConfig.label, IntegranteMesa._meta.model_name)):
if 'composicao_mesa' in request.POST:
try:
@ -2213,7 +2216,7 @@ def get_materias_ordem_do_dia(sessao_plenaria):
if o.tipo_votacao == 2:
for voto in VotoParlamentar.objects.filter(ordem=o.id):
voto_nominal.append(
(voto.parlamentar.nome_completo, voto.voto))
(voto.parlamentar.nome_parlamentar, voto.voto))
voto = RegistroVotacao.objects.filter(ordem=o.id).last()
if voto:
@ -2534,7 +2537,8 @@ class ExpedienteView(FormMixin, DetailView):
msg = _('Registro salvo com sucesso')
messages.add_message(self.request, messages.SUCCESS, msg)
self.logger.info(
'user=' + username + '. ExpedienteSessao(sessao_plenaria_id={} e tipo_id={}) salvo com sucesso.'
'user=' + username +
'. ExpedienteSessao(sessao_plenaria_id={} e tipo_id={}) salvo com sucesso.'
.format(self.object.id, tipo))
return self.form_valid(form)
@ -2907,7 +2911,7 @@ class VotacaoView(SessaoPermissionMixin):
username = request.user.username
self.logger.error('user=' + username + '. Problemas ao salvar RegistroVotacao da materia de id={} '
'e da ordem de id={}. '.format(materia_id, ordem_id) + str(
e))
e))
return self.form_invalid(form)
else:
ordem = OrdemDia.objects.get(id=ordem_id)
@ -3805,10 +3809,27 @@ class PautaSessaoView(TemplateView):
reverse('sapl.sessao:pauta_sessao_detail', kwargs={'pk': sessao.pk}))
class PautaSessaoDetailView(DetailView):
class PautaSessaoDetailView(PautaMultiFormatOutputMixin, DetailView):
template_name = "sessao/pauta_sessao_detail.html"
model = SessaoPlenaria
queryset_values_for_formats = False
fields_base_report = [
[('id', 'ID'), ('titulo', 'Matéria'), ('autor', 'Autor'), ('ementa', 'Ementa'), ('situacao', 'Situação')],
[('id', 'ID'), ('titulo', 'Matéria'), ('autor', 'Autor'), ('ementa', 'Ementa'), ('situacao', 'Situação')]
]
fields_report = {
'csv': fields_base_report,
'xlsx': fields_base_report,
'json': fields_base_report,
}
item_context = [
('materia_expediente', 'Matérias do Expediente'),
('materias_ordem', 'Matérias da Ordem do Dia')
]
def get(self, request, *args, **kwargs):
from sapl.relatorios.views import relatorio_pauta_sessao_weasy # Evitar import ciclico
@ -3866,7 +3887,8 @@ class PautaSessaoDetailView(DetailView):
'resultado_observacao': resultado_observacao,
'situacao': ultima_tramitacao.status if ultima_tramitacao else _("Não informada"),
'processo': f'{str(numeracao.numero_materia)}/{str(numeracao.ano_materia)}' if numeracao else '-',
'autor': [str(x.autor) for x in m.materia.autoria_set.select_related('autor').all()]
'autor': [str(x.autor) for x in m.materia.autoria_set.select_related('autor').all()],
'turno': get_turno(ultima_tramitacao.turno) if ultima_tramitacao else ''
})
context.update({'materia_expediente': materias_expediente})
@ -3949,7 +3971,8 @@ class PautaSessaoDetailView(DetailView):
'resultado_observacao': resultado_observacao,
'situacao': ultima_tramitacao.status if ultima_tramitacao else _("Não informada"),
'processo': f'{str(numeracao.numero_materia)}/{str(numeracao.ano_materia)}' if numeracao else '-',
'autor': [str(x.autor) for x in Autoria.objects.select_related("autor").filter(materia_id=o.materia_id)]
'autor': [str(x.autor) for x in Autoria.objects.select_related("autor").filter(materia_id=o.materia_id)],
'turno': get_turno(ultima_tramitacao.turno) if ultima_tramitacao else ''
})
context.update({
@ -3964,13 +3987,26 @@ class PautaSessaoDetailView(DetailView):
return self.render_to_response(context)
class PesquisarSessaoPlenariaView(FilterView):
class PesquisarSessaoPlenariaView(MultiFormatOutputMixin, FilterView):
model = SessaoPlenaria
filterset_class = SessaoPlenariaFilterSet
paginate_by = 10
logger = logging.getLogger(__name__)
viewname = 'sapl.sessao:pesquisar_sessao'
queryset_values_for_formats = False
fields_base_report = [
'id', 'data_inicio', 'hora_inicio', 'data_fim', 'hora_fim', '',
]
fields_report = {
'csv': fields_base_report,
'xlsx': fields_base_report,
'json': fields_base_report,
}
def get_filterset_kwargs(self, filterset_class):
super().get_filterset_kwargs(filterset_class)
@ -3987,50 +4023,60 @@ class PesquisarSessaoPlenariaView(FilterView):
})
return kwargs
def hook_header_(self):
return force_text(_('Título'))
def hook_(self, obj):
return str(obj)
def hook_data_inicio(self, obj):
return str(obj.data_inicio or '')
def hook_data_fim(self, obj):
return str(obj.data_fim or '')
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['title'] = _('Pesquisar Sessão Plenária')
paginator = context['paginator']
page_obj = context['page_obj']
context['page_range'] = make_pagination(
page_obj.number, paginator.num_pages)
context['USE_SOLR'] = settings.USE_SOLR if hasattr(
settings, 'USE_SOLR') else False
return context
def get(self, request, *args, **kwargs):
super().get(request)
context['show_results'] = show_results_filter_set(
self.request.GET.copy())
# Se a pesquisa estiver quebrando com a paginação
# Olhe esta função abaixo
# Provavelmente você criou um novo campo no Form/FilterSet
# Então a ordem da URL está diferente
data = self.filterset.data
if data and data.get('data_inicio__year') is not None:
url = "&" + str(self.request.META['QUERY_STRING'])
if url.startswith("&page"):
ponto_comeco = url.find('data_inicio__year=') - 1
url = url[ponto_comeco:]
else:
url = ''
context['filter_url'] = url
context = self.get_context_data(filter=self.filterset,
object_list=self.object_list,
filter_url=url,
numero_res=len(self.object_list)
)
context['numero_res'] = len(self.object_list)
context['show_results'] = show_results_filter_set(
self.request.GET.copy())
return context
def get(self, request, *args, **kwargs):
r = super().get(request)
data = self.filterset.data
if not data:
return HttpResponseRedirect(
reverse(
self.viewname
) + f'?data_inicio__year={timezone.now().year}'
)
username = request.user.username
self.logger.debug('user=' + username + '. Pesquisa de SessaoPlenaria.')
return self.render_to_response(context)
return r
class PesquisarPautaSessaoView(PesquisarSessaoPlenariaView):
@ -4039,6 +4085,8 @@ class PesquisarPautaSessaoView(PesquisarSessaoPlenariaView):
logger = logging.getLogger(__name__)
viewname = 'sapl.sessao:pesquisar_pauta'
def get_filterset_kwargs(self, filterset_class):
kwargs = super().get_filterset_kwargs(filterset_class)
qs = kwargs.get('queryset')
@ -4319,7 +4367,7 @@ def mudar_ordem_materia_sessao(request):
class JustificativaAusenciaCrud(MasterDetailCrud):
model = JustificativaAusencia
public = [RP_LIST, RP_DETAIL, ]
public = [RP_LIST]
parent_field = 'sessao_plenaria'
class BaseMixin(MasterDetailCrud.BaseMixin):
@ -4425,8 +4473,14 @@ class LeituraEmBloco(PermissionRequiredForAppCrudMixin, ListView):
selectedlist = request.POST.getlist('marcadas_4')
if request.POST['origem'] == 'ordem':
models = OrdemDia.objects.filter(id__in=selectedlist)
presenca_model = PresencaOrdemDia
elif request.POST['origem'] == 'expediente':
models = ExpedienteMateria.objects.filter(id__in=selectedlist)
presenca_model = SessaoPlenariaPresenca
if (not verifica_presenca(request, presenca_model, self.kwargs['pk'], True) or
not verifica_sessao_iniciada(request, self.kwargs['pk'], True)):
return self.get(request, self.kwargs)
if not models:
messages.add_message(self.request, messages.ERROR,
@ -4451,8 +4505,11 @@ class LeituraEmBloco(PermissionRequiredForAppCrudMixin, ListView):
leituras.append(obj)
RegistroLeitura.objects.bulk_create(leituras)
models.update(resultado='Matéria Lida')
else:
messages.add_message(self.request, messages.ERROR, _('Nenhuma matéria selecionada para leitura em Bloco'))
messages.add_message(self.request, messages.ERROR, _(
'Nenhuma matéria selecionada para leitura em Bloco'))
return self.get(request, self.kwargs)
return HttpResponseRedirect(self.get_success_url())
@ -5258,7 +5315,7 @@ class CorrespondenciaCrud(MasterDetailCrud):
sessao_plenaria=self.kwargs['pk']).aggregate(
Max('numero_ordem'))['numero_ordem__max']
initial['numero_ordem'] = (
max_numero_ordem if max_numero_ordem else 0) + 1
max_numero_ordem if max_numero_ordem else 0) + 1
return initial

34
sapl/settings.py

@ -30,7 +30,7 @@ BASE_DIR = Path(__file__).ancestor(1)
PROJECT_DIR = Path(__file__).ancestor(2)
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = config('SECRET_KEY', default='')
SECRET_KEY = config('SECRET_KEY', default='32jk1h412l3kjh421lkj4hlkj234')
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = config('DEBUG', default=False, cast=bool)
@ -41,7 +41,7 @@ ALLOWED_HOSTS = ['*']
LOGIN_REDIRECT_URL = '/'
LOGIN_URL = '/login/?next='
SAPL_VERSION = '3.1.163-RC17'
SAPL_VERSION = '3.1.164-RC0'
if DEBUG:
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
@ -75,10 +75,13 @@ INSTALLED_APPS = (
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.forms',
'django_extensions',
'crispy_forms',
'floppyforms',
'waffle',
'drf_spectacular',
'rest_framework',
@ -137,6 +140,8 @@ MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'whitenoise.middleware.WhiteNoiseMiddleware',
'django_prometheus.middleware.PrometheusAfterMiddleware',
'waffle.middleware.WaffleMiddleware',
'sapl.middleware.CheckWeakPasswordMiddleware',
]
if DEBUG:
INSTALLED_APPS += ('debug_toolbar',)
@ -189,11 +194,14 @@ CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
'LOCATION': '/var/tmp/django_cache',
'OPTIONS': {"MAX_ENTRIES": 1000},
}
}
ROOT_URLCONF = 'sapl.urls'
FORM_RENDERER = 'django.forms.renderers.TemplatesSetting'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
@ -210,8 +218,7 @@ TEMPLATES = [
'sapl.context_processors.parliament_info',
'sapl.context_processors.mail_service_configured',
'sapl.context_processors.google_recaptcha_configured',
'sapl.context_processors.sapl_as_sapn',
'sapl.context_processors.enable_sapn',
],
'debug': DEBUG
},
@ -255,6 +262,16 @@ DEFAULT_FROM_EMAIL = config('DEFAULT_FROM_EMAIL', cast=str, default='')
SERVER_EMAIL = config('SERVER_EMAIL', cast=str, default='')
EMAIL_RUNNING = None
# Feature Flag
WAFFLE_FLAG_DEFAULT = False
WAFFLE_SWITCH_DEFAULT = False
WAFFLE_CREATE_MISSING_FLAGS = True
WAFFLE_LOG_MISSING_FLAGS = True
WAFFLE_CREATE_MISSING_SWITCHES = True
WAFFLE_LOG_MISSING_SWITCHES = True
WAFFLE_ENABLE_ADMIN_PAGES = True
MAX_DOC_UPLOAD_SIZE = 150 * 1024 * 1024 # 150MB
MAX_IMAGE_UPLOAD_SIZE = 2 * 1024 * 1024 # 2MB
DATA_UPLOAD_MAX_MEMORY_SIZE = 10 * 1024 * 1024 # 10MB
@ -321,9 +338,6 @@ DAB_FIELD_RENDERER = \
CRISPY_TEMPLATE_PACK = 'bootstrap4'
CRISPY_ALLOWED_TEMPLATE_PACKS = 'bootstrap4'
CRISPY_FAIL_SILENTLY = not DEBUG
FLOPPY_FORMS_USE_GIS = False
FORM_RENDERER = 'django.forms.renderers.DjangoTemplates'
# suprime texto de ajuda default do django-filter
FILTERS_HELP_TEXT_FILTER = False
@ -375,12 +389,12 @@ LOGGING = {
},
'loggers': {
'sapl': {
'handlers': ['applogfile'] + ['console_verbose'] if LOGGING_CONSOLE_VERBOSE else [],
'handlers': ['applogfile'] + (['console_verbose'] if LOGGING_CONSOLE_VERBOSE else []),
'level': 'DEBUG' if LOGGING_CONSOLE_VERBOSE else 'INFO',
'propagate': True,
},
'django': {
'handlers': ['applogfile'] + ['console_verbose'] if LOGGING_CONSOLE_VERBOSE else [],
'handlers': ['applogfile'] + (['console_verbose'] if LOGGING_CONSOLE_VERBOSE else []),
'level': 'ERROR',
'propagate': True,
},

13
sapl/static/sapl/frontend/css/chunk-vendors.045ec640.css

File diff suppressed because one or more lines are too long

BIN
sapl/static/sapl/frontend/css/chunk-vendors.045ec640.css.gz

Binary file not shown.

13
sapl/static/sapl/frontend/css/chunk-vendors.299c587b.css

File diff suppressed because one or more lines are too long

BIN
sapl/static/sapl/frontend/css/chunk-vendors.299c587b.css.gz

Binary file not shown.

BIN
sapl/static/sapl/frontend/css/compilacao.0baf3580.css.gz

Binary file not shown.

BIN
sapl/static/sapl/frontend/css/global.45591136.css.gz

Binary file not shown.

BIN
sapl/static/sapl/frontend/css/painel.e2b9504e.css.gz

Binary file not shown.

BIN
sapl/static/sapl/frontend/fonts/fa-brands-400.5d18d427.ttf

Binary file not shown.

BIN
sapl/static/sapl/frontend/fonts/fa-brands-400.5d18d427.ttf.gz

Binary file not shown.

BIN
sapl/static/sapl/frontend/fonts/fa-brands-400.87587a68.woff2

Binary file not shown.

BIN
sapl/static/sapl/frontend/fonts/fa-brands-400.9a905705.ttf

Binary file not shown.

BIN
sapl/static/sapl/frontend/fonts/fa-brands-400.9a905705.ttf.gz

Binary file not shown.

BIN
sapl/static/sapl/frontend/fonts/fa-brands-400.b6033b54.woff2

Binary file not shown.

BIN
sapl/static/sapl/frontend/fonts/fa-regular-400.3580b4a9.woff2

Binary file not shown.

BIN
sapl/static/sapl/frontend/fonts/fa-regular-400.3ccdbd3d.woff2

Binary file not shown.

BIN
sapl/static/sapl/frontend/fonts/fa-regular-400.67a0fb74.ttf

Binary file not shown.

BIN
sapl/static/sapl/frontend/fonts/fa-regular-400.67a0fb74.ttf.gz

Binary file not shown.

BIN
sapl/static/sapl/frontend/fonts/fa-regular-400.81482cd4.ttf

Binary file not shown.

BIN
sapl/static/sapl/frontend/fonts/fa-regular-400.81482cd4.ttf.gz

Binary file not shown.

BIN
sapl/static/sapl/frontend/fonts/fa-solid-900.0b0cc8a6.woff2

Binary file not shown.

BIN
sapl/static/sapl/frontend/fonts/fa-solid-900.69d3141a.ttf

Binary file not shown.

BIN
sapl/static/sapl/frontend/fonts/fa-solid-900.69d3141a.ttf.gz

Binary file not shown.

BIN
sapl/static/sapl/frontend/fonts/fa-solid-900.6a8db53d.ttf

Binary file not shown.

BIN
sapl/static/sapl/frontend/fonts/fa-solid-900.6a8db53d.ttf.gz

Binary file not shown.

BIN
sapl/static/sapl/frontend/fonts/fa-solid-900.fd0b155c.woff2

Binary file not shown.

BIN
sapl/static/sapl/frontend/fonts/fa-v4compatibility.2c070fd2.ttf

Binary file not shown.

BIN
sapl/static/sapl/frontend/fonts/fa-v4compatibility.2c070fd2.ttf.gz

Binary file not shown.

BIN
sapl/static/sapl/frontend/fonts/fa-v4compatibility.e4efb16c.ttf

Binary file not shown.

BIN
sapl/static/sapl/frontend/fonts/fa-v4compatibility.e4efb16c.ttf.gz

Binary file not shown.

BIN
sapl/static/sapl/frontend/img/down_arrow_select.jpg.gz

Binary file not shown.

19
sapl/static/sapl/frontend/js/chunk-vendors.5e41c7a6.js

File diff suppressed because one or more lines are too long

BIN
sapl/static/sapl/frontend/js/chunk-vendors.5e41c7a6.js.LICENSE.txt.gz

Binary file not shown.

BIN
sapl/static/sapl/frontend/js/chunk-vendors.5e41c7a6.js.gz

Binary file not shown.

19
sapl/static/sapl/frontend/js/chunk-vendors.e8ab4373.js

File diff suppressed because one or more lines are too long

244
sapl/static/sapl/frontend/js/chunk-vendors.5e41c7a6.js.LICENSE.txt → sapl/static/sapl/frontend/js/chunk-vendors.e8ab4373.js.LICENSE.txt

@ -5,8 +5,8 @@
*/
/*!
* Vue.js v2.7.14
* (c) 2014-2022 Evan You
* Vue.js v2.7.16
* (c) 2014-2023 Evan You
* Released under the MIT License.
*/
@ -22,273 +22,273 @@
*/
/*!
* jQuery UI :data 1.13.2
* http://jqueryui.com
* jQuery UI :data 1.13.3
* https://jqueryui.com
*
* Copyright jQuery Foundation and other contributors
* Copyright OpenJS Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
* https://jquery.org/license
*/
/*!
* jQuery UI Autocomplete 1.13.2
* http://jqueryui.com
* jQuery UI Autocomplete 1.13.3
* https://jqueryui.com
*
* Copyright jQuery Foundation and other contributors
* Copyright OpenJS Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
* https://jquery.org/license
*/
/*!
* jQuery UI Button 1.13.2
* http://jqueryui.com
* jQuery UI Button 1.13.3
* https://jqueryui.com
*
* Copyright jQuery Foundation and other contributors
* Copyright OpenJS Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
* https://jquery.org/license
*/
/*!
* jQuery UI Checkboxradio 1.13.2
* http://jqueryui.com
* jQuery UI Checkboxradio 1.13.3
* https://jqueryui.com
*
* Copyright jQuery Foundation and other contributors
* Copyright OpenJS Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
* https://jquery.org/license
*/
/*!
* jQuery UI Controlgroup 1.13.2
* http://jqueryui.com
* jQuery UI Controlgroup 1.13.3
* https://jqueryui.com
*
* Copyright jQuery Foundation and other contributors
* Copyright OpenJS Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
* https://jquery.org/license
*/
/*!
* jQuery UI Datepicker 1.13.2
* http://jqueryui.com
* jQuery UI Datepicker 1.13.3
* https://jqueryui.com
*
* Copyright jQuery Foundation and other contributors
* Copyright OpenJS Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
* https://jquery.org/license
*/
/*!
* jQuery UI Dialog 1.13.2
* http://jqueryui.com
* jQuery UI Dialog 1.13.3
* https://jqueryui.com
*
* Copyright jQuery Foundation and other contributors
* Copyright OpenJS Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
* https://jquery.org/license
*/
/*!
* jQuery UI Disable Selection 1.13.2
* http://jqueryui.com
* jQuery UI Disable Selection 1.13.3
* https://jqueryui.com
*
* Copyright jQuery Foundation and other contributors
* Copyright OpenJS Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
* https://jquery.org/license
*/
/*!
* jQuery UI Draggable 1.13.2
* http://jqueryui.com
* jQuery UI Draggable 1.13.3
* https://jqueryui.com
*
* Copyright jQuery Foundation and other contributors
* Copyright OpenJS Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
* https://jquery.org/license
*/
/*!
* jQuery UI Droppable 1.13.2
* http://jqueryui.com
* jQuery UI Droppable 1.13.3
* https://jqueryui.com
*
* Copyright jQuery Foundation and other contributors
* Copyright OpenJS Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
* https://jquery.org/license
*/
/*!
* jQuery UI Effects 1.13.2
* http://jqueryui.com
* jQuery UI Effects 1.13.3
* https://jqueryui.com
*
* Copyright jQuery Foundation and other contributors
* Copyright OpenJS Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
* https://jquery.org/license
*/
/*!
* jQuery UI Focusable 1.13.2
* http://jqueryui.com
* jQuery UI Focusable 1.13.3
* https://jqueryui.com
*
* Copyright jQuery Foundation and other contributors
* Copyright OpenJS Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
* https://jquery.org/license
*/
/*!
* jQuery UI Form Reset Mixin 1.13.2
* http://jqueryui.com
* jQuery UI Form Reset Mixin 1.13.3
* https://jqueryui.com
*
* Copyright jQuery Foundation and other contributors
* Copyright OpenJS Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
* https://jquery.org/license
*/
/*!
* jQuery UI Keycode 1.13.2
* http://jqueryui.com
* jQuery UI Keycode 1.13.3
* https://jqueryui.com
*
* Copyright jQuery Foundation and other contributors
* Copyright OpenJS Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
* https://jquery.org/license
*/
/*!
* jQuery UI Labels 1.13.2
* http://jqueryui.com
* jQuery UI Labels 1.13.3
* https://jqueryui.com
*
* Copyright jQuery Foundation and other contributors
* Copyright OpenJS Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
* https://jquery.org/license
*/
/*!
* jQuery UI Menu 1.13.2
* http://jqueryui.com
* jQuery UI Menu 1.13.3
* https://jqueryui.com
*
* Copyright jQuery Foundation and other contributors
* Copyright OpenJS Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
* https://jquery.org/license
*/
/*!
* jQuery UI Mouse 1.13.2
* http://jqueryui.com
* jQuery UI Mouse 1.13.3
* https://jqueryui.com
*
* Copyright jQuery Foundation and other contributors
* Copyright OpenJS Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
* https://jquery.org/license
*/
/*!
* jQuery UI Position 1.13.2
* http://jqueryui.com
* jQuery UI Position 1.13.3
* https://jqueryui.com
*
* Copyright jQuery Foundation and other contributors
* Copyright OpenJS Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
* https://jquery.org/license
*
* http://api.jqueryui.com/position/
* https://api.jqueryui.com/position/
*/
/*!
* jQuery UI Progressbar 1.13.2
* http://jqueryui.com
* jQuery UI Progressbar 1.13.3
* https://jqueryui.com
*
* Copyright jQuery Foundation and other contributors
* Copyright OpenJS Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
* https://jquery.org/license
*/
/*!
* jQuery UI Resizable 1.13.2
* http://jqueryui.com
* jQuery UI Resizable 1.13.3
* https://jqueryui.com
*
* Copyright jQuery Foundation and other contributors
* Copyright OpenJS Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
* https://jquery.org/license
*/
/*!
* jQuery UI Scroll Parent 1.13.2
* http://jqueryui.com
* jQuery UI Scroll Parent 1.13.3
* https://jqueryui.com
*
* Copyright jQuery Foundation and other contributors
* Copyright OpenJS Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
* https://jquery.org/license
*/
/*!
* jQuery UI Sortable 1.13.2
* http://jqueryui.com
* jQuery UI Sortable 1.13.3
* https://jqueryui.com
*
* Copyright jQuery Foundation and other contributors
* Copyright OpenJS Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
* https://jquery.org/license
*/
/*!
* jQuery UI Spinner 1.13.2
* http://jqueryui.com
* jQuery UI Spinner 1.13.3
* https://jqueryui.com
*
* Copyright jQuery Foundation and other contributors
* Copyright OpenJS Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
* https://jquery.org/license
*/
/*!
* jQuery UI Support for jQuery core 1.8.x and newer 1.13.2
* http://jqueryui.com
* jQuery UI Support for jQuery core 1.8.x and newer 1.13.3
* https://jqueryui.com
*
* Copyright jQuery Foundation and other contributors
* Copyright OpenJS Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
* https://jquery.org/license
*
*/
/*!
* jQuery UI Tabbable 1.13.2
* http://jqueryui.com
* jQuery UI Tabbable 1.13.3
* https://jqueryui.com
*
* Copyright jQuery Foundation and other contributors
* Copyright OpenJS Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
* https://jquery.org/license
*/
/*!
* jQuery UI Tabs 1.13.2
* http://jqueryui.com
* jQuery UI Tabs 1.13.3
* https://jqueryui.com
*
* Copyright jQuery Foundation and other contributors
* Copyright OpenJS Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
* https://jquery.org/license
*/
/*!
* jQuery UI Tooltip 1.13.2
* http://jqueryui.com
* jQuery UI Tooltip 1.13.3
* https://jqueryui.com
*
* Copyright jQuery Foundation and other contributors
* Copyright OpenJS Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
* https://jquery.org/license
*/
/*!
* jQuery UI Unique ID 1.13.2
* http://jqueryui.com
* jQuery UI Unique ID 1.13.3
* https://jqueryui.com
*
* Copyright jQuery Foundation and other contributors
* Copyright OpenJS Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
* https://jquery.org/license
*/
/*!
* jQuery UI Widget 1.13.2
* http://jqueryui.com
* jQuery UI Widget 1.13.3
* https://jqueryui.com
*
* Copyright jQuery Foundation and other contributors
* Copyright OpenJS Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
* https://jquery.org/license
*/
/*! jQuery UI - v1.13.2 - 2022-07-14
* http://jqueryui.com
/*! jQuery UI - v1.13.3 - 2024-04-26
* https://jqueryui.com
* Includes: widget.js, position.js, data.js, disable-selection.js, effect.js, effects/effect-blind.js, effects/effect-bounce.js, effects/effect-clip.js, effects/effect-drop.js, effects/effect-explode.js, effects/effect-fade.js, effects/effect-fold.js, effects/effect-highlight.js, effects/effect-puff.js, effects/effect-pulsate.js, effects/effect-scale.js, effects/effect-shake.js, effects/effect-size.js, effects/effect-slide.js, effects/effect-transfer.js, focusable.js, form-reset-mixin.js, jquery-patch.js, keycode.js, labels.js, scroll-parent.js, tabbable.js, unique-id.js, widgets/accordion.js, widgets/autocomplete.js, widgets/button.js, widgets/checkboxradio.js, widgets/controlgroup.js, widgets/datepicker.js, widgets/dialog.js, widgets/draggable.js, widgets/droppable.js, widgets/menu.js, widgets/mouse.js, widgets/progressbar.js, widgets/resizable.js, widgets/selectable.js, widgets/selectmenu.js, widgets/slider.js, widgets/sortable.js, widgets/spinner.js, widgets/tabs.js, widgets/tooltip.js
* Copyright jQuery Foundation and other contributors; Licensed MIT */
* Copyright OpenJS Foundation and other contributors; Licensed MIT */
/**
* @license

BIN
sapl/static/sapl/frontend/js/chunk-vendors.e8ab4373.js.LICENSE.txt.gz

Binary file not shown.

BIN
sapl/static/sapl/frontend/js/chunk-vendors.e8ab4373.js.gz

Binary file not shown.

1
sapl/static/sapl/frontend/js/compilacao.57574b17.js

File diff suppressed because one or more lines are too long

BIN
sapl/static/sapl/frontend/js/compilacao.57574b17.js.gz

Binary file not shown.

1
sapl/static/sapl/frontend/js/compilacao.d3adb1c1.js

File diff suppressed because one or more lines are too long

BIN
sapl/static/sapl/frontend/js/compilacao.d3adb1c1.js.gz

Binary file not shown.

2
sapl/static/sapl/frontend/js/global.2f5aff92.js

File diff suppressed because one or more lines are too long

BIN
sapl/static/sapl/frontend/js/global.2f5aff92.js.gz

Binary file not shown.

2
sapl/static/sapl/frontend/js/global.8ef6e932.js

File diff suppressed because one or more lines are too long

0
sapl/static/sapl/frontend/js/global.2f5aff92.js.LICENSE.txt → sapl/static/sapl/frontend/js/global.8ef6e932.js.LICENSE.txt

BIN
sapl/static/sapl/frontend/js/global.8ef6e932.js.gz

Binary file not shown.

2
sapl/static/sapl/frontend/js/painel.aa1df64a.js → sapl/static/sapl/frontend/js/painel.73a4fb17.js

@ -1 +1 @@
(()=>{"use strict";var u,f,e,o={4709:(e,r,o)=>{o(6992),o(8674),o(9601),o(7727)}},t={};function s(e){var r=t[e];return void 0!==r||(r=t[e]={id:e,loaded:!1,exports:{}},o[e].call(r.exports,r,r.exports,s),r.loaded=!0),r.exports}s.m=o,u=[],s.O=(e,r,o,t)=>{if(!r){for(var n=1/0,l=0;l<u.length;l++){for(var i,[r,o,t]=u[l],a=!0,d=0;d<r.length;d++)(!1&t||t<=n)&&Object.keys(s.O).every(e=>s.O[e](r[d]))?r.splice(d--,1):(a=!1,t<n&&(n=t));a&&(u.splice(l--,1),void 0!==(i=o()))&&(e=i)}return e}t=t||0;for(var l=u.length;0<l&&u[l-1][2]>t;l--)u[l]=u[l-1];u[l]=[r,o,t]},s.n=e=>{var r=e&&e.__esModule?()=>e.default:()=>e;return s.d(r,{a:r}),r},s.d=(e,r)=>{for(var o in r)s.o(r,o)&&!s.o(e,o)&&Object.defineProperty(e,o,{enumerable:!0,get:r[o]})},s.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),s.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),s.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},s.nmd=e=>(e.paths=[],e.children||(e.children=[]),e),s.j=567,f={567:0},s.O.j=e=>0===f[e],r=(e,r)=>{var o,t,n,[l,i,a]=r,d=0;if(l.some(e=>0!==f[e])){for(o in i)s.o(i,o)&&(s.m[o]=i[o]);a&&(n=a(s))}for(e&&e(r);d<l.length;d++)t=l[d],s.o(f,t)&&f[t]&&f[t][0](),f[t]=0;return s.O(n)},(e=self.webpackChunksapl_frontend=self.webpackChunksapl_frontend||[]).forEach(r.bind(null,0)),e.push=r.bind(null,e.push.bind(e));var r=s.O(void 0,[998],()=>s(4709));s.O(r)})();
(()=>{"use strict";var u,f,e,o={2918:(e,r,o)=>{o(3792),o(3362),o(9085),o(9391)}},t={};function s(e){var r=t[e];return void 0!==r||(r=t[e]={id:e,loaded:!1,exports:{}},o[e].call(r.exports,r,r.exports,s),r.loaded=!0),r.exports}s.m=o,u=[],s.O=(e,r,o,t)=>{if(!r){for(var n=1/0,l=0;l<u.length;l++){for(var i,[r,o,t]=u[l],a=!0,d=0;d<r.length;d++)(!1&t||t<=n)&&Object.keys(s.O).every(e=>s.O[e](r[d]))?r.splice(d--,1):(a=!1,t<n&&(n=t));a&&(u.splice(l--,1),void 0!==(i=o()))&&(e=i)}return e}t=t||0;for(var l=u.length;0<l&&u[l-1][2]>t;l--)u[l]=u[l-1];u[l]=[r,o,t]},s.n=e=>{var r=e&&e.__esModule?()=>e.default:()=>e;return s.d(r,{a:r}),r},s.d=(e,r)=>{for(var o in r)s.o(r,o)&&!s.o(e,o)&&Object.defineProperty(e,o,{enumerable:!0,get:r[o]})},s.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),s.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),s.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},s.nmd=e=>(e.paths=[],e.children||(e.children=[]),e),s.j=772,f={772:0},s.O.j=e=>0===f[e],r=(e,r)=>{var o,t,n,[l,i,a]=r,d=0;if(l.some(e=>0!==f[e])){for(o in i)s.o(i,o)&&(s.m[o]=i[o]);a&&(n=a(s))}for(e&&e(r);d<l.length;d++)t=l[d],s.o(f,t)&&f[t]&&f[t][0](),f[t]=0;return s.O(n)},(e=self.webpackChunksapl_frontend=self.webpackChunksapl_frontend||[]).forEach(r.bind(null,0)),e.push=r.bind(null,e.push.bind(e));var r=s.O(void 0,[504],()=>s(2918));s.O(r)})();

BIN
sapl/static/sapl/frontend/js/painel.73a4fb17.js.gz

Binary file not shown.

BIN
sapl/static/sapl/frontend/js/painel.aa1df64a.js.gz

Binary file not shown.

1
sapl/static/sapl/frontend/js/parlamentar.f0710dca.js

@ -0,0 +1 @@
(()=>{"use strict";var u,p,e,t={8020:(e,a,t)=>{t(3792),t(3362),t(9085),t(9391),t(6918),t(2008),t(2712),t(3288),t(6099),t(8781);var r=t(2893),i=t(8246),s=t(7906),n=t(2543);s.A.defaults.xsrfCookieName="csrftoken",s.A.defaults.xsrfHeaderName="X-CSRFToken",r.Ay.use(i.m0),new r.Ay({delimiters:["[[","]]"],el:"#app2",data:function(){return{nome_pesquisa:"",is_pesquisa:!1,legislatura_selecionada:"",legislaturas:[],parlamentares:[],visible_parlamentares:[],size_parlamentares:0,filter_ativo:!0,filter_titular:""}},watch:{nome_pesquisa:function(e){this.debouncepesquisaParlamentar()}},created:function(){this.debouncepesquisaParlamentar=n.debounce(this.pesquisaParlamentar,500)},methods:{getParlamentares:function(e){var a=this;!this.legislatura_selecionada&&"0"!==this.legislatura_selecionada.toString()||s.A.get("/api/parlamentares/legislatura/"+this.legislatura_selecionada+"/parlamentares/?get_all=true").then(function(e){a.parlamentares=e.data,a.visible_parlamentares=a.parlamentares,a.size_parlamentares=a.visible_parlamentares.length,a.checkTitularAtivo()}).catch(function(e){console.error("Ocorreu um erro ao obter os dados de parlamentares:"+e)})},pesquisaParlamentar:function(e){var a=this;s.A.get("/api/parlamentares/parlamentar/search_parlamentares/",{params:{nome_parlamentar:this.nome_pesquisa}}).then(function(e){a.parlamentares=e.data,a.visible_parlamentares=a.parlamentares,a.size_parlamentares=a.visible_parlamentares.length}).catch(function(e){console.error("Erro ao procurar parlamentar:"+e)})},checkTitularAtivo:function(e){this.visible_parlamentares=this.parlamentares,this.filter_ativo&&(this.visible_parlamentares=this.visible_parlamentares.filter(function(e){return e.ativo})),this.filter_titular&&(this.visible_parlamentares=this.visible_parlamentares.filter(function(e){return"Sim"===e.titular})),this.size_parlamentares=this.visible_parlamentares.length},pesquisaChange:function(e){this.is_pesquisa=!this.is_pesquisa,this.filter_ativo=!0,this.is_pesquisa?this.parlamentares=[]:this.getParlamentares()}},mounted:function(){var a=this;s.A.get("/api/parlamentares/legislatura/?get_all=true").then(function(e){a.legislaturas=e.data;var i=(new Date).getFullYear();a.legislatura_selecionada=a.legislaturas.reduce(function(e,a){var t=new Date(a.data_inicio+" 00:00").getFullYear(),r=new Date(a.data_fim+" 00:00").getFullYear();return e=t<=i&&i<=r?a.id:e},"")}).then(function(e){a.getParlamentares()}).catch(function(e){console.error("Ocorreu um erro ao obter os dados de legislação: "+e)})}})}},r={};function c(e){var a=r[e];return void 0!==a||(a=r[e]={id:e,loaded:!1,exports:{}},t[e].call(a.exports,a,a.exports,c),a.loaded=!0),a.exports}c.m=t,u=[],c.O=(e,a,t,r)=>{if(!a){for(var i=1/0,s=0;s<u.length;s++){for(var n,[a,t,r]=u[s],l=!0,o=0;o<a.length;o++)(!1&r||r<=i)&&Object.keys(c.O).every(e=>c.O[e](a[o]))?a.splice(o--,1):(l=!1,r<i&&(i=r));l&&(u.splice(s--,1),void 0!==(n=t()))&&(e=n)}return e}r=r||0;for(var s=u.length;0<s&&u[s-1][2]>r;s--)u[s]=u[s-1];u[s]=[a,t,r]},c.n=e=>{var a=e&&e.__esModule?()=>e.default:()=>e;return c.d(a,{a:a}),a},c.d=(e,a)=>{for(var t in a)c.o(a,t)&&!c.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:a[t]})},c.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),c.o=(e,a)=>Object.prototype.hasOwnProperty.call(e,a),c.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},c.nmd=e=>(e.paths=[],e.children||(e.children=[]),e),c.j=788,p={788:0},c.O.j=e=>0===p[e],a=(e,a)=>{var t,r,i,[s,n,l]=a,o=0;if(s.some(e=>0!==p[e])){for(t in n)c.o(n,t)&&(c.m[t]=n[t]);l&&(i=l(c))}for(e&&e(a);o<s.length;o++)r=s[o],c.o(p,r)&&p[r]&&p[r][0](),p[r]=0;return c.O(i)},(e=self.webpackChunksapl_frontend=self.webpackChunksapl_frontend||[]).forEach(a.bind(null,0)),e.push=a.bind(null,e.push.bind(e));var a=c.O(void 0,[504],()=>c(8020));c.O(a)})();

BIN
sapl/static/sapl/frontend/js/parlamentar.f0710dca.js.gz

Binary file not shown.

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save