From 40d5bea5bde172f14d76526c4be3d91b747ecaf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ses=C3=B3stris=20Vieira?= Date: Sat, 20 Nov 2021 21:43:20 -0300 Subject: [PATCH 1/3] Fixes #104 --- requirements/requirements.txt | 1 + sigi/apps/eventos/admin.py | 6 ++- sigi/apps/eventos/forms.py | 12 +++++ .../migrations/0013_modelodeclaracao.py | 30 +++++++++++++ sigi/apps/eventos/models.py | 43 ++++++++++++++++++ .../admin/eventos/evento/change_form.html | 10 +++++ .../eventos/{ => evento}/change_list.html | 0 .../templates/eventos/declaracao_pdf.html | 24 ++++++++++ .../templates/eventos/seleciona_modelo.html | 25 +++++++++++ sigi/apps/eventos/urls.py | 3 ++ sigi/apps/eventos/views.py | 44 +++++++++++++++++-- sigi/apps/home/templatetags/menu_conf.yaml | 2 + sigi/settings/base.py | 10 ++++- sigi/shortcuts.py | 14 +++--- sigi/urls.py | 1 + templates/base_report.html | 14 +++--- 16 files changed, 219 insertions(+), 20 deletions(-) create mode 100644 sigi/apps/eventos/forms.py create mode 100644 sigi/apps/eventos/migrations/0013_modelodeclaracao.py create mode 100644 sigi/apps/eventos/templates/admin/eventos/evento/change_form.html rename sigi/apps/eventos/templates/admin/eventos/{ => evento}/change_list.html (100%) create mode 100644 sigi/apps/eventos/templates/eventos/declaracao_pdf.html create mode 100644 sigi/apps/eventos/templates/eventos/seleciona_modelo.html diff --git a/requirements/requirements.txt b/requirements/requirements.txt index abfd1d4..ab6351f 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -21,3 +21,4 @@ requests==2.8.1 six==1.10.0 djangorestframework==2.4.8 django-ipware==1.1.6 +django-tinymce==2.6.0 \ No newline at end of file diff --git a/sigi/apps/eventos/admin.py b/sigi/apps/eventos/admin.py index 63ded3e..d6d9d03 100644 --- a/sigi/apps/eventos/admin.py +++ b/sigi/apps/eventos/admin.py @@ -23,7 +23,7 @@ from django.contrib import admin from django.db import models from django.http import HttpResponseRedirect from django.utils.translation import ugettext as _ -from sigi.apps.eventos.models import Modulo, TipoEvento, Funcao, Evento, Equipe, Convite +from sigi.apps.eventos.models import ModeloDeclaracao, Modulo, TipoEvento, Funcao, Evento, Equipe, Convite from sigi.apps.eventos.views import adicionar_eventos_carrinho class EventoAdminForm(forms.ModelForm): @@ -55,6 +55,10 @@ class FuncaoAdmin(admin.ModelAdmin): list_display = ('nome', 'descricao',) search_fields = ('nome', 'descricao',) +@admin.register(ModeloDeclaracao) +class ModeloDeclaracaoAdmin(admin.ModelAdmin): + list_display = ('nome', 'formato') + class EquipeInline(admin.TabularInline): model = Equipe diff --git a/sigi/apps/eventos/forms.py b/sigi/apps/eventos/forms.py new file mode 100644 index 0000000..5804aa0 --- /dev/null +++ b/sigi/apps/eventos/forms.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- + +from django import forms +from django.utils.translation import ugettext as _ +from sigi.apps.eventos.models import ModeloDeclaracao + +class SelecionaModeloForm(forms.Form): + modelo = forms.ModelChoiceField( + queryset=ModeloDeclaracao.objects.all(), + required=True, + label=_(u"Modelo de declaração"), + ) \ No newline at end of file diff --git a/sigi/apps/eventos/migrations/0013_modelodeclaracao.py b/sigi/apps/eventos/migrations/0013_modelodeclaracao.py new file mode 100644 index 0000000..a8d9e4b --- /dev/null +++ b/sigi/apps/eventos/migrations/0013_modelodeclaracao.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +import tinymce.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('eventos', '0012_auto_20211117_0657'), + ] + + operations = [ + migrations.CreateModel( + name='ModeloDeclaracao', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('nome', models.CharField(max_length=100, verbose_name='Nome do modelo')), + ('formato', models.CharField(default=b'A4 portrait', max_length=30, verbose_name='Formato da p\xe1gina', choices=[(b'A4 portrait', 'A4 retrato'), (b'A4 landscape', 'A4 paisagem'), (b'letter portrait', 'Carta retrato'), (b'letter landscape', 'Carta paisagem')])), + ('margem', models.PositiveIntegerField(default=4, help_text='Margem da p\xe1gina em cent\xedmetros', verbose_name='Margem')), + ('texto', tinymce.models.HTMLField(help_text='Use as seguintes marca\xe7\xf5es:', verbose_name='Texto da declara\xe7\xe3o')), + ], + options={ + 'verbose_name': 'modelo de declara\xe7\xe3o', + 'verbose_name_plural': 'modelos de declara\xe7\xe3o', + }, + bases=(models.Model,), + ), + ] diff --git a/sigi/apps/eventos/models.py b/sigi/apps/eventos/models.py index f9acacd..9a11315 100644 --- a/sigi/apps/eventos/models.py +++ b/sigi/apps/eventos/models.py @@ -10,6 +10,7 @@ from sigi.apps.casas.models import Orgao from sigi.apps.contatos.models import Municipio from sigi.apps.servidores.models import Servidor from django.core.exceptions import ValidationError +from tinymce.models import HTMLField class TipoEvento(models.Model): CATEGORIA_CHOICES = ( @@ -239,3 +240,45 @@ class Modulo(models.Model): nome=self.nome, tipo=self.get_tipo_display() ) + +class ModeloDeclaracao(models.Model): + FORMATO_CHOICES = ( + ('A4 portrait', _(u"A4 retrato")), + ('A4 landscape', _(u"A4 paisagem")), + ('letter portrait', _(u"Carta retrato")), + ('letter landscape', _(u"Carta paisagem")) + ) + nome = models.CharField(_(u"Nome do modelo"), max_length=100) + formato = models.CharField( + _(u"Formato da página"), + max_length=30, + choices=FORMATO_CHOICES, + default=FORMATO_CHOICES[0][0] + ) + margem = models.PositiveIntegerField( + _(u"Margem"), + help_text=_(u"Margem da página em centímetros"), + default=4 + ) + texto = HTMLField( + _(u"Texto da declaração"), + help_text=_(u"Use as seguintes marcações:") + ) + + class Meta: + verbose_name = _(u"modelo de declaração") + verbose_name_plural = _(u"modelos de declaração") + + def __unicode__(self): + return _(u"{nome} ({formato})").format( + nome=self.nome, + formato=self.get_formato_display() + ) diff --git a/sigi/apps/eventos/templates/admin/eventos/evento/change_form.html b/sigi/apps/eventos/templates/admin/eventos/evento/change_form.html new file mode 100644 index 0000000..5dc8236 --- /dev/null +++ b/sigi/apps/eventos/templates/admin/eventos/evento/change_form.html @@ -0,0 +1,10 @@ +{% extends "base_change_form.html" %} +{% load i18n %} + +{% block object-tools-items %} +
  • + + {% trans 'Declaração' %} +
  • + {{ block.super }} +{% endblock %} diff --git a/sigi/apps/eventos/templates/admin/eventos/change_list.html b/sigi/apps/eventos/templates/admin/eventos/evento/change_list.html similarity index 100% rename from sigi/apps/eventos/templates/admin/eventos/change_list.html rename to sigi/apps/eventos/templates/admin/eventos/evento/change_list.html diff --git a/sigi/apps/eventos/templates/eventos/declaracao_pdf.html b/sigi/apps/eventos/templates/eventos/declaracao_pdf.html new file mode 100644 index 0000000..ec00d5d --- /dev/null +++ b/sigi/apps/eventos/templates/eventos/declaracao_pdf.html @@ -0,0 +1,24 @@ +{% extends 'base_report.html' %} +{% load i18n %} + +{% block pagesize %}{{ pagesize }}{% endblock pagesize %} +{% block pagemargin %}4cm {{ pagemargin }}cm {{ pagemargin }}cm 2cm{% endblock pagemargin %} + +{% block report %} + {% for convite in evento.convite_set.all %} + {% with convite.casa.nome as casa %} + {% for nome in convite.nomes_participantes.splitlines %} + {% block text_body %}{% endblock %} + + {% endfor %} + {% endwith %} + {% endfor %} +{% endblock %} + +{%block page_foot%} + + + + +
    +{% endblock %} diff --git a/sigi/apps/eventos/templates/eventos/seleciona_modelo.html b/sigi/apps/eventos/templates/eventos/seleciona_modelo.html new file mode 100644 index 0000000..c3180f0 --- /dev/null +++ b/sigi/apps/eventos/templates/eventos/seleciona_modelo.html @@ -0,0 +1,25 @@ +{% extends "admin/base_site.html" %} +{% load i18n bootstrap3 %} + +{% block content_title %} +

    {% trans 'Emitir declaração de comparecimento' %}

    +{% endblock %} + +{% block content %} +{% if error %} + +{% endif %} +
    +
    {% csrf_token %} + {% csrf_token %} +
    + {% bootstrap_form form %} +
    + + {% trans "Voltar" %} +
    +
    +{% endblock %} + diff --git a/sigi/apps/eventos/urls.py b/sigi/apps/eventos/urls.py index 05af590..2d0a8f3 100644 --- a/sigi/apps/eventos/urls.py +++ b/sigi/apps/eventos/urls.py @@ -15,5 +15,8 @@ urlpatterns = patterns( url(r'^evento/carrinho/deleta_itens_carrinho$', 'deleta_itens_carrinho', name='deleta-itens-carrinho-evento'), # Error url(r'^evento/csv/$', 'export_csv', name='evento-export-csv'), # Error + url(r'^evento/(?P\w+)/declaracao/$', 'declaracao', + name='evento-declaracao'), + ) diff --git a/sigi/apps/eventos/views.py b/sigi/apps/eventos/views.py index dc530e5..a7f3184 100644 --- a/sigi/apps/eventos/views.py +++ b/sigi/apps/eventos/views.py @@ -21,18 +21,21 @@ import calendar import datetime import locale +import csv +from django import template from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidden from django.core.paginator import Paginator, InvalidPage, EmptyPage from django.contrib import messages from django.contrib.auth.decorators import login_required -from django.shortcuts import render +from django.shortcuts import get_object_or_404, render from django.utils import translation from django.utils.translation import ungettext, ugettext as _ +from django.http.response import JsonResponse, HttpResponse +from django.template import Template, Context from sigi.apps.eventos.models import Evento, Equipe, Convite +from sigi.apps.eventos.forms import SelecionaModeloForm from sigi.apps.servidores.models import Servidor -from sigi.shortcuts import render_to_pdf -import csv -from django.http.response import JsonResponse, HttpResponse +from sigi.shortcuts import render_to_pdf, pdf_renderer @login_required def calendario(request): @@ -369,3 +372,36 @@ def export_csv(request): return response +@login_required +def declaracao(request, id): + if request.method == 'POST': + form = SelecionaModeloForm(request.POST) + if form.is_valid(): + evento = get_object_or_404(Evento, id=id) + modelo = form.cleaned_data['modelo'] + template_string = ( + """ + {% extends "eventos/declaracao_pdf.html" %} + {% block text_body %}""" + + modelo.texto + """ + {% endblock %} + """ + ) + context = Context( + {'pagesize': modelo.formato, + 'pagemargin': modelo.margem, + 'evento': evento, + 'data': datetime.date.today(), + } + ) + template = Template(template_string) + # return HttpResponse(template.render(context)) + return pdf_renderer(template, context, 'declaracao.pdf') + else: + form = SelecionaModeloForm() + + return render( + request, + 'eventos/seleciona_modelo.html', + {'form': form, 'evento_id': id} + ) \ No newline at end of file diff --git a/sigi/apps/home/templatetags/menu_conf.yaml b/sigi/apps/home/templatetags/menu_conf.yaml index 4d3f9f0..a230afd 100644 --- a/sigi/apps/home/templatetags/menu_conf.yaml +++ b/sigi/apps/home/templatetags/menu_conf.yaml @@ -85,6 +85,8 @@ main_menu: url: eventos/tipoevento/ - title: Funções na equipe de eventos url: eventos/funcao/ + - title: Modelos de declaração + url: eventos/modelodeclaracao/ # Removidos diff --git a/sigi/settings/base.py b/sigi/settings/base.py index e123cbc..cd57c67 100644 --- a/sigi/settings/base.py +++ b/sigi/settings/base.py @@ -74,7 +74,7 @@ INSTALLED_APPS = ( 'easy_thumbnails', 'image_cropping', 'rest_framework', - + 'tinymce', ) MIDDLEWARE_CLASSES = ( @@ -188,3 +188,11 @@ REST_FRAMEWORK = { WHOIS_WHITELIST = [ '127.0.0.1', ] + +# TinyMCE configuration +TINYMCE_SPELLCHECKER = True +TINYMCE_COMPRESSOR = True +TINYMCE_DEFAULT_CONFIG = { + 'theme': "advanced", + "height": 500, +} \ No newline at end of file diff --git a/sigi/shortcuts.py b/sigi/shortcuts.py index d27986c..4f31978 100644 --- a/sigi/shortcuts.py +++ b/sigi/shortcuts.py @@ -24,12 +24,7 @@ def fetch_resources(uri, rel): uri.replace(settings.MEDIA_URL, "")) return path - -def render_to_pdf(template_src, context_dict): - filename = template_src.replace('.html', '').replace('_pdf', '.pdf') - template = get_template(template_src) - context = Context(context_dict) - +def pdf_renderer(template, context, filename='report.pdf'): html = template.render(context) response = HttpResponse(content_type='application/pdf') @@ -41,3 +36,10 @@ def render_to_pdf(template_src, context_dict): if pdf.err: return HttpResponse(_(u'We had some errors
    %s
    ') % escape(html)) return response + +def render_to_pdf(template_src, context_dict): + filename = template_src.replace('.html', '').replace('_pdf', '.pdf') + template = get_template(template_src) + context = Context(context_dict) + + return pdf_renderer(template, context,filename) \ No newline at end of file diff --git a/sigi/urls.py b/sigi/urls.py index 7998344..d269737 100644 --- a/sigi/urls.py +++ b/sigi/urls.py @@ -20,6 +20,7 @@ urlpatterns = patterns( url(r'^ocorrencias/', include('sigi.apps.ocorrencias.urls')), url(r'^eventos/', include('sigi.apps.eventos.urls')), url(r'^whois/', include('sigi.apps.whois.urls')), + url(r'^tinymce/', include('tinymce.urls')), url(r'^', include('sigi.apps.home.urls')), url(r'^', include(admin.site.urls)), diff --git a/templates/base_report.html b/templates/base_report.html index c5cc6e5..96e4495 100644 --- a/templates/base_report.html +++ b/templates/base_report.html @@ -11,11 +11,9 @@ text-align: center; } td.header_text p { - margin: 0px; - font-size: 1.4em; - } - td.header_text { - width: 550px; + margin: 5px; + font-size: 1.2em; + text-align: center; } h1 { font-size: 2em; @@ -58,12 +56,12 @@ } @page { size: {% block pagesize %}{{ pagesize }}{% endblock pagesize %}; - margin: 4cm 1cm 1cm 2cm; + margin: {% block pagemargin %}4cm 1cm 1cm 2cm{% endblock pagemargin %}; font-family: "Helvetica, Arial, sans-serif"; font-size: 2em; @frame header { -pdf-frame-content: header; - top: 1cm; + {% block header-settings %}top: 1cm;{% endblock header-settings %} } @frame footer { -pdf-frame-content: footer; @@ -84,7 +82,7 @@

    {% trans 'SENADO FEDERAL' %}

    -

    {% trans 'ILB - PROGRAMA INTERLEGIS' %}

    +

    {% trans 'INSTITUTO LEGISLATIVO BRASILEIRO - ILB / INTERLEGIS' %}

    {% block subsecretaria %}{% endblock %}

    From 76fe971d3e01cb14be4450f9d318493b4651d0be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ses=C3=B3stris=20Vieira?= Date: Sun, 21 Nov 2021 11:52:54 -0300 Subject: [PATCH 2/3] Move form de admin.py para forms.py --- sigi/apps/eventos/admin.py | 21 +-------------------- sigi/apps/eventos/forms.py | 22 +++++++++++++++++++++- 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/sigi/apps/eventos/admin.py b/sigi/apps/eventos/admin.py index d6d9d03..048ce57 100644 --- a/sigi/apps/eventos/admin.py +++ b/sigi/apps/eventos/admin.py @@ -25,26 +25,7 @@ from django.http import HttpResponseRedirect from django.utils.translation import ugettext as _ from sigi.apps.eventos.models import ModeloDeclaracao, Modulo, TipoEvento, Funcao, Evento, Equipe, Convite from sigi.apps.eventos.views import adicionar_eventos_carrinho - -class EventoAdminForm(forms.ModelForm): - class Meta: - model = Evento - fields = ('tipo_evento', 'nome', 'descricao', 'virtual', 'solicitante', - 'data_inicio', 'data_termino', 'carga_horaria', - 'casa_anfitria', 'municipio', 'local', 'publico_alvo', - 'total_participantes', 'status', 'data_cancelamento', - 'motivo_cancelamento', ) - - def clean(self): - cleaned_data = super(EventoAdminForm, self).clean() - data_inicio = cleaned_data.get("data_inicio") - data_termino = cleaned_data.get("data_termino") - - if data_inicio > data_termino: - raise forms.ValidationError( - _(u"Data término deve ser posterior à data inicio"), - code="invalid_period" - ) +from sigi.apps.eventos.forms import EventoAdminForm @admin.register(TipoEvento) class TipoEventAdmin(admin.ModelAdmin): diff --git a/sigi/apps/eventos/forms.py b/sigi/apps/eventos/forms.py index 5804aa0..7ec8869 100644 --- a/sigi/apps/eventos/forms.py +++ b/sigi/apps/eventos/forms.py @@ -2,7 +2,27 @@ from django import forms from django.utils.translation import ugettext as _ -from sigi.apps.eventos.models import ModeloDeclaracao +from sigi.apps.eventos.models import ModeloDeclaracao, Evento + +class EventoAdminForm(forms.ModelForm): + class Meta: + model = Evento + fields = ('tipo_evento', 'nome', 'descricao', 'virtual', 'solicitante', + 'data_inicio', 'data_termino', 'carga_horaria', + 'casa_anfitria', 'municipio', 'local', 'publico_alvo', + 'total_participantes', 'status', 'data_cancelamento', + 'motivo_cancelamento', ) + + def clean(self): + cleaned_data = super(EventoAdminForm, self).clean() + data_inicio = cleaned_data.get("data_inicio") + data_termino = cleaned_data.get("data_termino") + + if data_inicio > data_termino: + raise forms.ValidationError( + _(u"Data término deve ser posterior à data inicio"), + code="invalid_period" + ) class SelecionaModeloForm(forms.Form): modelo = forms.ModelChoiceField( From 6f7fa20950b715ee63fb8a154b1c2c45652abdbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ses=C3=B3stris=20Vieira?= Date: Mon, 22 Nov 2021 10:17:50 -0300 Subject: [PATCH 3/3] Fixes #114 --- sigi/apps/eventos/views.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sigi/apps/eventos/views.py b/sigi/apps/eventos/views.py index a7f3184..8a3adb5 100644 --- a/sigi/apps/eventos/views.py +++ b/sigi/apps/eventos/views.py @@ -87,10 +87,10 @@ def calendario(request): for linha in linhas: sobrepoe = False for e in linha: - if (((evento['evento'].data_inicio >= e['evento'].data_inicio) and - (evento['evento'].data_inicio <= e['evento'].data_termino)) or - ((evento['evento'].data_termino >= e['evento'].data_inicio) and - (evento['evento'].data_termino <= e['evento'].data_termino))): + if (((evento['evento'].data_inicio.date() >= e['evento'].data_inicio.date()) and + (evento['evento'].data_inicio.date() <= e['evento'].data_termino.date())) or + ((evento['evento'].data_termino.date() >= e['evento'].data_inicio.date()) and + (evento['evento'].data_termino.date() <= e['evento'].data_termino.date()))): sobrepoe = True break if not sobrepoe: @@ -109,7 +109,7 @@ def calendario(request): if anterior is None: anterior = evento continue - anterior['close'] = (evento['evento'].data_inicio - anterior['evento'].data_termino).days-1 + anterior['close'] = (evento['evento'].data_inicio.date() - anterior['evento'].data_termino.date()).days-1 evento['start'] = 0 anterior = evento