Browse Source

Exportar dados de oficinas para CSV

pull/66/head
Sesostris Vieira 4 years ago
parent
commit
c23371695c
  1. 25
      sigi/apps/eventos/admin.py
  2. 1
      sigi/apps/eventos/templates/admin/eventos/change_list.html
  3. 63
      sigi/apps/eventos/templates/eventos/carrinho.html
  4. 9
      sigi/apps/eventos/urls.py
  5. 246
      sigi/apps/eventos/views.py

25
sigi/apps/eventos/admin.py

@ -18,10 +18,12 @@
# along with this program; if not, write to the Free Software # along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
from django.contrib import admin
from django import forms from django import forms
from django.contrib import admin
from django.http import HttpResponseRedirect
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from sigi.apps.eventos.models import TipoEvento, Funcao, Evento, Equipe, Convite 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 EventoAdminForm(forms.ModelForm):
class Meta: class Meta:
@ -69,4 +71,23 @@ class EventoAdmin(admin.ModelAdmin):
raw_id_fields = ('casa_anfitria', 'municipio',) raw_id_fields = ('casa_anfitria', 'municipio',)
search_fields = ('nome', 'tipo_evento__nome', 'casa_anfitria__search_text', search_fields = ('nome', 'tipo_evento__nome', 'casa_anfitria__search_text',
'municipio__search_text', 'solicitante') 'municipio__search_text', 'solicitante')
inlines = (EquipeInline, ConviteInline) 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")

1
sigi/apps/eventos/templates/admin/eventos/change_list.html

@ -0,0 +1 @@
{% extends "change_list_with_cart.html" %}

63
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 %}<h1>{% trans 'Eventos no Carrinho' %}</h1>{% endblock %}
{% block mensagem%}
<ul class="messagelist">
{%if carIsEmpty%}
<li class="warning">{% trans 'O carrinho está vazio, sendo assim todos os eventos entram na lista para exportação de acordo com os filtros aplicados.' %}</li>
{%else%}
<li>{{paginas.paginator.count}} {% trans 'Eventos no carrinho' %}.</li>
{%endif%}
</ul>
{% endblock %}
{% block action %}deleta_itens_carrinho{% endblock %}
{% block tabela %}
<table class="table table-striped">
<thead class="thead-dark">
<tr>
{%if not carIsEmpty%}
<th><!-- <input type="checkbox" id="action-toggle" style="display: inline;">-->
</th>
{% endif %}
<th>{% trans 'Nome do evento' %}</th>
<th>{% trans 'Tipo evento' %}</th>
<th>{% trans 'Status' %}</th>
<th>{% trans 'Data de início' %}</th>
<th>{% trans 'Data de término' %}</th>
<th>{% trans 'município' %}</th>
<th>{% trans 'Solicitante' %}</th>
</tr>
</thead>
<tbody>
{% for evento in paginas.object_list %}
<tr>
{%if not carIsEmpty%}
<th><input type="checkbox" name="_selected_action"
value="{{evento.id|safe}}" class="action-select" />
</th>
{% endif %}
<td style="text-align: left;">{{evento.nome}}</td>
<td>{{evento.tipo_evento}}</td>
<td>{{evento.get_status_display}}</td>
<td>{{evento.data_inicio}}</td>
<td>{{evento.data_termino}}</td>
<td>{{evento.municipio}}</td>
<td>{{evento.solicitante}}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}
{% block botoes %}
<a class="btn btn-primary" href="../csv/{{query_str}}">{% trans "Exportar CVS" %}</a>
{% endblock %}

9
sigi/apps/eventos/urls.py

@ -7,4 +7,13 @@ urlpatterns = patterns(
# Painel de ocorrencias # Painel de ocorrencias
url(r'^calendario/$', 'calendario', name='eventos-calendario'), url(r'^calendario/$', 'calendario', name='eventos-calendario'),
url(r'^alocacaoequipe/$', 'alocacao_equipe', name='eventos-alocacaoequipe'), 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
) )

246
sigi/apps/eventos/views.py

