def is_authenticated(request): class AccountMixin(object):
    """
    Mixin to use in views that will retrieve an account object (out of
    ``account_queryset``) associated to a slug parameter (``account_url_kwarg``)
    in the URL.
    If either ``account_url_kwarg`` is ``None`` or absent from the URL pattern,
    ``account`` will default to the ``request.user`` when the account model is
    compatible with the `User` model, else ``account`` will be ``None``.
    """
    account_queryset = get_account_model().objects.all()
    account_lookup_field = settings.ACCOUNT_LOOKUP_FIELD
    account_url_kwarg = 'organization'

    @property
    def account(self):
        if not hasattr(self, '_account'):
            if (self.account_url_kwarg is not None
                and self.account_url_kwarg in self.kwargs):
                if self.account_queryset is None:
                    raise ImproperlyConfigured(
                        "%(cls)s.account_queryset is None. Define "
                        "%(cls)s.account_queryset." % {
                            'cls': self.__class__.__name__
                        }
                    )
                if self.account_lookup_field is None:
                    raise ImproperlyConfigured(
                        "%(cls)s.account_lookup_field is None. Define "
                        "%(cls)s.account_lookup_field as the field used "
                        "to retrieve accounts in the database." % {
                            'cls': self.__class__.__name__
                        }
                    )
                kwargs = {'%s__exact' % self.account_lookup_field:
                    self.kwargs.get(self.account_url_kwarg)}
                try:
                    self._account = self.account_queryset.filter(**kwargs).get()
                except self.account_queryset.model.DoesNotExist:
                    #pylint: disable=protected-access
                    raise Http404(_("No %(verbose_name)s found matching"\
                        "the query") % {'verbose_name':
                        self.account_queryset.model._meta.verbose_name})
            else:
                if (isinstance(get_account_model(), get_user_model()) and
                    is_authenticated(self.request)):
                    self._account = self.request.user
                self._account = None
        return self._account

    def get_context_data(self, **kwargs):
        context = super(AccountMixin, self).get_context_data(**kwargs)
        context.update({'account': self.account})
        return context

    def get_reverse_kwargs(self):
        """
        List of kwargs taken from the url that needs to be passed through
        to ``get_success_url``.
        """
        return [self.account_url_kwarg]

    def get_url_kwargs(self):
        kwargs = {}
        for url_kwarg in self.get_reverse_kwargs():
            url_kwarg_val = self.kwargs.get(url_kwarg, None)
            if url_kwarg_val:
                kwargs.update({url_kwarg: url_kwarg_val})
        return kwargs class SampleMixin(AccountMixin):
    """
    Returns a ``Sample`` to a ``Campaign``.
    """

    # We have to use a special url_kwarg here because 'sample'
    # interfers with the way rest_framework handles **kwargs.
    sample_url_kwarg = 'sample'
    campaign_url_kwarg = 'campaign'
    path_url_kwarg = 'path'

    def get_reverse_kwargs(self):
        """
        List of kwargs taken from the url that needs to be passed through
        to ``get_success_url``.
        """
        return super(SampleMixin, self).get_reverse_kwargs() + [
            self.sample_url_kwarg, self.campaign_url_kwarg, self.path_url_kwarg]

    @property
    def path(self):
        if not hasattr(self, '_path'):
            self._path = self.kwargs.get(self.path_url_kwarg, '')
            if self._path and not self._path.startswith('/'):
                self._path = '/%s' % self._path
        return self._path

    @property
    def sample(self):
        if not hasattr(self, '_sample'):
            self._sample = self.get_sample()
        return self._sample

    def get_sample(self, url_kwarg=None):
        """
        Returns the ``Sample`` object associated with this URL.
        """
        if not url_kwarg:
            url_kwarg = self.sample_url_kwarg
        sample = None
        sample_slug = self.kwargs.get(url_kwarg)
        if sample_slug:
            # We have an id for the sample, let's get it and check
            # the user has rights to it.
            try:
                sample = Sample.objects.filter(slug=sample_slug).select_related(
                    'campaign').get()
            except Sample.DoesNotExist:
                raise Http404("Cannot find Sample(slug='%s')" % sample_slug)
        else:
            # Well no id, let's see if we can find a sample from
            # a campaign slug and a account
            campaign_slug = self.kwargs.get(self.campaign_url_kwarg)
            if campaign_slug:
                try:
                    sample = Sample.objects.filter(account=self.account,
                        campaign__slug=campaign_slug).select_related(
                        'campaign').get()
                except Sample.DoesNotExist:
                    raise Http404("Cannot find Sample(account__slug='%s',"\
                        " campaign__slug='%s')" % (self.account, campaign_slug))
        return sample class Answer(models.Model):
    """
    An Answer to a Question as part of Sample to a Campaign.
    """
    objects = AnswerManager()

    created_at = models.DateTimeField(auto_now_add=True)
    question = models.ForeignKey(settings.QUESTION_MODEL,
        on_delete=models.PROTECT)
    metric = models.ForeignKey(Metric, on_delete=models.PROTECT)
    unit = models.ForeignKey(Unit, on_delete=models.PROTECT, null=True)
    measured = models.IntegerField(null=True)
    denominator = models.IntegerField(null=True, default=1)
    collected_by = models.ForeignKey(settings.AUTH_USER_MODEL,
        null=True, on_delete=models.PROTECT)
    # XXX Optional fields when the answer is part of a campaign.
    sample = models.ForeignKey(Sample, on_delete=models.CASCADE,
        related_name='answers')

    class Meta:
        unique_together = ('sample', 'question', 'metric')

    def __str__(self):
        return '%s-%d' % (self.sample.slug, self.rank)

    @property
    def as_text_value(self):
        if self.unit.system in Unit.NUMERICAL_SYSTEMS:
            return self.measured
        return Choice.objects.get(pk=self.measured).text

    @property
    def rank(self):
        if not hasattr(self, '_rank'):
            self._rank = EnumeratedQuestions.objects.get(
                campaign=self.sample.campaign, question=self.question).rank
        return self._rank

    def get_multiple_choices(self):
        text = Choice.objects.get(pk=self.measured).text
        return text.replace('[', '').replace(']', '').replace(
            'u\'', '').replace('\'', '').split(', ') class Choice(models.Model):
    """
    Choice for a multiple choice question.
    """
    unit = models.ForeignKey(Unit, on_delete=models.PROTECT)
    rank = models.IntegerField(
        help_text=_("used to order choice when presenting a question"))
    text = models.TextField()
    descr = models.TextField()

    class Meta:
        unique_together = ('unit', 'rank')

    def __str__(self):
        return str(self.text) class Sample(models.Model):
    """
    Sample to a Campaign. A Sample is composed of multiple Answers
    to Questions.
    """
    objects = SampleManager()

    slug = models.SlugField(unique=True,
        help_text="Unique identifier for the sample. It can be used in a URL.")
    created_at = models.DateTimeField(auto_now_add=True,
        help_text="Date/time of creation (in ISO format)")
    campaign = models.ForeignKey(Campaign, null=True, on_delete=models.PROTECT)
    account = models.ForeignKey(settings.ACCOUNT_MODEL,
        null=True, on_delete=models.PROTECT, related_name='samples')
    time_spent = models.DurationField(default=datetime.timedelta,
        help_text="Total recorded time to complete the campaign")
    is_frozen = models.BooleanField(default=False,
        help_text="When True, answers to that sample cannot be updated.")
    extra = get_extra_field_class()(null=True)

    def __str__(self):
        return str(self.slug)

    def save(self, force_insert=False, force_update=False,
             using=None, update_fields=None):
        if not self.slug:
            self.slug = slugify(uuid.uuid4().hex)
        return super(Sample, self).save(
            force_insert=force_insert, force_update=force_update,
            using=using, update_fields=update_fields)

    def get_answers_by_rank(self):
        # We attempt to get Django ORM to generate SQL equivalent to:
        # ```
        # SELECT survey_answer.* FROM survey_answer
        # INNER JOIN survey_sample
        # ON survey_answer.sample_id = survey_sample.id
        # INNER JOIN survey_enumeratedquestions
        # ON survey_answer.question_id = survey_enumeratedquestions.question_id
        # ON survey_sample.campaign_id = survey_enumeratedquestions.campaign_id
        # WHERE survey_answer.sample_id = %(sample_id)d
        # ORDER BY survey_enumeratedquestions.rank
        # ```
        queryset = Answer.objects.filter(
            sample=self,
            question__campaigns__in=[self.campaign.pk]).order_by(
            'question__enumeratedquestions__rank').annotate(
                rank=models.F('question__enumeratedquestions__rank'))
        return queryset class Unit(models.Model):
    """
    Unit in which an ``Answer.measured`` value is collected.
    """

    SYSTEM_STANDARD = 0
    SYSTEM_IMPERIAL = 1
    SYSTEM_RANK = 2
    SYSTEM_ENUMERATED = 3
    SYSTEM_FREETEXT = 4

    SYSTEMS = [
            (SYSTEM_STANDARD, 'standard'),
            (SYSTEM_IMPERIAL, 'imperial'),
            (SYSTEM_RANK, 'rank'),
            (SYSTEM_ENUMERATED, 'enum'),
            (SYSTEM_FREETEXT, 'freetext'),
        ]

    NUMERICAL_SYSTEMS = [
        SYSTEM_STANDARD,
        SYSTEM_IMPERIAL,
        SYSTEM_RANK
    ]

    slug = models.SlugField(max_length=150, unique=True, db_index=True)
    title = models.CharField(max_length=150)
    system = models.PositiveSmallIntegerField(
        choices=SYSTEMS, default=SYSTEM_STANDARD)

    def __str__(self):
        return str(self.slug) class AnswerSerializer(serializers.ModelSerializer):
    """
    Serializer of ``Answer`` when used individually.
    """
    metric = serializers.SlugRelatedField(required=False,
        queryset=Metric.objects.all(), slug_field='slug',
        help_text=_("Metric the measured field represents"))
    unit = serializers.SlugRelatedField(required=False,
        queryset=Unit.objects.all(), slug_field='slug',
        help_text=_("Unit the measured field is in"))
    measured = serializers.CharField(required=True, allow_blank=True,
        help_text=_("measurement in unit"))

    created_at = serializers.DateTimeField(read_only=True,
        help_text=_("Date/time of creation (in ISO format)"))
    # We are not using a `UserSerializer` here because retrieving profile
    # information must go through the profiles API.
    collected_by = serializers.SlugRelatedField(required=False,
        queryset=get_user_model().objects.all(), slug_field='username',
        help_text=_("User that collected the answer"))

    class Meta(object):
        model = Answer
        fields = ('metric', 'unit', 'measured', 'created_at', 'collected_by')
        read_only_fields = ('created_at', 'collected_by') class SampleAnswerSerializer(AnswerSerializer):
    """
    Serializer of ``Answer`` when used in list.
    """
    question = CampaignQuestionSerializer()
    required = serializers.BooleanField(required=False)

    class Meta(object):
        model = AnswerSerializer.Meta.model
        fields = AnswerSerializer.Meta.fields + ('question', 'required')
        read_only_fields = AnswerSerializer.Meta.read_only_fields class SampleSerializer(serializers.ModelSerializer):

    campaign = serializers.SlugRelatedField(slug_field='slug',
        queryset=Campaign.objects.all(), required=False,
        help_text=("Campaign this sample is part of."))
    account = serializers.SlugRelatedField(slug_field='slug',
        queryset=get_account_model().objects.all(), required=False,
        help_text=("Account this sample belongs to."))

    class Meta(object):
        model = Sample
        fields = ('slug', 'account', 'created_at',
            'campaign', 'time_spent', 'is_frozen')
        read_only_fields = ('slug', 'account', 'created_at',
            'campaign', 'time_spent') def datetime_or_now(dtime_at=None):
    if not dtime_at:
        return datetime.datetime.utcnow().replace(tzinfo=utc)
    if isinstance(dtime_at, six.string_types):
        dtime_at = parse_datetime(dtime_at)
    if dtime_at.tzinfo is None:
        dtime_at = dtime_at.replace(tzinfo=utc)
    return dtime_at def get_question_model():
    """
    Returns the ``Question`` model that is active in this project.
    """
    try:
        return django_apps.get_model(settings.QUESTION_MODEL)
    except ValueError:
        raise ImproperlyConfigured(
            "QUESTION_MODEL must be of the form 'app_label.model_name'")
    except LookupError:
        raise ImproperlyConfigured("QUESTION_MODEL refers to model '%s'"\
" that has not been installed" % settings.QUESTION_MODEL)
import decimal, logging
from django.db import transaction
from django.db.models import Max
from django.db.utils import DataError
from rest_framework import generics, mixins
from rest_framework import response as http
from rest_framework.exceptions import ValidationError
from rest_framework.generics import get_object_or_404
from rest_framework.status import HTTP_200_OK, HTTP_201_CREATED
from ..compat import six, is_authenticated
from ..mixins import AccountMixin, SampleMixin
from ..models import Answer, Choice, Sample, Unit
from .serializers import (AnswerSerializer, SampleAnswerSerializer,
    SampleSerializer)
