<commit_before>from django.conf import settings
from django.contrib.sites.shortcuts import get_current_site
from django.core.urlresolvers import reverse
from django.db.models import Count, Q
from django.http import HttpResponseRedirect, Http404, JsonResponse
from django.utils import timezone
from django.utils.crypto import get_random_string
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import ensure_csrf_cookie
from django.views.generic import TemplateView, View, UpdateView
import floppyforms.__future__ as forms

from brambling.forms.orders import (SavedCardPaymentForm, OneTimePaymentForm,
                                    HostingForm, AttendeeBasicDataForm,
                                    AttendeeHousingDataForm, DwollaPaymentForm,
                                    SurveyDataForm, CheckPaymentForm)
from brambling.mail import send_order_receipt, send_order_alert
from brambling.models import (Item, BoughtItem, ItemOption,
                              BoughtItemDiscount, Discount, Order,
                              Attendee, EventHousing)
from brambling.views.utils import (get_event_or_404, get_event_admin_nav,
                                   ajax_required, clear_expired_carts,
                                   Workflow, Step, get_dwolla)


ORDER_CODE_SESSION_KEY = '_brambling_order_code'
ORDER_CODE_ALLOWED_CHARS = 'abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789'


class OrderStep(Step):
    view_name = None

    @property
    def url(self):
        kwargs = {'event_slug': self.workflow.event.slug}
        if self.workflow.order.person_id is None:
            kwargs['code'] = self.workflow.order.code
        return reverse(self.view_name, kwargs=kwargs)


class ShopStep(OrderStep):
    name = 'Shop'
    slug = 'shop'
    view_name = 'brambling_event_shop'

    def _is_completed(self):
        order = self.workflow.order
        if not order:
            return False
        return (order.checked_out or
                (order.cart_start_time is not None and
                 order.bought_items.filter(status=BoughtItem.RESERVED,
                                           item_option__item__category=Item.PASS).exists()))


class AttendeeStep(OrderStep):
    name = 'Attendees'
    slug = 'attendees'
    view_name = 'brambling_event_attendee_list'

    def _is_completed(self):
        if not self.workflow.order:
            return False
        return not self.workflow.order.bought_items.filter(attendee__isnull=True).exists()

    def get_errors(self):
        errors = []
        order = self.workflow.order
        # All attendees must have at least one class or pass
        total_count = order.attendees.count()
        with_count = order.attendees.filter(bought_items__item_option__item__category=Item.PASS).count()
        if with_count != total_count:
            errors.append('All attendees must have exactly one pass')

        # Attendees may not have more than one pass.
        attendees = order.attendees.filter(
            bought_items__item_option__item__category=Item.PASS
        ).distinct().annotate(
            Count('bought_items')
        ).filter(
            bought_items__count__gte=2
        )
        if len(attendees) > 0:
            if len(attendees) == 1:
                error = '{} has too many passes (more than one).'.format(attendees[0])
            else:
                error = 'The following attendees have too many passes (more than one): ' + ", ".join(attendees)
            errors.append(error)

        # All attendees must have basic data filled out.
        missing_data = order.attendees.filter(basic_completed=False)
        if len(missing_data) > 0:
            if len(missing_data) == 1:
                error = '{} is missing basic data'.format(missing_data[0])
            else:
                error = 'The following attendees are missing basic data: ' + ", ".join(missing_data)
            errors.append(error)

        # All items must be assigned to an attendee.
        if order.bought_items.filter(attendee__isnull=True).exists():
            errors.append('All items in order must be assigned to an attendee.')
        return errors


class HousingStep(OrderStep):
    name = 'Housing'
    slug = 'housing'
    view_name = 'brambling_event_attendee_housing'

    @classmethod
    def include_in(cls, workflow):
        return workflow.event.collect_housing_data

    def is_active(self):
        if not self.workflow.order:
            return False
        if not hasattr(self, '_active'):
            self._active = self.workflow.order.attendees.filter(housing_status=Attendee.NEED).exists()
        return self._active

    def _is_completed(self):
        if not self.workflow.order:
            return False
        return not self.workflow.order.attendees.filter(
            housing_status=Attendee.NEED,
            housing_completed=False
        ).exists()

    def get_errors(self):
        errors = []
        order = self.workflow.order

        missing_housing = order.attendees.filter(housing_status=Attendee.NEED,
                                                 housing_completed=False).exists()
        if missing_housing:
            errors.append("Some attendees are missing housing data.")
        return errors


