# -*- coding: utf-8 -*-
import datetime
import os.path
import json
import csv

from django.core.urlresolvers import reverse
from django.conf import settings
from django.shortcuts import redirect, get_object_or_404, render
from django.contrib.formtools.wizard.views import NamedUrlSessionWizardView
from django.core.files.storage import FileSystemStorage
from django.contrib.auth.decorators import login_required
from django.views.decorators.csrf import csrf_exempt
from django.views.generic.detail import SingleObjectMixin, DetailView
from django.views.generic.list import ListView
from django.db import transaction
from django.db.models import Q
from django.contrib.formtools.wizard.storage.session import SessionStorage
from django.utils.translation import ugettext_lazy as _
from django.http import Http404, HttpResponse, HttpResponseRedirect, HttpResponseBadRequest
from django.utils.http import urlencode, urlquote
from django.utils.timezone import now
from django.contrib import messages
from django.utils.safestring import mark_safe
from django.utils.datastructures import SortedDict
from django.contrib.formtools.wizard.forms import ManagementForm
from django.contrib.auth.models import User

from ..base.models import (Request, Transition, Entity, Status,
        DocumentLicence)
from ..base.models.request import get_default_status
from ..base.rbac import get_workable_requests, get_workflows, get_entity_filter
from ..utils import sort_by_frequency

from forms import DocumentUploadForm, DocumentDetailsForm, ReproDetailsForm, \
                    DeliveryForm, CopyrigtsForm, ValidationForm, ReproOriginForm, \
                    FinancialForm, CostForm
import app_settings
from utils import field_completion

import models
import utils


named_new_request_forms = (
    ('document_upload', DocumentUploadForm),
    ('document_details', DocumentDetailsForm),
    ('repro_origin', ReproOriginForm),
    ('reprography', ReproDetailsForm),
    ('delivery', DeliveryForm),
    ('document_copyrights', CopyrigtsForm),
    ('financial_information', FinancialForm),
    ('real_cost', CostForm),
    ('validation', ValidationForm),
)

class DummyStorage(SessionStorage):
    def set_step_files(self, step, files):
        super(DummyStorage, self).set_step_files(step, {})

    def get_step_files(self, step):
        return None