from ..utils import datetime_or_now, get_question_model
            {
                "question": {
                    "path": "product-design",
                    "default_metric": "weight",
                    "title": "Product design"
                },
                "metric": "weight",
                "measured": "2"
                "unit": "kilograms"
            },
            {
                "question": {
                    "path": "packaging-design",
                    "default_metric": "weight",
                    "title": "Packaging design"
                },
                "metric": "weight",
                "measured": "3"
                "unit": "kilograms"
            }
        ]
    }
    """
    serializer_class = SampleAnswerSerializer

    # Used to POST and create an answer.
    @property
    def question(self):
        if not hasattr(self, '_question'):
            self._question = get_object_or_404(
                get_question_model().objects.all(), path=self.path)
        return self._question

    def get_queryset(self):
        if self.sample.is_frozen:
            return Answer.objects.filter(sample=self.sample,
                question__path__startswith=self.path).select_related(
                    'question', 'metric', 'unit', 'collected_by')

        queryset = Answer.objects.raw("""SELECT
    answers.id AS id,
    answers.created_at AS created_at,
    survey_question.id AS question_id,
    answers.metric_id AS metric_id,
    answers.unit_id AS unit_id,
    answers.measured AS measured,
    answers.denominator AS denominator,
    answers.collected_by_id AS collected_by_id,
    answers.sample_id AS sample_id,
    survey_enumeratedquestions.rank AS _rank,
    survey_enumeratedquestions.required AS required