@ -3,17 +3,17 @@
# sigi.apps.eventos.views # sigi.apps.eventos.views
# #
# Copyright (C) 2015 Interlegis # Copyright (C) 2015 Interlegis
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software # along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
@ -21,11 +21,14 @@
import calendar import calendar
import datetime import datetime
import locale 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.contrib.auth.decorators import login_required
from django.shortcuts import render from django.shortcuts import render
from django.utils import translation from django.utils import translation
from django.utils.translation import ungettext, ugettext as _ 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.apps.servidores.models import Servidor
from sigi.shortcuts import render_to_pdf from sigi.shortcuts import render_to_pdf
import csv import csv
@ -36,28 +39,28 @@ def calendario(request):
mes_pesquisa = int(request.GET.get('mes', datetime.date.today().month)) mes_pesquisa = int(request.GET.get('mes', datetime.date.today().month))
ano_pesquisa = int(request.GET.get('ano', datetime.date.today().year)) ano_pesquisa = int(request.GET.get('ano', datetime.date.today().year))
formato = request.GET.get('fmt', 'html') formato = request.GET.get('fmt', 'html')
dia1 = datetime.date(ano_pesquisa, mes_pesquisa, 1) dia1 = datetime.date(ano_pesquisa, mes_pesquisa, 1)
mes_anterior = dia1 - datetime.timedelta(days=1) mes_anterior = dia1 - datetime.timedelta(days=1)
mes_seguinte = dia1.replace(day=28) + datetime.timedelta(days=4) # Ugly hack mes_seguinte = dia1.replace(day=28) + datetime.timedelta(days=4) # Ugly hack
mes_seguinte = mes_seguinte.replace(day=1) mes_seguinte = mes_seguinte.replace(day=1)
data = {'mes_pesquisa': mes_pesquisa, 'ano_pesquisa': ano_pesquisa} 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_inicio__month=mes_anterior.month).exists():
data['prev_button'] = {'mes': mes_anterior.month, 'ano': mes_anterior.year } 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_inicio__month=mes_seguinte.month).exists():
data['next_button'] = {'mes': mes_seguinte.month, 'ano': mes_seguinte.year } data['next_button'] = {'mes': mes_seguinte.month, 'ano': mes_seguinte.year }
c = calendar.Calendar(6) c = calendar.Calendar(6)
dates = reduce(lambda x,y: x+y, c.monthdatescalendar(ano_pesquisa, mes_pesquisa)) dates = reduce(lambda x,y: x+y, c.monthdatescalendar(ano_pesquisa, mes_pesquisa))
eventos = [] 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'): data_inicio__month=mes_pesquisa).order_by('data_inicio'):
start = dates.index(evento.data_inicio) start = dates.index(evento.data_inicio)
if not evento.data_termino in dates: if not evento.data_termino in dates:
@ -75,13 +78,13 @@ def calendario(request):
# Agrupa os eventos em linhas para melhorar a visualização # Agrupa os eventos em linhas para melhorar a visualização
linhas = [] linhas = []
for evento in eventos: for evento in eventos:
encaixado = False encaixado = False
for linha in linhas: for linha in linhas:
sobrepoe = False sobrepoe = False
for e in linha: 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_inicio <= e['evento'].data_termino)) or
((evento['evento'].data_termino >= e['evento'].data_inicio) and ((evento['evento'].data_termino >= e['evento'].data_inicio) and
(evento['evento'].data_termino <= e['evento'].data_termino))): (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 # Adiciona uma nova linha porque este evento não se encaixa em nenhuma existente
linhas.append([evento]) 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: for linha in linhas:
anterior = None anterior = None
for evento in linha: for evento in linha:
@ -110,27 +113,27 @@ def calendario(request):
data['dates'] = dates data['dates'] = dates
data['eventos'] = eventos data['eventos'] = eventos
data['linhas'] = linhas data['linhas'] = linhas
if formato == 'pdf': if formato == 'pdf':
return render_to_pdf('eventos/calendario_pdf.html', data ) return render_to_pdf('eventos/calendario_pdf.html', data )
return render(request, 'eventos/calendario.html', data) return render(request, 'eventos/calendario.html', data)
@login_required @login_required
def alocacao_equipe(request): def alocacao_equipe(request):
ano_pesquisa = int(request.GET.get('ano', datetime.date.today().year)) ano_pesquisa = int(request.GET.get('ano', datetime.date.today().year))
formato = request.GET.get('fmt', 'html') formato = request.GET.get('fmt', 'html')
data = {'ano_pesquisa': ano_pesquisa} data = {'ano_pesquisa': ano_pesquisa}
if Evento.objects.filter(data_inicio__year=ano_pesquisa-1).exists(): if Evento.objects.filter(data_inicio__year=ano_pesquisa-1).exists():
data['prev_button'] = {'ano': ano_pesquisa - 1 } data['prev_button'] = {'ano': ano_pesquisa - 1 }
if Evento.objects.filter(data_inicio__year=ano_pesquisa+1).exists(): if Evento.objects.filter(data_inicio__year=ano_pesquisa+1).exists():
data['next_button'] = {'ano': ano_pesquisa + 1 } data['next_button'] = {'ano': ano_pesquisa + 1 }
dados = [] dados = []
for evento in Evento.objects.filter(data_inicio__year=ano_pesquisa).exclude(status='C').prefetch_related('equipe_set'): 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(): for p in evento.equipe_set.all():
registro = None registro = None
@ -141,36 +144,36 @@ def alocacao_equipe(request):
if not registro: if not registro:
registro = [p.membro.pk, p.membro.nome_completo, [{'dias': 0, 'eventos': 0} for x in range(1,13)]] registro = [p.membro.pk, p.membro.nome_completo, [{'dias': 0, 'eventos': 0} for x in range(1,13)]]
dados.append(registro) 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]['dias'] += (evento.data_termino - evento.data_inicio).days + 1
registro[2][evento.data_inicio.month-1]['eventos'] += 1 registro[2][evento.data_inicio.month-1]['eventos'] += 1
dados.sort(lambda x, y: cmp(x[1], y[1])) dados.sort(lambda x, y: cmp(x[1], y[1]))
lang = (translation.to_locale(translation.get_language())+'.utf8').encode() lang = (translation.to_locale(translation.get_language())+'.utf8').encode()
locale.setlocale(locale.LC_ALL, lang) locale.setlocale(locale.LC_ALL, lang)
meses = [calendar.month_name[m] for m in range(1,13)] meses = [calendar.month_name[m] for m in range(1,13)]
linhas = [[_(u"Servidor")] + meses + ['total']] linhas = [[_(u"Servidor")] + meses + ['total']]
for r in dados: for r in dados:
r[2].append(reduce(lambda x,y:{'dias': x['dias'] + y['dias'], r[2].append(reduce(lambda x,y:{'dias': x['dias'] + y['dias'],
'eventos': x['eventos'] + y['eventos']}, r[2])) '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"%(dias)s dia", u"%(dias)s dias", d['dias']) + " em " +
ungettext(u"%(eventos)s evento", u"%(eventos)s eventos", d['eventos']) 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]]) ) % 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(): # 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)] # 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'): # 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 # part.evento.data_inicio).days + 1
# dados[part.evento.data_inicio.month-1]['eventos'] += 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]) # 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 data['linhas'] = linhas
if formato == 'pdf': if formato == 'pdf':
return render_to_pdf('eventos/alocacao_equipe_pdf.html', data) return render_to_pdf('eventos/alocacao_equipe_pdf.html', data)
elif formato == 'csv': elif formato == 'csv':
@ -187,5 +190,178 @@ def alocacao_equipe(request):
'meses': {m[0]: m[1] for m in zip(meses+['total'], d[2])} 'meses': {m[0]: m[1] for m in zip(meses+['total'], d[2])}
} for d in dados]} } for d in dados]}
return JsonResponse(result) return JsonResponse(result)
return render(request, 'eventos/alocacao_equipe.html', data) 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

Loading…
Cancel
Save