class RequestWizardView(NamedUrlSessionWizardView, SingleObjectMixin):
    model = Request
    template_name = 'new_request.html'
    file_storage = FileSystemStorage(location = os.path.join(settings.MEDIA_ROOT, 'temporary-uploads'))
    storage_name = 'polynum.request.views.DummyStorage'

    def dispatch(self, request, *args, **kwargs):
        self.kwargs = kwargs
        self.object = self.get_object() # cache the current object
        return super(RequestWizardView, self).dispatch(request, *args, **kwargs)

    def get_template_names(self):
        return ['%s.%s' % (self.steps.current, self.template_name), self.template_name]

    def done(self, form_list, **kwargs):
        ctx = self.get_context_data(form_list[0], **kwargs)
        if ctx.get('validated'):
            instance = self.object
            instance.status = get_default_status()
            instance.save()
            return redirect('list_request')
        for step, form, missings in ctx['all_forms']:
            if missings:
                return redirect(self.get_step_url(step=step))
        raise NotImplemented

    def get_frequent_financial_codes(self, user):
        frequent_codes = list(Request.objects.filter(
                Q(user=user)|Q(history__user=user)) \
                        .order_by('-creation_date') \
                        .distinct()[:15] \
                        .values_list('financial_code', flat=True))
        instance = self.object
        codes = instance.entity.accounting_codes().values_list('code', flat=True)
        frequent_codes.extend(codes)
        return map(lambda x: (x,x), sort_by_frequency(filter(None, frequent_codes)))

    def get_frequent_entities(self, user):
        return sort_by_frequency(filter(lambda x: x[1] is not None, list(Request.objects.filter(
                Q(user=user)|Q(history__user=user))
                        .order_by('-creation_date')
                        .distinct()[:15]
                        .select_related('entity')
                        .values_list('entity__name', 'entity__id'))
                        + list(Entity.objects.filter(
                            code__in=user.get_preferred_entities())
                            .values_list('name', 'id'))))

    def get_form_kwargs(self, step):
        kwargs = super(RequestWizardView, self).get_form_kwargs(self)
        if step == 'financial_information':
            kwargs['frequent_financial_codes'] = self.get_frequent_financial_codes(
                    self.request.user)
        if step == 'repro_origin':
            kwargs['frequent_entities'] = self.get_frequent_entities(
                    self.request.user)
        return kwargs

    def get_context_data(self, form, **kwargs):
        '''
        Add all_forms = list of (step, form, pprint_data), where pprint_data is
        a list of (name, value) created by each form, representing a "nice"
        view of the stored datas
        '''
        context = super(RequestWizardView, self).get_context_data(form=form, **kwargs)
        # cleaned_data = self.get_all_cleaned_data() or {}
        all_forms = []
        validated = True
        for step, form in self.get_form_list().items():
            form_instance = self.get_form(step=step)
            pprint_data = form_instance.pprint_data({})
            if pprint_data:
                missings = [ l[0] for l in pprint_data if len(l) > 2 and l[2]]
            else:
                missings = []
            validated = validated and not bool(missings)
            all_forms += [ (step, form, pprint_data, missings) ]
        context['all_forms'] = all_forms
        context['validated'] = validated
        context['object'] = self.object
        context['workflows'] = get_workflows(self.request.user, context['object']) \
                .exclude(action__special_type='edit').filter(action__validate_request__lte=validated).order_by('-warn', 'default')
        context['roots'] = [(e.pk, e.get_description().strip(), e.code) for e in
                Entity.objects.filter(depth=app_settings.ENTITY_ROOTS_DEPTH,
                    is_active=True)]
        return context

    def get_prefix(self, *args, **kwargs):
        prefix = super(RequestWizardView, self).get_prefix(*args, **kwargs)
        prefix += '_%s' % kwargs.get('pk', '')
        return prefix

    def get_form_instance(self, step):
        return self.object

    def get_step_url(self, step):
        return reverse(self.url_name, kwargs={self.pk_url_kwarg: self.object.pk, 'step': step})

    def render_next_step(self, form, **kwargs):
        if hasattr(form, 'cleaned_data') and hasattr(form, 'save') and form.is_valid():
            form.save()
            if hasattr(form, 'save_m2m'):
                form.save_m2m()
        # special case when coming from the validation page
        if 'correction' in self.request.GET:
            return redirect(self.get_step_url('validation'))
        pprint_data = form.pprint_data({})
        # do not apply when going to the validation page, that's useless
        if any([x[2] for x in pprint_data]) and self.steps.next != 'validation':
            field_names = [x[0] for x in pprint_data if x[2]]
            messages.warning(self.request,
            _(u"Vous n'avez pas remplis les champs obligatoires suivants: "
              u"%s. Vous pouvez tout de même continuer à remplir votre demande, "
              u"et aussi la sauver comme brouillon. Mais vous ne pourrez pas "
              u"la soumettre tant que ces champs ne seront pas remplis")
            % ', '.join(map(unicode, field_names)))
        return super(RequestWizardView, self).render_next_step(form, **kwargs)

    def get_form_list(self):
        transitions = get_workflows(self.request.user, self.object) \
                .select_related().filter(action__special_type='edit')
        pages = reduce(set.__or__, [set(transition.action.edit_pages()) for transition in transitions], set())
        return SortedDict([
            (key,value) for key, value in  super(RequestWizardView, self).get_form_list().iteritems()
                            if key in pages or key == 'validation'])

    @transaction.commit_on_success
    def post(self, *args, **kwargs):
        """
        Surcharge de NamedUrlSessionWizardView.post pour enregistrer le 
        """
        wizard_goto_step = self.request.POST.get('wizard_goto_step', None)
        delete = 'delete-uploadfile' in self.request.POST
        if delete or (wizard_goto_step and wizard_goto_step in self.get_form_list()):
            # Check if form was refreshed
            management_form = ManagementForm(self.request.POST, prefix=self.prefix)
            if not management_form.is_valid():
                messages.error(self.request, _(u'Une erreur a eu lieu, veuillez réessayer.'))
                return redirect(self.get_step_url(self.steps.current))

            form_current_step = management_form.cleaned_data['current_step']
            if (form_current_step != self.steps.current and
                    self.storage.current_step is not None):
                # form refreshed, change current step
                self.storage.current_step = form_current_step

            if delete:
                request = self.object
                if not request.is_paper():
                    request.uploadfile.delete()
                    request.uploadfile = None
                    request.save()
                return redirect(self.get_step_url(form_current_step))

            # get the form for the current step
            form = self.get_form(data=self.request.POST, files=self.request.FILES)

            # and try to validate
            if form.is_valid():
                # if the form is valid, store the cleaned data and files.
                self.storage.set_step_data(self.steps.current, self.process_step(form))
                self.storage.set_step_files(self.steps.current, self.process_step_files(form))
                if hasattr(form, 'save'):
                    form.save()
                if hasattr(form, 'save_m2m'):
                    form.save_m2m()
            self.storage.current_step = wizard_goto_step
            return redirect(self.get_step_url(wizard_goto_step))
        return super(RequestWizardView, self).post(*args, **kwargs)

    def render(self, form=None, **kwargs):
        from django.forms.models import model_to_dict
        if self.steps.current == 'document_copyrights' and self.object:
            message = None
            if self.object.usage and self.object.usage.no_diffusion:
                message = _(u"L'usage « {0} » n'autorisant pas la diffusion, "
                          u"l'étape « Options de diffusion » a donc été automatiquement sautée.").format(self.object.usage)
            if self.object.is_from_remote_request:
                message = _(u"La requête venant d'une plate-forme distante, "
                          u"l'étape « Options de diffusion » a donc été automatiquement sautée.").format(self.object.usage)
            if self.object.is_paper():
                message = _(u"La requête ne comportant pas de fichier, "
                      u"l'étape « Options de diffusion » a donc été automatiquement sautée.").format(self.object.usage)
            if message:
                messages.info(self.request, message)
                return self.render_next_step(form, **kwargs)
        if hasattr(form, 'instance') and form.instance and not form.is_bound and kwargs.get('step') != 'document_copyrights':
            data = dict(((form.prefix+'-'+k,v) for k,v in model_to_dict(form.instance).iteritems()))
            for choice in form.instance.choices.all().select_related():
                data[form.add_prefix('profile_option_%s' % choice.option.id)] =\
                        unicode(choice.id)
            form = self.get_form(data=data)
        form.full_clean()
        return super(RequestWizardView, self).render(form=form, **kwargs)

