diff --git a/sigi/apps/eventos/admin.py b/sigi/apps/eventos/admin.py
index 088d82c..e82717e 100644
--- a/sigi/apps/eventos/admin.py
+++ b/sigi/apps/eventos/admin.py
@@ -18,10 +18,12 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-from django.contrib import admin
from django import forms
+from django.contrib import admin
+from django.http import HttpResponseRedirect
from django.utils.translation import ugettext as _
from sigi.apps.eventos.models import TipoEvento, Funcao, Evento, Equipe, Convite
+from sigi.apps.eventos.views import adicionar_eventos_carrinho
class EventoAdminForm(forms.ModelForm):
class Meta:
@@ -69,4 +71,23 @@ class EventoAdmin(admin.ModelAdmin):
raw_id_fields = ('casa_anfitria', 'municipio',)
search_fields = ('nome', 'tipo_evento__nome', 'casa_anfitria__search_text',
'municipio__search_text', 'solicitante')
- inlines = (EquipeInline, ConviteInline)
\ No newline at end of file
+ inlines = (EquipeInline, ConviteInline)
+ actions = ['adicionar_eventos', ]
+
+ def adicionar_eventos(self, request, queryset):
+ if 'carrinho_eventos' in request.session:
+ q1 = len(request.session['carrinho_eventos'])
+ else:
+ q1 = 0
+ response = adicionar_eventos_carrinho(request, queryset=queryset)
+ q2 = len(request.session['carrinho_eventos'])
+ quant = q2 - q1
+ if quant:
+ self.message_user(request, str(q2 - q1) + " " +
+ _(u"Eventos adicionados no carrinho"))
+ else:
+ self.message_user(request, _(u"Os Eventos selecionados "
+ u"já foram adicionados anteriormente"))
+ return HttpResponseRedirect('.')
+ adicionar_eventos.short_description = _(u"Armazenar eventos no carrinho "
+ u"para exportar")
diff --git a/sigi/apps/eventos/templates/admin/eventos/change_list.html b/sigi/apps/eventos/templates/admin/eventos/change_list.html
new file mode 100644
index 0000000..83ebd55
--- /dev/null
+++ b/sigi/apps/eventos/templates/admin/eventos/change_list.html
@@ -0,0 +1 @@
+{% extends "change_list_with_cart.html" %}
\ No newline at end of file
diff --git a/sigi/apps/eventos/templates/eventos/carrinho.html b/sigi/apps/eventos/templates/eventos/carrinho.html
new file mode 100644
index 0000000..554f8b4
--- /dev/null
+++ b/sigi/apps/eventos/templates/eventos/carrinho.html
@@ -0,0 +1,63 @@
+{% extends "admin/carrinho.html" %}
+{% load admin_list i18n %}
+{% block extrastyle %}
+ {{ block.super }}
+ {#% include "admin/tabs_style.html" %#}
+{% endblock %}
+
+{% block title %}{% trans 'Eventos no Carrinho | SIGI' %}{% endblock %}
+{% block content_title %}
{% trans 'Eventos no Carrinho' %}
{% endblock %}
+
+{% block mensagem%}
+
+ {%if carIsEmpty%}
+ - {% trans 'O carrinho está vazio, sendo assim todos os eventos entram na lista para exportação de acordo com os filtros aplicados.' %}
+ {%else%}
+ - {{paginas.paginator.count}} {% trans 'Eventos no carrinho' %}.
+ {%endif%}
+
+{% endblock %}
+
+{% block action %}deleta_itens_carrinho{% endblock %}
+
+{% block tabela %}
+
+{% endblock %}
+
+{% block botoes %}
+{% trans "Exportar CVS" %}
+{% endblock %}
\ No newline at end of file
diff --git a/sigi/apps/eventos/urls.py b/sigi/apps/eventos/urls.py
index a648c7c..05af590 100644
--- a/sigi/apps/eventos/urls.py
+++ b/sigi/apps/eventos/urls.py
@@ -7,4 +7,13 @@ urlpatterns = patterns(
# Painel de ocorrencias
url(r'^calendario/$', 'calendario', name='eventos-calendario'),
url(r'^alocacaoequipe/$', 'alocacao_equipe', name='eventos-alocacaoequipe'),
+ # Carrinho
+ url(r'^evento/carrinho/$', 'visualizar_carrinho',
+ name='visualizar-carrinho-evento'),
+ url(r'^evento/carrinho/excluir_carrinho/$', 'excluir_carrinho',
+ name='excluir-carrinho-evento'), # Error
+ 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
+
)
diff --git a/sigi/apps/eventos/views.py b/sigi/apps/eventos/views.py
index e6ab84b..164f644 100644
--- a/sigi/apps/eventos/views.py
+++ b/sigi/apps/eventos/views.py
@@ -3,17 +3,17 @@
# sigi.apps.eventos.views
#
# Copyright (C) 2015 Interlegis
-#
+#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
-#
+#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
-#
+#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
@@ -21,11 +21,14 @@
import calendar
import datetime
import locale
+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.utils import translation
from django.utils.translation import ungettext, ugettext as _
-from sigi.apps.eventos.models import Evento
+from sigi.apps.eventos.models import Evento, Equipe, Convite
from sigi.apps.servidores.models import Servidor
from sigi.shortcuts import render_to_pdf
import csv
@@ -36,28 +39,28 @@ def calendario(request):
mes_pesquisa = int(request.GET.get('mes', datetime.date.today().month))
ano_pesquisa = int(request.GET.get('ano', datetime.date.today().year))
formato = request.GET.get('fmt', 'html')
-
+
dia1 = datetime.date(ano_pesquisa, mes_pesquisa, 1)
mes_anterior = dia1 - datetime.timedelta(days=1)
mes_seguinte = dia1.replace(day=28) + datetime.timedelta(days=4) # Ugly hack
mes_seguinte = mes_seguinte.replace(day=1)
-
+
data = {'mes_pesquisa': mes_pesquisa, 'ano_pesquisa': ano_pesquisa}
- if Evento.objects.filter(data_inicio__year=mes_anterior.year,
+ if Evento.objects.filter(data_inicio__year=mes_anterior.year,
data_inicio__month=mes_anterior.month).exists():
data['prev_button'] = {'mes': mes_anterior.month, 'ano': mes_anterior.year }
-
- if Evento.objects.filter(data_inicio__year=mes_seguinte.year,
+
+ if Evento.objects.filter(data_inicio__year=mes_seguinte.year,
data_inicio__month=mes_seguinte.month).exists():
data['next_button'] = {'mes': mes_seguinte.month, 'ano': mes_seguinte.year }
-
+
c = calendar.Calendar(6)
dates = reduce(lambda x,y: x+y, c.monthdatescalendar(ano_pesquisa, mes_pesquisa))
-
+
eventos = []
-
- for evento in Evento.objects.filter(data_inicio__year=ano_pesquisa,
+
+ for evento in Evento.objects.filter(data_inicio__year=ano_pesquisa,
data_inicio__month=mes_pesquisa).order_by('data_inicio'):
start = dates.index(evento.data_inicio)
if not evento.data_termino in dates:
@@ -75,13 +78,13 @@ def calendario(request):
# Agrupa os eventos em linhas para melhorar a visualização
linhas = []
-
+
for evento in eventos:
encaixado = False
for linha in linhas:
sobrepoe = False
for e in linha:
- if (((evento['evento'].data_inicio >= e['evento'].data_inicio) and
+ 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))):
@@ -96,7 +99,7 @@ def calendario(request):
# Adiciona uma nova linha porque este evento não se encaixa em nenhuma existente
linhas.append([evento])
- # Recalcula as distâncias dos eventos por linha para encaixar no calendário
+ # Recalcula as distâncias dos eventos por linha para encaixar no calendário
for linha in linhas:
anterior = None
for evento in linha:
@@ -110,27 +113,27 @@ def calendario(request):
data['dates'] = dates
data['eventos'] = eventos
data['linhas'] = linhas
-
+
if formato == 'pdf':
return render_to_pdf('eventos/calendario_pdf.html', data )
-
+
return render(request, 'eventos/calendario.html', data)
@login_required
def alocacao_equipe(request):
ano_pesquisa = int(request.GET.get('ano', datetime.date.today().year))
formato = request.GET.get('fmt', 'html')
-
+
data = {'ano_pesquisa': ano_pesquisa}
-
+
if Evento.objects.filter(data_inicio__year=ano_pesquisa-1).exists():
data['prev_button'] = {'ano': ano_pesquisa - 1 }
-
+
if Evento.objects.filter(data_inicio__year=ano_pesquisa+1).exists():
data['next_button'] = {'ano': ano_pesquisa + 1 }
-
+
dados = []
-
+
for evento in Evento.objects.filter(data_inicio__year=ano_pesquisa).exclude(status='C').prefetch_related('equipe_set'):
for p in evento.equipe_set.all():
registro = None
@@ -141,36 +144,36 @@ def alocacao_equipe(request):
if not registro:
registro = [p.membro.pk, p.membro.nome_completo, [{'dias': 0, 'eventos': 0} for x in range(1,13)]]
dados.append(registro)
-
+
registro[2][evento.data_inicio.month-1]['dias'] += (evento.data_termino - evento.data_inicio).days + 1
registro[2][evento.data_inicio.month-1]['eventos'] += 1
-
+
dados.sort(lambda x, y: cmp(x[1], y[1]))
-
+
lang = (translation.to_locale(translation.get_language())+'.utf8').encode()
locale.setlocale(locale.LC_ALL, lang)
meses = [calendar.month_name[m] for m in range(1,13)]
-
+
linhas = [[_(u"Servidor")] + meses + ['total']]
-
+
for r in dados:
r[2].append(reduce(lambda x,y:{'dias': x['dias'] + y['dias'],
'eventos': x['eventos'] + y['eventos']}, r[2]))
- linhas.append([r[1]] +
+ linhas.append([r[1]] +
[_(ungettext(u"%(dias)s dia", u"%(dias)s dias", d['dias']) + " em " +
ungettext(u"%(eventos)s evento", u"%(eventos)s eventos", d['eventos'])
) % d if d['dias'] > 0 or d['eventos'] > 0 else '' for d in r[2]])
-
+
# for registro in Servidor.objects.filter(equipe_evento__evento__data_inicio__year=ano_pesquisa).exclude(equipe_evento__evento__status='C').distinct():
# dados = [{'dias': 0, 'eventos': 0} for x in range(1,13)]
# for part in registro.equipe_evento.filter(evento__data_inicio__year=ano_pesquisa).exclude(evento__status='C'):
-# dados[part.evento.data_inicio.month-1]['dias'] += (part.evento.data_termino -
+# dados[part.evento.data_inicio.month-1]['dias'] += (part.evento.data_termino -
# part.evento.data_inicio).days + 1
# dados[part.evento.data_inicio.month-1]['eventos'] += 1
# dados.append([registro.nome_completo] + [_(ungettext(u"%(dias)s dia", u"%(dias)s dias", d['dias']) + " em " + ungettext(u"%(eventos)s evento", u"%(eventos)s eventos", d['eventos'])) % d if d['dias'] > 0 or d['eventos'] > 0 else '' for d in dados])
-
+
data['linhas'] = linhas
-
+
if formato == 'pdf':
return render_to_pdf('eventos/alocacao_equipe_pdf.html', data)
elif formato == 'csv':
@@ -187,5 +190,178 @@ def alocacao_equipe(request):
'meses': {m[0]: m[1] for m in zip(meses+['total'], d[2])}
} for d in dados]}
return JsonResponse(result)
-
- return render(request, 'eventos/alocacao_equipe.html', data)
\ No newline at end of file
+
+ return render(request, 'eventos/alocacao_equipe.html', data)
+
+# Views e functions para carrinho de exportação
+
+def query_ordena(qs, o):
+ from sigi.apps.eventos.admin import EventoAdmin
+ list_display = EventoAdmin.list_display
+ order_fields = []
+
+ for order_number in o.split('.'):
+ order_number = int(order_number)
+ order = ''
+ if order_number != abs(order_number):
+ order_number = abs(order_number)
+ order = '-'
+ order_fields.append(order + list_display[order_number - 1])
+ qs = qs.order_by(*order_fields)
+ return qs
+
+def get_for_qs(get, qs):
+ kwargs = {}
+ for k, v in get.iteritems():
+ if str(k) not in ('page', 'pop', 'q', '_popup', 'o', 'ot'):
+ kwargs[str(k)] = v
+ qs = qs.filter(**kwargs)
+ if 'o' in get:
+ qs = query_ordena(qs, get['o'])
+ return qs
+
+def carrinhoOrGet_for_qs(request):
+ if 'carrinho_eventos' in request.session:
+ ids = request.session['carrinho_eventos']
+ qs = Evento.objects.filter(pk__in=ids)
+ else:
+ qs = Evento.objects.all()
+ if request.GET:
+ qs = get_for_qs(request.GET, qs)
+ return qs
+
+def adicionar_eventos_carrinho(request, queryset=None, id=None):
+ if request.method == 'POST':
+ ids_selecionados = request.POST.getlist('_selected_action')
+ if 'carrinho_eventos' not in request.session:
+ request.session['carrinho_eventos'] = ids_selecionados
+ else:
+ lista = request.session['carrinho_eventos']
+ # Verifica se id já não está adicionado
+ for id in ids_selecionados:
+ if id not in lista:
+ lista.append(id)
+ request.session['carrinho_eventos'] = lista
+
+@login_required
+def visualizar_carrinho(request):
+ qs = carrinhoOrGet_for_qs(request)
+ paginator = Paginator(qs, 100)
+
+ try:
+ page = int(request.GET.get('page', '1'))
+ except ValueError:
+ page = 1
+
+ try:
+ paginas = paginator.page(page)
+ except (EmptyPage, InvalidPage):
+ paginas = paginator.page(paginator.num_pages)
+
+ carrinhoIsEmpty = not('carrinho_eventos' in request.session)
+
+ return render(
+ request,
+ 'eventos/carrinho.html',
+ {
+ 'carIsEmpty': carrinhoIsEmpty,
+ 'paginas': paginas,
+ 'query_str': '?' + request.META['QUERY_STRING']
+ }
+ )
+
+@login_required
+def excluir_carrinho(request):
+ if 'carrinho_eventos' in request.session:
+ del request.session['carrinho_eventos']
+ messages.info(request, u'O carrinho foi esvaziado')
+ return HttpResponseRedirect('../../')
+
+@login_required
+def deleta_itens_carrinho(request):
+ if request.method == 'POST':
+ ids_selecionados = request.POST.getlist('_selected_action')
+ removed = 0
+ if 'carrinho_eventos' in request.session:
+ lista = request.session['carrinho_eventos']
+ for item in ids_selecionados:
+ lista.remove(item)
+ removed += 1
+ if lista:
+ request.session['carrinho_eventos'] = lista
+ else:
+ del lista
+ del request.session['carrinho_eventos']
+ messages.info(request, u"{0} itens removidos do carrinho".format(removed))
+ return HttpResponseRedirect('.')
+
+@login_required
+def export_csv(request):
+ def serialize(r, field):
+ value = (getattr(r, 'get_{0}_display'.format(field.name), None) or
+ getattr(r, field.name, ""))
+ if callable(value):
+ value = value()
+ if value is None:
+ value = ""
+ return unicode(value).encode('utf8')
+
+ eventos_fields = Evento._meta.fields
+ equipe_fields = Equipe._meta.fields
+ convite_fields = Convite._meta.fields
+
+ eventos = carrinhoOrGet_for_qs(request)
+ eventos.select_related('equipe', 'convite')
+
+ if not eventos:
+ messages.info(request, _(u"Nenhum evento a exportar"))
+ return HttpResponseRedirect('../')
+
+ max_equipe = max([e.equipe_set.count() for e in eventos])
+ max_convite = max([e.convite_set.count() for e in eventos])
+
+ head = [f.verbose_name.encode('utf8') for f in eventos_fields
+ if f.name != 'id']
+ head.extend([f.verbose_name.encode('utf8')+"_{0}".format(i+1)
+ for i in range(max_equipe) for f in Equipe._meta.fields
+ if f.name not in ('id', 'evento')])
+ head.extend([f.verbose_name.encode('utf8')+"_{0}".format(i+1)
+ for i in range(max_convite) for f in Convite._meta.fields
+ if f.name not in ('id', 'evento')])
+ head.append('total_participantes')
+
+ response = HttpResponse(content_type='text/csv')
+ response['Content-Disposition'] = 'attachment; filename=eventos.csv'
+
+ writer = csv.DictWriter(response, fieldnames=head)
+ writer.writeheader()
+
+ for evento in eventos:
+ reg = {f.verbose_name.encode('utf8'): serialize(evento, f)
+ for f in Evento._meta.fields if f.name != 'id'}
+ reg['total_participantes'] = 0
+ idx = 1
+ for membro in evento.equipe_set.all():
+ reg.update(
+ {
+ "{0}_{1}".format(f.verbose_name.encode('utf8'), idx):
+ serialize(membro, f) for f in Equipe._meta.fields
+ if f.name not in ('id', 'evento')
+ }
+ )
+ idx += 1
+ idx = 1
+ for convite in evento.convite_set.all():
+ reg.update(
+ {
+ "{0}_{1}".format(f.verbose_name.encode('utf8'), idx):
+ serialize(convite, f) for f in Convite._meta.fields
+ if f.name not in ('id', 'evento')
+ }
+ )
+ reg['total_participantes'] += convite.qtde_participantes
+ idx += 1
+ writer.writerow(reg)
+
+ return response
+