mirror of https://github.com/interlegis/sigi.git
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.
1071 lines
36 KiB
1071 lines
36 KiB
import datetime
|
|
import time
|
|
from typing import Any
|
|
from moodle import Moodle
|
|
from django.db.models import Q
|
|
from django.conf import settings
|
|
from django.contrib import admin, messages
|
|
from django.core.exceptions import ValidationError
|
|
from django.http import HttpResponse
|
|
from django.shortcuts import get_object_or_404, render, redirect
|
|
from django.template import Template, Context
|
|
from django.urls import path, reverse
|
|
from django.utils import timezone
|
|
from django.utils.safestring import mark_safe
|
|
from django.utils.translation import gettext as _
|
|
from django_weasyprint.utils import django_url_fetcher
|
|
from django_weasyprint.views import WeasyTemplateResponse
|
|
from import_export.fields import Field
|
|
from tinymce.models import HTMLField
|
|
from tinymce.widgets import AdminTinyMCE
|
|
from weasyprint import HTML
|
|
from sigi.apps.eventos.models import (
|
|
Checklist,
|
|
Cronograma,
|
|
ModeloDeclaracao,
|
|
Modulo,
|
|
TipoEvento,
|
|
Solicitacao,
|
|
AnexoSolicitacao,
|
|
ItemSolicitado,
|
|
Funcao,
|
|
Evento,
|
|
Equipe,
|
|
Convite,
|
|
Anexo,
|
|
)
|
|
from sigi.apps.eventos.forms import EventoAdminForm, SelecionaModeloForm
|
|
from sigi.apps.utils import abreviatura
|
|
from sigi.apps.utils.filters import EmptyFilter, DateRangeFilter
|
|
from sigi.apps.utils.mixins import (
|
|
CartExportMixin,
|
|
LabeledResourse,
|
|
ValueLabeledResource,
|
|
)
|
|
|
|
|
|
class SolicitacaoResource(LabeledResourse):
|
|
oficinas = Field(column_name="oficinas solicitadas")
|
|
oficinas_uf = Field(column_name="número de oficinas realizadas na UF")
|
|
|
|
class Meta:
|
|
model = Solicitacao
|
|
fields = (
|
|
"num_processo",
|
|
"status",
|
|
"senador",
|
|
"data_pedido",
|
|
"data_recebido_coperi",
|
|
"oficinas",
|
|
"casa__nome",
|
|
"casa__municipio__nome",
|
|
"casa__municipio__uf__nome",
|
|
"casa__municipio__uf__regiao",
|
|
"casa__municipio__populacao",
|
|
"oficinas_uf",
|
|
"estimativa_casas",
|
|
"estimativa_servidores",
|
|
)
|
|
export_order = fields
|
|
|
|
def dehydrate_status(self, obj):
|
|
return obj.get_status_display()
|
|
|
|
def dehydrate_oficinas(self, obj):
|
|
return ", ".join(
|
|
[i.tipo_evento.sigla for i in obj.itemsolicitado_set.all()]
|
|
)
|
|
|
|
def dehydrate_oficinas_uf(sekf, obj):
|
|
return Evento.objects.filter(
|
|
status__in=[Evento.STATUS_CONFIRMADO, Evento.STATUS_REALIZADO],
|
|
casa_anfitria__municipio__uf=obj.casa.municipio.uf,
|
|
data_inicio__year__gte=timezone.localdate().year - 2,
|
|
).count()
|
|
|
|
def dehydrate_casa__municipio__uf__regiao(self, obj):
|
|
return obj.casa.municipio.uf.get_regiao_display()
|
|
|
|
|
|
class EventoResource(ValueLabeledResource):
|
|
# categoria_evento = Field(column_name="tipo_evento__categoria")
|
|
# status = Field(column_name="status")
|
|
class Meta:
|
|
model = Evento
|
|
fields = (
|
|
"id",
|
|
"tipo_evento__nome",
|
|
"tipo_evento__categoria",
|
|
"nome",
|
|
"descricao",
|
|
"virtual",
|
|
"solicitante",
|
|
"num_processo",
|
|
"data_pedido",
|
|
"data_inicio",
|
|
"data_termino",
|
|
"carga_horaria",
|
|
"casa_anfitria__nome",
|
|
"casa_anfitria__logradouro",
|
|
"casa_anfitria__bairro",
|
|
"casa_anfitria__municipio__nome",
|
|
"casa_anfitria__municipio__uf__sigla",
|
|
"casa_anfitria__cep",
|
|
"casa_anfitria__email",
|
|
"local",
|
|
"observacao",
|
|
"publico_alvo",
|
|
"total_participantes",
|
|
"status",
|
|
"data_cancelamento",
|
|
"motivo_cancelamento",
|
|
"equipe__membro__nome_completo",
|
|
"equipe__funcao__nome",
|
|
"convite__casa__nome",
|
|
"convite__casa__municipio__nome",
|
|
"convite__casa__municipio__uf__sigla",
|
|
"convite__casa__cep",
|
|
"convite__casa__email",
|
|
"convite__qtde_participantes",
|
|
"convite__nomes_participantes",
|
|
)
|
|
export_order = fields
|
|
|
|
def dehydrate_tipo_evento__categoria(self, obj):
|
|
return dict(TipoEvento.CATEGORIA_CHOICES)[
|
|
obj["tipo_evento__categoria"]
|
|
]
|
|
|
|
def dehydrate_virtual(self, obj):
|
|
return "Sim" if obj["virtual"] else "Não"
|
|
|
|
def dehydrate_status(self, obj):
|
|
return dict(Evento.STATUS_CHOICES)[obj["status"]]
|
|
|
|
|
|
class ChecklistInline(admin.StackedInline):
|
|
model = Checklist
|
|
|
|
|
|
class EquipeInline(admin.StackedInline):
|
|
model = Equipe
|
|
autocomplete_fields = ("membro", "funcao")
|
|
|
|
|
|
class ConviteInline(admin.StackedInline):
|
|
model = Convite
|
|
autocomplete_fields = ("casa",)
|
|
|
|
|
|
class ModuloInline(admin.StackedInline):
|
|
model = Modulo
|
|
autocomplete_fields = ("apresentador", "monitor")
|
|
|
|
|
|
class AnexoInline(admin.StackedInline):
|
|
model = Anexo
|
|
exclude = ("data_pub", "convite")
|
|
|
|
|
|
class CronogramaInline(admin.StackedInline):
|
|
model = Cronograma
|
|
extra = 0
|
|
|
|
|
|
class ItemSolicitadoInline(admin.StackedInline):
|
|
model = ItemSolicitado
|
|
fields = (
|
|
"tipo_evento",
|
|
"virtual",
|
|
"inicio_desejado",
|
|
"status",
|
|
"justificativa",
|
|
"servidor",
|
|
"data_analise",
|
|
"evento",
|
|
)
|
|
readonly_fields = ("servidor", "data_analise", "evento")
|
|
extra = 1
|
|
autocomplete_fields = ("tipo_evento",)
|
|
|
|
|
|
class AnexoSolicitacaoInline(admin.TabularInline):
|
|
model = AnexoSolicitacao
|
|
readonly_fields = ("data_pub",)
|
|
|
|
|
|
@admin.register(TipoEvento)
|
|
class TipoEventoAdmin(admin.ModelAdmin):
|
|
list_display = ["nome", "categoria"]
|
|
list_filter = ["categoria", "casa_solicita"]
|
|
search_fields = ["nome"]
|
|
inlines = [ChecklistInline]
|
|
|
|
|
|
@admin.register(Solicitacao)
|
|
class SolicitacaoAdmin(CartExportMixin, admin.ModelAdmin):
|
|
resource_class = SolicitacaoResource
|
|
list_display = (
|
|
"casa",
|
|
"get_sigad_url",
|
|
"status",
|
|
"senador",
|
|
"data_pedido",
|
|
"data_recebido_coperi",
|
|
"get_oficinas",
|
|
"get_municipio",
|
|
"get_uf",
|
|
"get_regiao",
|
|
"get_populacao",
|
|
"get_oficinas_uf",
|
|
"estimativa_casas",
|
|
"estimativa_servidores",
|
|
)
|
|
list_filter = (
|
|
"casa__municipio__uf",
|
|
"casa__municipio__uf__regiao",
|
|
"senador",
|
|
"itemsolicitado__tipo_evento",
|
|
"status",
|
|
)
|
|
list_select_related = ["casa", "casa__municipio", "casa__municipio__uf"]
|
|
list_display_links = ("casa",)
|
|
search_fields = (
|
|
"casa__search_text",
|
|
"casa__municipio__search_text",
|
|
"casa__municipio__uf__search_text",
|
|
"senador",
|
|
)
|
|
date_hierarchy = "data_pedido"
|
|
fieldsets = (
|
|
(
|
|
None,
|
|
{
|
|
"fields": [
|
|
"casa",
|
|
"senador",
|
|
"num_processo",
|
|
"descricao",
|
|
"data_pedido",
|
|
"data_recebido_coperi",
|
|
]
|
|
},
|
|
),
|
|
(
|
|
_("Autorização"),
|
|
{
|
|
"fields": [
|
|
"status",
|
|
"servidor",
|
|
"data_analise",
|
|
"justificativa",
|
|
]
|
|
},
|
|
),
|
|
(
|
|
_("Contato da Casa"),
|
|
{
|
|
"fields": [
|
|
"contato",
|
|
"email_contato",
|
|
"telefone_contato",
|
|
"whatsapp_contato",
|
|
]
|
|
},
|
|
),
|
|
(
|
|
_("Participação esperada"),
|
|
{"fields": ["estimativa_casas", "estimativa_servidores"]},
|
|
),
|
|
)
|
|
readonly_fields = ("servidor", "data_analise")
|
|
inlines = (ItemSolicitadoInline, AnexoSolicitacaoInline)
|
|
autocomplete_fields = ("casa",)
|
|
|
|
def save_model(self, request, obj, form, change):
|
|
if change:
|
|
old_obj = Solicitacao.objects.get(id=obj.id)
|
|
else:
|
|
old_obj = obj
|
|
if (
|
|
obj.status != Solicitacao.STATUS_SOLICITADO
|
|
and obj.status != old_obj.status
|
|
):
|
|
obj.servidor = (
|
|
request.user.servidor
|
|
if hasattr(request.user, "servidor")
|
|
else None
|
|
)
|
|
obj.data_analise = timezone.localtime()
|
|
return super().save_model(request, obj, form, change)
|
|
|
|
def save_formset(self, request, form, formset, change):
|
|
if formset.model == ItemSolicitado:
|
|
obj = form.instance
|
|
instances = formset.save(commit=False)
|
|
|
|
if hasattr(request.user, "servidor"):
|
|
servidor = request.user.servidor
|
|
else:
|
|
servidor = None
|
|
|
|
agora = timezone.localtime()
|
|
|
|
for item in instances:
|
|
if (
|
|
obj.status == Solicitacao.STATUS_SOLICITADO
|
|
and item.status != ItemSolicitado.STATUS_SOLICITADO
|
|
):
|
|
item.status = ItemSolicitado.STATUS_SOLICITADO
|
|
self.message_user(
|
|
request,
|
|
_(
|
|
f"O item {item} teve o status mudado para "
|
|
"SOLICITADO porque a solicitação ainda não foi "
|
|
"autorizada"
|
|
),
|
|
messages.WARNING,
|
|
)
|
|
if (
|
|
obj.status == Solicitacao.STATUS_REJEITADO
|
|
and item.status != ItemSolicitado.STATUS_REJEITADO
|
|
):
|
|
item.status = ItemSolicitado.STATUS_REJEITADO
|
|
self.message_user(
|
|
request,
|
|
_(
|
|
f"O item {item} teve o status mudado para "
|
|
"REJEITADO porque a solicitação inteira foi "
|
|
"rejeitada"
|
|
),
|
|
messages.WARNING,
|
|
)
|
|
if (
|
|
obj.status == Solicitacao.STATUS_CONCLUIDO
|
|
and item.status == ItemSolicitado.STATUS_SOLICITADO
|
|
):
|
|
item.status = ItemSolicitado.STATUS_REJEITADO
|
|
self.message_user(
|
|
request,
|
|
_(
|
|
f"O item {item} teve o status mudado para "
|
|
"REJEITADO porque a solicitação foi concluída e "
|
|
"ele ainda estava em aberto"
|
|
),
|
|
messages.WARNING,
|
|
)
|
|
|
|
if (
|
|
item.status == ItemSolicitado.STATUS_SOLICITADO
|
|
and item.evento is not None
|
|
):
|
|
item.evento.status = Evento.STATUS_ACONFIRMAR
|
|
self.message_user(
|
|
request,
|
|
_(
|
|
f"Status do evento {item.evento} alterado para "
|
|
f"{item.evento.get_status_display()}"
|
|
),
|
|
messages.INFO,
|
|
)
|
|
elif item.status == ItemSolicitado.STATUS_AUTORIZADO:
|
|
item.servidor = servidor
|
|
item.data_analise = agora
|
|
if item.evento is None:
|
|
item.evento = Evento(
|
|
tipo_evento=item.tipo_evento,
|
|
nome=_(
|
|
f"{item.tipo_evento} em {item.solicitacao.casa}"[
|
|
:100
|
|
]
|
|
),
|
|
descricao=_(
|
|
f"{item.tipo_evento} em {item.solicitacao.casa}"
|
|
),
|
|
virtual=item.virtual,
|
|
solicitante=item.solicitacao.senador,
|
|
num_processo=item.solicitacao.num_processo,
|
|
data_pedido=item.solicitacao.data_pedido,
|
|
data_recebido_coperi=item.solicitacao.data_recebido_coperi,
|
|
data_inicio=item.inicio_desejado,
|
|
data_termino=item.inicio_desejado
|
|
+ datetime.timedelta(
|
|
days=item.tipo_evento.duracao
|
|
),
|
|
casa_anfitria=item.solicitacao.casa,
|
|
observacao=_(
|
|
f"Autorizado por {servidor} com a justificativa '{item.justificativa}"
|
|
),
|
|
status=Evento.STATUS_CONFIRMADO,
|
|
contato=item.solicitacao.contato,
|
|
telefone=item.solicitacao.telefone_contato,
|
|
)
|
|
self.message_user(
|
|
request,
|
|
_(f"Evento {item.evento} criado automaticamente."),
|
|
messages.INFO,
|
|
)
|
|
else:
|
|
item.evento.status = Evento.STATUS_CONFIRMADO
|
|
item.evento.observacao += _(
|
|
f"\nConfirmado por {servidor} com a justificativa: {item.justificativa}"
|
|
)
|
|
item.evento.data_cancelamento = None
|
|
item.evento.motivo_cancelamento = ""
|
|
self.message_user(
|
|
request,
|
|
_(
|
|
f"Status do evento {item.evento} alterado para "
|
|
f"{item.evento.get_status_display()}"
|
|
),
|
|
messages.INFO,
|
|
)
|
|
elif item.status == ItemSolicitado.STATUS_REJEITADO:
|
|
item.servidor = servidor
|
|
item.data_analise = agora
|
|
if item.evento is not None:
|
|
item.evento.status = Evento.STATUS_CANCELADO
|
|
item.evento.observacao += _(
|
|
f"\nCancelado por {servidor} com a justificativa: {item.justificativa}"
|
|
)
|
|
item.evento.data_cancelamento = timezone.localdate()
|
|
item.evento.motivo_cancelamento = _(
|
|
f"\nCancelado por {servidor} com a justificativa: {item.justificativa}"
|
|
)
|
|
self.message_user(
|
|
request,
|
|
_(
|
|
f"Status do evento {item.evento} alterado para "
|
|
f"{item.evento.get_status_display()}"
|
|
),
|
|
messages.INFO,
|
|
)
|
|
if item.evento:
|
|
item.evento.tipo_evento = item.tipo_evento
|
|
item.evento.nome = _(
|
|
f"{item.tipo_evento} em {item.solicitacao.casa}"[:100]
|
|
)
|
|
item.evento.descricao = _(
|
|
f"{item.tipo_evento} em {item.solicitacao.casa}"
|
|
)
|
|
item.evento.virtual = item.virtual
|
|
item.evento.solicitante = item.solicitacao.senador
|
|
item.evento.num_processo = item.solicitacao.num_processo
|
|
item.evento.data_pedido = item.solicitacao.data_pedido
|
|
item.evento.data_recebido_coperi = (
|
|
item.solicitacao.data_recebido_coperi
|
|
)
|
|
item.evento.data_inicio = item.inicio_desejado
|
|
item.evento.data_termino = (
|
|
item.inicio_desejado
|
|
+ datetime.timedelta(days=item.tipo_evento.duracao)
|
|
)
|
|
item.evento.casa_anfitria = item.solicitacao.casa
|
|
item.evento.contato = item.solicitacao.contato
|
|
item.evento.telefone = item.solicitacao.telefone_contato
|
|
item.evento.save()
|
|
item.save()
|
|
|
|
if (
|
|
obj.status == Solicitacao.STATUS_AUTORIZADO
|
|
and not obj.itemsolicitado_set.filter(
|
|
status=ItemSolicitado.STATUS_SOLICITADO
|
|
).exists()
|
|
):
|
|
obj.status = Solicitacao.STATUS_CONCLUIDO
|
|
obj.save()
|
|
self.message_user(
|
|
request,
|
|
_(
|
|
"Status da solicitação alterado automaticamente para "
|
|
"Concluído pois não há mais itens a serem analisados"
|
|
),
|
|
messages.INFO,
|
|
)
|
|
|
|
return super().save_formset(request, form, formset, change)
|
|
|
|
@admin.display(description=_("Oficinas solicitadas"))
|
|
def get_oficinas(self, obj):
|
|
return mark_safe(
|
|
"<ul><li>"
|
|
+ "</li><li>".join(
|
|
[i.tipo_evento.sigla for i in obj.itemsolicitado_set.all()]
|
|
)
|
|
+ "</li></ul>"
|
|
)
|
|
|
|
@admin.display(
|
|
description=_("Município"), ordering="casa__municipio__nome"
|
|
)
|
|
def get_municipio(self, obj):
|
|
return obj.casa.municipio.nome
|
|
|
|
@admin.display(description=_("UF"), ordering="casa__municipio__uf__nome")
|
|
def get_uf(self, obj):
|
|
return obj.casa.municipio.uf.nome
|
|
|
|
@admin.display(
|
|
description=_("Região"), ordering="casa__municipio__uf__regiao"
|
|
)
|
|
def get_regiao(self, obj):
|
|
return obj.casa.municipio.uf.get_regiao_display()
|
|
|
|
@admin.display(
|
|
description=_("População"), ordering="casa__municipio__populacao"
|
|
)
|
|
def get_populacao(self, obj):
|
|
return obj.casa.municipio.populacao
|
|
|
|
|
|
@admin.register(Funcao)
|
|
class FuncaoAdmin(admin.ModelAdmin):
|
|
list_display = (
|
|
"nome",
|
|
"descricao",
|
|
)
|
|
search_fields = (
|
|
"nome",
|
|
"descricao",
|
|
)
|
|
|
|
|
|
@admin.register(ModeloDeclaracao)
|
|
class ModeloDeclaracaoAdmin(admin.ModelAdmin):
|
|
list_display = ("nome", "formato")
|
|
formfield_overrides = {HTMLField: {"widget": AdminTinyMCE}}
|
|
|
|
|
|
@admin.register(Evento)
|
|
class EventoAdmin(CartExportMixin, admin.ModelAdmin):
|
|
form = EventoAdminForm
|
|
resource_class = EventoResource
|
|
date_hierarchy = "data_inicio"
|
|
list_display = (
|
|
"get_banner",
|
|
"get_tipo_evento",
|
|
"nome",
|
|
"turma",
|
|
"status",
|
|
"publicar",
|
|
"link_sigad",
|
|
"data_inicio",
|
|
"data_termino",
|
|
"get_municipio",
|
|
"solicitante",
|
|
"total_participantes",
|
|
)
|
|
list_display_links = ("get_banner", "nome")
|
|
list_filter = (
|
|
"status",
|
|
"publicar",
|
|
("num_processo", EmptyFilter),
|
|
"tipo_evento",
|
|
"tipo_evento__categoria",
|
|
("data_inicio", DateRangeFilter),
|
|
"virtual",
|
|
"solicitante",
|
|
)
|
|
autocomplete_fields = (
|
|
"tipo_evento",
|
|
"casa_anfitria",
|
|
)
|
|
search_fields = (
|
|
"nome",
|
|
"tipo_evento__nome",
|
|
"casa_anfitria__search_text",
|
|
"casa_anfitria__municipio__search_text",
|
|
"solicitante",
|
|
"num_processo",
|
|
)
|
|
inlines = (
|
|
EquipeInline,
|
|
ConviteInline,
|
|
ModuloInline,
|
|
AnexoInline,
|
|
CronogramaInline,
|
|
)
|
|
save_as = True
|
|
|
|
@admin.display(description=_("Tipo Evento"), ordering="tipo_evento__nome")
|
|
def get_tipo_evento(self, obj):
|
|
return obj.tipo_evento.nome
|
|
|
|
@admin.display(
|
|
description=_("Município"), ordering="casa_anfitria__municipio"
|
|
)
|
|
def get_municipio(self, obj):
|
|
if obj.casa_anfitria:
|
|
return str(obj.casa_anfitria.municipio)
|
|
else:
|
|
return None
|
|
|
|
@admin.display(
|
|
description=_("número do processo SIGAD"), ordering="num_processo"
|
|
)
|
|
def link_sigad(self, obj):
|
|
if obj.pk is None:
|
|
return ""
|
|
return mark_safe(obj.get_sigad_url())
|
|
|
|
@admin.display(description=_("banner"))
|
|
def get_banner(self, obj):
|
|
if obj.banner:
|
|
return mark_safe(
|
|
f'<img src="{obj.banner.url}" width="60" height="60" />'
|
|
)
|
|
else:
|
|
return ""
|
|
|
|
def render_change_form(self, request, context, add, change, form_url, obj):
|
|
perm = request.user.has_perm("eventos.createcourse_evento")
|
|
context.update(
|
|
{
|
|
"can_createcourse": (
|
|
perm
|
|
and obj
|
|
and obj.moodle_courseid is None
|
|
and obj.tipo_evento.moodle_template_courseid is not None
|
|
and obj.tipo_evento.moodle_categoryid is not None
|
|
),
|
|
"can_updateparticipantes": (
|
|
perm and obj and obj.moodle_courseid is not None
|
|
),
|
|
}
|
|
)
|
|
return super().render_change_form(
|
|
request, context, add, change, form_url, obj
|
|
)
|
|
|
|
def lookup_allowed(self, lookup, value):
|
|
return super(EventoAdmin, self).lookup_allowed(
|
|
lookup, value
|
|
) or lookup in [
|
|
"tipo_evento__nome__exact",
|
|
"tipo_evento__nome__contains",
|
|
]
|
|
|
|
def get_urls(self):
|
|
urls = super().get_urls()
|
|
model_info = self.get_model_info()
|
|
my_urls = [
|
|
path(
|
|
"<path:object_id>/declaracao/",
|
|
self.admin_site.admin_view(self.declaracao_report),
|
|
name="%s_%s_declaracaoreport" % model_info,
|
|
),
|
|
path(
|
|
"<path:object_id>/gant/",
|
|
self.admin_site.admin_view(self.gant_report),
|
|
name="%s_%s_gantreport" % model_info,
|
|
),
|
|
path(
|
|
"<path:object_id>/checklist/",
|
|
self.admin_site.admin_view(self.checklist_report),
|
|
name="%s_%s_checklistreport" % model_info,
|
|
),
|
|
path(
|
|
"<path:object_id>/comunicacao/",
|
|
self.admin_site.admin_view(self.plano_comunicacao),
|
|
name="%s_%s_comunicacaoreport" % model_info,
|
|
),
|
|
path(
|
|
"<path:object_id>/createcourse/",
|
|
self.admin_site.admin_view(self.create_course),
|
|
name="%s_%s_createcourse" % model_info,
|
|
),
|
|
path(
|
|
"<path:object_id>/updateparticipantes/",
|
|
self.admin_site.admin_view(self.update_participantes),
|
|
name="%s_%s_updateparticipantes" % model_info,
|
|
),
|
|
]
|
|
return my_urls + urls
|
|
|
|
def declaracao_report(self, request, object_id):
|
|
if request.method == "POST":
|
|
form = SelecionaModeloForm(request.POST)
|
|
if form.is_valid():
|
|
evento = get_object_or_404(Evento, id=object_id)
|
|
modelo = form.cleaned_data["modelo"]
|
|
membro = (
|
|
evento.equipe_set.filter(assina_oficio=True).first()
|
|
or evento.equipe_set.first()
|
|
)
|
|
if membro:
|
|
servidor = membro.membro
|
|
else:
|
|
servidor = None
|
|
template_string = (
|
|
"""
|
|
{% extends "eventos/declaracao_pdf.html" %}
|
|
{% block text_body %}"""
|
|
+ modelo.texto
|
|
+ """
|
|
{% endblock %}
|
|
"""
|
|
)
|
|
context = Context(
|
|
{
|
|
"pagesize": modelo.formato,
|
|
"pagemargin": modelo.margem,
|
|
"evento": evento,
|
|
"servidor": servidor,
|
|
"data": evento.data_inicio.date(),
|
|
}
|
|
)
|
|
string = Template(template_string).render(context)
|
|
# return HttpResponse(string)
|
|
response = HttpResponse(
|
|
headers={
|
|
"Content-Type": "application/pdf",
|
|
"Content-Disposition": 'attachment; filename="declaração.pdf"',
|
|
}
|
|
)
|
|
pdf = HTML(
|
|
string=string,
|
|
url_fetcher=django_url_fetcher,
|
|
encoding="utf-8",
|
|
base_url=request.build_absolute_uri("/"),
|
|
)
|
|
pdf.write_pdf(target=response)
|
|
return response
|
|
else:
|
|
form = SelecionaModeloForm()
|
|
|
|
context = {
|
|
"form": form,
|
|
"evento_id": object_id,
|
|
"opts": self.model._meta,
|
|
"preserved_filters": self.get_preserved_filters(request),
|
|
}
|
|
return render(
|
|
request, "admin/eventos/evento/seleciona_modelo.html", context
|
|
)
|
|
|
|
def gant_report(self, request, object_id):
|
|
evento = get_object_or_404(Evento, id=object_id)
|
|
change_url = (
|
|
reverse(
|
|
"admin:%s_%s_change" % self.get_model_info(), args=[object_id]
|
|
)
|
|
+ "?"
|
|
+ self.get_preserved_filters(request)
|
|
)
|
|
cronograma = list(
|
|
evento.cronograma_set.order_by("data_prevista_inicio")
|
|
)
|
|
if not cronograma:
|
|
self.message_user(
|
|
request,
|
|
_(
|
|
"Não há um cronograma definido para a realização deste evento. Impossível gerar um gráfico de Gant"
|
|
),
|
|
messages.ERROR,
|
|
)
|
|
return redirect(change_url)
|
|
|
|
inicio = min(
|
|
cronograma[0].data_prevista_inicio,
|
|
cronograma[0].data_inicio or cronograma[0].data_prevista_inicio,
|
|
)
|
|
termino = max(
|
|
cronograma[-1].data_prevista_termino,
|
|
cronograma[-1].data_termino
|
|
or cronograma[-1].data_prevista_termino,
|
|
)
|
|
datas = [
|
|
inicio + datetime.timedelta(days=x)
|
|
for x in range((termino - inicio).days + 1)
|
|
]
|
|
context = {
|
|
"cronograma": cronograma,
|
|
"datas": datas,
|
|
"hoje": datetime.date.today(),
|
|
"title": evento.nome,
|
|
}
|
|
|
|
return WeasyTemplateResponse(
|
|
filename="grafico-gant.pdf",
|
|
request=request,
|
|
template="admin/eventos/evento/gant_report.html",
|
|
context=context,
|
|
content_type="application/pdf",
|
|
)
|
|
|
|
def checklist_report(self, request, object_id):
|
|
evento = get_object_or_404(Evento, id=object_id)
|
|
change_url = (
|
|
reverse(
|
|
"admin:%s_%s_change" % self.get_model_info(), args=[object_id]
|
|
)
|
|
+ "?"
|
|
+ self.get_preserved_filters(request)
|
|
)
|
|
cronograma = list(
|
|
evento.cronograma_set.order_by("data_prevista_inicio")
|
|
)
|
|
if not cronograma:
|
|
self.message_user(
|
|
request,
|
|
_(
|
|
"Não há um cronograma definido para a realização deste evento. Impossível gerar um checklist"
|
|
),
|
|
messages.ERROR,
|
|
)
|
|
return redirect(change_url)
|
|
|
|
context = {"cronograma": cronograma, "title": evento.nome}
|
|
return WeasyTemplateResponse(
|
|
filename="checklist.pdf",
|
|
request=request,
|
|
template="admin/eventos/evento/checklist_report.html",
|
|
context=context,
|
|
content_type="application/pdf",
|
|
)
|
|
|
|
def plano_comunicacao(self, request, object_id):
|
|
evento = get_object_or_404(Evento, id=object_id)
|
|
change_url = (
|
|
reverse(
|
|
"admin:%s_%s_change" % self.get_model_info(), args=[object_id]
|
|
)
|
|
+ "?"
|
|
+ self.get_preserved_filters(request)
|
|
)
|
|
cronograma = list(
|
|
evento.cronograma_set.order_by("data_prevista_inicio")
|
|
)
|
|
if not cronograma:
|
|
self.message_user(
|
|
request,
|
|
_(
|
|
"Não há um cronograma definido para a realização deste evento. Impossível gerar um plano de comunicação"
|
|
),
|
|
messages.ERROR,
|
|
)
|
|
return redirect(change_url)
|
|
|
|
matrix = {}
|
|
for etapa in cronograma:
|
|
for responsavel in etapa.responsaveis.splitlines():
|
|
if responsavel not in matrix:
|
|
matrix[responsavel] = {}
|
|
for destinatario in etapa.comunicar_inicio.splitlines():
|
|
if destinatario not in matrix[responsavel]:
|
|
matrix[responsavel][destinatario] = []
|
|
matrix[responsavel][destinatario].append(
|
|
_(f"Início da etapa {etapa.nome}")
|
|
)
|
|
for destinatario in etapa.comunicar_termino.splitlines():
|
|
if destinatario not in matrix[responsavel]:
|
|
matrix[responsavel][destinatario] = []
|
|
matrix[responsavel][destinatario].append(
|
|
_(f"Término da etapa {etapa.nome}")
|
|
)
|
|
responsaveis = list(matrix.keys())
|
|
destinatarios = list(
|
|
{x for xs in [v.keys() for v in matrix.values()] for x in xs}
|
|
)
|
|
responsaveis.sort()
|
|
destinatarios.sort()
|
|
matrix = {
|
|
resp: {
|
|
dest: matrix[resp][dest] if dest in matrix[resp] else []
|
|
for dest in destinatarios
|
|
}
|
|
for resp in responsaveis
|
|
}
|
|
context = {
|
|
"matrix": matrix,
|
|
"destinatarios": destinatarios,
|
|
"title": evento.nome,
|
|
}
|
|
return WeasyTemplateResponse(
|
|
filename="comunicação.pdf",
|
|
request=request,
|
|
template="admin/eventos/evento/plano_comunicacao.html",
|
|
context=context,
|
|
content_type="application/pdf",
|
|
)
|
|
|
|
def create_course(self, request, object_id):
|
|
evento = get_object_or_404(Evento, id=object_id)
|
|
change_url = (
|
|
reverse(
|
|
"admin:%s_%s_change" % self.get_model_info(), args=[object_id]
|
|
)
|
|
+ "?"
|
|
+ self.get_preserved_filters(request)
|
|
)
|
|
if evento.moodle_courseid is not None:
|
|
self.message_user(
|
|
request,
|
|
_("Este evento já tem curso associado no Saberes"),
|
|
level=messages.ERROR,
|
|
)
|
|
return redirect(change_url)
|
|
if (
|
|
evento.tipo_evento.moodle_template_courseid is None
|
|
or evento.tipo_evento.moodle_categoryid is None
|
|
):
|
|
self.message_user(
|
|
request,
|
|
_("Este tipo de evento não possui template no Saberes"),
|
|
level=messages.ERROR,
|
|
)
|
|
return redirect(change_url)
|
|
if evento.data_inicio is None or evento.data_termino is None:
|
|
self.message_user(
|
|
request,
|
|
_(
|
|
"O evento precisa ter datas de início e término para criar "
|
|
"curso no Saberes."
|
|
),
|
|
level=messages.ERROR,
|
|
)
|
|
return redirect(change_url)
|
|
|
|
if evento.turma == "":
|
|
self.message_user(
|
|
request,
|
|
_(
|
|
"Preencha (e salve!) o campo Turma para poder criar o "
|
|
"curso no Saberes"
|
|
),
|
|
level=messages.ERROR,
|
|
)
|
|
return redirect(change_url)
|
|
|
|
api_url = f"{settings.MOODLE_BASE_URL}/webservice/rest/server.php"
|
|
mws = Moodle(api_url, settings.MOODLE_API_TOKEN)
|
|
fullname = f"{evento.tipo_evento.nome} - {evento.casa_anfitria.municipio.nome}/{evento.casa_anfitria.municipio.uf.sigla} - {evento.tipo_evento.prefixo_turma}{evento.turma}"
|
|
shortname = f"{abreviatura(evento.tipo_evento.nome)} - {evento.tipo_evento.prefixo_turma}{evento.turma}"
|
|
inicio = int(time.mktime(evento.data_inicio.astimezone().timetuple()))
|
|
fim = int(time.mktime(evento.data_termino.astimezone().timetuple()))
|
|
erros = []
|
|
try: # Criar novo curso a partir do template
|
|
novo_curso = mws.core.course.duplicate_course(
|
|
evento.tipo_evento.moodle_template_courseid,
|
|
fullname=fullname,
|
|
shortname=shortname,
|
|
categoryid=evento.tipo_evento.moodle_categoryid,
|
|
visible=0,
|
|
)
|
|
evento.moodle_courseid = novo_curso.id
|
|
evento.save()
|
|
except Exception as e:
|
|
self.message_user(
|
|
request,
|
|
_(
|
|
"Ocorreu um erro ao criar o curso no Saberes com "
|
|
f"a mensagem {e.message}"
|
|
),
|
|
level=messages.ERROR,
|
|
)
|
|
return redirect(change_url)
|
|
try: # Atualiza configuração do curso
|
|
changes = {
|
|
"id": novo_curso.id,
|
|
"summary": evento.descricao,
|
|
"startdate": inicio,
|
|
"enddate": fim,
|
|
}
|
|
res = mws.core.course.update_courses([changes])
|
|
except Exception as e:
|
|
erros.append(
|
|
_(
|
|
"Falha na tentativa de alterar o sumário e as datas de "
|
|
"início e término do curso, com a seguinte mensagem: "
|
|
f"{e.message}"
|
|
)
|
|
)
|
|
try: # Matricular professores/membros
|
|
membros = evento.equipe_set.exclude(
|
|
membro__moodle_userid=None
|
|
).exclude(funcao__moodle_roleid=None)
|
|
equipe = []
|
|
for membro in membros:
|
|
equipe.append(
|
|
{
|
|
"roleid": membro.funcao.moodle_roleid,
|
|
"userid": membro.membro.moodle_userid,
|
|
"courseid": evento.moodle_courseid,
|
|
}
|
|
)
|
|
mws.enrol.manual.enrol_users(equipe)
|
|
except Exception as e:
|
|
erros.append(
|
|
_(
|
|
"Falha ao tentar inscrever a equipe no curso do Saberes, "
|
|
f"com a seguinte mensagem: {e.message}"
|
|
)
|
|
)
|
|
context = {
|
|
"evento": evento,
|
|
"fullname": fullname,
|
|
"shortname": shortname,
|
|
"membros": membros,
|
|
"erros": erros,
|
|
"opts": self.model._meta,
|
|
"preserved_filters": self.get_preserved_filters(request),
|
|
}
|
|
return render(
|
|
request, "admin/eventos/evento/createcourse.html", context
|
|
)
|
|
|
|
def update_participantes(self, request, object_id):
|
|
evento = get_object_or_404(Evento, id=object_id)
|
|
change_url = (
|
|
reverse(
|
|
"admin:%s_%s_change" % self.get_model_info(), args=[object_id]
|
|
)
|
|
+ "?"
|
|
+ self.get_preserved_filters(request)
|
|
)
|
|
if evento.moodle_courseid is None:
|
|
self.message_user(
|
|
request,
|
|
_("Este evento não tem curso associado no Saberes"),
|
|
level=messages.ERROR,
|
|
)
|
|
return redirect(change_url)
|
|
|
|
api_url = f"{settings.MOODLE_BASE_URL}/webservice/rest/server.php"
|
|
mws = Moodle(api_url, settings.MOODLE_API_TOKEN)
|
|
try:
|
|
inscritos = mws.post(
|
|
"core_enrol_get_enrolled_users",
|
|
courseid=evento.moodle_courseid,
|
|
)
|
|
except Exception as e:
|
|
self.message_user(
|
|
request,
|
|
_(
|
|
"Ocorreu um erro ao acessar o curso no Saberes com "
|
|
f"a mensagem {e.message}"
|
|
),
|
|
level=messages.ERROR,
|
|
)
|
|
return redirect(change_url)
|
|
evento.total_participantes = len(
|
|
list(
|
|
filter(
|
|
lambda u: any(
|
|
r["roleid"] in settings.MOODLE_STUDENT_ROLES
|
|
for r in u["roles"]
|
|
),
|
|
inscritos,
|
|
)
|
|
)
|
|
)
|
|
evento.save()
|
|
self.message_user(
|
|
request,
|
|
_(
|
|
f"Foram encontrados {evento.total_participantes} alunos "
|
|
"no Saberes"
|
|
),
|
|
level=messages.SUCCESS,
|
|
)
|
|
return redirect(change_url)
|
|
|