class RequestDetails(DetailView):
    model = Request
    template_name = 'request_detail.html'
    context_object_name = 'poly_request'
    queryset = Request.objects.select_related().prefetch_related('choices', 'base_profile__choices')

    def get_context_data(self, **kwargs):
        ctx = super(RequestDetails, self).get_context_data(**kwargs)
        request = self.get_object()
        ctx['docname'] = request.name
        if not request.is_paper():
            ctx['filename'] = os.path.basename(request.uploadfile.name)
            if not ctx['docname']:
                ctx['docname'] = ctx['filename']
        ctx['creation'] = request.history_set.start()
        ctx['end'] = request.history_set.end()
        # Let only one edit transition be displayed
        transitions = []
        already_one_edit_transition = False
        t1 = get_workflows(self.request.user, request) \
                .filter(action__validate_request__lte=request.validate(),
                        show_on_detail=True).order_by('action__id')
        # FIXME in Django 1.6
        if t1:
            t1 = t1.distinct('action', 'destination')
        t2 = Transition.objects.filter(id__in=t1) \
                .order_by('-warn', 'default')
        for transition in t2:
            if transition.action.special_type == 'edit':
                if already_one_edit_transition:
                    continue
                already_one_edit_transition = True
            transitions.append(transition)
        ctx['workflows'] = transitions
        ctx['use_pdf_viewer'] = app_settings.USE_PDF_VIEWER
        ctx['entities'] = []
        if request.entity is not None:
            def entity_parent(x, kind):
                return getattr(x.entity.parent_of_type(kind), 'description', None)
            for caption, ref in app_settings.ENTITY_TYPE_TO_SHOW:
                what = entity_parent(request, ref)
                if what is None or what == request.entity:
                    continue
                ctx['entities'].append((caption, what))
        return ctx

class RequestHistory(DetailView):
    model = Request
    template_name = 'request_history.html'
    context_object_name = 'poly_request'

@login_required
@transaction.commit_on_success
def new_request(request):
    try:
        default_licence = DocumentLicence.objects.get(default=True)
    except (DocumentLicence.DoesNotExist, DocumentLicence.MultipleObjectsReturned):
        default_licence = None
    request = Request.objects.create(
            user=request.user,
            status=get_default_status(),
            contact_email=getattr(request.user, 'email', ''),
            contact_telephone1=getattr(request.user, 'telephoneNumber', ''),
            contact_telephone2=getattr(request.user, 'supannAutreTelephone', ''),
            contact_bureau=getattr(request.user, 'roomNumber', ''),
            sponsor=u'%(first_name)s %(last_name)s (%(username)s)' % request.user._wrapped.__dict__,
            licence=default_licence,
    )
    return redirect('request_wizard', pk=request.pk)

