<filename>src/pretix/control/views/orderimport.py
import logging
from datetime import timedelta

from django.conf import settings
from django.contrib import messages
from django.shortcuts import get_object_or_404, redirect
from django.urls import reverse
from django.utils.functional import cached_property
from django.utils.timezone import now
from django.utils.translation import ugettext_lazy as _
from django.views.generic import FormView, TemplateView

from pretix.base.models import CachedFile
from pretix.base.services.orderimport import import_orders, parse_csv
from pretix.base.views.tasks import AsyncAction
from pretix.control.forms.orderimport import ProcessForm
from pretix.control.permissions import EventPermissionRequiredMixin

logger = logging.getLogger(__name__)


class ImportView(EventPermissionRequiredMixin, TemplateView):
    template_name = 'pretixcontrol/orders/import_start.html'
    permission = 'can_change_orders'

    def post(self, request, *args, **kwargs):
        if 'file' not in request.FILES:
            return redirect(reverse('control:event.orders.import', kwargs={
                'event': request.event.slug,
                'organizer': request.organizer.slug,
            }))
        if not request.FILES['file'].name.lower().endswith('.csv'):
            messages.error(request, _('Please only upload CSV files.'))
            return redirect(reverse('control:event.orders.import', kwargs={
                'event': request.event.slug,
                'organizer': request.organizer.slug,
            }))
        if request.FILES['file'].size > 1024 * 1024 * 10:
            messages.error(request, _('Please do not upload files larger than 10 MB.'))
            return redirect(reverse('control:event.orders.import', kwargs={
                'event': request.event.slug,
                'organizer': request.organizer.slug,
            }))

        cf = CachedFile.objects.create(
            expires=now() + timedelta(days=1),
            date=now(),
            filename='import.csv',
            type='text/csv',
        )
        cf.file.save('import.csv', request.FILES['file'])
        return redirect(reverse('control:event.orders.import.process', kwargs={
            'event': request.event.slug,
            'organizer': request.organizer.slug,
            'file': cf.id
        }))


class ProcessView(EventPermissionRequiredMixin, AsyncAction, FormView):
    permission = 'can_change_orders'
    template_name = 'pretixcontrol/orders/import_process.html'
    form_class = ProcessForm
    task = import_orders
    known_errortypes = ['DataImportError']

    def get_form_kwargs(self):
        k = super().get_form_kwargs()
        k.update({
            'event': self.request.event,
            'initial': self.request.event.settings.order_import_settings,
            'headers': self.parsed.fieldnames
        })
        return k

    def form_valid(self, form):
        self.request.event.settings.order_import_settings = form.cleaned_data
        return self.do(
            self.request.event.pk, self.file.id, form.cleaned_data, self.request.LANGUAGE_CODE,
            self.request.user.pk
        )

    @cached_property
    def file(self):
        return get_object_or_404(CachedFile, pk=self.kwargs.get("file"), filename="import.csv")

    @cached_property
    def parsed(self):
        return parse_csv(self.file.file, 1024 * 1024)

    def get(self, request, *args, **kwargs):
        if 'async_id' in request.GET and settings.HAS_CELERY:
            return self.get_result(request)
        return FormView.get(self, request, *args, **kwargs)

    def get_success_message(self, value):
        return _('The import was successful.')

    def get_success_url(self, value):
        return reverse('control:event.orders', kwargs={
            'event': self.request.event.slug,
            'organizer': self.request.organizer.slug,
        })

    def dispatch(self, request, *args, **kwargs):
        if not self.parsed:
            messages.error(request, _('We\'ve been unable to parse the uploaded file as a CSV file.'))
            return redirect(reverse('control:event.orders.import', kwargs={
                'event': request.event.slug,
                'organizer': request.organizer.slug,
            }))
        return super().dispatch(request, *args, **kwargs)

    def get_error_url(self):
        return reverse('control:event.orders.import.process', kwargs={
            'event': self.request.event.slug,
            'organizer': self.request.organizer.slug,
            'file': self.file.id
        })

    def get_context_data(self, **kwargs):
        ctx = super().get_context_data(**kwargs)
        ctx['file'] = self.file
        ctx['parsed'] = self.parsed
        ctx['sample_rows'] = list(self.parsed)[:3]
        return ctx