class SurveyStep(OrderStep):
    name = 'Survey'
    slug = 'survey'
    view_name = 'brambling_event_survey'

    @classmethod
    def include_in(cls, workflow):
        return workflow.event.collect_survey_data

    def _is_completed(self):
        if not self.workflow.order:
            return False
        return self.workflow.order.survey_completed


class HostingStep(OrderStep):
    name = 'Hosting'
    slug = 'hosting'
    view_name = 'brambling_event_hosting'

    @classmethod
    def include_in(cls, workflow):
        return workflow.event.collect_housing_data

    def is_active(self):
        if not self.workflow.order:
            return False
        if not hasattr(self, '_active'):
            self._active = self.workflow.order.attendees.exclude(housing_status=Attendee.NEED).exists()
        return self._active

    def _is_completed(self):
        if not self.workflow.order:
            return False
        return (not self.workflow.order.providing_housing) or EventHousing.objects.filter(
            event=self.workflow.event,
            order=self.workflow.order
        ).exists()


class OrderEmailStep(OrderStep):
    name = 'Email'
    slug = 'email'
    view_name = 'brambling_event_order_email'

    @classmethod
    def include_in(cls, workflow):
        return workflow.order is None or workflow.order.person is None

    def _is_completed(self):
        if not self.workflow.order:
            return False
        return bool(self.workflow.order.email)


class PaymentStep(OrderStep):
    name = 'Payment'
    slug = 'payment'
    view_name = 'brambling_event_order_summary'

    def _is_completed(self):
        return False


class RegistrationWorkflow(Workflow):
    step_classes = [ShopStep, AttendeeStep, HousingStep, SurveyStep,
                    HostingStep, OrderEmailStep, PaymentStep]


class ShopWorkflow(Workflow):
    step_classes = [ShopStep, AttendeeStep, HousingStep, PaymentStep]


class SurveyWorkflow(Workflow):
    step_classes = [SurveyStep, PaymentStep]


class HostingWorkflow(Workflow):
    step_classes = [HostingStep, PaymentStep]


