Browse Source

Adicionar checklist e cronograma em eventos

pull/159/head
Sesostris Vieira 2 years ago
parent
commit
1dc831e39b
  1. 172
      sigi/apps/eventos/admin.py
  2. 58
      sigi/apps/eventos/migrations/0023_cronograma_checklist.py
  3. 177
      sigi/apps/eventos/models.py
  4. 22
      sigi/apps/eventos/templates/admin/eventos/evento/change_form.html
  5. 61
      sigi/apps/eventos/templates/admin/eventos/evento/checklist_report.html
  6. 125
      sigi/apps/eventos/templates/admin/eventos/evento/gant_report.html
  7. 27
      sigi/apps/eventos/templates/admin/eventos/evento/gant_report_classes.html
  8. 35
      sigi/apps/eventos/templates/admin/eventos/evento/plano_comunicacao.html

172
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(
"<path:object_id>/gant/",
self.admin_site.admin_view(self.gant_report),
name="%s_%s_gantreport" % self.get_model_info(),
),
path(
"<path:object_id>/checklist/",
self.admin_site.admin_view(self.checklist_report),
name="%s_%s_checklistreport" % self.get_model_info(),
),
path(
"<path:object_id>/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",
)

58
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',
},
),
]

177
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
)

22
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 %}
<li>
@ -8,5 +8,23 @@
{% trans "Declaração" %}
</a>
</li>
<li>
<a href="{% url opts|admin_urlname:'gantreport' object_id %}">
<i class="left material-icons" aria-hidden="true">insert_chart</i>
{% trans "Gráfico de Gant" %}
</a>
</li>
<li>
<a href="{% url opts|admin_urlname:'checklistreport' object_id %}">
<i class="left material-icons" aria-hidden="true">picture_as_pdf</i>
{% trans "Checklist" %}
</a>
</li>
<li>
<a href="{% url opts|admin_urlname:'comunicacaoreport' object_id %}">
<i class="left material-icons" aria-hidden="true">picture_as_pdf</i>
{% trans "Plano de comunicação" %}
</a>
</li>
{{ block.super }}
{% endblock %}

61
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 %}
<h3>{% trans 'Check list' %}</h3>
<table repeat="1">
<thead>
<tr>
<th rowspan="2">{% trans 'Etapa' %}</th>
<th rowspan="2">{% trans 'Descrição' %}</th>
<th rowspan="2">{% trans 'Duração (dias)' %}</th>
<th colspan="2">{% trans 'Previsão' %}</th>
<th colspan="2">{% trans 'Realizado' %}</th>
<th rowspan="2">{% trans 'Depende de' %}</th>
<th rowspan="2">{% trans 'Dependentes' %}</th>
<th rowspan="2">{% trans 'Responsáveis' %}</th>
<th colspan="2">{% trans 'Comunicar' %}</th>
<th rowspan="2">{% trans 'Recursos necessários' %}</th>
</tr>
<tr>
<th>{% trans 'Início' %}</th>
<th>{% trans 'Término' %}</th>
<th>{% trans 'Início' %}</th>
<th>{% trans 'Término' %}</th>
<th>{% trans 'Início' %}</th>
<th>{% trans 'Término' %}</th>
</tr>
</thead>
{% for etapa in cronograma %}
<tr>
<td>{{ etapa }}</td>
<td>{{ etapa.descricao }}</td>
<td class="center-align">{{ etapa.duracao }}</td>
<td class="center-align">{{ etapa.data_prevista_inicio|date:"SHORT_DATE_FORMAT" }}</td>
<td class="center-align">{{ etapa.data_prevista_termino|date:"SHORT_DATE_FORMAT" }}</td>
<td class="center-align">{{ etapa.data_inicio|date:"SHORT_DATE_FORMAT" }}</td>
<td class="center-align">{{ etapa.data_termino|date:"SHORT_DATE_FORMAT" }}</td>
<td>
<ul>
{% for e in etapa.get_dependencias %}
<li>{{ e }}</li>
{% endfor %}
</ul>
</td>
<td>
<ul>
{% for e in etapa.get_dependentes %}
<li>{{ e }}</li>
{% endfor %}
</ul>
</td>
<td>{{ etapa.responsaveis }}</td>
<td>{{ etapa.comunicar_inicio }}</td>
<td>{{ etapa.comunicar_termino }}</td>
<td>{{ etapa.recursos }}</td>
</tr>
{% endfor %}
</table>
{% endblock %}

125
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 %}
<div class="legenda">
<div>Legenda:</div>
<div class="previsao">{% trans "Planejado" %}</div>
<div class="iniciar-hoje">{% trans "Iniciar hoje" %}</div>
<div class="iniciado">{% trans "Iniciado" %}</div>
<div class="iniciou-atrasado">{% trans "Iniciou atrasado" %}</div>
<div class="atrasado">{% trans "Atrasado" %}</div>
<div class="concluido-no-prazo">{% trans "Concluído no prazo" %}</div>
<div class="concluido-com-atraso">{% trans "Concluído com atraso" %}</div>
</div>
<div class="legenda">
<div>Marcadores:</div>
<div class="inicio-previsto">{% trans "Início planejado" %}</div>
<div class="inicio-real">{% trans "Início real" %}</div>
<div class="termino-previsto">{% trans "Término planejado" %}</div>
<div class="termino-real">{% trans "Término real" %}</div>
</div>
<table repeat="1">
<thead>
<tr>
<th>{% trans 'Etapa' %}</th>
{% for data in datas %}
<th class="rotate{% if data == hoje %} hoje{% endif %}"><div>{{ data|date:"SHORT_DATE_FORMAT" }}</div></th>
{% endfor %}
</tr>
</thead>
{% for etapa in cronograma %}
<tr>
<td>{{ etapa }}</td>
{% for data in datas %}
<td class="{% include 'admin/eventos/evento/gant_report_classes.html' %}"></td>
{% endfor %}
</tr>
{% endfor %}
</table>
{% endblock main_content %}

27
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 %}

35
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 %}
<h3>{% trans 'Plano de comunicação' %}</h3>
<table repeat="1">
<thead>
<tr>
<th rowspan="2">{% trans 'Responsável' %}</th>
<th colspan="{{ destinatarios|length }}">{% trans 'Destinatários' %}</th>
</tr>
<tr>
{% for d in destinatarios %}
<th>{{ d }}</th>
{% endfor %}
</tr>
</thead>
{% for resp, com in matrix.items %}
<tr>
<td>{{ resp }}</td>
{% for d, lista in com.items %}
<td>
<ul>
{% for item in lista %}
<li>{{ item }}</li>
{% endfor %}
</ul>
</td>
{% endfor %}
</tr>
{% endfor %}
</table>
{% endblock %}
Loading…
Cancel
Save