FROM survey_question
INNER JOIN survey_enumeratedquestions
  ON survey_question.id = survey_enumeratedquestions.question_id
LEFT OUTER JOIN (SELECT * FROM survey_answer WHERE sample_id=%(sample)d)
  AS answers ON survey_question.id = answers.question_id
WHERE survey_enumeratedquestions.campaign_id = %(campaign)d
  AND survey_question.path LIKE '%(prefix)s%%%%';""" % {
      'sample': self.sample.pk,
      'campaign': self.sample.campaign.pk,
      'prefix': self.path
  }).prefetch_related(
      'question', 'question__default_metric', 'metric', 'unit', 'collected_by')
        return queryset

    def get_serializer_class(self):
        if self.request.method.lower() == 'post':
            return AnswerSerializer
        return super(SampleAnswersAPIView, self).get_serializer_class()

    def get_serializer(self, *args, **kwargs):
        if isinstance(self.request.data, list):
            kwargs.update({'many': True})
        return super(SampleAnswersAPIView, self).get_serializer(
            *args, **kwargs)

    def post(self, request, *args, **kwargs):
        """
        Updates an answer in a sample

        **Tags**: survey

        **Examples**

        .. code-block:: http

            POST /api/sample/46f66f70f5ad41b29c4df08f683a9a7a/answers/water-use\
 HTTP/1.1

        .. code-block:: json

            {
                "metric": "weight",
                "measured": "1",
                "unit": "kilograms"
            },

        responds

        .. code-block:: json

    .. code-block:: json

            {
                "metric": "weight",
                "measured": "1",
                "unit": "kilograms"
            },
        """
        #pylint:disable=useless-super-delegation
        return super(SampleAnswersAPIView, self).post(request, *args, **kwargs)

    def create(self, request, *args, **kwargs):
        #pylint:disable=unused-argument,too-many-locals,too-many-statements
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        validated_data = serializer.validated_data
        if not isinstance(serializer.validated_data, list):
            validated_data = [serializer.validated_data]


        user = self.request.user if is_authenticated(self.request) else None