mirror of https://github.com/interlegis/sapl.git
committed by
GitHub
261 changed files with 24769 additions and 1212 deletions
@ -1,73 +1,123 @@ |
|||
FROM python:3.9-slim-buster |
|||
|
|||
# Setup env |
|||
ENV LANG C.UTF-8 |
|||
ENV LC_ALL C.UTF-8 |
|||
ENV PYTHONDONTWRITEBYTECODE 1 |
|||
ENV PYTHONUNBUFFERED=1 |
|||
ENV DEBIAN_FRONTEND noninteractive |
|||
|
|||
ENV BUILD_PACKAGES apt-utils apt-file libpq-dev graphviz-dev build-essential git pkg-config \ |
|||
python3-dev libxml2-dev libjpeg-dev libssl-dev libffi-dev libxslt1-dev \ |
|||
libcairo2-dev software-properties-common python3-setuptools python3-pip |
|||
|
|||
## NAO EH PRA TIRAR O vim DA LISTA DE COMANDOS INSTALADOS!!! |
|||
ENV RUN_PACKAGES graphviz python3-lxml python3-magic postgresql-client python3-psycopg2 \ |
|||
poppler-utils curl jq bash vim python3-venv tzdata nodejs \ |
|||
fontconfig ttf-dejavu python nginx |
|||
|
|||
RUN mkdir -p /var/interlegis/sapl |
|||
# ---------- 1) BUILDER ---------- |
|||
FROM python:3.12-slim-bookworm AS builder |
|||
|
|||
ENV LANG=C.UTF-8 LC_ALL=C.UTF-8 PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1 \ |
|||
DEBIAN_FRONTEND=noninteractive \ |
|||
VENV_DIR=/opt/venv \ |
|||
PIP_NO_CACHE_DIR=on |
|||
|
|||
# Dev headers e toolchain só no builder |
|||
RUN apt-get update && apt-get install -y --no-install-recommends \ |
|||
build-essential git pkg-config \ |
|||
libpq-dev libxml2-dev libjpeg-dev libssl-dev libffi-dev libxslt1-dev \ |
|||
libcairo2-dev libpango1.0-dev libgdk-pixbuf-2.0-dev libharfbuzz-dev \ |
|||
libfreetype6-dev zlib1g-dev \ |
|||
&& rm -rf /var/lib/apt/lists/* |
|||
|
|||
# Venv independente do sistema |
|||
RUN python -m venv "${VENV_DIR}" \ |
|||
&& "${VENV_DIR}/bin/pip" install --upgrade pip setuptools wheel |
|||
|
|||
WORKDIR /build |
|||
|
|||
# Copie APENAS os requirements primeiro para maximizar cache |
|||
COPY requirements/ ./requirements/ |
|||
|
|||
# Instale os requisitos de produção |
|||
# ATENÇÃO: se seu código importa prompt_toolkit em runtime, inclua em requirements.txt: |
|||
# prompt_toolkit>=3,<4 |
|||
RUN "${VENV_DIR}/bin/pip" install -r requirements/requirements.txt |
|||
|
|||
# Opcional: verificação de conflitos (falha cedo se faltar algo) |
|||
RUN "${VENV_DIR}/bin/pip" check || true |
|||
|
|||
|
|||
# ---------- 2) RUNTIME ---------- |
|||
FROM python:3.12-slim-bookworm AS runtime |
|||
|
|||
ARG WITH_GRAPHVIZ=1 |
|||
ARG WITH_POPPLER=1 |
|||
ARG WITH_PSQL_CLIENT=1 |
|||
ARG WITH_NGINX=1 |
|||
|
|||
ENV LANG=C.UTF-8 LC_ALL=C.UTF-8 PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1 \ |
|||
DEBIAN_FRONTEND=noninteractive \ |
|||
VENV_DIR=/opt/venv \ |
|||
PATH=/opt/venv/bin:$PATH \ |
|||
PIP_NO_CACHE_DIR=on |
|||
|
|||
# Pacotes de runtime (sem *-dev) |
|||
# Removi python3/python3-venv (já temos o Python da base) |
|||
RUN set -eux; \ |
|||
apt-get update; \ |
|||
apt-get install -y --no-install-recommends \ |
|||
curl jq bash tzdata fontconfig tini libmagic1 \ |
|||
libcairo2 libpango-1.0-0 libpangocairo-1.0-0 libgdk-pixbuf-2.0-0 \ |
|||
libharfbuzz0b libfreetype6 libjpeg62-turbo zlib1g fonts-dejavu-core; \ |
|||
if [ "$WITH_GRAPHVIZ" = "1" ]; then apt-get install -y --no-install-recommends graphviz; fi; \ |
|||
if [ "$WITH_POPPLER" = "1" ]; then apt-get install -y --no-install-recommends poppler-utils; fi; \ |
|||
if [ "$WITH_PSQL_CLIENT" = "1" ]; then apt-get install -y --no-install-recommends postgresql-client; fi; \ |
|||
if [ "$WITH_NGINX" = "1" ]; then apt-get install -y --no-install-recommends nginx; fi; \ |
|||
rm -rf /var/lib/apt/lists/* |
|||
|
|||
# Usuários/grupos (idempotente) |
|||
RUN useradd --system --no-create-home --shell /usr/sbin/nologin sapl || true \ |
|||
&& groupadd -r nginx || true \ |
|||
&& usermod -aG nginx www-data || true \ |
|||
&& usermod -aG nginx sapl || true |
|||
|
|||
# Estrutura de diretórios |
|||
RUN mkdir -p /var/interlegis/sapl /var/interlegis/sapl/data /var/interlegis/sapl/media /var/interlegis/sapl/run \ |
|||
&& chown -R root:nginx /var/interlegis/sapl /var/interlegis/sapl/run \ |
|||
&& chmod -R g+rwX /var/interlegis/sapl \ |
|||
&& chmod 2775 /var/interlegis/sapl /var/interlegis/sapl/run \ |
|||
&& find /var/interlegis/sapl -type d -exec chmod g+s {} + |
|||
|
|||
WORKDIR /var/interlegis/sapl/ |
|||
|
|||
ADD . /var/interlegis/sapl/ |
|||
|
|||
RUN apt-get update && \ |
|||
apt-get upgrade -y && \ |
|||
apt-get install -y --no-install-recommends $BUILD_PACKAGES $RUN_PACKAGES && \ |
|||
fc-cache -fv && \ |
|||
pip3 install --no-cache-dir --upgrade pip setuptools && \ |
|||
rm -f /etc/nginx/conf.d/* && \ |
|||
pip install --no-cache-dir -r /var/interlegis/sapl/requirements/dev-requirements.txt --upgrade setuptools && \ |
|||
SUDO_FORCE_REMOVE=yes apt-get purge -y --auto-remove $BUILD_PACKAGES && \ |
|||
apt-get autoremove && apt-get clean && rm -rf /var/lib/apt/lists/* |
|||
|
|||
WORKDIR /var/interlegis/sapl/ |
|||
ADD . /var/interlegis/sapl/ |
|||
|
|||
COPY docker/start.sh $HOME |
|||
COPY docker/solr_cli.py $HOME |
|||
COPY docker/wait-for-pg.sh $HOME |
|||
COPY docker/wait-for-solr.sh $HOME |
|||
COPY docker/create_admin.py $HOME |
|||
COPY docker/genkey.py $HOME |
|||
COPY docker/gunicorn_start.sh $HOME |
|||
|
|||
COPY docker/config/nginx/sapl.conf /etc/nginx/conf.d |
|||
COPY docker/config/nginx/nginx.conf /etc/nginx/nginx.conf |
|||
# Traga o venv pré-instalado |
|||
COPY --from=builder ${VENV_DIR} ${VENV_DIR} |
|||
|
|||
# Código da aplicação (depois do venv para aproveitar cache) |
|||
COPY . /var/interlegis/sapl/ |
|||
|
|||
# Nginx (somente se instalado) |
|||
RUN if [ "$WITH_NGINX" = "1" ]; then \ |
|||
rm -f /etc/nginx/conf.d/*; \ |
|||
cp docker/config/nginx/sapl.conf /etc/nginx/conf.d/sapl.conf; \ |
|||
cp docker/config/nginx/nginx.conf /etc/nginx/nginx.conf; \ |
|||
fi |
|||
|
|||
# Scripts + gunicorn.conf no diretório da app |
|||
RUN install -m 755 docker/startup_scripts/start.sh /var/interlegis/sapl/start.sh \ |
|||
&& install -m 755 docker/startup_scripts/wait-for-pg.sh /var/interlegis/sapl/wait-for-pg.sh \ |
|||
&& install -m 755 docker/startup_scripts/wait-for-solr.sh /var/interlegis/sapl/wait-for-solr.sh \ |
|||
&& install -m 644 docker/startup_scripts/solr_cli.py /var/interlegis/sapl/solr_cli.py \ |
|||
&& install -m 644 docker/startup_scripts/create_admin.py /var/interlegis/sapl/create_admin.py \ |
|||
&& install -m 644 docker/startup_scripts/genkey.py /var/interlegis/sapl/genkey.py \ |
|||
&& install -m 644 docker/startup_scripts/gunicorn.conf.py /var/interlegis/sapl/gunicorn.conf.py |
|||
|
|||
# (Se possível, evite copiar .env no build. Use secrets/variáveis em runtime.) |
|||
COPY docker/config/env_dockerfile /var/interlegis/sapl/sapl/.env |
|||
|
|||
RUN python3 manage.py collectstatic --noinput --clear |
|||
|
|||
# Remove .env(fake) e sapl.db da imagem |
|||
RUN rm -rf /var/interlegis/sapl/sapl/.env && \ |
|||
rm -rf /var/interlegis/sapl/sapl.db |
|||
|
|||
RUN chmod +x /var/interlegis/sapl/start.sh && \ |
|||
chmod +x /var/interlegis/sapl/wait-for-solr.sh && \ |
|||
chmod +x /var/interlegis/sapl/wait-for-pg.sh && \ |
|||
ln -sf /dev/stdout /var/log/nginx/access.log && \ |
|||
ln -sf /dev/stderr /var/log/nginx/error.log && \ |
|||
mkdir /var/log/sapl/ && touch /var/interlegis/sapl/sapl.log && \ |
|||
ln -s /var/interlegis/sapl/sapl.log /var/log/sapl/sapl.log |
|||
|
|||
# Debian não possui usuário 'nginx' necessário para o Debian |
|||
RUN useradd --no-create-home nginx |
|||
# Logs (só se nginx estiver presente) |
|||
RUN if [ "$WITH_NGINX" = "1" ]; then \ |
|||
ln -sf /dev/stdout /var/log/nginx/access.log; \ |
|||
ln -sf /dev/stderr /var/log/nginx/error.log; \ |
|||
fi \ |
|||
&& mkdir -p /var/log/sapl/ \ |
|||
&& ln -sf /var/interlegis/sapl/sapl.log /var/log/sapl/sapl.log |
|||
|
|||
ENV DEBIAN_FRONTEND teletype |
|||
# Cache de fontes e collectstatic |
|||
# NÃO atualizamos pip aqui (já veio pronto do builder) |
|||
RUN fc-cache -fv \ |
|||
&& python manage.py collectstatic --noinput --clear \ |
|||
&& rm -f /var/interlegis/sapl/sapl/.env /var/interlegis/sapl/sapl.db || true |
|||
|
|||
EXPOSE 80/tcp 443/tcp |
|||
ENV DEBIAN_FRONTEND=teletype |
|||
|
|||
VOLUME ["/var/interlegis/sapl/data", "/var/interlegis/sapl/media"] |
|||
EXPOSE 80 443 |
|||
VOLUME ["/var/interlegis/sapl/data", "/var/interlegis/sapl/media", "/var/log/sapl/"] |
|||
|
|||
ENTRYPOINT ["/usr/bin/tini","--"] |
|||
CMD ["/var/interlegis/sapl/start.sh"] |
|||
|
@ -1,50 +0,0 @@ |
|||
#!/usr/bin/env bash |
|||
|
|||
## |
|||
## |
|||
## PARA USO EXCLUSIVO DO CONTAINER DOCKER DO SAPL!!! |
|||
## EVITE USAR PARA CHAMADA DIRETAS |
|||
## |
|||
## |
|||
|
|||
# As seen in http://tutos.readthedocs.org/en/latest/source/ndg.html |
|||
|
|||
SAPL_DIR="/var/interlegis/sapl" |
|||
|
|||
# Seta um novo diretório foi passado como raiz para o SAPL |
|||
# caso esse tenha sido passado como parâmetro |
|||
if [ "$1" ] |
|||
then |
|||
SAPL_DIR="$1" |
|||
fi |
|||
|
|||
NAME="SAPL" # Name of the application (*) |
|||
DJANGODIR=/var/interlegis/sapl/ # Django project directory (*) |
|||
SOCKFILE=/var/interlegis/sapl/run/gunicorn.sock # we will communicate using this unix socket (*) |
|||
USER=`whoami` # the user to run as (*) |
|||
GROUP=`whoami` # the group to run as (*) |
|||
NUM_WORKERS=3 # how many worker processes should Gunicorn spawn (*) |
|||
# NUM_WORKERS = 2 * CPUS + 1 |
|||
TIMEOUT=300 |
|||
MAX_REQUESTS=100 # number of requests before restarting worker |
|||
DJANGO_SETTINGS_MODULE=sapl.settings # which settings file should Django use (*) |
|||
DJANGO_WSGI_MODULE=sapl.wsgi # WSGI module name (*) |
|||
|
|||
echo "Starting $NAME as `whoami` on base dir $SAPL_DIR" |
|||
|
|||
# Create the run directory if it doesn't exist |
|||
RUNDIR=$(dirname $SOCKFILE) |
|||
test -d $RUNDIR || mkdir -p $RUNDIR |
|||
|
|||
# Start your Django Unicorn |
|||
# Programs meant to be run under supervisor should not daemonize themselves (do not use --daemon) |
|||
exec gunicorn ${DJANGO_WSGI_MODULE}:application \ |
|||
--name $NAME \ |
|||
--log-level debug \ |
|||
--timeout $TIMEOUT \ |
|||
--workers $NUM_WORKERS \ |
|||
--max-requests $MAX_REQUESTS \ |
|||
--user $USER \ |
|||
--access-logfile /var/log/sapl/access.log \ |
|||
--error-logfile /var/log/sapl/error.log \ |
|||
--bind=unix:$SOCKFILE |
@ -1,147 +0,0 @@ |
|||
#!/usr/bin/env bash |
|||
|
|||
create_env() { |
|||
echo "[ENV FILE] creating .env file..." |
|||
# check if file exists |
|||
if [ -f "/var/interlegis/sapl/data/secret.key" ]; then |
|||
KEY=`cat /var/interlegis/sapl/data/secret.key` |
|||
else |
|||
KEY=`python3 genkey.py` |
|||
echo $KEY > data/secret.key |
|||
fi |
|||
|
|||
FILENAME="/var/interlegis/sapl/sapl/.env" |
|||
|
|||
if [ -z "${DATABASE_URL:-}" ]; then |
|||
DATABASE_URL="postgresql://sapl:sapl@sapldb:5432/sapl" |
|||
fi |
|||
|
|||
# ALWAYS replace the content of .env variable |
|||
# If want to conditionally create only if absent then use IF below |
|||
# if [ ! -f $FILENAME ]; then |
|||
|
|||
touch $FILENAME |
|||
|
|||
# explicitly use '>' to erase any previous content |
|||
echo "SECRET_KEY="$KEY > $FILENAME |
|||
# now only appends |
|||
echo "DATABASE_URL = "$DATABASE_URL >> $FILENAME |
|||
echo "DEBUG = ""${DEBUG-False}" >> $FILENAME |
|||
echo "EMAIL_USE_TLS = ""${USE_TLS-True}" >> $FILENAME |
|||
echo "EMAIL_PORT = ""${EMAIL_PORT-587}" >> $FILENAME |
|||
echo "EMAIL_HOST = ""${EMAIL_HOST-''}" >> $FILENAME |
|||
echo "EMAIL_HOST_USER = ""${EMAIL_HOST_USER-''}" >> $FILENAME |
|||
echo "EMAIL_HOST_PASSWORD = ""${EMAIL_HOST_PASSWORD-''}" >> $FILENAME |
|||
echo "EMAIL_SEND_USER = ""${EMAIL_HOST_USER-''}" >> $FILENAME |
|||
echo "DEFAULT_FROM_EMAIL = ""${EMAIL_HOST_USER-''}" >> $FILENAME |
|||
echo "SERVER_EMAIL = ""${EMAIL_HOST_USER-''}" >> $FILENAME |
|||
echo "USE_SOLR = ""${USE_SOLR-False}" >> $FILENAME |
|||
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." |
|||
} |
|||
|
|||
create_env |
|||
|
|||
/bin/bash wait-for-pg.sh $DATABASE_URL |
|||
|
|||
yes yes | python3 manage.py migrate |
|||
|
|||
|
|||
## SOLR |
|||
USE_SOLR="${USE_SOLR:=False}" |
|||
SOLR_URL="${SOLR_URL:=http://localhost:8983}" |
|||
SOLR_COLLECTION="${SOLR_COLLECTION:=sapl}" |
|||
NUM_SHARDS=${NUM_SHARDS:=1} |
|||
RF=${RF:=1} |
|||
MAX_SHARDS_PER_NODE=${MAX_SHARDS_PER_NODE:=1} |
|||
IS_ZK_EMBEDDED="${IS_ZK_EMBEDDED:=False}" |
|||
|
|||
if [ "${USE_SOLR-False}" == "True" ] || [ "${USE_SOLR-False}" == "true" ]; then |
|||
|
|||
echo "Solr configurations" |
|||
echo "===================" |
|||
echo "URL: $SOLR_URL" |
|||
echo "COLLECTION: $SOLR_COLLECTION" |
|||
echo "NUM_SHARDS: $NUM_SHARDS" |
|||
echo "REPLICATION FACTOR: $RF" |
|||
echo "MAX SHARDS PER NODE: $MAX_SHARDS_PER_NODE" |
|||
echo "ASSUME ZK EMBEDDED: $IS_ZK_EMBEDDED" |
|||
echo "=========================================" |
|||
|
|||
echo "running Solr script" |
|||
/bin/bash wait-for-solr.sh $SOLR_URL |
|||
CHECK_SOLR_RETURN=$? |
|||
|
|||
if [ $CHECK_SOLR_RETURN == 1 ]; then |
|||
echo "Connecting to Solr..." |
|||
|
|||
|
|||
if [ "${IS_ZK_EMBEDDED-False}" == "True" ] || [ "${IS_ZK_EMBEDDED-False}" == "true" ]; then |
|||
ZK_EMBEDDED="--embedded_zk" |
|||
echo "Assuming embedded ZooKeeper instalation..." |
|||
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) |
|||
|
|||
echo $user_created |
|||
|
|||
cmd=$(echo $user_created | grep 'ADMIN_USER_EXISTS') |
|||
user_exists=$? |
|||
|
|||
cmd=$(echo $user_created | grep 'MISSING_ADMIN_PASSWORD') |
|||
lack_pwd=$? |
|||
|
|||
if [ $user_exists -eq 0 ]; then |
|||
echo "[SUPERUSER CREATION] User admin already exists. Not creating" |
|||
fi |
|||
|
|||
if [ $lack_pwd -eq 0 ]; then |
|||
echo "[SUPERUSER] Environment variable $ADMIN_PASSWORD for superuser admin was not set. Leaving container" |
|||
# return -1 |
|||
fi |
|||
|
|||
# Backfilling AuditLog's JSON field |
|||
time ./manage.py backfill_auditlog & |
|||
|
|||
echo "-------------------------------------" |
|||
echo "| ███████╗ █████╗ ██████╗ ██╗ |" |
|||
echo "| ██╔════╝██╔══██╗██╔══██╗██║ |" |
|||
echo "| ███████╗███████║██████╔╝██║ |" |
|||
echo "| ╚════██║██╔══██║██╔═══╝ ██║ |" |
|||
echo "| ███████║██║ ██║██║ ███████╗ |" |
|||
echo "| ╚══════╝╚═╝ ╚═╝╚═╝ ╚══════╝ |" |
|||
echo "-------------------------------------" |
|||
|
|||
/bin/sh gunicorn_start.sh & |
|||
/usr/sbin/nginx -g "daemon off;" |
@ -0,0 +1,80 @@ |
|||
# /var/interlegis/sapl/gunicorn.conf.py |
|||
|
|||
import os |
|||
import pathlib |
|||
import multiprocessing |
|||
|
|||
# ---- SAPL app configuration ---- |
|||
NAME = "SAPL" |
|||
DJANGODIR = "/var/interlegis/sapl" |
|||
SOCKFILE = f"unix:{DJANGODIR}/run/gunicorn.sock" |
|||
USER = "sapl" |
|||
GROUP = "nginx" |
|||
NUM_WORKERS = int(os.getenv("WEB_CONCURRENCY", "3")) |
|||
THREADS = int(os.getenv("GUNICORN_THREADS", "8")) |
|||
TIMEOUT = int(os.getenv("GUNICORN_TIMEOUT", "300")) |
|||
MAX_REQUESTS = 1000 |
|||
WORKER_CLASS = "gthread" |
|||
DJANGO_SETTINGS = "sapl.settings" |
|||
WSGI_APP = "sapl.wsgi:application" |
|||
|
|||
# ---- gunicorn settings ---- |
|||
# Equivalent of: --name |
|||
proc_name = NAME |
|||
|
|||
# Equivalent of: --bind=unix:... |
|||
# For quick testing via browser, you can switch to: bind = "0.0.0.0:8000" |
|||
bind = f"unix:{SOCKFILE}" |
|||
umask = 0o007 |
|||
user = USER |
|||
group = GROUP |
|||
|
|||
# Ensure imports work like in your script’s working dir |
|||
chdir = DJANGODIR |
|||
|
|||
# Allow starting with just: gunicorn -c gunicorn.conf.py |
|||
wsgi_app = WSGI_APP |
|||
|
|||
# Logs |
|||
loglevel = "debug" |
|||
accesslog = "/var/log/sapl/access.log" |
|||
errorlog = "/var/log/sapl/error.log" |
|||
# errorlog = "-" # send to stderr (so you see it in docker logs or terminal) |
|||
# accesslog = "-" # send to stdout |
|||
capture_output = True # capture print/tracebacks from app |
|||
|
|||
# Worker/process lifecycle |
|||
workers = NUM_WORKERS |
|||
worker_class = WORKER_CLASS |
|||
threads = THREADS |
|||
timeout = TIMEOUT |
|||
graceful_timeout = 30 |
|||
keepalive = 10 |
|||
backlog = 2048 |
|||
max_requests = MAX_REQUESTS |
|||
max_requests_jitter = 100 |
|||
|
|||
# Environment (same as exporting before running) |
|||
raw_env = [ |
|||
f"DJANGO_SETTINGS_MODULE={DJANGO_SETTINGS}", |
|||
# If you’re using ReportLab and seeing segfaults with PDFs, keep this: |
|||
# "RL_NOACCEL=1", |
|||
] |
|||
|
|||
# If you previously enabled preload and saw segfaults with native libs, keep it off: |
|||
preload_app = False |
|||
|
|||
|
|||
# Create the run/ directory for the UNIX socket (your script did this) |
|||
def on_starting(server): |
|||
pathlib.Path(SOCKFILE).parent.mkdir(parents=True, exist_ok=True) |
|||
|
|||
|
|||
# Close DB connections after fork (safer when using preload or certain DB drivers) |
|||
def post_fork(server, worker): |
|||
try: |
|||
from django import db |
|||
db.connections.close_all() |
|||
except Exception: |
|||
# Django not initialized yet or not available |
|||
pass |
@ -0,0 +1,289 @@ |
|||
#!/usr/bin/env bash |
|||
set -Eeuo pipefail |
|||
IFS=$'\n\t' |
|||
|
|||
DATA_DIR="/var/interlegis/sapl/data" |
|||
APP_DIR="/var/interlegis/sapl/sapl" |
|||
ENV_FILE="$APP_DIR/.env" |
|||
SECRET_FILE="$DATA_DIR/secret.key" |
|||
|
|||
mkdir -p "$DATA_DIR" "$APP_DIR" |
|||
|
|||
log() { printf '[%s] %s\n' "$(date -Is)" "$*"; } |
|||
err() { printf '[%s] ERROR: %s\n' "$(date -Is)" "$*" >&2; } |
|||
|
|||
cleanup() { jobs -p | xargs -r kill 2>/dev/null || true; } |
|||
trap cleanup TERM INT EXIT |
|||
|
|||
# --- new function --- |
|||
configure_pg_timezone() { |
|||
: "${DATABASE_URL:=postgresql://sapl:sapl@sapldb:5432/sapl}" |
|||
: "${DB_TIMEZONE:=America/Sao_Paulo}" |
|||
: "${DB_NAME:=}" |
|||
: "${DB_ROLE:=}" |
|||
|
|||
log "Checking database/role timezone defaults…" |
|||
|
|||
# Detect DB and role if not provided |
|||
if [[ -z "$DB_NAME" ]]; then |
|||
DB_NAME="$(psql "$DATABASE_URL" -At -v ON_ERROR_STOP=1 -c 'select current_database();')" |
|||
fi |
|||
if [[ -z "$DB_ROLE" ]]; then |
|||
DB_ROLE="$(psql "$DATABASE_URL" -At -v ON_ERROR_STOP=1 -c 'select current_user;')" |
|||
fi |
|||
|
|||
# What is the effective timezone for this DB/role right now? |
|||
current_tz="$(psql "$DATABASE_URL" -At -v ON_ERROR_STOP=1 -c 'show time zone;')" |
|||
current_tz_lower="${current_tz,,}" |
|||
|
|||
# Consider these as already UTC |
|||
if [[ "$current_tz_lower" == "utc" || "$current_tz_lower" == "etc/utc" ]]; then |
|||
log "Timezone already UTC for DB='$DB_NAME' ROLE='$DB_ROLE' (SHOW TIME ZONE => $current_tz). Skipping ALTERs." |
|||
return |
|||
fi |
|||
|
|||
log "Timezone is '$current_tz' (not UTC). Applying persistent defaults…" |
|||
|
|||
# Persist at database level (requires DB owner or superuser) |
|||
if psql "$DATABASE_URL" -v ON_ERROR_STOP=1 -q \ |
|||
-c "ALTER DATABASE \"$DB_NAME\" SET timezone TO '$DB_TIMEZONE';"; then |
|||
log "ALTER DATABASE \"$DB_NAME\" SET timezone TO '$DB_TIMEZONE' applied." |
|||
else |
|||
err "ALTER DATABASE \"$DB_NAME\" failed. Need DB owner or superuser." |
|||
exit 1 |
|||
fi |
|||
|
|||
# Persist at role level (requires superuser) |
|||
if psql "$DATABASE_URL" -v ON_ERROR_STOP=1 -q \ |
|||
-c "ALTER ROLE \"$DB_ROLE\" SET timezone TO '$DB_TIMEZONE';"; then |
|||
log "ALTER ROLE \"$DB_ROLE\" SET timezone TO '$DB_TIMEZONE' applied." |
|||
else |
|||
err "ALTER ROLE \"$DB_ROLE\" failed. Need superuser privileges." |
|||
exit 1 |
|||
fi |
|||
|
|||
# Re-check (new session shows the new default) |
|||
verify_tz="$(psql "$DATABASE_URL" -At -v ON_ERROR_STOP=1 -c 'show time zone;')" |
|||
log "SHOW TIME ZONE now => $verify_tz (new sessions will inherit the defaults)." |
|||
} |
|||
|
|||
|
|||
create_secret() { |
|||
if [[ -f "$SECRET_FILE" ]]; then |
|||
SECRET_KEY="$(<"$SECRET_FILE")" |
|||
else |
|||
log "Generating SECRET_KEY..." |
|||
SECRET_KEY="$(python3 genkey.py)" |
|||
umask 177 |
|||
printf '%s\n' "$SECRET_KEY" > "$SECRET_FILE" |
|||
chmod 600 "$SECRET_FILE" |
|||
fi |
|||
export SECRET_KEY |
|||
} |
|||
|
|||
write_env_file() { |
|||
: "${DATABASE_URL:=postgresql://sapl:sapl@sapldb:5432/sapl}" |
|||
: "${DEBUG:=False}" |
|||
: "${EMAIL_USE_TLS:=True}" |
|||
: "${EMAIL_PORT:=587}" |
|||
: "${EMAIL_HOST:=}" |
|||
: "${EMAIL_HOST_USER:=}" |
|||
: "${EMAIL_HOST_PASSWORD:=}" |
|||
: "${DEFAULT_FROM_EMAIL:=$EMAIL_HOST_USER}" |
|||
: "${SERVER_EMAIL:=$EMAIL_HOST_USER}" |
|||
: "${USE_SOLR:=False}" |
|||
: "${SOLR_COLLECTION:=sapl}" |
|||
: "${SOLR_URL:=http://localhost:8983}" |
|||
: "${IS_ZK_EMBEDDED:=False}" |
|||
: "${NUM_SHARDS:=1}" |
|||
: "${RF:=1}" |
|||
: "${MAX_SHARDS_PER_NODE:=1}" |
|||
: "${ENABLE_SAPN:=False}" |
|||
|
|||
tmp="$(mktemp)" |
|||
{ |
|||
printf 'SECRET_KEY=%s\n' "$SECRET_KEY" |
|||
printf 'DATABASE_URL=%s\n' "$DATABASE_URL" |
|||
printf 'DEBUG=%s\n' "$DEBUG" |
|||
printf 'EMAIL_USE_TLS=%s\n' "$EMAIL_USE_TLS" |
|||
printf 'EMAIL_PORT=%s\n' "$EMAIL_PORT" |
|||
printf 'EMAIL_HOST=%s\n' "$EMAIL_HOST" |
|||
printf 'EMAIL_HOST_USER=%s\n' "$EMAIL_HOST_USER" |
|||
printf 'EMAIL_HOST_PASSWORD=%s\n' "$EMAIL_HOST_PASSWORD" |
|||
printf 'EMAIL_SEND_USER=%s\n' "$EMAIL_HOST_USER" |
|||
printf 'DEFAULT_FROM_EMAIL=%s\n' "$DEFAULT_FROM_EMAIL" |
|||
printf 'SERVER_EMAIL=%s\n' "$SERVER_EMAIL" |
|||
printf 'USE_SOLR=%s\n' "$USE_SOLR" |
|||
printf 'SOLR_COLLECTION=%s\n' "$SOLR_COLLECTION" |
|||
printf 'SOLR_URL=%s\n' "$SOLR_URL" |
|||
printf 'IS_ZK_EMBEDDED=%s\n' "$IS_ZK_EMBEDDED" |
|||
printf 'NUM_SHARDS=%s\n' "$NUM_SHARDS" |
|||
printf 'RF=%s\n' "$RF" |
|||
printf 'MAX_SHARDS_PER_NODE=%s\n' "$MAX_SHARDS_PER_NODE" |
|||
printf 'ENABLE_SAPN=%s\n' "$ENABLE_SAPN" |
|||
} > "$tmp" |
|||
|
|||
chmod 600 "$tmp" |
|||
mv -f "$tmp" "$ENV_FILE" |
|||
log "[ENV] wrote $ENV_FILE" |
|||
} |
|||
|
|||
wait_for_pg() { |
|||
: "${DATABASE_URL:=postgresql://sapl:sapl@sapldb:5432/sapl}" |
|||
log "Waiting for Postgres..." |
|||
/bin/bash wait-for-pg.sh "$DATABASE_URL" |
|||
} |
|||
|
|||
migrate_db() { |
|||
log "Running Django migrations..." |
|||
python3 manage.py migrate --noinput |
|||
} |
|||
|
|||
# In start.sh (near your other helpers) |
|||
configure_solr() { |
|||
# respect envs, with sane defaults |
|||
local USE="${USE_SOLR:-False}" |
|||
local URL="${SOLR_URL:-http://admin:solr@localhost:8983}" |
|||
local COL="${SOLR_COLLECTION:-sapl}" |
|||
local SHARDS="${NUM_SHARDS:-1}" |
|||
local RF="${RF:-1}" |
|||
local MS="${MAX_SHARDS_PER_NODE:-1}" |
|||
local IS_ZK="${IS_ZK_EMBEDDED:-False}" |
|||
|
|||
# total wait time before we give up (seconds) |
|||
local WAIT_TIMEOUT="${SOLR_WAIT_TIMEOUT:-30}" |
|||
# per probe max seconds |
|||
local PROBE_TIMEOUT="${SOLR_PROBE_TIMEOUT:-3}" |
|||
# sleep between probes |
|||
local SLEEP_SECS="${SOLR_WAIT_INTERVAL:-2}" |
|||
|
|||
# feature flag OFF by default unless we confirm Solr |
|||
./manage.py waffle_switch SOLR_SWITCH off --create || true |
|||
|
|||
# Fast exit if disabled |
|||
if [[ "${USE,,}" != "true" ]]; then |
|||
echo "[SOLR] USE_SOLR=$USE → skipping Solr initialization." |
|||
return 0 |
|||
fi |
|||
|
|||
echo "[SOLR] Best-effort wait (<= ${WAIT_TIMEOUT}s): $URL, collection=$COL" |
|||
|
|||
local deadline=$((SECONDS + WAIT_TIMEOUT)) |
|||
while (( SECONDS < deadline )); do |
|||
# Try a cheap SolrCloud endpoint; swap for /solr/admin/info/system if you prefer |
|||
if curl -fsS --max-time "${PROBE_TIMEOUT}" \ |
|||
"${URL%/}/solr/admin/collections?action=LIST" >/dev/null; then |
|||
echo "[SOLR] Reachable. Kicking off background configuration…" |
|||
|
|||
# optional flag if ZK is embedded |
|||
local ZK_FLAG="" |
|||
if [[ "${IS_ZK,,}" == "true" ]]; then |
|||
ZK_FLAG="--embedded_zk" |
|||
fi |
|||
|
|||
( |
|||
set -Eeuo pipefail |
|||
python3 solr_cli.py \ |
|||
-u "$URL" -c "$COL" -s "$SHARDS" -rf "$RF" -ms "$MS" $ZK_FLAG |
|||
./manage.py waffle_switch SOLR_SWITCH on --create |
|||
echo "[SOLR] Configuration done, SOLR_SWITCH=on." |
|||
) >/var/log/sapl/solr_init.log 2>&1 & disown |
|||
|
|||
return 0 |
|||
fi |
|||
sleep "${SLEEP_SECS}" |
|||
done |
|||
|
|||
echo "[SOLR] Not reachable within ${WAIT_TIMEOUT}s. Proceeding without Solr (SOLR_SWITCH=off)." |
|||
return 0 |
|||
} |
|||
|
|||
configure_sapn() { |
|||
if [[ "${ENABLE_SAPN,,}" == "true" ]]; then |
|||
log "Enabling SAPN" |
|||
python3 manage.py waffle_switch SAPN_SWITCH on --create |
|||
else |
|||
log "Disabling SAPN" |
|||
python3 manage.py waffle_switch SAPN_SWITCH off --create |
|||
fi |
|||
} |
|||
|
|||
create_admin() { |
|||
log "Creating admin user..." |
|||
out="$(python3 create_admin.py 2>&1 || true)" |
|||
printf '%s\n' "$out" |
|||
|
|||
if grep -q 'MISSING_ADMIN_PASSWORD' <<<"$out"; then |
|||
err "[SUPERUSER] ADMIN_PASSWORD not set. Exiting." |
|||
exit 1 |
|||
fi |
|||
} |
|||
|
|||
fix_logging_and_socket_perms() { |
|||
local APP_DIR="/var/interlegis/sapl" |
|||
local LOG_FILE="$APP_DIR/sapl.log" |
|||
|
|||
# dirs |
|||
mkdir -p "$APP_DIR/run" |
|||
chown -R root:nginx "$APP_DIR" |
|||
chmod 2775 "$APP_DIR" "$APP_DIR/run" |
|||
chmod -R g+rwX "$APP_DIR" |
|||
|
|||
# new files/sockets → 660 |
|||
umask 0007 |
|||
|
|||
# ensure log file is owned by sapl and writable |
|||
install -Dm0660 /dev/null "$LOG_FILE" |
|||
chown sapl:nginx "$LOG_FILE" |
|||
|
|||
# stale socket cleanup (if any) |
|||
rm -f "$APP_DIR/run/gunicorn.sock" 2>/dev/null || true |
|||
} |
|||
|
|||
setup_cache_dir() { |
|||
# if you later move cache under /var/interlegis/sapl/cache, this line can read an env var |
|||
local CACHE_DIR="${DJANGO_CACHE_DIR:-/var/tmp/django_cache}" |
|||
|
|||
mkdir -p "$CACHE_DIR" |
|||
chown -R sapl:nginx "$CACHE_DIR" |
|||
chmod -R 2775 "$CACHE_DIR" |
|||
find "$CACHE_DIR" -type d -exec chmod g+s {} + |
|||
|
|||
# keep your global umask; 0007 ensures new files are rw for owner+group |
|||
umask 0007 |
|||
} |
|||
|
|||
start_services() { |
|||
log "Starting gunicorn..." |
|||
gunicorn -c gunicorn.conf.py & |
|||
log "Starting nginx..." |
|||
exec /usr/sbin/nginx -g "daemon off;" |
|||
} |
|||
|
|||
main() { |
|||
create_secret |
|||
write_env_file |
|||
wait_for_pg |
|||
configure_pg_timezone |
|||
migrate_db |
|||
configure_solr || true |
|||
configure_sapn |
|||
create_admin |
|||
setup_cache_dir |
|||
fix_logging_and_socket_perms |
|||
|
|||
cat <<'BANNER' |
|||
------------------------------------- |
|||
| ███████╗ █████╗ ██████╗ ██╗ | |
|||
| ██╔════╝██╔══██╗██╔══██╗██║ | |
|||
| ███████╗███████║██████╔╝██║ | |
|||
| ╚════██║██╔══██║██╔═══╝ ██║ | |
|||
| ███████║██║ ██║██║ ███████╗ | |
|||
| ╚══════╝╚═╝ ╚═╝╚═╝ ╚══════╝ | |
|||
------------------------------------- |
|||
BANNER |
|||
|
|||
start_services |
|||
} |
|||
|
|||
main "$@" |
File diff suppressed because it is too large
File diff suppressed because it is too large
@ -1,9 +1,11 @@ |
|||
-r test-requirements.txt |
|||
|
|||
autopep8==1.2.4 |
|||
beautifulsoup4==4.9.1 |
|||
django-debug-toolbar==1.11.1 |
|||
ipdb==0.13.3 |
|||
pdbpp==0.9.2 |
|||
pip-review==0.4 |
|||
pipdeptree==0.10.1 |
|||
autopep8==2.3.2 |
|||
beautifulsoup4==4.13.5 |
|||
django-debug-toolbar==3.2.4 |
|||
ipdb==0.13.13 |
|||
fancycompleter==0.11.1 |
|||
pdbpp==0.11.7 |
|||
pip-review==1.3.0 |
|||
pipdeptree==2.28.0 |
|||
pydevd-pycharm~=203.7148.7 |
|||
|
@ -1,11 +1,10 @@ |
|||
-r requirements.txt |
|||
coverage==4.4 |
|||
django-webtest==1.9.7 |
|||
flake8==2.6.2 |
|||
isort==4.2.5 |
|||
model-bakery==1.1.0 |
|||
pep8==1.7.0 |
|||
pytest==5.4.3 |
|||
pytest-cov==2.10.0 |
|||
pytest-django==3.8.0 |
|||
webtest==2.0.21 |
|||
coverage==7.6.1 |
|||
django-webtest==1.9.8 |
|||
flake8==7.1.1 |
|||
isort==5.13.2 |
|||
model-bakery==1.5.0 |
|||
pycodestyle==2.12.1 |
|||
pytest==8.3.3 |
|||
pytest-cov==5.0.0 |
|||
WebTest==3.0.6 |
|||
|
@ -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'), |
|||
), |
|||
] |
@ -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'), |
|||
), |
|||
] |
@ -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'), |
|||
), |
|||
] |
@ -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 |
|||
"""), |
|||
] |
@ -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) |
@ -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'), |
|||
), |
|||
] |
@ -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'), |
|||
), |
|||
] |
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue