Browse Source

Permitir editar serviços na tela de Orgão

pull/159/head
Sesostris Vieira 3 years ago
parent
commit
6c054c37a4
  1. 45
      sigi/apps/casas/admin.py
  2. 109
      sigi/apps/casas/models.py
  3. 245
      sigi/apps/servicos/admin.py
  4. 31
      sigi/apps/servicos/forms.py
  5. 41
      sigi/apps/servicos/migrations/0010_delete_casaatendida_alter_tiposervico_options_and_more.py
  6. 48
      sigi/apps/servicos/migrations/0011_remove_servico_contato_administrativo_and_more.py
  7. 67
      sigi/apps/servicos/models.py
  8. 2
      sigi/menu_conf.yaml

45
sigi/apps/casas/admin.py

@ -293,51 +293,16 @@ class ServicoInline(admin.TabularInline):
model = Servico
fields = (
"tipo_servico",
"link_url",
"url",
"hospedagem_interlegis",
"data_ativacao",
"data_alteracao",
"data_desativacao",
"link_servico",
"motivo_desativacao",
)
readonly_fields = [
"tipo_servico",
"link_url",
"hospedagem_interlegis",
"data_ativacao",
"data_alteracao",
"data_desativacao",
"link_servico",
]
extra = 0
max_num = 0
can_delete = False
ordering = ("-data_alteracao",)
def link_url(self, servico):
if servico.data_desativacao is not None:
return servico.url
return mark_safe(
f'<a href="{servico.url}" target="_blank">{servico.url}</a>'
)
link_url.short_description = _("URL do serviço")
def link_servico(self, obj):
if obj.pk is None:
return ""
opts = self.opts
url = reverse(
f"admin:{opts.app_label}_{opts.model_name}_change", args=[obj.pk]
)
return mark_safe(
f'<a href="{url}"><i class="material-icons Tiny">edit</i></a>'
)
link_servico.short_description = _("Editar Serviço")
def has_add_permission(self, request, obj):
return False
readonly_fields = ["data_alteracao"]
extra = 1
ordering = ("tipo_servico", "-data_alteracao")
class OcorrenciaInline(admin.TabularInline):

109
sigi/apps/casas/models.py

@ -183,115 +183,6 @@ class Orgao(models.Model):
except Funcionario.DoesNotExist:
return None
def gerarCodigoInterlegis(self):
codigo = self.codigo_interlegis
if codigo == "":
if self.tipo.sigla == "AL": # Assembléias são tratadas a parte
codigo = "A" + self.municipio.uf.sigla
if Orgao.objects.filter(codigo_interlegis=codigo).count() <= 0:
# Só grava o código se ele for inédito
self.codigo_interlegis = codigo
self.save()
return codigo
# Se já existe, então trata a Assembleia como uma Casa qualquer.
cityName = normalize("NFKD", self.municipio.nome).encode(
"ascii", "ignore"
)
cityName = cityName.upper().strip()
cityName = cityName.replace(" DA ", " ")
cityName = cityName.replace(" DE ", " ")
cityName = cityName.replace(" DO ", " ")
cityName = filter(lambda x: x in ascii_uppercase + " ", cityName)
# estratégia 1 - Pegar as 1ª letra de cada nome da cidade
codigo = "".join([x[0] for x in cityName.split(" ")[:3]])
# Se o código ficou com menos que três letras, pegar as 2 primeiras
if len(codigo) < 3:
codigo = "".join([x[0:2] for x in cityName.split(" ")[:3]])[:3]
# Se ainda ficou com menos de três letras, então o nome da cidade só
# tem uma palavra. Pegue as três primeiras letras da palavra
if len(codigo) < 3:
codigo = cityName[:3]
# Se o código já existir, substituir a última letra do código pela
# última letra do nome da cidade, e ir recuando, letra a letra,
# até achar um novo código.
cityName = cityName.replace(" ", "")
ultima = len(cityName)
while (
Orgao.objects.filter(codigo_interlegis=codigo).count() > 0
and ultima > 0
):
codigo = codigo[:2] + cityName[ultima - 1 : ultima]
ultima -= 1
# Se usou todas as letras do nome na última posição e ainda assim
# não gerou um código único, então vamos compor o nome usando as
# três primeiras consoantes.
if Orgao.objects.filter(codigo_interlegis=codigo).count() > 0:
codigo_cons = (
cityName.replace("A", "")
.replace("E", "")
.replace("I", "")
.replace("O", "")
.replace("", "")[:3]
)
if (
len(codigo_cons) == 3
and Orgao.objects.filter(codigo_interlegis=codigo).count()
> 0
):
codigo = codigo_cons
# Se ainda não gerou um nome único, vamos colocar dígitos no
# último caractere, de A a Z
i = "A"
while (
Orgao.objects.filter(codigo_interlegis=codigo).count() > 0
and i <= "Z"
):
codigo = codigo[:2] + str(i)
i = chr(ord(i) + 1)
# Se não encontrou, comece a gerar strings com 3 letras aleatórias
# tiradas do nome da cidade, até gerar uma que não existe. Tentar
# 100 vezes apenas
i = 0
while (
Orgao.objects.filter(codigo_interlegis=codigo).count() > 0
and i < 100
):
codigo = (
random.choice(cityName)
+ random.choice(cityName)
+ random.choice(cityName)
)
i += 1
# Caramba! Só resta então gerar o código com 3 letras aleatórias
# quaisquer do alfabeto!
i = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
while Orgao.objects.filter(codigo_interlegis=codigo).count() > 0:
codigo = random.choice(i) + random.choice(i) + random.choice(i)
self.codigo_interlegis = codigo
self.save()
return codigo
def __str__(self):
return self.nome

245
sigi/apps/servicos/admin.py

@ -5,16 +5,9 @@ from django.utils.safestring import mark_safe
from django.http import Http404, HttpResponseRedirect
from django.utils.translation import gettext as _
from import_export.fields import Field
from sigi.apps.casas.admin import FuncionariosInline, GerentesInterlegisFilter
from sigi.apps.casas.models import Orgao
from sigi.apps.servicos.models import (
Servico,
LogServico,
CasaAtendida,
TipoServico,
)
from sigi.apps.casas.admin import GerentesInterlegisFilter
from sigi.apps.servicos.models import Servico, LogServico, TipoServico
from sigi.apps.servicos.filters import ServicoAtivoFilter, DataUtimoUsoFilter
from sigi.apps.servicos.forms import ServicoFormAdmin
from sigi.apps.utils.filters import DateRangeFilter
from sigi.apps.utils.mixins import CartExportMixin, LabeledResourse
@ -31,9 +24,6 @@ class ServicoExportResourse(LabeledResourse):
"casa_legislativa__municipio__uf__sigla",
"casa_legislativa__email",
"telefone_casa",
"contato_tecnico__nome",
"contato_tecnico__email",
"contato_tecnico__nota",
"tipo_servico__nome",
"url",
"hospedagem_interlegis",
@ -61,21 +51,6 @@ class LogServicoInline(admin.StackedInline):
extra = 1
class ContatosInline(FuncionariosInline):
can_delete = False # Equipe do SEIT não pode excluir pessoas de contato
# SEIT see all contacts, including President
def get_queryset(self, request):
return self.model.objects.all()
def get_queryset(self, request):
return (
self.model.objects.exclude(desativado=True)
.extra(select={"ult_null": "ult_alteracao is null"})
.order_by("ult_null", "-ult_alteracao")
# A função extra foi usada para quando existir um registro com o campo igual a null não aparecer na frente dos mais novos
)
@admin.register(TipoServico)
class TipoServicoAdmin(admin.ModelAdmin):
list_display = (
@ -89,15 +64,13 @@ class TipoServicoAdmin(admin.ModelAdmin):
@admin.register(Servico)
class ServicoAdmin(CartExportMixin, admin.ModelAdmin):
form = ServicoFormAdmin
actions = [
"calcular_data_uso",
]
list_display = (
"tipo_servico",
"casa_legislativa",
"get_codigo_interlegis",
"get_uf",
"tipo_servico",
"hospedagem_interlegis",
"data_ativacao",
"data_desativacao",
@ -105,47 +78,17 @@ class ServicoAdmin(CartExportMixin, admin.ModelAdmin):
"data_ultimo_uso",
"get_link_erro",
)
fieldsets = (
(
None,
{
"fields": (
fields = [
"casa_legislativa",
"data_ativacao",
)
},
),
(
_("Serviço"),
{
"fields": (
"tipo_servico",
("url", "hospedagem_interlegis"),
("nome_servidor", "porta_servico", "senha_inicial"),
)
},
),
(
_("Contatos"),
{
"fields": (
"contato_tecnico",
"contato_administrativo",
)
},
),
(
_("Alterações"),
{
"fields": (
"url",
"hospedagem_interlegis",
"data_ativacao",
"data_alteracao",
"data_desativacao",
"motivo_desativacao",
)
},
),
)
readonly_fields = ("casa_legislativa", "data_ativacao", "data_alteracao")
]
readonly_fields = ["data_alteracao"]
list_filter = (
"tipo_servico",
"hospedagem_interlegis",
@ -155,7 +98,6 @@ class ServicoAdmin(CartExportMixin, admin.ModelAdmin):
("casa_legislativa__gerentes_interlegis", GerentesInterlegisFilter),
"casa_legislativa__municipio__uf",
)
list_display_links = []
ordering = (
"casa_legislativa__municipio__uf",
"casa_legislativa",
@ -165,14 +107,6 @@ class ServicoAdmin(CartExportMixin, admin.ModelAdmin):
search_fields = ("casa_legislativa__search_text",)
resource_class = ServicoExportResourse
def get_codigo_interlegis(self, obj):
return obj.casa_legislativa.codigo_interlegis
get_codigo_interlegis.short_description = _("Código Interlegis")
get_codigo_interlegis.admin_order_field = (
"casa_legislativa__codigo_interlegis"
)
def get_uf(self, obj):
return "%s" % (obj.casa_legislativa.municipio.uf)
@ -225,87 +159,6 @@ class ServicoAdmin(CartExportMixin, admin.ModelAdmin):
"casa_legislativa__municipio__uf__codigo_ibge__exact",
]
def add_view(self, request, form_url="", extra_context=None):
id_casa = request.GET.get("id_casa", None)
if not id_casa:
raise Http404
return super(ServicoAdmin, self).add_view(
request, form_url, extra_context=extra_context
)
def response_add(self, request, obj):
opts = obj._meta
msg = _('The %(name)s "%(obj)s" was added successfully.') % {
"name": force_str(opts.verbose_name),
"obj": force_str(obj),
}
if "_addanother" in request.POST:
self.message_user(
request,
msg
+ " "
+ (
_("You may add another %s below.")
% force_str(opts.verbose_name)
),
)
return HttpResponseRedirect(
request.path + "?id_casa=%s" % (obj.casa_legislativa.id,)
)
elif "_save" in request.POST:
self.message_user(request, msg)
return HttpResponseRedirect(
reverse(
"admin:servicos_casaatendida_change",
args=[obj.casa_legislativa.id],
)
)
return super(ServicoAdmin, self).response_add(request, obj)
def response_change(self, request, obj):
opts = obj._meta
msg = _('The %(name)s "%(obj)s" was changed successfully.') % {
"name": force_str(opts.verbose_name),
"obj": force_str(obj),
}
if "_addanother" in request.POST:
self.message_user(
request,
msg
+ " "
+ (
_("You may add another %s below.")
% force_str(opts.verbose_name)
),
)
return HttpResponseRedirect(
"../add/?id_casa=%s" % (obj.casa_legislativa.id,)
)
elif "_save" in request.POST:
self.message_user(request, msg)
return HttpResponseRedirect(
reverse(
"admin:servicos_casaatendida_change",
args=[obj.casa_legislativa.id],
)
)
return super(ServicoAdmin, self).response_change(request, obj)
def save_form(self, request, form, change):
obj = super(ServicoAdmin, self).save_form(request, form, change)
if not change:
id_casa = request.GET.get("id_casa", None)
if not id_casa:
raise Http404
obj.casa_legislativa = Orgao.objects.get(pk=id_casa)
return obj
def changelist_view(self, request, extra_context=None):
from sigi.apps.convenios.views import normaliza_data
@ -318,83 +171,3 @@ class ServicoAdmin(CartExportMixin, admin.ModelAdmin):
request,
extra_context={"query_str": "?" + request.META["QUERY_STRING"]},
)
@admin.register(CasaAtendida)
class CasaAtendidaAdmin(admin.ModelAdmin):
actions = None
list_display = (
"codigo_interlegis",
"nome",
"get_servicos",
)
ordering = ["nome"]
fieldsets = (
(
"Casa Legislativa",
{
"fields": (
("codigo_interlegis", "nome"),
("logradouro", "bairro", "municipio", "cep"),
("email", "pagina_web"),
)
},
),
)
readonly_fields = ("nome", "logradouro", "bairro", "municipio", "cep")
inlines = (ContatosInline,)
list_filter = (
"tipo",
"servico__tipo_servico",
"municipio__uf__nome",
"servico__casa_legislativa__convenio__projeto",
)
search_fields = (
"search_text",
"cnpj",
"bairro",
"logradouro",
"cep",
"municipio__nome",
"municipio__uf__nome",
"municipio__codigo_ibge",
"pagina_web",
"observacoes",
)
def get_servicos(self, obj):
result = [
f"{servico.tipo_servico.nome} ({servico.status_servico}). "
f"Contato: {servico.contato_administrativo.nome}"
for servico in obj.servico_set.all()
]
return mark_safe("<ul><li>" + "</li><li>".join(result) + "</li></ul>")
get_servicos.short_description = _("Serviços")
def lookup_allowed(self, lookup, value):
return super(CasaAtendidaAdmin, self).lookup_allowed(
lookup, value
) or lookup in [
"municipio__uf__codigo_ibge__exact",
"servico__tipo_servico__id__exact",
]
def change_view(self, request, object_id, extra_context=None):
# Se a Casa ainda não é atendida, gerar o código interlegis para ela
# Assim ela passa a ser uma casa atendida
casa = Orgao.objects.get(id=object_id)
if casa.codigo_interlegis == "":
casa.gerarCodigoInterlegis()
return super(CasaAtendidaAdmin, self).change_view(
request, object_id, extra_context=extra_context
)
def has_add_permission(self, request):
return False # Nunca é permitido inserir uma nova Casa Legislativa por aqui
def has_delete_permission(self, request, obj=None):
return False # Nunca deletar casas por aqui

31
sigi/apps/servicos/forms.py

@ -1,31 +0,0 @@
from django.forms.models import ModelForm
from django.utils.encoding import force_str
from sigi.apps.servicos.models import Servico, CasaAtendida
class ServicoFormAdmin(ModelForm):
class Meta:
model = Servico
fields = "__all__"
def __init__(self, *args, **kwargs):
super(ServicoFormAdmin, self).__init__(*args, **kwargs)
self.fields["contato_tecnico"].choices = ()
self.fields["contato_administrativo"].choices = ()
if self.instance.casa_legislativa_id:
id_casa = self.instance.casa_legislativa_id
elif "initial" in kwargs and "id_casa" in kwargs["initial"]:
id_casa = kwargs["initial"]["id_casa"]
self.instance.casa_legislativa_id = id_casa
else:
id_casa = None
if id_casa:
casa = CasaAtendida.objects.get(pk=id_casa)
contatos = [
(f.id, force_str(f)) for f in casa.funcionario_set.all()
]
self.fields["contato_tecnico"].choices = contatos
self.fields["contato_administrativo"].choices = contatos

41
sigi/apps/servicos/migrations/0010_delete_casaatendida_alter_tiposervico_options_and_more.py

@ -0,0 +1,41 @@
# Generated by Django 4.0.4 on 2022-05-16 15:22
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('servicos', '0009_alter_casamanifesta_email_alter_casamanifesta_id_and_more'),
]
operations = [
migrations.DeleteModel(
name='CasaAtendida',
),
migrations.AlterModelOptions(
name='tiposervico',
options={'verbose_name': 'tipo de serviço', 'verbose_name_plural': 'tipos de serviço'},
),
migrations.AlterField(
model_name='servico',
name='tipo_servico',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='servicos.tiposervico', verbose_name='tipo de serviço'),
),
migrations.AlterField(
model_name='tiposervico',
name='template_email_altera',
field=models.TextField(blank=True, help_text='Use:<br/>\n {url} para incluir a URL do serviço,<br/>\n {senha} para incluir a senha inicial do serviço', verbose_name='template de email de alteração'),
),
migrations.AlterField(
model_name='tiposervico',
name='template_email_ativa',
field=models.TextField(blank=True, help_text='Use:<br/>\n {url} para incluir a URL do serviço,<br/>\n {senha} para incluir a senha inicial do serviço', verbose_name='template de email de ativação'),
),
migrations.AlterField(
model_name='tiposervico',
name='template_email_desativa',
field=models.TextField(blank=True, help_text='Use:<br/>\n {url} para incluir a URL do serviço,<br/>\n {senha} para incluir a senha inicial do serviço<br/>{motivo} para incluir o motivo da desativação do serviço', verbose_name='template de email de desativação'),
),
]

48
sigi/apps/servicos/migrations/0011_remove_servico_contato_administrativo_and_more.py

@ -0,0 +1,48 @@
# Generated by Django 4.0.4 on 2022-05-16 18:36
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('servicos', '0010_delete_casaatendida_alter_tiposervico_options_and_more'),
]
operations = [
migrations.RemoveField(
model_name='servico',
name='contato_administrativo',
),
migrations.RemoveField(
model_name='servico',
name='contato_tecnico',
),
migrations.RemoveField(
model_name='servico',
name='nome_servidor',
),
migrations.RemoveField(
model_name='servico',
name='porta_servico',
),
migrations.RemoveField(
model_name='servico',
name='senha_inicial',
),
migrations.AlterField(
model_name='tiposervico',
name='template_email_altera',
field=models.TextField(blank=True, help_text='Use a marcação {url} para incluir a URL do serviço,<br/>', verbose_name='template de email de alteração'),
),
migrations.AlterField(
model_name='tiposervico',
name='template_email_ativa',
field=models.TextField(blank=True, help_text='Use a marcação {url} para incluir a URL do serviço,<br/>', verbose_name='template de email de ativação'),
),
migrations.AlterField(
model_name='tiposervico',
name='template_email_desativa',
field=models.TextField(blank=True, help_text='Use a marcação {url} para incluir a URL do serviço,<br/><br/>{motivo} para incluir o motivo da desativação do serviço', verbose_name='template de email de desativação'),
),
]

67
sigi/apps/servicos/models.py

@ -6,9 +6,7 @@ from django.utils.translation import gettext as _
class TipoServico(models.Model):
MODO_CHOICES = (("H", _("Hospedagem")), ("R", _("Registro")))
email_help = """Use:<br/>
{url} para incluir a URL do serviço,<br/>
{senha} para incluir a senha inicial do serviço"""
email_help = "Use a marcação {url} para incluir a URL do serviço,<br/>"
string_pesquisa_help = (
"Parâmetros da pesquisa para averiguar a data da "
"última atualização do serviço. Formato:<br/>"
@ -24,13 +22,13 @@ class TipoServico(models.Model):
_("string de pesquisa"), blank=True, help_text=string_pesquisa_help
)
template_email_ativa = models.TextField(
_("Template de email de ativação"), help_text=email_help, blank=True
_("template de email de ativação"), help_text=email_help, blank=True
)
template_email_altera = models.TextField(
_("Template de email de alteração"), help_text=email_help, blank=True
_("template de email de alteração"), help_text=email_help, blank=True
)
template_email_desativa = models.TextField(
_("Template de email de desativação"),
_("template de email de desativação"),
help_text=email_help
+ _("<br/>{motivo} para incluir o motivo da desativação do serviço"),
blank=True,
@ -42,8 +40,8 @@ class TipoServico(models.Model):
return self.servico_set.filter(data_desativacao=None).count()
class Meta:
verbose_name = _("Tipo de serviço")
verbose_name_plural = _("Tipos de serviço")
verbose_name = _("tipo de serviço")
verbose_name_plural = _("tipos de serviço")
def __str__(self):
return self.nome
@ -54,39 +52,12 @@ class Servico(models.Model):
Orgao, on_delete=models.PROTECT, verbose_name=_("Casa Legislativa")
)
tipo_servico = models.ForeignKey(
TipoServico, on_delete=models.PROTECT, verbose_name=_("Tipo de serviço")
)
contato_tecnico = models.ForeignKey(
Funcionario,
on_delete=models.PROTECT,
verbose_name=_("Contato técnico"),
related_name="contato_tecnico",
)
contato_administrativo = models.ForeignKey(
Funcionario,
on_delete=models.PROTECT,
verbose_name=_("Contato administrativo"),
related_name="contato_administrativo",
TipoServico, on_delete=models.PROTECT, verbose_name=_("tipo de serviço")
)
url = models.URLField(_("URL do serviço"), blank=True)
hospedagem_interlegis = models.BooleanField(
_("Hospedagem no Interlegis?"), default=False
)
nome_servidor = models.CharField(
_("Hospedado em"),
max_length=60,
blank=True,
help_text=_(
"Se hospedado no Interlegis, informe o nome do servidor."
"<br/>Senão, informe o nome do provedor de serviços."
),
)
porta_servico = models.PositiveSmallIntegerField(
_("Porta de serviço (instância)"), blank=True, null=True
)
senha_inicial = models.CharField(
_("Senha inicial"), max_length=33, blank=True
)
data_ativacao = models.DateField(_("Data de ativação"), default=date.today)
data_alteracao = models.DateField(
_("Data da última alteração"), blank=True, null=True, auto_now=True
@ -285,10 +256,7 @@ class Servico(models.Model):
body = self.tipo_servico.template_email_desativa
elif (
self.tipo_servico != original.tipo_servico
or self.contato_tecnico != original.contato_tecnico
or self.url != original.url
or self.nome_servidor != original.nome_servidor
or self.senha_inicial != original.senha_inicial
):
# Serviço foi alterado
subject = _("INTERLEGIS - Alteração de serviço %s") % (
@ -301,10 +269,8 @@ class Servico(models.Model):
return # sem enviar email
# Prepara e envia o email
body = (
body.replace("{url}", self.url)
.replace("{senha}", self.senha_inicial)
.replace("{motivo}", self.motivo_desativacao)
body = body.replace("{url}", self.url).replace(
"{motivo}", self.motivo_desativacao
)
# send_mail(subject, body, DEFAULT_FROM_EMAIL, \
@ -332,21 +298,6 @@ class LogServico(models.Model):
verbose_name_plural = _("Logs do serviço")
class CasaAtendidaManager(models.Manager):
def get_queryset(self):
qs = super(CasaAtendidaManager, self).get_queryset()
qs = qs.exclude(codigo_interlegis="")
return qs
class CasaAtendida(Orgao):
class Meta:
proxy = True
verbose_name_plural = _("Casas atendidas")
objects = CasaAtendidaManager()
class CasaManifesta(models.Model):
casa_legislativa = models.OneToOneField(Orgao, on_delete=models.CASCADE)
data_manifestacao = models.DateTimeField(auto_now_add=True)

2
sigi/menu_conf.yaml

@ -51,8 +51,6 @@ main_menu:
- title: Serviços SEIT
icon: cloud_done
children:
- title: Casas atendidas
view_name: admin:servicos_casaatendida_changelist
- title: Lista de serviços
view_name: admin:servicos_servico_changelist
- title: Ocorrências

Loading…
Cancel
Save