mirror of https://github.com/interlegis/sigi.git
21 changed files with 827 additions and 169 deletions
@ -1,21 +1,30 @@ |
|||||
from django.core.management.base import BaseCommand |
from django.core.management.base import BaseCommand |
||||
from sigi.apps.eventos.models import Evento |
|
||||
from django.utils import timezone |
from django.utils import timezone |
||||
|
from sigi.apps.eventos.models import Evento |
||||
|
from sigi.apps.eventos.saberes import SaberesSyncException |
||||
|
|
||||
|
|
||||
class Command(BaseCommand): |
class Command(BaseCommand): |
||||
help = "Carrega dados de participantes de eventos do Moodle para o SIGI" |
help = "Carrega dados de participantes de eventos do Moodle para o SIGI" |
||||
|
|
||||
def handle(self, *args, **options): |
def handle(self, *args, **options): |
||||
for evento in Evento.objects.exclude(moodle_courseid=None).filter( |
eventos = Evento.objects.exclude(moodle_courseid=None).filter( |
||||
data_termino__lt=timezone.localtime() |
data_termino__lt=timezone.localtime() |
||||
): |
) |
||||
|
self.stdout.write(f"Processando {eventos.count()} eventos:") |
||||
|
counter = 0 |
||||
|
for evento in eventos: |
||||
|
counter += 1 |
||||
try: |
try: |
||||
evento.sincroniza_saberes() |
evento.sincroniza_saberes() |
||||
self.stdout.write( |
self.stdout.write( |
||||
self.style.SUCCESS(f"✔ {evento.nome} sincronizado.") |
self.style.SUCCESS( |
||||
|
f"✔ {counter}: {evento.nome} sincronizado." |
||||
|
) |
||||
) |
) |
||||
except Evento.SaberesSyncException as err: |
except SaberesSyncException as err: |
||||
self.stdout.write( |
self.stdout.write( |
||||
self.style.ERROR(f"✖ {evento.nome}: {err.message}") |
self.style.ERROR( |
||||
|
f"✖ {counter}: {evento.nome}: {err.message}" |
||||
|
) |
||||
) |
) |
||||
|
@ -0,0 +1,80 @@ |
|||||
|
# Generated by Django 5.2.1 on 2025-08-29 14:08 |
||||
|
|
||||
|
import django.db.models.deletion |
||||
|
import tinymce.models |
||||
|
from django.db import migrations, models |
||||
|
|
||||
|
|
||||
|
class Migration(migrations.Migration): |
||||
|
|
||||
|
dependencies = [ |
||||
|
("casas", "0027_alter_orgao_email"), |
||||
|
("eventos", "0063_participantesevento_and_more"), |
||||
|
] |
||||
|
|
||||
|
operations = [ |
||||
|
migrations.AlterField( |
||||
|
model_name="modelodeclaracao", |
||||
|
name="texto", |
||||
|
field=tinymce.models.HTMLField( |
||||
|
help_text="Use as seguintes marcações:<ul><li>{{ casa.nome }} para o nome da Casa Legislativa / órgão</li><li>{{ casa.municipio.uf.sigla }} para a sigla da UF da Casa legislativa</li><li>{{ participante.cpf }} para o CPF do visitante</li><li>{{ participante.nome }} para o nome do visitante</li><li>{{ participante.email }} para o e-mail do visitante</li><li>{{ participante.local_trabalho }} para o cargo / função / local de trabalho do visitante</li><li>{{ data }} para a data de emissão da declaração</li><li>{{ evento.data_inicio }} para a data/hora do início da visita</li><li>{{ evento.data_termino }} para a data/hora do término da visita</li><li>{{ evento.nome }} para o nome do evento</li><li>{{ evento.descricao }} para a descrição do evento</li><li>{% include 'eventos/snippets/comitiva.html' %} para a tabela com toda a comitiva da visita</li></ul>", |
||||
|
verbose_name="Texto da declaração", |
||||
|
), |
||||
|
), |
||||
|
migrations.CreateModel( |
||||
|
name="Participante", |
||||
|
fields=[ |
||||
|
( |
||||
|
"id", |
||||
|
models.BigAutoField( |
||||
|
auto_created=True, |
||||
|
primary_key=True, |
||||
|
serialize=False, |
||||
|
verbose_name="ID", |
||||
|
), |
||||
|
), |
||||
|
("cpf", models.CharField(max_length=30, verbose_name="CPF")), |
||||
|
( |
||||
|
"nome", |
||||
|
models.CharField( |
||||
|
max_length=100, verbose_name="nome completo" |
||||
|
), |
||||
|
), |
||||
|
( |
||||
|
"email", |
||||
|
models.EmailField( |
||||
|
blank=True, max_length=254, verbose_name="e-mail" |
||||
|
), |
||||
|
), |
||||
|
( |
||||
|
"local_trabalho", |
||||
|
models.TextField( |
||||
|
blank=True, verbose_name="local de trabalho / cargo" |
||||
|
), |
||||
|
), |
||||
|
( |
||||
|
"casa_legislativa", |
||||
|
models.ForeignKey( |
||||
|
blank=True, |
||||
|
null=True, |
||||
|
on_delete=django.db.models.deletion.SET_NULL, |
||||
|
to="casas.orgao", |
||||
|
verbose_name="casa legislativa", |
||||
|
), |
||||
|
), |
||||
|
( |
||||
|
"evento", |
||||
|
models.ForeignKey( |
||||
|
on_delete=django.db.models.deletion.CASCADE, |
||||
|
to="eventos.evento", |
||||
|
verbose_name="evento", |
||||
|
), |
||||
|
), |
||||
|
], |
||||
|
options={ |
||||
|
"verbose_name": "participante", |
||||
|
"verbose_name_plural": "participantes", |
||||
|
"ordering": ("casa_legislativa", "nome"), |
||||
|
}, |
||||
|
), |
||||
|
] |
@ -0,0 +1,168 @@ |
|||||
|
# Generated by Django 5.2.1 on 2025-08-28 01:37 |
||||
|
import re |
||||
|
from django.db import migrations |
||||
|
|
||||
|
|
||||
|
def forward(apps, schema_editor): |
||||
|
# Monta regexps # |
||||
|
cargos = r"|".join( |
||||
|
[ |
||||
|
"AUXILIAR LEGISLATIVA", |
||||
|
"Acessor Parlamentar", |
||||
|
"Administrador", |
||||
|
"Agente Administrativo de Eventos", |
||||
|
"Agente Legislativo", |
||||
|
"Analista de Revisão Pessoal", |
||||
|
"Analista de TI", |
||||
|
"Analista jurídico", |
||||
|
"Assessora Especial da Secretaria Legislativa e Presidente da Comissão de Regulamentação de Cargos", |
||||
|
"ASSESSOR JURÍDICO DA PRESIDÊNCIA", |
||||
|
"Assessor Jurídico", |
||||
|
"Assessor Legislativo", |
||||
|
"Assessor Parlamentar", |
||||
|
"Assessor de Comunicação", |
||||
|
"Assessora Jurídica", |
||||
|
"Assessora", |
||||
|
"Assessor", |
||||
|
"Auxiliar Administrativa", |
||||
|
"Chefe de Gabinete da Presidência", |
||||
|
"Chefe de Gabinete", |
||||
|
"Controlador Geral da Câmara", |
||||
|
"Controlador Interno", |
||||
|
"Controlador", |
||||
|
"Coord do Departamento Legislativo", |
||||
|
"Coordenador de Tecnologia da Informação", |
||||
|
"Coordenador do Departamento Legislativo da Câmara", |
||||
|
"Coordenadora", |
||||
|
"Coordenadora da Escola do Parlamento", |
||||
|
"DIRETOR GERAL", |
||||
|
"Deputado", |
||||
|
"Diretor Administrativo", |
||||
|
"Diretor", |
||||
|
"Diretor Geral", |
||||
|
"Diretor Hospital", |
||||
|
"Diretor Legislativo", |
||||
|
"Diretor Tesoureiro", |
||||
|
"Diretor de Compras e Licitações", |
||||
|
"Diretor de Matérias e Protocolo", |
||||
|
"Diretor de TI", |
||||
|
"Diretor de Tecnologia e Informação", |
||||
|
"Diretor do Departamento Legislativo", |
||||
|
"Diretor do Legislativo", |
||||
|
"Diretora Administrativa", |
||||
|
"Diretora", |
||||
|
"Diretora Legislativa", |
||||
|
"Diretora da Escola do Legislativo", |
||||
|
"Diretora geral da Câmara", |
||||
|
"Motorista", |
||||
|
"O Subprocurador Geral", |
||||
|
"Ouvidor", |
||||
|
"Ouvidor Geral", |
||||
|
"PRESIDENTE", |
||||
|
"Prefeito", |
||||
|
"Presidente", |
||||
|
"Presidente Vereador", |
||||
|
"Presidente Vereadora", |
||||
|
"Presidente da Escola do Legislativo", |
||||
|
"Presidente do InGEPE", |
||||
|
"Primeira Secretária", |
||||
|
"Procurador Geral", |
||||
|
"Procurador", |
||||
|
"Procuradora", |
||||
|
"Secretária", |
||||
|
"Secretária Saúde", |
||||
|
"Secretária da Casa Civil", |
||||
|
"Secretária-Geral", |
||||
|
"Secretário Administrativo", |
||||
|
"Secretário", |
||||
|
"Secretário de Administração", |
||||
|
"Secretário de Planejamento", |
||||
|
"Secretário de Saúde", |
||||
|
"Secretário-geral da Câmara", |
||||
|
"Servidor", |
||||
|
"Servidora", |
||||
|
"Tesoureiro", |
||||
|
"Técnica Administrativa", |
||||
|
"Técnico Legislativo da Secretaria Legislativa e Presidente da Comissão Sistêmica de Sustentabilidade Legislativa", |
||||
|
"Técnico Legislativo", |
||||
|
"VEREADOR", |
||||
|
"Verador", |
||||
|
"Vereadora", |
||||
|
"Vice Prefeita", |
||||
|
"Vice Prefeito", |
||||
|
"Vice Presidente", |
||||
|
"Vice-Prefeito", |
||||
|
"a Vereadora", |
||||
|
"o Assessor Jurídico", |
||||
|
"o Presidente", |
||||
|
"o Servidor", |
||||
|
"o Vereador", |
||||
|
] |
||||
|
) |
||||
|
patterns = [ |
||||
|
re.compile(p, re.IGNORECASE) |
||||
|
for p in [ |
||||
|
r"(?P<nome>.+?)[,?][ ?]inscrit[o|a] no CPF[ ?](?P<cpf>.+?)[,?][ ?](?P<local_trabalho>.+)", |
||||
|
r"(?P<nome>.+?)[ ]*-[ ]*(?P<local_trabalho>.+)", |
||||
|
r"(?P<local_trabalho>.+?)( *):( *)(?P<nome>.+)", |
||||
|
r"(?P<nome>.+?)( *);( *)[cpf?][ *](?P<cpf>.+)", |
||||
|
r"(?P<nome>.+?)( *),( *)(?P<local_trabalho>.+)", |
||||
|
r"(?P<nome>.+?) CPF (?P<cpf>.+)", |
||||
|
r"(?P<nome>.+?)(?P<cpf>[\d.]*[-]*\d+)", |
||||
|
r"(?P<nome>.+?)[ ]*\((?P<local_trabalho>.+)\)", |
||||
|
rf"(?P<local_trabalho>{cargos})[ ]*(?P<nome>.+)", |
||||
|
rf"(?P<nome>.+?)[ ]*(?P<local_trabalho>{cargos})", |
||||
|
] |
||||
|
] |
||||
|
|
||||
|
Evento = apps.get_model("eventos", "Evento") |
||||
|
eventos = ( |
||||
|
Evento.objects.filter(tipo_evento__categoria="V") |
||||
|
.exclude(convite=None) |
||||
|
.exclude(convite__nomes_participantes="") |
||||
|
).prefetch_related("convite_set") |
||||
|
|
||||
|
for evento in eventos: |
||||
|
evento.participante_set.all().delete() |
||||
|
for convite in evento.convite_set.all(): |
||||
|
participantes = convite.nomes_participantes.strip().splitlines() |
||||
|
for nome in participantes: |
||||
|
if nome.strip() == "": |
||||
|
continue |
||||
|
for pattern in patterns: |
||||
|
match = pattern.match(nome) |
||||
|
if match is not None: |
||||
|
break |
||||
|
if match is None: |
||||
|
dados = {"nome": nome} |
||||
|
else: |
||||
|
dados = { |
||||
|
k: v.strip() for k, v in match.groupdict().items() |
||||
|
} |
||||
|
if len(dados["nome"]) > 100: |
||||
|
dados["nome"] = dados["nome"][:100] |
||||
|
if "cpf" in dados and len(dados["cpf"]) > 30: |
||||
|
dados["cpf"] = dados["cpf"][:30] |
||||
|
evento.participante_set.create( |
||||
|
casa_legislativa_id=convite.casa_id, **dados |
||||
|
) |
||||
|
|
||||
|
|
||||
|
def backward(apps, schema_editor): |
||||
|
Evento = apps.get_model("eventos", "Evento") |
||||
|
Participante = apps.get_model("eventos", "Participante") |
||||
|
eventos = ( |
||||
|
Evento.objects.filter(tipo_evento__categoria="V") |
||||
|
.exclude(convite=None) |
||||
|
.exclude(convite__nomes_participantes="") |
||||
|
) |
||||
|
Participante.objects.filter(evento__in=eventos).delete() |
||||
|
|
||||
|
|
||||
|
class Migration(migrations.Migration): |
||||
|
|
||||
|
dependencies = [ |
||||
|
("eventos", "0064_alter_modelodeclaracao_texto_participante"), |
||||
|
] |
||||
|
|
||||
|
operations = [migrations.RunPython(forward, backward)] |
@ -0,0 +1,27 @@ |
|||||
|
# Generated by Django 5.2.1 on 2025-08-29 13:47 |
||||
|
|
||||
|
from django.db import migrations |
||||
|
|
||||
|
SQL_STMT = """ |
||||
|
create view viw_eventos_participante as |
||||
|
select ep.evento_id as id_evento, |
||||
|
ep.casa_legislativa_id as id_casa, |
||||
|
ep.cpf, |
||||
|
ep.nome, |
||||
|
ep.email, |
||||
|
ep.local_trabalho |
||||
|
from eventos_participante ep; |
||||
|
grant select on viw_eventos_participante to sigi_qs; |
||||
|
""" |
||||
|
SQL_REVERSE_STMT = "DROP VIEW viw_eventos_participante;" |
||||
|
|
||||
|
|
||||
|
class Migration(migrations.Migration): |
||||
|
|
||||
|
dependencies = [ |
||||
|
("eventos", "0065_participantes_visitas"), |
||||
|
] |
||||
|
|
||||
|
operations = [ |
||||
|
migrations.RunSQL(sql=SQL_STMT, reverse_sql=SQL_REVERSE_STMT) |
||||
|
] |
@ -0,0 +1,281 @@ |
|||||
|
import lxml |
||||
|
from difflib import SequenceMatcher |
||||
|
from moodle import Moodle |
||||
|
from django.db import models |
||||
|
from django.conf import settings |
||||
|
from django.utils.translation import gettext as _ |
||||
|
from sigi.apps.utils import to_ascii |
||||
|
from sigi.apps.contatos.models import UnidadeFederativa |
||||
|
from sigi.apps.casas.models import Orgao |
||||
|
|
||||
|
|
||||
|
CAR_ESP = {x: " " for x in range(33, 65) if x < 48 or x > 58} |
||||
|
CONECTIVOS = ["a", "e", "o", "da", "de", "do", "na", "no", "em"] |
||||
|
|
||||
|
|
||||
|
def canonize_full(s): |
||||
|
"""canoniza uma string removendo símbolos, artigos e outros conectivos |
||||
|
|
||||
|
Args: |
||||
|
s (str): A string a ser canonizada |
||||
|
|
||||
|
Returns: |
||||
|
(str, list): a string canonizada e a lista de palavras |
||||
|
""" |
||||
|
s = to_ascii(s.lower()).strip().translate(CAR_ESP) |
||||
|
palavras = [ |
||||
|
p.strip() |
||||
|
for p in s.split(" ") |
||||
|
if p.strip() != "" and p.strip() not in CONECTIVOS |
||||
|
] |
||||
|
s = " ".join(palavras) |
||||
|
return (s, palavras) |
||||
|
|
||||
|
|
||||
|
def canonize(s): |
||||
|
"""canoniza uma string retornando apenas a string canonizada |
||||
|
|
||||
|
Args: |
||||
|
s (str): A string a ser canonizada |
||||
|
|
||||
|
Returns: |
||||
|
str: a string canonizada |
||||
|
""" |
||||
|
return canonize_full(s)[0] |
||||
|
|
||||
|
|
||||
|
class SaberesSyncException(Exception): |
||||
|
@property |
||||
|
def message(self): |
||||
|
return str(self) |
||||
|
|
||||
|
|
||||
|
class EventoSaberes(Moodle): |
||||
|
_inscritos = None |
||||
|
_participantes = None |
||||
|
_aprovados = None |
||||
|
evento = None |
||||
|
_ufs = None |
||||
|
|
||||
|
def __init__(self, evento): |
||||
|
url = f"{settings.MOODLE_BASE_URL}/webservice/rest/server.php" |
||||
|
super().__init__(url, settings.MOODLE_API_TOKEN) |
||||
|
self.evento = evento |
||||
|
self._inscritos = None |
||||
|
self._participantes = None |
||||
|
self._aprovados = None |
||||
|
self._ufs = { |
||||
|
canonize(uf.nome): uf for uf in UnidadeFederativa.objects.all() |
||||
|
} |
||||
|
|
||||
|
def get_inscritos(self): |
||||
|
if self.evento.moodle_courseid is None: |
||||
|
raise SaberesSyncException( |
||||
|
_( |
||||
|
f"O evento {self.evento} não tem curso associado no Saberes" |
||||
|
), |
||||
|
) |
||||
|
|
||||
|
if self._inscritos is None: |
||||
|
try: |
||||
|
self._inscritos = self.post( |
||||
|
"core_enrol_get_enrolled_users", |
||||
|
courseid=self.evento.moodle_courseid, |
||||
|
) |
||||
|
except Exception as e: |
||||
|
raise SaberesSyncException( |
||||
|
_( |
||||
|
"Ocorreu um erro ao acessar o curso no Saberes com " |
||||
|
f"a mensagem {e.message}" |
||||
|
), |
||||
|
) |
||||
|
for i in self._inscritos: |
||||
|
if "customfields" in i: |
||||
|
i["dictcustomfields"] = { |
||||
|
f["shortname"]: canonize( |
||||
|
lxml.html.fromstring(f["value"]).text_content() |
||||
|
) |
||||
|
for f in i["customfields"] |
||||
|
} |
||||
|
uf_nome = ( |
||||
|
i["dictcustomfields"][settings.MOODLE_UF_CUSTOMFIELD] |
||||
|
if settings.MOODLE_UF_CUSTOMFIELD |
||||
|
in i["dictcustomfields"] |
||||
|
else None |
||||
|
) |
||||
|
i["uf"] = ( |
||||
|
self._ufs[uf_nome] if uf_nome in self._ufs else None |
||||
|
) |
||||
|
return self._inscritos |
||||
|
|
||||
|
def get_participantes(self): |
||||
|
if self._participantes is None: |
||||
|
self._participantes = list( |
||||
|
filter( |
||||
|
lambda u: any( |
||||
|
r["roleid"] in settings.MOODLE_STUDENT_ROLES |
||||
|
for r in u["roles"] |
||||
|
), |
||||
|
self.get_inscritos(), |
||||
|
) |
||||
|
) |
||||
|
return self._participantes |
||||
|
|
||||
|
def get_aprovados(self): |
||||
|
if self._aprovados is None: |
||||
|
for participante in self.get_participantes(): |
||||
|
try: |
||||
|
participante["completion_data"] = self.post( |
||||
|
"core_completion_get_course_completion_status", |
||||
|
courseid=self.evento.moodle_courseid, |
||||
|
userid=participante["id"], |
||||
|
) |
||||
|
except Exception: |
||||
|
participante["completed"] = False |
||||
|
participante["completion_data"] = None |
||||
|
continue |
||||
|
participante["completed"] = participante["completion_data"][ |
||||
|
"completionstatus" |
||||
|
]["completed"] or any( |
||||
|
filter( |
||||
|
lambda c: c["type"] |
||||
|
== settings.MOODLE_COMPLETE_CRITERIA_TYPE |
||||
|
and c["complete"], |
||||
|
participante["completion_data"]["completionstatus"][ |
||||
|
"completions" |
||||
|
], |
||||
|
) |
||||
|
) |
||||
|
self._aprovados = list( |
||||
|
filter(lambda p: p["completed"], self.get_participantes()) |
||||
|
) |
||||
|
return self._aprovados |
||||
|
|
||||
|
def identifica_orgaos(self): |
||||
|
obj_list = ( |
||||
|
Orgao.objects.all() |
||||
|
.order_by() |
||||
|
.annotate(uf_sigla=models.F("municipio__uf__sigla")) |
||||
|
) |
||||
|
assembleias = obj_list.filter(tipo__sigla="AL") |
||||
|
orgaos = [(o, canonize(f"{o.nome} {o.uf_sigla}")) for o in obj_list] |
||||
|
siglados = {canonize(o.sigla): o for o in obj_list if o.sigla != ""} |
||||
|
siglados.update({canonize(f"ALE{o.uf_sigla}"): o for o in assembleias}) |
||||
|
siglados.update({canonize(f"AL{o.uf_sigla}"): o for o in assembleias}) |
||||
|
kcm = ["camara", "municipal", "vereadores"] |
||||
|
kal = ["assembleia", "legislativa", "estado"] |
||||
|
ufs = self._ufs |
||||
|
try: |
||||
|
senado = Orgao.objects.get(nome__iexact="senado federal") |
||||
|
except Exception: |
||||
|
senado = None |
||||
|
|
||||
|
def get_names(name, uf, municipio): |
||||
|
municipio = canonize(municipio) |
||||
|
uf_sigla = canonize(uf.sigla) if uf else None |
||||
|
name, palavras = canonize_full(name) |
||||
|
names = [name] |
||||
|
# Acrescenta uma versão com a sigla do estado se já não tiver # |
||||
|
if uf_sigla and uf_sigla not in palavras: |
||||
|
names.insert(0, f"{name} {uf_sigla}") # Coloca como primeiro |
||||
|
# Corrige grafia das palavras-chave para Câmara |
||||
|
matches = { |
||||
|
s: [ |
||||
|
p |
||||
|
for p in palavras |
||||
|
if SequenceMatcher(a=s, b=p).ratio() > 0.8 |
||||
|
] |
||||
|
for s in kcm |
||||
|
} |
||||
|
for kw in matches: |
||||
|
for s in matches[kw]: |
||||
|
name = name.replace(s, kw) |
||||
|
# Elimina o termo vereadores |
||||
|
if "vereadores" in name: |
||||
|
if "municipal" in name: |
||||
|
name = name.replace("vereadores", "") # Só elimina |
||||
|
else: |
||||
|
name = name.replace( |
||||
|
"vereadores", "municipal" |
||||
|
) # troca por municipal |
||||
|
names.append(canonize(name)) |
||||
|
if "camara" in name: |
||||
|
if "municipal" not in name: |
||||
|
name = name.replace("camara", "camara municipal") |
||||
|
names.append(canonize(name)) |
||||
|
# Cria versão canonica com o nome do municipio e a UF |
||||
|
if uf_sigla: |
||||
|
names.append( |
||||
|
canonize(f"camara municipal {municipio} {uf_sigla}") |
||||
|
) |
||||
|
# Corrige grafia das palavras-chave para Assembleia |
||||
|
matches = { |
||||
|
s: [ |
||||
|
p |
||||
|
for p in palavras |
||||
|
if SequenceMatcher(a=s, b=p).ratio() > 0.8 |
||||
|
] |
||||
|
for s in kal |
||||
|
} |
||||
|
for kw in matches: |
||||
|
for s in matches[kw]: |
||||
|
name = name.replace(s, kw) |
||||
|
if "assembleia" in name: |
||||
|
name = name.replace("estado", "") # Elimina o termo "estado" |
||||
|
# Adiciona "legislativa" se necessário |
||||
|
if "legislativa" not in name: |
||||
|
name = name.replace("assembleia", "assembleia legislativa") |
||||
|
names.append(canonize(name)) |
||||
|
# Cria versão canonica com o nome e sigla da UF |
||||
|
if uf_sigla: |
||||
|
names.append( |
||||
|
canonize(f"assembleia legislativa {uf} {uf_sigla}") |
||||
|
) |
||||
|
# remove duplicados sem mudar a ordem |
||||
|
names = list(dict.fromkeys(names)) |
||||
|
return names |
||||
|
|
||||
|
for p in self.get_participantes(): |
||||
|
if ( |
||||
|
"dictcustomfields" in p |
||||
|
and settings.MOODLE_ORGAO_CUSTOMFIELD in p["dictcustomfields"] |
||||
|
and p["dictcustomfields"][ |
||||
|
settings.MOODLE_ORGAO_CUSTOMFIELD |
||||
|
].strip() |
||||
|
!= "" |
||||
|
): |
||||
|
nome_orgao = p["dictcustomfields"][ |
||||
|
settings.MOODLE_ORGAO_CUSTOMFIELD |
||||
|
] |
||||
|
municipio = ( |
||||
|
p["dictcustomfields"][ |
||||
|
settings.MOODLE_MUNICIPIO_CUSTOMFIELD |
||||
|
] |
||||
|
if settings.MOODLE_MUNICIPIO_CUSTOMFIELD |
||||
|
in p["dictcustomfields"] |
||||
|
else p["city"] if "city" in p else "" |
||||
|
) |
||||
|
nomes_possiveis = get_names(nome_orgao, p["uf"], municipio) |
||||
|
for nome in nomes_possiveis: |
||||
|
semelhantes = Orgao.get_semelhantes(nome, orgaos) |
||||
|
if len(semelhantes) > 0: |
||||
|
p["orgao"] = semelhantes[-1][0] |
||||
|
break |
||||
|
if "orgao" not in p: |
||||
|
# Buscar por sigla |
||||
|
nome, palavras = canonize_full(nome_orgao) |
||||
|
for nome in palavras: |
||||
|
if nome in siglados: |
||||
|
p["orgao"] = siglados[nome] |
||||
|
break |
||||
|
# Pode ser servidor do Senado - última chance ;D |
||||
|
if ( |
||||
|
"orgao" not in p |
||||
|
and senado is not None |
||||
|
and settings.MOODLE_SERVSENADO_CUSTOMFIELD |
||||
|
in p["dictcustomfields"] |
||||
|
and not p["dictcustomfields"][ |
||||
|
settings.MOODLE_SERVSENADO_CUSTOMFIELD |
||||
|
].startswith("nao ") |
||||
|
): |
||||
|
p["orgao"] = senado |
@ -0,0 +1,51 @@ |
|||||
|
{% extends "admin/change_form_object_tools.html" %} |
||||
|
{% load i18n admin_urls djbs_extras %} |
||||
|
|
||||
|
{% block object-tools-items %} |
||||
|
{{ block.super }} |
||||
|
|
||||
|
{% if original.equipe_set.exists %} |
||||
|
{% url opts|admin_urlname:'custos' original.pk|admin_urlquote as custos_url %} |
||||
|
<a class="addlink nav-link custos" href="{% add_preserved_filters custos_url %}" aria-labelledby="text-tool-custos" title="{% translate 'Relatório de custos' %}"> |
||||
|
{% icon "money" %} <span id="text-tool-custos" class="d-lg-none">{% translate "Relatório de custos" %}</span> |
||||
|
</a> |
||||
|
{% endif %} |
||||
|
|
||||
|
{% if original.cronograma_set.exists %} |
||||
|
{% url opts|admin_urlname:'gantreport' original.pk|admin_urlquote as gant_url %} |
||||
|
{% url opts|admin_urlname:'checklistreport' original.pk|admin_urlquote as checklist_url %} |
||||
|
{% url opts|admin_urlname:'comunicacaoreport' original.pk|admin_urlquote as comunicacao_url %} |
||||
|
<a class="addlink nav-link gant" href="{% add_preserved_filters gant_url %}" aria-labelledby="text-tool-gant" title="{% translate 'Gráfico de gant' %}"> |
||||
|
{% icon "chart" %} <span id="text-tool-gant" class="d-lg-none">{% translate "Gráfico de gant" %}</span> |
||||
|
</a> |
||||
|
<a class="addlink nav-link checklist" href="{% add_preserved_filters checklist_url %}" aria-labelledby="text-tool-checklist" title="{% translate 'Checklist' %}"> |
||||
|
{% icon "checklist" %} <span id="text-tool-checklist" class="d-lg-none">{% translate "Checklist" %}</span> |
||||
|
</a> |
||||
|
<a class="addlink nav-link comunicacao" href="{% add_preserved_filters comunicacao_url %}" aria-labelledby="text-tool-comunicacao" title="{% translate 'Plano de comunicação' %}"> |
||||
|
{% icon "speak" %} <span id="text-tool-comunicacao" class="d-lg-none">{% translate "Plano de comunicação" %}</span> |
||||
|
</a> |
||||
|
{% endif %} |
||||
|
|
||||
|
{% if original.moodle_courseid is None %} |
||||
|
{% if original.tipo_evento.moodle_template_courseid is not None and evento.tipo_evento.moodle_categoryid is not None %} |
||||
|
{% url opts|admin_urlname:'createcourse' original.pk|admin_urlquote as createcourse_url %} |
||||
|
<a class="addlink nav-link createcourse" href="{% add_preserved_filters createcourse_url %}" aria-labelledby="text-tool-createcourse" title="{% translate 'Criar curso no Saberes' %}"> |
||||
|
{% icon "create" %} <span id="text-tool-createcourse" class="d-lg-none">{% translate "Criar curso no Saberes" %}</span> |
||||
|
</a> |
||||
|
{% endif %} |
||||
|
{% endif %} |
||||
|
|
||||
|
{% if original.moodle_courseid is not None %} |
||||
|
{% url opts|admin_urlname:'updateparticipantes' original.pk|admin_urlquote as updateparticipantes_url %} |
||||
|
<a class="addlink nav-link updateparticipantes" href="{% add_preserved_filters updateparticipantes_url %}" aria-labelledby="text-tool-updateparticipantes" title="{% translate 'Atualizar lista de participantes (Saberes)' %}"> |
||||
|
{% icon "refresh" %} <span id="text-tool-updateparticipantes" class="d-lg-none">{% translate "Atualizar lista de participantes (Saberes)" %}</span> |
||||
|
</a> |
||||
|
{% endif %} |
||||
|
|
||||
|
{% if original.tipo_evento.categoria == "V" %} |
||||
|
{% url opts|admin_urlname:'declaracaoreport' original.pk|admin_urlquote as declaracao_url %} |
||||
|
<a class="addlink nav-link declaracao" href="{% add_preserved_filters declaracao_url %}" aria-labelledby="text-tool-declaracao" title="{% translate 'Declaração' %}"> |
||||
|
{% icon "pdf" %} <span id="text-tool-declaracao" class="d-lg-none">{% translate "Declaração" %}</span> |
||||
|
</a> |
||||
|
{% endif %} |
||||
|
{% endblock %} |
@ -0,0 +1,26 @@ |
|||||
|
{% extends "admin/change_form.html" %} |
||||
|
{% load i18n static admin_urls djbs_extras %} |
||||
|
|
||||
|
{% block content %} |
||||
|
<div id="content-main" class="container"> |
||||
|
<div class="card"> |
||||
|
<div class="card-body"> |
||||
|
<form id="select-form" name="select-form" action="" method="post" novalidate> |
||||
|
{% csrf_token %} |
||||
|
{{ form }} |
||||
|
</form> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="submit-row btn-toolbar hstack justify-content-end mx-2 my-5 gap-2"> |
||||
|
<button class="default btn btn-outline-success" type="submit" form="select-form" name="submit" value="print"> |
||||
|
{% icon "print" %} {% trans "Imprimir" %} |
||||
|
</button> |
||||
|
{% url opts|admin_urlname:'change' evento_id|admin_urlquote as change_url %} |
||||
|
<a href="{% add_preserved_filters change_url %}" class="closelink btn btn-outline-success" title="{% translate 'Close' %}"> |
||||
|
{% icon "dismiss" %} {% translate 'Close' %} |
||||
|
</a> |
||||
|
</div> |
||||
|
</div> |
||||
|
{% endblock %} |
||||
|
|
@ -0,0 +1,20 @@ |
|||||
|
{% load i18n %} |
||||
|
<table> |
||||
|
<tr> |
||||
|
<th>{% trans "CPF" %}</th> |
||||
|
<th>{% trans "Nome" %}</th> |
||||
|
<th>{% trans "Cargo / função / setor" %}</th> |
||||
|
</tr> |
||||
|
{% for membro in evento.participante_set.all %} |
||||
|
{% ifchanged membro.casa_legislativa %} |
||||
|
<tr> |
||||
|
<td colspan="3">{{ membro.casa_legislativa.nome }}</td> |
||||
|
</tr> |
||||
|
{% endifchanged %} |
||||
|
<tr> |
||||
|
<td>{{ membro.cpf }}</td> |
||||
|
<td>{{ membro.nome }}</td> |
||||
|
<td>{{ membro.local_trabalho }}</td> |
||||
|
</tr> |
||||
|
{% endfor %} |
||||
|
</table> |
@ -1,49 +0,0 @@ |
|||||
{% extends "admin/base_site.html" %} |
|
||||
{% load i18n static admin_urls %} |
|
||||
|
|
||||
{% block extrastyle %} |
|
||||
{{ block.super }} |
|
||||
<link rel="stylesheet" href="{% static 'material/admin/css/submit_line.min.css' %}"> |
|
||||
{% endblock %} |
|
||||
|
|
||||
{% block breadcrumbs %}{% endblock %} |
|
||||
|
|
||||
{% block messages %} |
|
||||
{% if error %} |
|
||||
<ul class="messagelist"> |
|
||||
<li class="error">{{ error|capfirst }}</li> |
|
||||
</ul> |
|
||||
{% endif %} |
|
||||
{% endblock messages %} |
|
||||
|
|
||||
{% block content %} |
|
||||
<div class="container"> |
|
||||
<div class="card"> |
|
||||
<div class="card-content"> |
|
||||
<span class="card-title">{% trans 'Emitir declaração de comparecimento' %}</span> |
|
||||
<form id="select-form" name="select-form" action="" method="post" novalidate> |
|
||||
{% csrf_token %} |
|
||||
<div class="form-group"> |
|
||||
{{ form }} |
|
||||
</div> |
|
||||
</form> |
|
||||
</div> |
|
||||
<div class="card-action"> |
|
||||
<div class="submit-row"> |
|
||||
<div class="open-actions"> |
|
||||
<button class="default waves-effect waves-light btn" type="submit" form="select-form" name="submit" value="print"> |
|
||||
<i class="material-icons">picture_as_pdf</i> |
|
||||
{% trans "Imprimir" %} |
|
||||
</button> |
|
||||
{% url opts|admin_urlname:'change' evento_id|admin_urlquote as change_url %} |
|
||||
<a class="default waves-effect waves-light btn" role="button" href="{% add_preserved_filters change_url %}"> |
|
||||
<i class="material-icons">undo</i> |
|
||||
{% trans "Voltar" %} |
|
||||
</a> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
{% endblock %} |
|
||||
|
|
Loading…
Reference in new issue