class OrderMixin(object):
    current_step_slug = None

    def dispatch(self, request, *args, **kwargs):
        # TODO: Isn't this a circular redirect?
        if request.user.is_authenticated() and not request.user.is_active:
            return HttpResponseRedirect(reverse('brambling_event_root', args=args, kwargs=kwargs))

        self.event = get_event_or_404(kwargs['event_slug'])
        if not self.event.viewable_by(request.user):
            raise Http404

        try:
            self.order = self.get_order()
        except Order.DoesNotExist:
            raise Http404

        if self.order and self.order.cart_is_expired():
            self.order.delete_cart()

        self.workflow = self.get_workflow()
        if self.workflow is None or self.current_step_slug is None:
            self.current_step = None
        else:
            self.current_step = self.workflow.steps.get(self.current_step_slug)
            if (not self.current_step or
                    not self.current_step.is_active() or
                    not self.current_step.is_accessible()):
                for step in reversed(self.workflow.steps.values()):
                    if step.is_accessible() and step.is_active():
                        url_kwargs = {'event_slug': kwargs['event_slug']}
                        if kwargs.get('code'):
                            url_kwargs['code'] = kwargs['code']
                        return HttpResponseRedirect(reverse(step.view_name, kwargs=url_kwargs))
        return super(OrderMixin, self).dispatch(request, *args, **kwargs)

    @property
    def is_admin_request(self):
        if not hasattr(self, '_is_admin_request'):
            self._is_admin_request = self.event.editable_by(self.request.user)
        return self._is_admin_request

    def get_order(self):
        order_kwargs = {
            'event': self.event,
            'person': self.request.user if self.request.user.is_authenticated() else None,
        }
        code_in_url = False
        order = None
        session_orders = self.request.session.get(ORDER_CODE_SESSION_KEY, {})

        if self.kwargs.get('code'):
            if str(self.event.pk) in session_orders:
                del session_orders[str(self.event.pk)]
            order_kwargs['code'] = self.kwargs['code']
            code_in_url = True
        elif str(self.event.pk) in session_orders:
            order_kwargs['code'] = session_orders[str(self.event.pk)]
        try:
            if order_kwargs['person'] or order_kwargs.get('code'):
                order = Order.objects.get(**order_kwargs)
        except Order.DoesNotExist:
            # If it was in the URL, re-raise the error.
            if code_in_url:
                raise

            # Also be sure to remove the code from the session,
            # if that's what they were using.
            if order_kwargs.get('code'):
                del session_orders[str(self.event.pk)]

                # If the user is authenticated, try to get the order
                # they have for this event. Maybe the code snuck in there
                # somehow?
                if self.request.user.is_authenticated():
                    try:
                        order = Order.objects.get(
                            event=self.event,
                            person=self.request.user
                        )
                    except Order.DoesNotExist:
                        # See if the order exists and belongs to an
                        # unauthenticated user. If so, and if that user hasn't
                        # checked out yet, assume that the user created an
                        # account mid-order and re-assign it.
                        try:
                            order = Order.objects.get(
                                event=self.event,
                                person__isnull=True,
                                code=order_kwargs['code'],
                                checked_out=False
                            )
                        except Order.DoesNotExist:
                            pass
                        else:
                            order.person = self.request.user
                            order.save()
        return order

    def create_order(self):
        person = self.request.user if self.request.user.is_authenticated() else None
        code = get_random_string(8,ORDER_CODE_ALLOWED_CHARS)

        while Order.objects.filter(event=self.event, code=code).exists():
            code = get_random_string(8,ORDER_CODE_ALLOWED_CHARS)
        order = Order.objects.create(event=self.event, person=person, code=code)

        if not self.request.user.is_authenticated():
            session_orders = self.request.session.get(ORDER_CODE_SESSION_KEY, {})
            session_orders[str(self.event.pk)] = code
            self.request.session[ORDER_CODE_SESSION_KEY] = session_orders
        return order<commit_msg>PEP 8 compliance<commit_after>from django.conf import settings
from django.contrib.sites.shortcuts import get_current_site
from django.core.urlresolvers import reverse
from django.db.models import Count, Q
from django.http import HttpResponseRedirect, Http404, JsonResponse
from django.utils import timezone
from django.utils.crypto import get_random_string
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import ensure_csrf_cookie
from django.views.generic import TemplateView, View, UpdateView
import floppyforms.__future__ as forms

from brambling.forms.orders import (SavedCardPaymentForm, OneTimePaymentForm,
                                    HostingForm, AttendeeBasicDataForm,
                                    AttendeeHousingDataForm, DwollaPaymentForm,
                                    SurveyDataForm, CheckPaymentForm)
from brambling.mail import send_order_receipt, send_order_alert
from brambling.models import (Item, BoughtItem, ItemOption,
                              BoughtItemDiscount, Discount, Order,
                              Attendee, EventHousing)
from brambling.views.utils import (get_event_or_404, get_event_admin_nav,
                                   ajax_required, clear_expired_carts,
                                   Workflow, Step, get_dwolla)


ORDER_CODE_SESSION_KEY = '_brambling_order_code'
ORDER_CODE_ALLOWED_CHARS = 'abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789'


class OrderStep(Step):
    view_name = None

    @property
    def url(self):
        kwargs = {'event_slug': self.workflow.event.slug}
        if self.workflow.order.person_id is None:
            kwargs['code'] = self.workflow.order.code
        return reverse(self.view_name, kwargs=kwargs)


class ShopStep(OrderStep):
    name = 'Shop'
    slug = 'shop'
    view_name = 'brambling_event_shop'

    def _is_completed(self):
        order = self.workflow.order
        if not order:
            return False
        return (order.checked_out or
                (order.cart_start_time is not None and
                 order.bought_items.filter(status=BoughtItem.RESERVED,
                                           item_option__item__category=Item.PASS).exists()))


