# -*- encoding: utf-8 -*- import os import json import StringIO import warnings from importlib import import_module from datetime import datetime from pymongo import ASCENDING from django.views.generic.base import TemplateView from django.views.generic.list import ListView from django.utils.datastructures import DotExpandedDict from django.utils.safestring import SafeUnicode from django.http import HttpResponse, HttpResponseRedirect, Http404 from django.core.urlresolvers import reverse from django.views.generic.edit import FormView from django.contrib import messages from django.shortcuts import render_to_response from django.conf import settings from utils.forms import BadFormValidation from utils.data_structure import dict_merge from polls.models import (WIDGET_TYPES, Structure, Poll, PollResult, PollResultFile) from polls.models import is_image_file from polls.forms import PollForm from pollster.models import Pollster module_path = settings.CLOCK_CLASS.split('.')[:-1] module_path = ".".join(module_path) module = import_module(module_path) class_name = settings.CLOCK_CLASS.split('.')[-1:][0] Clock = getattr(module, class_name) __all__ = [ 'StructureFormView', 'PollFormView', 'PollListView', 'UnploadPollResultFormView' ] def clean_data(value): if isinstance(value, dict): for key_, value_ in value.iteritems(): value[key_] = clean_data(value_) return value if isinstance(value, list): if len(value) == 1: value = clean_data(value.pop()) else: value = [clean_data(value_) for value_ in value] return value if not is_image_file(value): value = value.strip(' ') if value == '': value = None if value == 'True' or value in [u'on']: value = True if value == 'False': value = False if isinstance(value, unicode): return SafeUnicode(value) else: return value class PollFormView(FormView): template_name = "poll-form.html" form_class = PollForm def get_context_data(self, **kwargs): context = super(PollFormView, self).get_context_data(**kwargs) form = context['form'] # If id and Wrong id or poll is not open => 404 _id = self.kwargs.get('id', None) poll = Poll.get(id=_id) if _id else Poll() if _id and not poll: raise Http404() if poll and not poll.is_open(): if not self.request.user.is_superuser: raise Http404() poll = Poll(data=form.data) if form.is_bound else poll context.update({ "poll": poll, "pollsters_id": map(lambda p: str(p.id), poll.get_pollsters()), "STATUS_CHOICES": Poll.status_choices(), "POLLSTER_CHOICES": form.fields['pollsters'].choices, }) return context def form_valid(self, form): try: poll = form.save() except BadFormValidation: msg = u'Ocurrió un error, no se guardó la encuesta.' messages.add_message(self.request, messages.ERROR, msg) return self.render_to_response(self.get_context_data(form=form)) # TODO: Logear except Exception: pass # TODO: Logear else: msg = u'La encuesta: "%s" fué guardada correctamente.' % poll.name messages.add_message(self.request, messages.SUCCESS, msg) if not self.request.user.is_superuser: return HttpResponseRedirect(reverse('index')) if poll.is_open() and self.request.GET.get('continue', None): return HttpResponseRedirect( reverse( 'sociologist:structure.builder', kwargs={'poll_id': str(poll.id)} ) ) else: return HttpResponseRedirect( reverse( 'sociologist:poll_edit', kwargs={'id': str(poll.id)} ) ) return self.form_invalid(form) class PollListView(ListView): template_name = "poll-list.html" context_object_name = "polls" def get_queryset(self, *args, **kwargs): user = self.request.user assigned_to = lambda poll: ", ".join( map(lambda p: p.username, poll.get_pollsters())) return [ { 'name': poll.name.capitalize(), 'status': poll.status, 'is_open': poll.is_open(), 'assigned_to': assigned_to(poll) if assigned_to(poll) else "-", 'action_result_view': { 'disabled': "disabled" if not poll.has_result() else "", 'url': reverse( 'sociologist:poll_result_detail', kwargs={'poll_id': str(poll.id)} ), }, 'action_edit': { 'disabled': "disabled" if not poll.is_open() and ( not poll.is_open() and not user.is_superuser) else "", 'url': reverse( 'sociologist:poll_edit', kwargs={'id': str(poll.id)} ), }, 'action_structure_builder': { 'disabled': "disabled" if not poll.is_open() else "", 'url': reverse( 'sociologist:structure.builder', kwargs={'poll_id': str(poll.id)} ) }, 'action_download': { 'disabled': "", 'url': reverse( 'sociologist:poll_download', kwargs={'poll_id': str(poll.id)} ), }, 'action_clone': { 'disabled': "", 'url': reverse( 'sociologist:poll_clone', kwargs={'id': str(poll.id)} ), }, 'background_color': "#f2dede" if poll.is_open() else "#e8f4db" } for poll in Poll.all( sort=[('status', ASCENDING), ('name', ASCENDING)]) ] class StructureFormView(TemplateView): template_name = "poll-structure-form.html" def get(self, request, *args, **kwargs): context = self.get_context_data() context.update({'structure': self.poll.structure}) return self.render_to_response(context) def get_context_data(self, **kwargs): context = super(StructureFormView, self).get_context_data(**kwargs) # If id and Wrong id or poll is not open => 404 _id = _id = self.kwargs.get('poll_id', None) self.poll = Poll.get(id=_id) if _id else None if _id and not self.poll: raise Http404() if self.poll and not self.poll.is_open(): raise Http404() context.update({'WIDGET_TYPES': WIDGET_TYPES, 'poll': self.poll}) return context def post(self, request, *args, **kwargs): context = self.get_context_data() data = {} data_post = DotExpandedDict(dict(request.POST.lists())) data_files = DotExpandedDict(dict(request.FILES.lists())) data = dict_merge(data_post, data_files) for key, value in data.items(): data[key] = clean_data(value) _id = data.get('id', None) structure = Structure( data={"groups": data.get('groups', {}), 'id': _id}, poll=self.poll ) if structure.is_valid(new_data=data): try: structure.save_image_options(tmp=False) structure.save_image_fields() structure.save() except Exception: msg = u'Ocurrió un error, no se guardó la estructura.' messages.add_message(self.request, messages.ERROR, msg) # Rollback img options to tmp directory if not structure.id: structure.image_rollback() else: messages.add_message( self.request, messages.SUCCESS, u'La estructura de la encuesta "%s" fué' ' guardada correctamente.' % self.poll.name ) return HttpResponseRedirect( reverse( 'sociologist:structure.builder', kwargs={"poll_id": str(self.poll.id)} ) ) else: structure.save_image_options(tmp=True) structure.save_image_fields() context.update({'errors': structure.errors}) messages.add_message( self.request, messages.ERROR, u'Hubo errores de validación, ' 'para guardar asegurese de corregirlos.' ) context.update({'structure': structure}) return self.render_to_response(context) def download_poll(request, poll_id=None): # Wrong id => 404 _id = poll_id poll = Poll.get(id=_id) if not _id or not poll: raise Http404() if request.user.is_pollster: # In here, somebody is downloading a poll, but is not a pollster. if poll and poll.is_open(): raise Http404() file_name = datetime.now().strftime("%d_%m_%Y_%H_%M_%S") poll_template = None pollster = getattr(request.user, "pollster", None) with warnings.catch_warnings(record=True) as w: poll_template = poll.get_template(pollster=pollster) if len(w) == 1: # In here, A logged user that is pollster is trying download # a poll that is not assigned to him. pass if poll_template: # Download json file response = HttpResponse( poll_template, content_type='application/json') response['Content-Disposition'] = ( 'attachment; filename=%s.json' % file_name) return response else: msg = "No se pudo descargar la encuesta." messages.add_message(request, messages.ERROR, msg) return HttpResponseRedirect(request.meta['HTTP_REFERER']) def opt_thumb(request, poll_id, img_name): tmp_path = settings.IMAGE_OPTIONS_ROOT + '/%s/tmp/%s' % ( poll_id, img_name) path = settings.IMAGE_OPTIONS_ROOT + '/%s/%s' % ( poll_id, img_name) src = '' if os.path.exists(tmp_path): src = tmp_path media_url = settings.IMAGE_OPTIONS_MEDIA_URL + '/%s/tmp/%s' % ( poll_id, img_name) elif os.path.exists(path): src = path media_url = settings.IMAGE_OPTIONS_MEDIA_URL + '/%s/%s' % ( poll_id, img_name) return render_to_response( 'image_option_thumbnail.html', {"img": open(src), "img_src": media_url} ) def csv_download(request, poll_id): csv = Poll.get(poll_id).get_result().to_csv() response = HttpResponse(csv, content_type='text/csv;') response['Content-Disposition'] = ( 'attachment; filename="%s_result.csv"' % poll_id) return response class UnploadPollResultFormView(TemplateView): template_name = "poll-result-form.html" def post(self, request, *args, **kwargs): context = self.get_context_data() files = dict(request.FILES.lists()).get('result', []) # si se subió algún archivo: if len(files): # Esto hace todo to_json = [] # genera una lista de dict python en to_json for file in files: json_dst = StringIO.StringIO() for chunk in file.chunks(): json_dst.write(chunk) json_dst.seek(0) to_json.append(json.load(json_dst, 'utf-8')) file.seek(0) # comprueba si los archivos ya existían y los excluye poll_id = to_json[0].get("poll_id", None) results_path = "%s/%s" % (settings.RESULT_BCK_ROOT, poll_id) uploaded_files = [] for index, file in enumerate(files): file_path = "%s/%s" % (results_path, file.name) if os.path.exists(file_path): uploaded_files.append(file.name) del files[index] if len(uploaded_files): msg = u'Los siguientes resultados ya se encuentran \ publicados: %s y no serán procesados.' % ( ", ".join(uploaded_files)) messages.add_message(self.request, messages.INFO, msg) # si no hay más archivos, vuelve al usuario if not len(files): return self.render_to_response(context) # revisa si el primero de los archivos es del usuario actual # si no, vuelve al usuario if len(to_json): pollster_id = to_json[0].get("pollster_id", None) if str(request.user.pollster.id) != pollster_id: pollster_username = to_json[0].get( "pollster_username", None) if not pollster_username: pollster_username = "Desconocido!" msg = u'El sistema cree que este archivo resultado \ no pertence a una encuesta realizada por usted. \ Asegúrese de estar usando el mismo usuario que \ utilizó para descargar este modelo de encuesta. \ Usuario esperado: %s.' % pollster_username messages.add_message(request, messages.ERROR, msg) return self.render_to_response(context) # crea un poll result con todos los archivos implicados poll_result = PollResult(to_json) poll = poll_result.get_poll() pollster = Pollster.get_by_user_id(request.user.id) # valida que el pollster esté asignado a la poll en cuestión assigned_polls_id = [ p.id for p in poll.assigned_to_pollster(pollster)] if poll.id not in assigned_polls_id: msg = u'Estos son resultados de una encuesta \ que no le fué asignada.' messages.add_message(self.request, messages.ERROR, msg) else: # End validations date_time_string = Clock.get_time_string() uploaded_files = [ (f.name, f.temporary_file_path()) for f in files ] for name, path in uploaded_files: result_file = PollResultFile(path) result_file.name = name result_file.set_upload_timestamp(date_time_string) result_file.save() processed_files = [file.name for file in files] messages.add_message( self.request, messages.SUCCESS, u'Los resultados se guardaron con éxito.\ Los siguientes archivos fueron procesados: \ %s' % ", ".join(processed_files) ) # si no hay ningún archivo else: msg = u'Necesita seleccionar un archivo.' messages.add_message(self.request, messages.INFO, msg) return self.render_to_response(context)