Sistema de Informações Gerenciais do Interlegis
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

430 lines
16 KiB

# -*- coding: utf-8 -*-
#
# 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.
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 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, Modulo
from sigi.apps.eventos.forms import SelecionaModeloForm
from sigi.apps.servidores.models import Servidor
from sigi.shortcuts import render_to_pdf, pdf_renderer
@login_required
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,
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,
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,
data_inicio__month=mes_pesquisa).order_by('data_inicio'):
start = dates.index(evento.data_inicio.date())
if not evento.data_termino.date() in dates:
lastday = dates[-1]
while lastday < evento.data_termino.date():
lastday = lastday + datetime.timedelta(days=1)
dates.append(lastday)
eventos.append({'evento': evento, 'start': start})
# Calcula a distância dos eventos para as bordas do calendário
for evento in eventos:
end = dates.index(evento['evento'].data_termino.date())
evento['duration'] = end-evento['start']+1
evento['close'] = len(dates)-end-1
# 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.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:
# Adiona o evento em uma linha que ele não sobrepoe nenhum outro
linha.append(evento)
encaixado = True
break
if not encaixado:
# 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
for linha in linhas:
anterior = None
for evento in linha:
if anterior is None:
anterior = evento
continue
anterior['close'] = (evento['evento'].data_inicio.date() - anterior['evento'].data_termino.date()).days-1
evento['start'] = 0
anterior = evento
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
for r in dados:
if r[0] == p.membro.pk:
registro = r
break
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]] +
[_(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 -
# 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':
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename="alocacao_equipe_%s.csv"' % (ano_pesquisa,)
writer = csv.writer(response)
asc_list = [[s.encode('utf-8') if isinstance(s, unicode) else s for s in l] for l in linhas]
writer.writerows(asc_list)
return response
elif formato == 'json':
result = {'ano': ano_pesquisa,
'equipe': [{'pk': d[0],
'nome_completo': d[1],
'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)
# 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 rm_rows(lista,reg):
for a in lista:
if a in lista:
reg.pop(a,None)
else:
pass
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 = 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])
mun_casa = u'Município da Casa Anfitriã'.encode('utf8')
uf_casa = u'UF da Casa Anfitriã'.encode('utf8')
reg_casa = u'Região da Casa Anfitriã'.encode('utf8')
head = [f.verbose_name.encode('utf8') for f in Evento._meta.fields]
head.extend([mun_casa, uf_casa, reg_casa])
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') for f in Convite._meta.fields
if f.name not in ('id', 'evento')])
head.extend([f.verbose_name.encode('utf8') for f in Modulo._meta.fields
if f.name not in ('id', 'evento')])
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename=eventos.csv'
rm_list = ['Descrição do evento', 'Local do evento', 'Público alvo', 'Motivo do cancelamento', 'Descrição do módulo']
for a in head:
if 'Observações_' in a:
rm_list.append(a)
for a in rm_list:
if a in head:
head.remove(a)
else:
pass
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 evento.casa_anfitria is None:
reg[mun_casa] = ""
reg[uf_casa] = ""
reg[reg_casa] = ""
else:
reg[mun_casa] = evento.casa_anfitria.municipio.nome.encode('utf8')
reg[uf_casa] = evento.casa_anfitria.municipio.uf.sigla.\
encode('utf8')
reg[reg_casa] = evento.casa_anfitria.municipio.uf.\
get_regiao_display().encode('utf8')
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
for convite in evento.convite_set.all():
reg.update(
{f.verbose_name.encode('utf8'): serialize(convite, f)
for f in Convite._meta.fields
if f.name not in ('id', 'evento')}
)
rm_rows(rm_list,reg)
writer.writerow(reg)
if evento.convite_set.count() == 0:
rm_rows(rm_list,reg)
writer.writerow(reg)
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}
)