class AttendeeStep(OrderStep):
    name = 'Attendees'
    slug = 'attendees'
    view_name = 'brambling_event_attendee_list'

    def _is_completed(self):
        if not self.workflow.order:
            return False
        return not self.workflow.order.bought_items.filter(attendee__isnull=True).exists()

    def get_errors(self):
        errors = []
        order = self.workflow.order
        # All attendees must have at least one class or pass
        total_count = order.attendees.count()
        with_count = order.attendees.filter(bought_items__item_option__item__category=Item.PASS).count()
        if with_count != total_count:
            errors.append('All attendees must have exactly one pass')

        # Attendees may not have more than one pass.
        attendees = order.attendees.filter(
            bought_items__item_option__item__category=Item.PASS
        ).distinct().annotate(
            Count('bought_items')
        ).filter(
            bought_items__count__gte=2
        )
        if len(attendees) > 0:
            if len(attendees) == 1:
                error = '{} has too many passes (more than one).'.format(attendees[0])
            else:
                error = 'The following attendees have too many passes (more than one): ' + ", ".join(attendees)
            errors.append(error)

        # All attendees must have basic data filled out.
        missing_data = order.attendees.filter(basic_completed=False)
        if len(missing_data) > 0:
            if len(missing_data) == 1:
                error = '{} is missing basic data'.format(missing_data[0])
            else:
                error = 'The following attendees are missing basic data: ' + ", ".join(missing_data)
            errors.append(error)

        # All items must be assigned to an attendee.
        if order.bought_items.filter(attendee__isnull=True).exists():
            errors.append('All items in order must be assigned to an attendee.')
        return errors


class HousingStep(OrderStep):
    name = 'Housing'
    slug = 'housing'
    view_name = 'brambling_event_attendee_housing'

    @classmethod
    def include_in(cls, workflow):
        return workflow.event.collect_housing_data

    def is_active(self):
        if not self.workflow.order:
            return False
        if not hasattr(self, '_active'):
            self._active = self.workflow.order.attendees.filter(housing_status=Attendee.NEED).exists()
        return self._active

    def _is_completed(self):
        if not self.workflow.order:
            return False
        return not self.workflow.order.attendees.filter(
            housing_status=Attendee.NEED,
            housing_completed=False
        ).exists()

    def get_errors(self):
        errors = []
        order = self.workflow.order

        missing_housing = order.attendees.filter(housing_status=Attendee.NEED,
                                                 housing_completed=False).exists()
        if missing_housing:
            errors.append("Some attendees are missing housing data.")
        return errors


class SurveyStep(OrderStep):
    name = 'Survey'
    slug = 'survey'
    view_name = 'brambling_event_survey'

    @classmethod
    def include_in(cls, workflow):
        return workflow.event.collect_survey_data

    def _is_completed(self):
        if not self.workflow.order:
            return False
        return self.workflow.order.survey_completed


class HostingStep(OrderStep):
    name = 'Hosting'
    slug = 'hosting'
    view_name = 'brambling_event_hosting'

    @classmethod
    def include_in(cls, workflow):
        return workflow.event.collect_housing_data

    def is_active(self):
        if not self.workflow.order:
            return False
        if not hasattr(self, '_active'):
            self._active = self.workflow.order.attendees.exclude(housing_status=Attendee.NEED).exists()
        return self._active

    def _is_completed(self):
        if not self.workflow.order:
            return False
        return (not self.workflow.order.providing_housing) or EventHousing.objects.filter(
            event=self.workflow.event,
            order=self.workflow.order
        ).exists()


class OrderEmailStep(OrderStep):
    name = 'Email'
    slug = 'email'
    view_name = 'brambling_event_order_email'

    @classmethod
    def include_in(cls, workflow):
        return workflow.order is None or workflow.order.person is None

    def _is_completed(self):
        if not self.workflow.order:
            return False
        return bool(self.workflow.order.email)


class PaymentStep(OrderStep):
    name = 'Payment'
    slug = 'payment'
    view_name = 'brambling_event_order_summary'

    def _is_completed(self):
        return False


class RegistrationWorkflow(Workflow):
    step_classes = [ShopStep, AttendeeStep, HousingStep, SurveyStep,
                    HostingStep, OrderEmailStep, PaymentStep]


class ShopWorkflow(Workflow):
    step_classes = [ShopStep, AttendeeStep, HousingStep, PaymentStep]


class SurveyWorkflow(Workflow):
    step_classes = [SurveyStep, PaymentStep]


class HostingWorkflow(Workflow):
    step_classes = [HostingStep, PaymentStep]


