Sistema de Apoio ao Processo Legislativo
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

748 lines
24 KiB

from django.db import models
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from image_cropping.fields import ImageCropField, ImageRatioField
from model_utils import Choices
from sapl.base.models import Autor
from sapl.decorators import vigencia_atual
from sapl.utils import (LISTA_DE_UFS, YES_NO_CHOICES, SaplGenericRelation,
get_settings_auth_user_model,
intervalos_tem_intersecao,
restringe_tipos_de_arquivo_img, texto_upload_path)
class Legislatura(models.Model):
numero = models.PositiveIntegerField(verbose_name=_("Número"))
data_inicio = models.DateField(verbose_name=_("Data Início"))
data_fim = models.DateField(verbose_name=_("Data Fim"))
data_eleicao = models.DateField(verbose_name=_("Data Eleição"))
observacao = models.TextField(blank=True, verbose_name=_("Observação"))
class Meta:
ordering = ["-data_inicio"]
verbose_name = _("Legislatura")
verbose_name_plural = _("Legislaturas")
ordering = ("-numero", "-data_inicio", "-data_fim")
def atual(self):
current_year = timezone.now().year
if not self.data_fim:
self.data_fim = timezone.now().date()
return self.data_inicio.year <= current_year <= self.data_fim.year
@vigencia_atual
def __str__(self):
if not self.data_fim:
self.data_fim = timezone.now().date()
return _("%(numero)sª (%(start)s - %(end)s)") % {
"numero": self.numero,
"start": self.data_inicio.year,
"end": self.data_fim.year,
}
class SessaoLegislativa(models.Model):
TIPO_SESSAO_CHOICES = Choices(
("O", "ordinaria", _("Ordinária")),
("E", "extraordinaria", _("Extraordinária")),
)
legislatura = models.ForeignKey(
Legislatura,
on_delete=models.PROTECT,
verbose_name=Legislatura._meta.verbose_name,
)
numero = models.PositiveIntegerField(verbose_name=_("Número"))
tipo = models.CharField(
max_length=1, verbose_name=_("Tipo"), choices=TIPO_SESSAO_CHOICES
)
data_inicio = models.DateField(verbose_name=_("Data Início"))
data_fim = models.DateField(verbose_name=_("Data Fim"))
data_inicio_intervalo = models.DateField(
blank=True, null=True, verbose_name=_("Início Intervalo")
)
data_fim_intervalo = models.DateField(
blank=True, null=True, verbose_name=_("Fim Intervalo")
)
class Meta:
verbose_name = _("Sessão Legislativa")
verbose_name_plural = _("Sessões Legislativas")
ordering = ["-data_inicio", "-data_fim"]
@vigencia_atual
def __str__(self):
return _("%(numero)sº (%(inicio)s - %(fim)s)") % {
"numero": self.numero,
"inicio": self.data_inicio.year,
"fim": self.data_fim.year,
}
class Coligacao(models.Model):
legislatura = models.ForeignKey(
Legislatura, on_delete=models.PROTECT, verbose_name=_("Legislatura")
)
nome = models.CharField(max_length=50, verbose_name=_("Nome"))
numero_votos = models.PositiveIntegerField(
blank=True, null=True, verbose_name=_("Nº Votos Recebidos (Coligação)")
)
class Meta:
verbose_name = _("Coligação")
verbose_name_plural = _("Coligações")
ordering = ("legislatura", "nome")
def __str__(self):
return self.nome
def get_logo_media_path(instance, subpath, filename):
return "./sapl/partidos/%s/%s/%s" % (instance, subpath, filename)
def logo_upload_path(instance, filename):
return get_logo_media_path(instance, "logo", filename)
class Partido(models.Model):
sigla = models.CharField(max_length=20, verbose_name=_("Sigla"))
nome = models.CharField(max_length=50, verbose_name=_("Nome"))
data_criacao = models.DateField(
blank=True, null=True, verbose_name=_("Data Criação")
)
data_extincao = models.DateField(
blank=True, null=True, verbose_name=_("Data Extinção")
)
logo_partido = models.ImageField(
blank=True,
null=True,
upload_to=logo_upload_path,
verbose_name=_("Logo Partido"),
validators=[restringe_tipos_de_arquivo_img],
)
observacao = models.TextField(blank=True, verbose_name=_("Observação"))
class Meta:
verbose_name = _("Partido")
verbose_name_plural = _("Partidos")
ordering = ["sigla", "nome"]
def __str__(self):
return _("%(sigla)s - %(nome)s") % {"sigla": self.sigla, "nome": self.nome}
class ComposicaoColigacao(models.Model):
# TODO M2M
partido = models.ForeignKey(
Partido, on_delete=models.PROTECT, verbose_name=_("Partidos da Coligação")
)
coligacao = models.ForeignKey(Coligacao, on_delete=models.PROTECT)
class Meta:
verbose_name = _("Composição Coligação")
verbose_name_plural = _("Composição Coligações")
ordering = ("partido",)
def __str__(self):
return _("%(partido)s - %(coligacao)s") % {
"partido": self.partido,
"coligacao": self.coligacao,
}
class NivelInstrucao(models.Model):
descricao = models.CharField(max_length=50, verbose_name=_("Nível de Instrução"))
class Meta:
ordering = ["descricao"]
verbose_name = _("Nível Instrução")
verbose_name_plural = _("Níveis Instrução")
def __str__(self):
return self.descricao
class SituacaoMilitar(models.Model):
descricao = models.CharField(max_length=50, verbose_name=_("Situação Militar"))
class Meta:
verbose_name = _("Tipo Situação Militar")
verbose_name_plural = _("Tipos Situações Militares")
ordering = ["descricao"]
def __str__(self):
return self.descricao
def foto_upload_path(instance, filename):
return texto_upload_path(instance, filename, subpath="")
def true_false_none(x):
if x == "True":
return True
elif x == "False":
return False
else:
return None
class Parlamentar(models.Model):
FEMININO = "F"
MASCULINO = "M"
SEXO_CHOICE = ((FEMININO, _("Feminino")), (MASCULINO, _("Masculino")))
nivel_instrucao = models.ForeignKey(
NivelInstrucao,
blank=True,
null=True,
on_delete=models.PROTECT,
verbose_name=_("Nível Instrução"),
)
situacao_militar = models.ForeignKey(
SituacaoMilitar,
blank=True,
null=True,
on_delete=models.PROTECT,
verbose_name=_("Situação Militar"),
)
nome_completo = models.CharField(max_length=50, verbose_name=_("Nome Completo"))
nome_parlamentar = models.CharField(
max_length=50, verbose_name=_("Nome Parlamentar")
)
sexo = models.CharField(max_length=1, verbose_name=_("Sexo"), choices=SEXO_CHOICE)
data_nascimento = models.DateField(
blank=True, null=True, verbose_name=_("Data Nascimento")
)
cpf = models.CharField(max_length=14, blank=True, verbose_name=_("C.P.F"))
rg = models.CharField(max_length=20, blank=True, verbose_name=_("R.G."))
titulo_eleitor = models.CharField(
max_length=25, blank=True, verbose_name=_("Título de Eleitor")
)
numero_gab_parlamentar = models.CharField(
max_length=10, blank=True, verbose_name=_("Nº Gabinete")
)
telefone = models.CharField(max_length=50, blank=True, verbose_name=_("Telefone"))
telefone_celular = models.CharField(
max_length=50, blank=True, verbose_name=_("Telefone Celular")
)
fax = models.CharField(max_length=50, blank=True, verbose_name=_("Fax"))
endereco_residencia = models.CharField(
max_length=100, blank=True, verbose_name=_("Endereço Residencial")
)
municipio_residencia = models.CharField(
max_length=50, blank=True, verbose_name=_("Município")
)
uf_residencia = models.CharField(
max_length=2, blank=True, choices=LISTA_DE_UFS, verbose_name=_("UF")
)
cep_residencia = models.CharField(max_length=9, blank=True, verbose_name=_("CEP"))
telefone_residencia = models.CharField(
max_length=50, blank=True, verbose_name=_("Telefone Residencial")
)
fax_residencia = models.CharField(
max_length=50, blank=True, verbose_name=_("Fax Residencial")
)
endereco_web = models.URLField(
max_length=100, blank=True, verbose_name=_("HomePage")
)
profissao = models.CharField(max_length=50, blank=True, verbose_name=_("Profissão"))
email = models.EmailField(max_length=100, blank=True, verbose_name=_("E-mail"))
locais_atuacao = models.CharField(
max_length=100, blank=True, verbose_name=_("Locais de Atuação")
)
ativo = models.BooleanField(
db_index=True,
default=False,
choices=YES_NO_CHOICES,
verbose_name=_("Ativo na Casa?"),
)
biografia = models.TextField(blank=True, verbose_name=_("Biografia"))
fotografia = ImageCropField(
verbose_name=_("Fotografia"),
upload_to=foto_upload_path,
validators=[restringe_tipos_de_arquivo_img],
null=True,
blank=True,
)
cropping = ImageRatioField(
"fotografia",
"128x128",
verbose_name=_("Avatar"),
size_warning=True,
help_text=_(
"A configuração do Avatar " "é possível após a atualização da fotografia."
),
)
# campo conceitual de reversão genérica para o model Autor que dá a
# o meio possível de localização de tipos de autores.
autor = SaplGenericRelation(
Autor,
related_query_name="parlamentar_set",
fields_search=(
# na primeira posição dever ser campo simples sem __
("nome_completo", "__icontains"),
("nome_parlamentar", "__icontains"),
("filiacao__partido__sigla", "__icontains"),
),
)
class Meta:
verbose_name = _("Parlamentar")
verbose_name_plural = _("Parlamentares")
ordering = ["nome_parlamentar"]
def __str__(self):
return self.nome_parlamentar
@property
def filiacao_atual(self):
ultima_filiacao = self.filiacao_set.order_by("-data").first()
if ultima_filiacao and not ultima_filiacao.data_desfiliacao:
return ultima_filiacao.partido.sigla
else:
return _("Sem Partido")
@property
def avatar_html(self):
return (
'<img class="avatar-parlamentar" src=' + self.fotografia.url + ">"
if self.fotografia
else ""
)
def delete(self, using=None, keep_parents=False):
if self.fotografia:
self.fotografia.delete()
return models.Model.delete(self, using=using, keep_parents=keep_parents)
def save(
self, force_insert=False, force_update=False, using=None, update_fields=None
):
if not self.pk and self.fotografia:
fotografia = self.fotografia
self.fotografia = None
models.Model.save(
self,
force_insert=force_insert,
force_update=force_update,
using=using,
update_fields=update_fields,
)
self.fotografia = fotografia
return models.Model.save(
self,
force_insert=force_insert,
force_update=force_update,
using=using,
update_fields=update_fields,
)
class TipoDependente(models.Model):
descricao = models.CharField(max_length=150, verbose_name=_("Descrição"))
class Meta:
verbose_name = _("Tipo de Dependente")
verbose_name_plural = _("Tipos de Dependente")
ordering = ["descricao"]
def __str__(self):
return self.descricao
class Dependente(models.Model):
FEMININO = "F"
MASCULINO = "M"
SEXO_CHOICE = ((FEMININO, _("Feminino")), (MASCULINO, _("Masculino")))
tipo = models.ForeignKey(
TipoDependente, on_delete=models.PROTECT, verbose_name=_("Tipo")
)
parlamentar = models.ForeignKey(Parlamentar, on_delete=models.CASCADE)
nome = models.CharField(max_length=150, verbose_name=_("Nome"))
sexo = models.CharField(max_length=1, verbose_name=_("Sexo"), choices=SEXO_CHOICE)
data_nascimento = models.DateField(
blank=True, null=True, verbose_name=_("Data Nascimento")
)
cpf = models.CharField(max_length=14, blank=True, verbose_name=_("CPF"))
rg = models.CharField(max_length=15, blank=True, verbose_name=_("RG"))
titulo_eleitor = models.CharField(
max_length=15, blank=True, verbose_name=_("Nº Título Eleitor")
)
class Meta:
verbose_name = _("Dependente")
verbose_name_plural = _("Dependentes")
ordering = ("parlamentar", "nome")
def __str__(self):
return self.nome
class Filiacao(models.Model):
data = models.DateField(verbose_name=_("Data Filiação"))
parlamentar = models.ForeignKey(Parlamentar, on_delete=models.CASCADE)
partido = models.ForeignKey(
Partido, on_delete=models.PROTECT, verbose_name=_("Partido")
)
data_desfiliacao = models.DateField(
blank=True, null=True, verbose_name=_("Data Desfiliação")
)
class Meta:
verbose_name = _("Filiação")
verbose_name_plural = _("Filiações")
# A ordenação descrescente por data é importante para listagem de
# parlamentares e tela de Filiações do Parlamentar
ordering = ("parlamentar", "-data", "-data_desfiliacao")
def __str__(self):
return _("%(parlamentar)s - %(partido)s") % {
"parlamentar": self.parlamentar,
"partido": self.partido,
}
class TipoAfastamento(models.Model):
descricao = models.CharField(max_length=50, verbose_name=_("Descrição"))
indicador = models.CharField(
max_length=1,
verbose_name=_("Indicador"),
default="F",
choices=[
("A", _("Afastamento")),
("F", _("Fim de Mandato")),
],
)
dispositivo = models.CharField(
max_length=50, blank=True, verbose_name=_("Dispositivo")
)
class Meta:
verbose_name = _("Tipo de Afastamento")
verbose_name_plural = _("Tipos de Afastamento")
ordering = ["descricao"]
def __str__(self):
return self.descricao
class Mandato(models.Model):
parlamentar = models.ForeignKey(Parlamentar, on_delete=models.CASCADE)
tipo_afastamento = models.ForeignKey(
TipoAfastamento, blank=True, null=True, on_delete=models.PROTECT
)
legislatura = models.ForeignKey(
Legislatura, on_delete=models.PROTECT, verbose_name=_("Legislatura")
)
coligacao = models.ForeignKey(
Coligacao,
blank=True,
null=True,
on_delete=models.PROTECT,
verbose_name=_("Coligação"),
)
# TODO what is this field??????
tipo_causa_fim_mandato = models.PositiveIntegerField(blank=True, null=True)
data_inicio_mandato = models.DateField(
verbose_name=_("Início do Mandato"), blank=False, null=True
)
data_fim_mandato = models.DateField(
verbose_name=_("Fim do Mandato"), blank=True, null=True
)
votos_recebidos = models.PositiveIntegerField(
blank=True, null=True, verbose_name=_("Votos Recebidos (Mandato)")
)
data_expedicao_diploma = models.DateField(
blank=True, null=True, verbose_name=_("Expedição do Diploma")
)
titular = models.BooleanField(
db_index=True,
default=True,
choices=YES_NO_CHOICES,
verbose_name=_("Parlamentar Titular"),
)
observacao = models.TextField(blank=True, verbose_name=_("Observação"))
class Meta:
verbose_name = _("Mandato")
verbose_name_plural = _("Mandatos")
ordering = ("parlamentar", "legislatura__numero")
def __str__(self):
return _("%(parlamentar)s %(legislatura)s") % {
"parlamentar": self.parlamentar,
"legislatura": self.legislatura,
}
def get_partidos(self):
filicacoes = Filiacao.objects.filter(parlamentar=self.parlamentar).order_by(
"data"
)
return [
f.partido.sigla
for f in filicacoes
if intervalos_tem_intersecao(
self.legislatura.data_inicio,
self.legislatura.data_fim,
f.data,
f.data_desfiliacao or timezone.datetime.max.date(),
)
]
class CargoMesa(models.Model):
# TODO M2M ????
descricao = models.CharField(max_length=50, verbose_name=_("Cargo na Mesa"))
unico = models.BooleanField(
choices=YES_NO_CHOICES, verbose_name=_("Cargo Único"), default=True
)
id_ordenacao = models.PositiveIntegerField(
blank=True,
null=True,
verbose_name=_("Posição na Ordenação"),
)
class Meta:
verbose_name = _("Cargo na Mesa")
verbose_name_plural = _("Cargos na Mesa")
ordering = ["id_ordenacao", "unico", "descricao"]
def __str__(self):
return self.descricao
class MesaDiretora(models.Model):
data_inicio = models.DateField(verbose_name=_("Data Início"), null=True)
data_fim = models.DateField(verbose_name=_("Data Fim"), null=True)
sessao_legislativa = models.ForeignKey(SessaoLegislativa, on_delete=models.PROTECT)
descricao = models.TextField(verbose_name=_("Descrição"), blank=True)
class Meta:
verbose_name = _("Mesa Diretora")
verbose_name_plural = _("Mesas Diretoras")
ordering = ("-data_inicio", "-sessao_legislativa")
def __str__(self):
return _("Mesa da %(sessao)s sessao da %(legislatura)s Legislatura") % {
"sessao": self.sessao_legislativa,
"legislatura": self.sessao_legislativa.legislatura,
}
class ComposicaoMesa(models.Model):
# TODO M2M ???? Ternary?????
parlamentar = models.ForeignKey(Parlamentar, on_delete=models.PROTECT)
cargo = models.ForeignKey(CargoMesa, on_delete=models.PROTECT)
mesa_diretora = models.ForeignKey(MesaDiretora, on_delete=models.PROTECT, null=True)
class Meta:
verbose_name = _("Ocupação de cargo na Mesa")
verbose_name_plural = _("Ocupações de cargo na Mesa")
ordering = ("cargo", "parlamentar")
def __str__(self):
return _("%(parlamentar)s - %(cargo)s") % {
"parlamentar": self.parlamentar,
"cargo": self.cargo,
}
class Frente(models.Model):
"""
* Uma frente agrupa vários parlamentares
* Cada parlamentar pode fazer parte de uma ou mais frentes
* Uma frente pode existir por mais de uma legislatura?
"""
nome = models.CharField(max_length=80, verbose_name=_("Nome da Frente"))
descricao = models.TextField(blank=True, verbose_name=_("Descrição"))
data_criacao = models.DateField(verbose_name=_("Data Criação"))
data_extincao = models.DateField(
blank=True, null=True, verbose_name=_("Data Dissolução")
)
# campo conceitual de reversão genérica para o model Autor que dá a
# o meio possível de localização de tipos de autores.
autor = SaplGenericRelation(
Autor,
related_query_name="frente_set",
fields_search=(
("nome", "__icontains"),
("descricao", "__icontains"),
("frenteparlamentar__parlamentar__filiacao__partido__sigla", "__icontains"),
("frenteparlamentar__parlamentar__filiacao__partido__nome", "__icontains"),
),
)
class Meta:
verbose_name = _("Frente Parlamentar")
verbose_name_plural = _("Frentes Parlamentares")
ordering = ("nome", "-data_criacao")
def get_parlamentares(self):
return Parlamentar.objects.filter(ativo=True)
def __str__(self):
return self.nome
class FrenteCargo(models.Model):
nome_cargo = models.CharField(
max_length=80, verbose_name=_("Cargo de frente parlamentar")
)
cargo_unico = models.BooleanField(
default=False, choices=YES_NO_CHOICES, verbose_name=_("Cargo único?")
)
class Meta:
verbose_name = _("Cargo de Frente Parlamentar")
verbose_name_plural = _("Cargos de Frente Parlamentar")
ordering = (
"cargo_unico",
"nome_cargo",
)
def __str__(self):
return f"{self.nome_cargo}"
class FrenteParlamentar(models.Model):
frente = models.ForeignKey(
Frente, verbose_name=_("Frente parlamentar"), on_delete=models.CASCADE
)
parlamentar = models.ForeignKey(
Parlamentar, verbose_name=_("Parlamentar"), on_delete=models.CASCADE
)
cargo = models.ForeignKey(
FrenteCargo,
verbose_name=_("Cargo na frente parlamentar"),
on_delete=models.PROTECT,
)
data_entrada = models.DateField(verbose_name=_("Data Entrada"))
data_saida = models.DateField(blank=True, null=True, verbose_name=_("Data Saída"))
class Meta:
verbose_name = _("Parlamentar de frente parlamentar")
verbose_name_plural = _("Parlamentares de frente parlamentar")
ordering = ("frente", "parlamentar", "cargo")
def __str__(self):
return f"{self.parlamentar} - {self.cargo.nome_cargo} - {self.frente}"
class Votante(models.Model):
parlamentar = models.ForeignKey(
Parlamentar,
verbose_name=_("Parlamentar"),
on_delete=models.PROTECT,
related_name="votante_set",
)
user = models.ForeignKey(
get_settings_auth_user_model(),
on_delete=models.PROTECT,
verbose_name=_("User"),
related_name="votante_set",
)
data = models.DateTimeField(
verbose_name=_("Data"), auto_now_add=True, max_length=30, null=True, blank=True
)
class Meta:
verbose_name = _("Usuário Votante")
verbose_name_plural = _("Usuários Votantes")
permissions = (("can_vote", _("Can Vote")),)
ordering = ("parlamentar", "-data")
def __str__(self):
return self.user.username
class Bloco(models.Model):
"""
* blocos podem existir por mais de uma legislatura
"""
nome = models.CharField(max_length=120, verbose_name=_("Nome do Bloco"))
partidos = models.ManyToManyField(Partido, blank=True, verbose_name=_("Partidos"))
data_criacao = models.DateField(
blank=False, null=True, verbose_name=_("Data Criação")
)
data_extincao = models.DateField(
blank=True, null=True, verbose_name=_("Data Dissolução")
)
descricao = models.TextField(blank=True, verbose_name=_("Descrição"))
# campo conceitual de reversão genérica para o model Autor que dá a
# o meio possível de localização de tipos de autores.
autor = SaplGenericRelation(
Autor,
related_query_name="bloco_set",
fields_search=(
("nome", "__icontains"),
("descricao", "__icontains"),
("partidos__sigla", "__icontains"),
("partidos__nome", "__icontains"),
),
)
class Meta:
verbose_name = _("Bloco Parlamentar")
verbose_name_plural = _("Blocos Parlamentares")
ordering = ("nome",)
def __str__(self):
return self.nome
class BlocoCargo(models.Model):
nome_cargo = models.CharField(
max_length=120, verbose_name=_("Cargo do bloco parlamentar")
)
cargo_unico = models.BooleanField(
default=False, choices=YES_NO_CHOICES, verbose_name=_("Cargo único?")
)
class Meta:
verbose_name = _("Cargo de Bloco Parlamentar")
verbose_name_plural = _("Cargos de Bloco Parlamentar")
ordering = (
"cargo_unico",
"nome_cargo",
)
def __str__(self):
return f"{self.nome_cargo}"
class BlocoMembro(models.Model):
bloco = models.ForeignKey(
Bloco, verbose_name=_("Bloco parlamentar"), on_delete=models.CASCADE
)
parlamentar = models.ForeignKey(
Parlamentar, verbose_name=_("Parlamentar"), on_delete=models.CASCADE
)
cargo = models.ForeignKey(
BlocoCargo,
verbose_name=_("Cargo na bloco parlamentar"),
on_delete=models.PROTECT,
)
data_entrada = models.DateField(verbose_name=_("Data Entrada"))
data_saida = models.DateField(blank=True, null=True, verbose_name=_("Data Saída"))
class Meta:
verbose_name = _("Membro de bloco parlamentar")
verbose_name_plural = _("Membros de bloco parlamentar")
ordering = ("bloco", "parlamentar", "cargo")
def __str__(self):
return f"{self.parlamentar} - {self.cargo.nome_cargo} - {self.bloco}"