From 1dc831e39b992934b5ddd1a6bab1d0f6c3dc3335 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ses=C3=B3stris=20Vieira?= Date: Fri, 15 Jul 2022 23:42:16 -0300 Subject: [PATCH] Adicionar checklist e cronograma em eventos --- sigi/apps/eventos/admin.py | 172 +++++++++++++++-- .../migrations/0023_cronograma_checklist.py | 58 ++++++ sigi/apps/eventos/models.py | 177 +++++++++++++++++- .../admin/eventos/evento/change_form.html | 22 ++- .../eventos/evento/checklist_report.html | 61 ++++++ .../admin/eventos/evento/gant_report.html | 125 +++++++++++++ .../eventos/evento/gant_report_classes.html | 27 +++ .../eventos/evento/plano_comunicacao.html | 35 ++++ 8 files changed, 654 insertions(+), 23 deletions(-) create mode 100644 sigi/apps/eventos/migrations/0023_cronograma_checklist.py create mode 100644 sigi/apps/eventos/templates/admin/eventos/evento/checklist_report.html create mode 100644 sigi/apps/eventos/templates/admin/eventos/evento/gant_report.html create mode 100644 sigi/apps/eventos/templates/admin/eventos/evento/gant_report_classes.html create mode 100644 sigi/apps/eventos/templates/admin/eventos/evento/plano_comunicacao.html diff --git a/sigi/apps/eventos/admin.py b/sigi/apps/eventos/admin.py index 08a2af6..745bce9 100644 --- a/sigi/apps/eventos/admin.py +++ b/sigi/apps/eventos/admin.py @@ -1,10 +1,16 @@ +import datetime from django.contrib import admin from django.http import HttpResponseRedirect +from django.shortcuts import get_object_or_404, render +from django.urls import path from django.utils.translation import gettext as _ +from django_weasyprint.views import WeasyTemplateResponse from import_export.fields import Field from tinymce.models import HTMLField from tinymce.widgets import AdminTinyMCE from sigi.apps.eventos.models import ( + Checklist, + Cronograma, ModeloDeclaracao, Modulo, TipoEvento, @@ -84,9 +90,37 @@ class EventoResource(ValueLabeledResource): return "Sim" if obj["convite__participou"] else "Não" +class ChecklistInline(admin.StackedInline): + model = Checklist + + +class EquipeInline(admin.StackedInline): + model = Equipe + + +class ConviteInline(admin.StackedInline): + model = Convite + autocomplete_fields = ("casa",) + + +class ModuloInline(admin.StackedInline): + model = Modulo + + +class AnexoInline(admin.StackedInline): + model = Anexo + exclude = ("data_pub", "convite") + + +class CronogramaInline(admin.StackedInline): + model = Cronograma + extra = 0 + + @admin.register(TipoEvento) class TipoEventAdmin(admin.ModelAdmin): search_fields = ("nome",) + inlines = [ChecklistInline] @admin.register(Funcao) @@ -107,24 +141,6 @@ class ModeloDeclaracaoAdmin(admin.ModelAdmin): formfield_overrides = {HTMLField: {"widget": AdminTinyMCE}} -class EquipeInline(admin.StackedInline): - model = Equipe - - -class ConviteInline(admin.StackedInline): - model = Convite - autocomplete_fields = ("casa",) - - -class ModuloInline(admin.StackedInline): - model = Modulo - - -class AnexoInline(admin.StackedInline): - model = Anexo - exclude = ("data_pub", "convite") - - @admin.register(Evento) class EventoAdmin(CartExportMixin, admin.ModelAdmin): form = EventoAdminForm @@ -161,7 +177,13 @@ class EventoAdmin(CartExportMixin, admin.ModelAdmin): "municipio__search_text", "solicitante", ) - inlines = (EquipeInline, ConviteInline, ModuloInline, AnexoInline) + inlines = ( + EquipeInline, + ConviteInline, + ModuloInline, + AnexoInline, + CronogramaInline, + ) save_as = True def link_sigad(self, obj): @@ -179,3 +201,115 @@ class EventoAdmin(CartExportMixin, admin.ModelAdmin): "tipo_evento__nome__exact", "tipo_evento__nome__contains", ] + + def get_urls(self): + urls = super().get_urls() + my_urls = [ + path( + "/gant/", + self.admin_site.admin_view(self.gant_report), + name="%s_%s_gantreport" % self.get_model_info(), + ), + path( + "/checklist/", + self.admin_site.admin_view(self.checklist_report), + name="%s_%s_checklistreport" % self.get_model_info(), + ), + path( + "/comunicacao/", + self.admin_site.admin_view(self.plano_comunicacao), + name="%s_%s_comunicacaoreport" % self.get_model_info(), + ), + ] + return my_urls + urls + + def gant_report(self, request, object_id): + evento = get_object_or_404(Evento, id=object_id) + cronograma = list( + evento.cronograma_set.order_by("data_prevista_inicio") + ) + 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) + cronograma = list( + evento.cronograma_set.order_by("data_prevista_inicio") + ) + 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) + matrix = {} + for etapa in evento.cronograma_set.order_by("data_prevista_inicio"): + 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", + ) diff --git a/sigi/apps/eventos/migrations/0023_cronograma_checklist.py b/sigi/apps/eventos/migrations/0023_cronograma_checklist.py new file mode 100644 index 0000000..4523a17 --- /dev/null +++ b/sigi/apps/eventos/migrations/0023_cronograma_checklist.py @@ -0,0 +1,58 @@ +# Generated by Django 4.0.6 on 2022-07-16 02:40 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('eventos', '0022_alter_anexo_data_pub'), + ] + + operations = [ + migrations.CreateModel( + name='Cronograma', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('etapa', models.CharField(max_length=10, verbose_name='sigla da etapa')), + ('nome', models.CharField(max_length=100, verbose_name='nome da etapa')), + ('descricao', models.TextField(help_text='Descrição detalhada das atividades realizadas na etapa', verbose_name='descrição da etapa')), + ('duracao', models.PositiveBigIntegerField(verbose_name='duração (em dias)')), + ('data_prevista_inicio', models.DateField(blank=True, null=True, verbose_name='data prevista de início')), + ('data_prevista_termino', models.DateField(blank=True, null=True, verbose_name='data prevista de término')), + ('data_inicio', models.DateField(blank=True, null=True, verbose_name='data de início')), + ('data_termino', models.DateField(blank=True, null=True, verbose_name='data de término')), + ('dependencia', models.CharField(blank=True, help_text='Sigla da etapa que precisa ser concluída para que esta seja iniciada', max_length=200, verbose_name='depende da etapa')), + ('responsaveis', models.TextField(blank=True, help_text='Pessoas, setores, órgãos.', verbose_name='responsáveis pela tarefa')), + ('comunicar_inicio', models.TextField(blank=True, help_text='Lista de pessoas/órgãos para comunicar quando a tarefa for iniciada. Coloque um por linha.', verbose_name='comunicar inicio para')), + ('comunicar_termino', models.TextField(blank=True, help_text='Lista de pessoas/órgãos para comunicar quando a tarefa for concluída. Coloque um por linha.', verbose_name='comunicar término para')), + ('recursos', models.TextField(help_text='Lista de recursos necessários para desenvolver a tarefa', verbose_name='recursos necessários')), + ('evento', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='eventos.evento')), + ], + options={ + 'verbose_name': 'cronograma', + 'verbose_name_plural': 'cronogramas', + }, + ), + migrations.CreateModel( + name='Checklist', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('etapa', models.CharField(max_length=10, verbose_name='sigla da etapa')), + ('nome', models.CharField(max_length=100, verbose_name='nome da etapa')), + ('descricao', models.TextField(help_text='Descrição detalhada das atividades realizadas na etapa', verbose_name='descrição da etapa')), + ('duracao', models.PositiveBigIntegerField(verbose_name='duração (em dias)')), + ('dependencia', models.CharField(blank=True, help_text='Siglas das etapas que precisam ser concluídas para que esta seja iniciada. Separe cada uma com um espaço.', max_length=200, verbose_name='depende da etapa')), + ('responsaveis', models.TextField(blank=True, help_text='Pessoas, setores, órgãos.', verbose_name='responsáveis pela tarefa')), + ('comunicar_inicio', models.TextField(blank=True, help_text='Lista de e-mails para comunicar quando a tarefa for iniciada', verbose_name='comunicar inicio para')), + ('comunicar_termino', models.TextField(blank=True, help_text='Lista de e-mails para comunicar quando a tarefa for concluída', verbose_name='comunicar término para')), + ('recursos', models.TextField(help_text='Lista de recursos necessários para desenvolver a tarefa', verbose_name='recursos necessários')), + ('tipo_evento', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='eventos.tipoevento')), + ], + options={ + 'verbose_name': 'checklist', + 'verbose_name_plural': 'checklists', + }, + ), + ] diff --git a/sigi/apps/eventos/models.py b/sigi/apps/eventos/models.py index 0eabc35..cf94e57 100644 --- a/sigi/apps/eventos/models.py +++ b/sigi/apps/eventos/models.py @@ -1,3 +1,4 @@ +import datetime import re from django.db import models from django.db.models import Sum @@ -137,13 +138,69 @@ class Evento(models.Model): self.motivo_cancelamento = "" if self.data_inicio > self.data_termino: raise ValidationError( - _("Data de término deve ser posterior à " "data de início") + _("Data de término deve ser posterior à data de início") ) total = self.convite_set.aggregate(total=Sum("qtde_participantes")) total = total["total"] if total and total > 0: self.total_participantes = total - return super(Evento, self).save(*args, **kwargs) + if ( + self.cronograma_set.count() == 0 + and self.tipo_evento.checklist_set.count() > 0 + ): + cronograma_list = [] + for item in self.tipo_evento.checklist_set.all(): + cronograma_list.append( + Cronograma( + evento=self, + etapa=item.etapa, + nome=item.nome, + descricao=item.descricao, + duracao=item.duracao, + dependencia=item.dependencia, + responsaveis=item.responsaveis, + comunicar_inicio=item.comunicar_inicio, + comunicar_termino=item.comunicar_termino, + recursos=item.recursos, + ) + ) + self.calcula_datas(cronograma_list) + for item in cronograma_list: + item.save() + elif self.cronograma_set.count() > 0: + cronograma_list = self.cronograma_set.all() + self.calcula_datas(cronograma_list) + for item in cronograma_list: + item.save() + super().save(*args, **kwargs) + + def calcula_datas(self, cronograma_list): + def ajusta_data(elemento, data_termino): + if ( + elemento.data_prevista_termino is None + or elemento.data_prevista_termino > data_termino + ): + elemento.data_prevista_termino = data_termino + elemento.data_prevista_inicio = ( + elemento.data_prevista_termino + - datetime.timedelta(days=elemento.duracao - 1) + ) + for item in cronograma_list: + if item.etapa in elemento.dependencia: + ajusta_data( + item, + elemento.data_prevista_inicio + - datetime.timedelta(days=1), + ) + + leafs = [ + item + for item in cronograma_list + if len([d for d in cronograma_list if item.etapa in d.dependencia]) + == 0 + ] + for item in leafs: + ajusta_data(item, self.data_termino.date()) class Funcao(models.Model): @@ -331,3 +388,119 @@ class Anexo(models.Model): def __str__(self): return _(f"{self.descricao} publicado em {self.data_pub}") + + +class Checklist(models.Model): + tipo_evento = models.ForeignKey(TipoEvento, on_delete=models.CASCADE) + etapa = models.CharField(_("sigla da etapa"), max_length=10) + nome = models.CharField(_("nome da etapa"), max_length=100) + descricao = models.TextField( + _("descrição da etapa"), + help_text=_("Descrição detalhada das atividades realizadas na etapa"), + ) + duracao = models.PositiveBigIntegerField(_("duração (em dias)")) + dependencia = models.CharField( + _("depende da etapa"), + max_length=200, + help_text=_( + "Siglas das etapas que precisam ser concluídas para que esta seja iniciada. Separe cada uma com um espaço." + ), + blank=True, + ) + responsaveis = models.TextField( + _("responsáveis pela tarefa"), + help_text=_("Pessoas, setores, órgãos."), + blank=True, + ) + comunicar_inicio = models.TextField( + _("comunicar inicio para"), + help_text=_( + "Lista de e-mails para comunicar quando a tarefa for iniciada" + ), + blank=True, + ) + comunicar_termino = models.TextField( + _("comunicar término para"), + help_text=_( + "Lista de e-mails para comunicar quando a tarefa for concluída" + ), + blank=True, + ) + recursos = models.TextField( + _("recursos necessários"), + help_text="Lista de recursos necessários para desenvolver a tarefa", + ) + + class Meta: + verbose_name = _("checklist") + verbose_name_plural = _("checklists") + + def __str__(self): + return _(f"{self.etapa}: {self.nome}") + + +class Cronograma(models.Model): + evento = models.ForeignKey(Evento, on_delete=models.CASCADE) + etapa = models.CharField(_("sigla da etapa"), max_length=10) + nome = models.CharField(_("nome da etapa"), max_length=100) + descricao = models.TextField( + _("descrição da etapa"), + help_text=_("Descrição detalhada das atividades realizadas na etapa"), + ) + duracao = models.PositiveBigIntegerField(_("duração (em dias)")) + data_prevista_inicio = models.DateField( + _("data prevista de início"), blank=True, null=True + ) + data_prevista_termino = models.DateField( + _("data prevista de término"), blank=True, null=True + ) + data_inicio = models.DateField(_("data de início"), blank=True, null=True) + data_termino = models.DateField(_("data de término"), blank=True, null=True) + dependencia = models.CharField( + _("depende da etapa"), + max_length=200, + help_text=_( + "Sigla da etapa que precisa ser concluída para que esta seja iniciada" + ), + blank=True, + ) + responsaveis = models.TextField( + _("responsáveis pela tarefa"), + help_text=_("Pessoas, setores, órgãos."), + blank=True, + ) + comunicar_inicio = models.TextField( + _("comunicar inicio para"), + help_text=_( + "Lista de pessoas/órgãos para comunicar quando a tarefa for iniciada. Coloque um por linha." + ), + blank=True, + ) + comunicar_termino = models.TextField( + _("comunicar término para"), + help_text=_( + "Lista de pessoas/órgãos para comunicar quando a tarefa for concluída. Coloque um por linha." + ), + blank=True, + ) + recursos = models.TextField( + _("recursos necessários"), + help_text="Lista de recursos necessários para desenvolver a tarefa", + ) + + class Meta: + verbose_name = _("cronograma") + verbose_name_plural = _("cronogramas") + + def __str__(self): + return _(f"{self.etapa}: {self.nome}") + + def get_dependencias(self): + return self.evento.cronograma_set.filter( + etapa__in=self.dependencia.split(" ") + ) + + def get_dependentes(self): + return self.evento.cronograma_set.filter( + dependencia__icontains=self.etapa + ) diff --git a/sigi/apps/eventos/templates/admin/eventos/evento/change_form.html b/sigi/apps/eventos/templates/admin/eventos/evento/change_form.html index 5eb2d0b..27e7600 100644 --- a/sigi/apps/eventos/templates/admin/eventos/evento/change_form.html +++ b/sigi/apps/eventos/templates/admin/eventos/evento/change_form.html @@ -1,5 +1,5 @@ -{% extends "admin/change_form.html" %} -{% load i18n %} +{% extends "admin/tabs_change_form.html" %} +{% load i18n admin_urls %} {% block object-tools-items %}
  • @@ -8,5 +8,23 @@ {% trans "Declaração" %}
  • +
  • + + + {% trans "Gráfico de Gant" %} + +
  • +
  • + + + {% trans "Checklist" %} + +
  • +
  • + + + {% trans "Plano de comunicação" %} + +
  • {{ block.super }} {% endblock %} diff --git a/sigi/apps/eventos/templates/admin/eventos/evento/checklist_report.html b/sigi/apps/eventos/templates/admin/eventos/evento/checklist_report.html new file mode 100644 index 0000000..28fa15b --- /dev/null +++ b/sigi/apps/eventos/templates/admin/eventos/evento/checklist_report.html @@ -0,0 +1,61 @@ +{% extends 'pdf/base_report.html' %} +{% load static i18n %} + +{% block page_size %}A4 landscape{% endblock %} + +{% block main_content %} +

    {% trans 'Check list' %}

    + + + + + + + + + + + + + + + + + + + + + + + + {% for etapa in cronograma %} + + + + + + + + + + + + + + + + {% endfor %} +
    {% trans 'Etapa' %}{% trans 'Descrição' %}{% trans 'Duração (dias)' %}{% trans 'Previsão' %}{% trans 'Realizado' %}{% trans 'Depende de' %}{% trans 'Dependentes' %}{% trans 'Responsáveis' %}{% trans 'Comunicar' %}{% trans 'Recursos necessários' %}
    {% trans 'Início' %}{% trans 'Término' %}{% trans 'Início' %}{% trans 'Término' %}{% trans 'Início' %}{% trans 'Término' %}
    {{ etapa }}{{ etapa.descricao }}{{ etapa.duracao }}{{ etapa.data_prevista_inicio|date:"SHORT_DATE_FORMAT" }}{{ etapa.data_prevista_termino|date:"SHORT_DATE_FORMAT" }}{{ etapa.data_inicio|date:"SHORT_DATE_FORMAT" }}{{ etapa.data_termino|date:"SHORT_DATE_FORMAT" }} +
      + {% for e in etapa.get_dependencias %} +
    • {{ e }}
    • + {% endfor %} +
    +
    +
      + {% for e in etapa.get_dependentes %} +
    • {{ e }}
    • + {% endfor %} +
    +
    {{ etapa.responsaveis }}{{ etapa.comunicar_inicio }}{{ etapa.comunicar_termino }}{{ etapa.recursos }}
    +{% endblock %} \ No newline at end of file diff --git a/sigi/apps/eventos/templates/admin/eventos/evento/gant_report.html b/sigi/apps/eventos/templates/admin/eventos/evento/gant_report.html new file mode 100644 index 0000000..df17000 --- /dev/null +++ b/sigi/apps/eventos/templates/admin/eventos/evento/gant_report.html @@ -0,0 +1,125 @@ +{% extends 'pdf/base_report.html' %} +{% load static i18n %} + +{% block page_size %}A4 landscape{% endblock %} + +{% block extra_style %} + {{ block.super }} + .rotate { + text-align: center; + white-space: nowrap; + vertical-align: middle; + min-width: 5px; + width: 5px; + height: 6em; + } + .rotate div { + transform: rotate(-90.0deg); + margin-left: -10em; + margin-right: -10em; + #padding: 5px; + } + table, th, td { + border: 1px solid white; + width: 100%; + border-collapse: collapse; + } + th,td { + border-bottom: 1px solid #808080; + padding: 4px; + } + tr:nth-child(even) { + background-color: white; + } + + .atrasado { + background-color: #f44336; # red + } + .iniciou-atrasado { + background-color: #ff9800; # orange + } + .iniciar-hoje { + background-color: #ffe0b2; # orange-lighten-4 + } + .iniciado { + background-color: #4caf50; # green + } + .concluido-com-atraso { + background-color: #ffee58; # yellow-lighten-1 + } + .concluido-no-prazo { + background-color: #2196f3; # blue + } + .previsao { + background-color: #b0bec5; # blue-grey-lighten-2 + } + .hoje { + border: 1px solid black; + } + .inicio-previsto { + border-left: 3px solid #ffb74d; # orange lighten-2 + } + .inicio-real { + border-left: 3px solid #1b5e20; # green darken-4 + } + .inicio-previsto.inicio-real { + border-left: 3px solid #4caf50; # green + } + .termino-previsto { + border-right: 3px solid #0d47a1; # blue darken-4 + } + .termino-real { + border-right: 3px solid #1a237e; # indigo darken-4 + } + .termino-previsto.termino-real { + border-right: 3px solid #2196f3; # blue + } + .legenda { + display: flex; + } + .legenda, .legenda>div { + padding: 8px; + font-weight: bolder; + } + .legenda>div { + text-align: center; + margin-right: 24px; + } +{% endblock %} +{% block main_content %} +
    +
    Legenda:
    +
    {% trans "Planejado" %}
    +
    {% trans "Iniciar hoje" %}
    +
    {% trans "Iniciado" %}
    +
    {% trans "Iniciou atrasado" %}
    +
    {% trans "Atrasado" %}
    +
    {% trans "Concluído no prazo" %}
    +
    {% trans "Concluído com atraso" %}
    +
    +
    +
    Marcadores:
    +
    {% trans "Início planejado" %}
    +
    {% trans "Início real" %}
    +
    {% trans "Término planejado" %}
    +
    {% trans "Término real" %}
    +
    + + + + + {% for data in datas %} + + {% endfor %} + + + {% for etapa in cronograma %} + + + {% for data in datas %} + + {% endfor %} + + {% endfor %} +
    {% trans 'Etapa' %}
    {{ data|date:"SHORT_DATE_FORMAT" }}
    {{ etapa }}
    +{% endblock main_content %} \ No newline at end of file diff --git a/sigi/apps/eventos/templates/admin/eventos/evento/gant_report_classes.html b/sigi/apps/eventos/templates/admin/eventos/evento/gant_report_classes.html new file mode 100644 index 0000000..18dc846 --- /dev/null +++ b/sigi/apps/eventos/templates/admin/eventos/evento/gant_report_classes.html @@ -0,0 +1,27 @@ +{% spaceless %} + {% if data == hoje %} hoje{% endif %} + + {% if data >= etapa.data_prevista_inicio and data <= etapa.data_prevista_termino or data >= etapa.data_inicio and data <= etapa.data_termino %} + {% if not etapa.data_inicio and etapa.data_prevista_inicio == hoje %} + iniciar-hoje + {% elif not etapa.data_inicio and etapa.data_prevista_inicio < hoje %} + atrasado + {% elif etapa.data_termino and etapa.data_termino <= etapa.data_prevista_termino %} + concluido-no-prazo + {% elif etapa.data_termino and etapa.data_termino > etapa.data_prevista_termino %} + concluido-com-atraso + {% elif not etapa.data_termino and etapa.data_prevista_termino < hoje %} + atrasado + {% elif etapa.data_inicio and not etapa.data_termino and etapa.data_inicio <= etapa.data_prevista_inicio and etapa.data_prevista_termino <= hoje %} + iniciado + {% elif etapa.data_inicio and not etapa.data_termino and etapa.data_inicio > etapa.data_prevista_inicio and etapa.data_prevista_termino <= hoje %} + iniciou-atrasado + {% else %} + previsao + {% endif %} + {% endif %} + {% if data == etapa.data_prevista_inicio %} inicio-previsto{% endif %} + {% if data == etapa.data_prevista_termino %} termino-previsto{% endif %} + {% if data == etapa.data_inicio %} inicio-real{% endif %} + {% if data == etapa.data_termino %} termino-real{% endif %} +{% endspaceless %} \ No newline at end of file diff --git a/sigi/apps/eventos/templates/admin/eventos/evento/plano_comunicacao.html b/sigi/apps/eventos/templates/admin/eventos/evento/plano_comunicacao.html new file mode 100644 index 0000000..aed79c9 --- /dev/null +++ b/sigi/apps/eventos/templates/admin/eventos/evento/plano_comunicacao.html @@ -0,0 +1,35 @@ +{% extends 'pdf/base_report.html' %} +{% load static i18n %} + +{% block page_size %}A4 landscape{% endblock %} + +{% block main_content %} +

    {% trans 'Plano de comunicação' %}

    + + + + + + + + {% for d in destinatarios %} + + {% endfor %} + + + {% for resp, com in matrix.items %} + + + {% for d, lista in com.items %} + + {% endfor %} + + {% endfor %} +
    {% trans 'Responsável' %}{% trans 'Destinatários' %}
    {{ d }}
    {{ resp }} +
      + {% for item in lista %} +
    • {{ item }}
    • + {% endfor %} +
    +
    +{% endblock %} \ No newline at end of file