class OrderMixin(object):
    current_step_slug = None

    def dispatch(self, request, *args, **kwargs):
        # TODO: Isn't this a circular redirect?
        if request.user.is_authenticated() and not request.user.is_active:
            return HttpResponseRedirect(reverse('brambling_event_root', args=args, kwargs=kwargs))

        self.event = get_event_or_404(kwargs['event_slug'])
        if not self.event.viewable_by(request.user):
            raise Http404

        try:
            self.order = self.get_order()
        except Order.DoesNotExist:
            raise Http404

        if self.order and self.order.cart_is_expired():
            self.order.delete_cart()

        self.workflow = self.get_workflow()
        if self.workflow is None or self.current_step_slug is None:
            self.current_step = None
        else:
            self.current_step = self.workflow.steps.get(self.current_step_slug)
            if (not self.current_step or
                    not self.current_step.is_active() or
                    not self.current_step.is_accessible()):
                for step in reversed(self.workflow.steps.values()):
                    if step.is_accessible() and step.is_active():
                        url_kwargs = {'event_slug': kwargs['event_slug']}
                        if kwargs.get('code'):
                            url_kwargs['code'] = kwargs['code']
                        return HttpResponseRedirect(reverse(step.view_name, kwargs=url_kwargs))
        return super(OrderMixin, self).dispatch(request, *args, **kwargs)

    @property
    def is_admin_request(self):
        if not hasattr(self, '_is_admin_request'):
            self._is_admin_request = self.event.editable_by(self.request.user)
        return self._is_admin_request

    def get_order(self):
        order_kwargs = {
            'event': self.event,
            'person': self.request.user if self.request.user.is_authenticated() else None,
        }
        code_in_url = False
        order = None
        session_orders = self.request.session.get(ORDER_CODE_SESSION_KEY, {})

        if self.kwargs.get('code'):
            if str(self.event.pk) in session_orders:
                del session_orders[str(self.event.pk)]
            order_kwargs['code'] = self.kwargs['code']
            code_in_url = True
        elif str(self.event.pk) in session_orders:
            order_kwargs['code'] = session_orders[str(self.event.pk)]
        try:
            if order_kwargs['person'] or order_kwargs.get('code'):
                order = Order.objects.get(**order_kwargs)
        except Order.DoesNotExist:
            # If it was in the URL, re-raise the error.
            if code_in_url:
                raise

            # Also be sure to remove the code from the session,
            # if that's what they were using.
            if order_kwargs.get('code'):
                del session_orders[str(self.event.pk)]

                # If the user is authenticated, try to get the order
                # they have for this event. Maybe the code snuck in there
                # somehow?
                if self.request.user.is_authenticated():
                    try:
                        order = Order.objects.get(
                            event=self.event,
                            person=self.request.user
                        )
                    except Order.DoesNotExist:
                        # See if the order exists and belongs to an
                        # unauthenticated user. If so, and if that user hasn't
                        # checked out yet, assume that the user created an
                        # account mid-order and re-assign it.
                        try:
                            order = Order.objects.get(
                                event=self.event,
                                person__isnull=True,
                                code=order_kwargs['code'],
                                checked_out=False
                            )
                        except Order.DoesNotExist:
                            pass
                        else:
                            order.person = self.request.user
                            order.save()
        return order

    def create_order(self):
        person = self.request.user if self.request.user.is_authenticated() else None
        code = get_random_string(8, ORDER_CODE_ALLOWED_CHARS)

        while Order.objects.filter(event=self.event, code=code).exists():
            code = get_random_string(8, ORDER_CODE_ALLOWED_CHARS)
        order = Order.objects.create(event=self.event, person=person, code=code)

        if not self.request.user.is_authenticated():
            session_orders = self.request.session.get(ORDER_CODE_SESSION_KEY, {})
            session_orders[str(self.event.pk)] = code
            self.request.session[ORDER_CODE_SESSION_KEY] = session_orders
        return order
{'fraction_non_alphanumeric': 0.08249342238413276, 'fraction_numerical': 0.0026310463468933415, 'mean_word_length': 2.07326781938052, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 18, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5292142', 'n_tokens_mistral': 7027, 'n_tokens_neox': 6697, 'n_words': 1525}