@transaction.commit_on_success
def request_delete(request, pk):
    instance = get_object_or_404(Request, pk=pk)
    instance.delete()
    return redirect('list_request')

request_wizard_step = login_required(RequestWizardView.as_view(named_new_request_forms,
    done_step_name='finished', url_name='request_wizard_step'))

def upload(request, attached_file):
    response = HttpResponse(attached_file.chunks(), mimetype='application/octet-stream')
    filename = urlquote(
            os.path.basename(attached_file.name).encode('utf-8'))
    response['Content-disposition'] = "attachment; filename*=UTF-8''%s" % filename
    return response

def request_download(request, pk):
    instance = get_object_or_404(Request, pk=pk)
    if not instance.uploadfile.name:
        raise Http404
    return upload(request, instance.uploadfile)

@transaction.commit_on_success
def request_action(request, pk, workflow_pk):
    poly_request = get_object_or_404(Request, pk=pk)
    try:
        workflow = get_workflows(request.user, poly_request).get(pk=workflow_pk)
    except Transition.DoesNotExist:
        transition = get_object_or_404(Transition, pk=workflow_pk)
        message = _(u"L'action « {0} » n'est pas autorisée sur la demande {1}.") \
                    .format(transition.action.name,
                            poly_request.request_number())
        messages.error(request, message)
        if get_workable_requests(request.user).filter(pk=pk).exists():
            return redirect('request_detail', pk=pk)
        else:
            return redirect('list_request')
    if workflow.comment and not request.POST.get('comment'):
        return render(request, 'request_action.html', locals())
    poly_request.act(request.user, workflow, description=request.POST.get('comment'))
    if workflow.action.ui_message:
        messages.info(request, mark_safe(workflow.action.ui_message))
    if workflow.is_edit:
        return redirect('request_wizard', pk=pk)
    elif workflow.is_delete:
        return redirect('request_delete', pk=pk)
    elif workflow.is_duplicate:
        return redirect('request_duplicate', pk=pk)
    return redirect('request_detail', pk=pk)

@transaction.commit_on_success
def request_duplicate(request, pk):
    from django.core.files.base import ContentFile
    poly_request = get_object_or_404(Request, pk=pk)
    new_poly_request = get_object_or_404(Request, pk=pk)
    new_poly_request.pk = None
    new_poly_request.creation_date = now()
    new_poly_request.month_order = -1
    new_poly_request.status = get_default_status()
    if poly_request.uploadfile:
        content = ContentFile(poly_request.uploadfile.read())
        filename = os.path.split(poly_request.uploadfile.name)[-1]
        new_poly_request.uploadfile.save(filename, content)
        poly_request.uploadfile.close()
    new_poly_request.save()
    new_poly_request.choices = poly_request.choices.all()
    return redirect('request_wizard', pk=new_poly_request.pk)

class ListRequest(ListView):
    template_name = 'list_request.html'
    model = Request
    paginate_by = app_settings.PAGINATE_BY
    context_object_name = 'requests'

    def dispatch(self, request, *args, **kwargs):
        self.request = request
        self.filters = []
        self.filter_choices = []
        self.sort = request.GET.get('sort')
        self.handle_filter_status()
        self.handle_search_filter()
        self.handle_entity_filter()
        self.handle_date_filter()
        self.handle_licence_filter()
        self.handle_free_text_filter()
        return super(ListRequest, self).dispatch(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        params = {}
        if 'from_date' in request.POST:
            params['from_date'] = request.POST['from_date']
        if 'to_date' in request.POST:
            params['to_date'] = request.POST['to_date']
        return HttpResponseRedirect(self.qs(**params))

    def get_queryset(self, without_filter=()):
        qs = super(ListRequest, self).get_queryset()
        qs = get_workable_requests(self.request.user)
            # apply filters
        qs = qs.filter(status__visible=True)
        for filter_name, value, display_list  in self.filters:
            if filter_name in without_filter:
                continue
            apply_filter = 'apply_filter_%s' % filter_name
            if hasattr(self, apply_filter):
                qs = getattr(self, apply_filter)(qs, value)
            else:
                qs = qs.filter(**{filter_name: value})
        # apply ordering
        if self.sort:
            qs = qs.order_by(self.sort)
        qs = qs.prefetch_related('choices').select_related('base_profile', 'user', 'entity', 'status')
        return qs

    def handle_filter_status(self):
        current = self.request.GET.get('filter_status')
        choices = []
        if current:
            display = Status.objects.get(id=current).name
            self.filters.append(('status', current, [(display, self.qs(**{'status': None}))]))
        choices = Status.objects \
                .filter(request__in=self.get_queryset(without_filter=('status',))) \
                .distinct().values_list('id', 'name')
        choices = [ 
                (self.qs(**{'status': status}), display) 
                    for status, display in choices 
        ]
        if choices:
            self.filter_choices.append({
                'caption': _(u'Sur le statut'),
                'choices': choices })

    def handle_search_filter(self):
        current = self.request.GET.get('filter_search')
        if current:
            self.filters.append(('search', current, [_(u'Contient le texte %s') % current,
                self.qs(**{'search': None})]))

    def apply_filter_search(sefl, qs, value):
        q = Q(uploadfile__contains=value) | Q(name__contains=value) \
                | Q(user__username__contains=value)
        return qs.filter(q)

    def handle_entity_filter(self,
            min_depth=app_settings.MIN_ENTITY_FILTER_DEPTH,
            max_depth=app_settings.MAX_ENTITY_FILTER_DEPTH):
        value = self.request.GET.get('filter_entity')
        qs = Entity.objects \
                .filter(depth__lte=max_depth, depth__gte=min_depth) \
                .filter(get_entity_filter(self.request.user))
        if False:
            request_table = qs.query.join((None, Request._meta.db_table, None, None),
                    promote=True, always_create=True)
            entity_table2 = qs.query.join((request_table, Entity._meta.db_table,
                "entity_id", "id"))
            qs = qs.extra(where=[
                '{0}.left_bound <= {1}.left_bound'.format(
                    Entity._meta.db_table, entity_table2),
                '{0}.right_bound >= {1}.right_bound'.format(
                    Entity._meta.db_table, entity_table2)])
        qs = qs.order_by('name')
        by_code = dict([(entity.pk, entity) for entity in qs])
        current = None
        path = []
        if value:
            path = value.split(',')
            path_entities = []
            for entity_code in path:
                try:
                    entity_code = int(entity_code)
                except ValueError:
                    return
                entity = by_code.get(entity_code)
                if not entity:
                    return
                path_entities.append(entity)
            current = path_entities[-1]
            display_list = [ (_(u'Composante %s') % path_entities[0].name,
                self.qs(**{'entity': None}))]
            delete_value = path[0]
            for entity in path_entities[1:]:
                display_list.append((entity.get_name(),
                    self.qs(**{'entity': delete_value})))
                delete_value = '%s,%s' % (delete_value, entity.pk)
            self.filters.append(('entity', current, display_list))
        parent = None
        if len(path) > 1:
            parent = by_code.get(path[-2])
        if current:
            if current.depth == max_depth or not current.children().exists():
                depth = current.depth
                path = path[:-1]
            else:
                depth = current.depth+1
        else:
            depth = min_depth
            path = []
        # filter on depth
        choices = [entity for entity in by_code.itervalues() if entity.depth==depth]
        # filter on parenting
        if parent:
            choices = [entity for entity in choices if
                    entity.left_bound>=parent.left_bound and
                    entity.right_bound<=parent.right_bound]
        # create filter choices
        choices = sorted(choices, key=lambda entity: entity.get_description().title())
        choices = [(self.qs(**{'entity': ','.join(path+[str(entity.pk)])}),
            entity.get_description().title()) for entity in choices if entity != current]
        if choices:
            self.filter_choices.append({
                'caption': _('Sur le service ou la composante'),
                'choices': choices,
            })

    def apply_filter_entity(self, qs, value):
        return qs.filter(entity__left_bound__gte=value.left_bound,
                entity__right_bound__lte=value.right_bound)

    def get_filters_field(self, field, caption):
        result = {}
        result['caption'] = caption
        current = self.filters.get(field)
        values = self.get_queryset().values_list(field, flat=True).distinct()
        if current:
            choices = [({field: None}, _('< Tous')), (None, values[0])]
        else:
            if not values:
                return []
            choices = [({field: a}, a) for a in values]
        choices = [ (self.qs(**a) if a else a, b) for a, b in choices ]
        result['choices'] = choices
        return [result]

    def qs(self, **kwargs):
        params = dict()
        for k in self.request.GET:
            params[k] = self.request.GET[k]
        for k,v in kwargs.iteritems():
            k = 'filter_%s' % k
            if v is None:
                params.pop(k, None)
            else:
                params[k] = v
        return '?%s' % urlencode(params)

    def handle_date_filter(self):
        from_date = self.request.GET.get('filter_from_date')
        to_date = self.request.GET.get('filter_to_date')
        if from_date:
            try:
                from_date = datetime.datetime.strptime(from_date, '%Y-%m-%d').date()
            except ValueError:
                from_date = None
        if to_date:
            try:
                to_date = datetime.datetime.strptime(to_date, '%Y-%m-%d').date()
            except ValueError:
                to_date = None
        display_list = []
        if from_date:
            display_list.append(
                    (_(u'À livrer après le %s') % from_date, self.qs(**{'from_date': None})))
        if to_date:
            display_list.append(
                    (_(u'À livrer avant le %s') % to_date, self.qs(**{'to_date': None})))
        if display_list:
            self.filters.append(('date', (from_date, to_date), display_list))

    def apply_filter_date(self, qs, value):
        from_date, to_date = value
        if from_date:
            qs = qs.filter(delivery_date__gte=from_date)
        if to_date:
            qs = qs.filter(delivery_date__lte=to_date)
        return qs

    def handle_licence_filter(self):
        licences = DocumentLicence.objects.all()
        choices = [(self.qs(**{'licence': licence.id}), licence.name) for licence in licences]
        self.filter_choices.append({
            'caption': _('Autorisation de diffusion'),
            'choices': choices})
        licence = self.request.GET.get('filter_licence')
        try:
            licence = DocumentLicence.objects.get(id=int(licence))
        except (TypeError, ValueError, DocumentLicence.DoesNotExist):
            pass
        else:
            self.filters.append(('licence',
                licence, ((_(u'Diffusion: %s') % licence.name,
                    self.qs(**{'licence': None})),)))

    def handle_free_text_filter(self):
        text = self.request.GET.get('filter_free_text')
        if text:
            self.filters.append(('free_text', text,
                    ((_(u'Contenant: %s') % text,
                        self.qs(**{'free_text': None})),)))


    def apply_filter_free_text(self, qs, value):
        for elt in value.split():
            m = Request.NUMBER_RE.match(elt)
            if m:
                year, month, month_order = m.groups()
                qs = qs.filter(creation_date__year=int(year),
                                   creation_date__month=int(month),
                                   month_order=int(month_order))
                continue
            m = Request.FINANCIAL_CODE_RE.match(elt)
            if m:
                financial_code = m.group()
                qs = qs.filter(financial_code=financial_code)
                continue
            qs = qs.filter(Q(entity__description__icontains=elt)
                |Q(entity__description_override__icontains=elt)
                |Q(entity__code__icontains=elt)
                |Q(user__username__icontains=elt)
                |Q(user__first_name__icontains=elt)
                |Q(user__last_name__icontains=elt)
                |Q(user__email__icontains=elt)
                |Q(sponsor__icontains=elt)
                |Q(name__icontains=elt)
                |Q(contact_email__icontains=elt))
        return qs

    def get_context_data(self, **kwargs):
        ctx = super(ListRequest, self).get_context_data(**kwargs)
        ctx['sort'] = self.sort
        ctx['filters'] = self.filters
        ctx['filter_choices'] = self.filter_choices
        ctx['total_cost'] = self.get_queryset().total_cost()
        ctx['qs'] = self.qs()
        ctx['now'] = now().strftime('%Y-%m-%d')
        ctx['csv_qs'] = self.qs(**{'csv': 1})
        return ctx

class CSVListRequest(ListRequest):
    def get(self, request, *args, **kwargs):
        response = HttpResponse(content_type="text/csv")
        response['Content-disposition'] = 'attachment; filename=listing.csv'
        writer = csv.writer(response, delimiter=';')
        response.write(u''.encode('utf_8_sig'))
        def validator(r):
            if r.history_set.last_show_in_details():
                return r.history_set.last_show_in_details().user.get_full_name()

        def choices(r):
            return u', '.join([choice.name for choice in r.choices.all()])

        fields = [('Numéro de demande', 'request_number_csv'), 
                ('Commanditaire', 'sponsor_display'), 
                ('Valideur', validator),
                'name', 
                'uploadfile', 
                'nb_pages', 
                'usage__name', 
                'licence__name', 
                'copyright',
                'comments', 
                ('Entité', 'entity__description')]
        def loader(kind):
            def entity_parent(x):
                if x.entity is None:
                    return None
                return getattr(x.entity.parent_of_type(kind), 'description', None)
            return entity_parent
        for caption, ref in app_settings.ENTITY_TYPE_TO_SHOW:
            fields.append((caption.encode('utf-8'), loader(ref)))
        fields += [
                'copies',
                'status__name', 
                'base_profile__name', 
                ('Choix de reprographie', choices),
                'details',
                'delivery_date', 
                'delivery_place__name', 
                'comments',
                'creation_date', 
                'modification_date', 
                'contact_email',
                'contact_telephone1', 
                'contact_telephone2', 
                'contact_bureau',
                'financial_code', 
                'financial_comment',
                'estimated_cost',
                'cost' ]
        normalized_fields = []
        for field in fields:
            if isinstance(field, tuple):
                caption, ref = field
            else:
                caption, ref = field, field
            if callable(ref):
                pass
            elif callable(getattr(Request, ref, None)):
                ref = getattr(Request, ref)
            normalized_fields.append((caption, ref))
        # Write headers
        writer.writerow([field[0] for field in normalized_fields])
        qs = self.get_queryset()
        value_fields = [field[1] for field in normalized_fields if not callable(field[1])]
        for instance, values in zip(qs, qs.values(*value_fields)):
            row = []
            for caption, ref in normalized_fields:
                if callable(ref):
                    cell = ref(instance)
                else:
                    cell = values[ref]
                if cell is None:
                    cell = _(u'Aucun(e)')
                try:
                    cell = float(cell)
                    cell = unicode(cell)
                    cell = cell.replace('.',',')
                except:
                    pass
                # try to fix newlines for excel
                cell = unicode(cell)
                cell.replace('\r\n', '\n')
                cell.replace('\r', '')
                row.append(cell.encode('utf-8'))
            writer.writerow(row)
        return response

def autocomplete(request, field_names=('name',)):
    completion_data = field_completion(request.user, field_names, without_scores='without_scores' in request.GET)
    response = HttpResponse(json.dumps(completion_data), mimetype='application/javascript')
    return response

@csrf_exempt
def remote_request(request):
    contents = []
    entity = None
    if 'uploadfile' not in request.FILES:
           return HttpResponseBadRequest(u'paramètres invalides', content_type='text/plain')
    if 'username' in request.POST and \
            not User.objects.filter(username=request.POST['username']).exists():
           contents.append(u"l'utilisateur « %s » n'est pas enregistré dans polynum" % request.POST['username'])
    if 'entity_hint' in request.POST:
        entities = Entity.objects.filter(code=request.POST['entity_hint'].lower())
        if entities:
            entity = entities[0]
        else:
            contents.append(u'entité « %s » inconnue' % request.POST['entity_hint'])
    else:
        entity = None
    try:
        utils.check_pdf(request.FILES['uploadfile'])
    except ValueError, e:
        return HttpResponseBadRequest(e.args[0], content_type='text/plain')
    request.FILES['uploadfile'].seek(0)
    temp_remote_request = models.RemoteRequest.objects.create(
            uploadfile=request.FILES['uploadfile'], entity=entity)
    response = redirect('finish_remote_request', pk=temp_remote_request.pk)
    response['Content-Type'] = 'text/plain'
    response.content = u''.join(contents)
    return response

@transaction.commit_on_success
def finish_remote_request(request, pk):
    temp_remote_request = get_object_or_404(models.RemoteRequest, pk=pk)
    document = Request.objects.create(user=request.user,
            status=get_default_status(),
            entity=temp_remote_request.entity,
            is_from_remote_request=True)
    document.uploadfile.save(os.path.basename(temp_remote_request.uploadfile.name),
            temp_remote_request.uploadfile, save=False)
    utils.fill_document_attributes_from_pdf_file(document, document.uploadfile)
    document.clean()
    document.save()
    temp_remote_request.delete()
    return redirect('request_wizard', pk=document.pk)

