Beispiel #1
0
def desktop_stable_feedback(request):
    # Use two instances of the same form because the template changes the text
    # based on the value of ``happy``.
    forms = {
        'happy': ResponseForm(initial={'happy': 1}),
        'sad': ResponseForm(initial={'happy': 0}),
    }

    if request.method == 'POST':
        response, form = _handle_feedback_post(request)
        if response:
            return response

        happy = smart_bool(request.POST.get('happy', None))
        if happy:
            forms['happy'] = form
        else:
            forms['sad'] = form

    return render(request, 'feedback/feedback.html', {'forms': forms})
Beispiel #2
0
def generic_feedback_dev(request, locale, product, version=None, channel=None):
    """IN DEVELOPMENT NEXT GENERATION GENERIC FEEDBACK FORM"""
    form = ResponseForm()

    if request.method == 'POST':
        return _handle_feedback_post(request, locale, product,
                                     version, channel)

    return render(request, 'feedback/generic_feedback_dev.html', {
        'form': form,
        'product': models.Product.from_slug(product),
    })
Beispiel #3
0
def generic_feedback(request, locale=None, product=None, version=None,
                     channel=None):
    """Generic feedback form for desktop and mobile"""
    form = ResponseForm()

    if request.method == 'POST':
        return _handle_feedback_post(request, locale, product,
                                     version, channel)

    return render(request, 'feedback/generic_feedback.html', {
        'form': form,
    })
Beispiel #4
0
def mobile_stable_feedback(request):
    form = ResponseForm()
    happy = None

    if request.method == 'POST':
        response, form = _handle_feedback_post(request)
        if response:
            return response
        happy = smart_bool(request.POST.get('happy', None), None)

    return render(request, 'feedback/mobile/feedback.html', {
        'form': form,
        'happy': happy,
    })
Beispiel #5
0
def _handle_feedback_post(request):
    form = ResponseForm(request.POST)
    if form.is_valid():
        data = form.cleaned_data

        # Most platforms aren't different enough between versions to care.
        # Windows is.
        platform = request.BROWSER.platform
        if platform == 'Windows':
            platform += ' ' + request.BROWSER.platform_version

        opinion = models.Response(
            # Data coming from the user
            happy=data['happy'],
            url=data['url'],
            description=data['description'],
            # Inferred data
            prodchan=_get_prodchan(request),
            user_agent=request.META.get('HTTP_USER_AGENT', ''),
            browser=request.BROWSER.browser,
            browser_version=request.BROWSER.browser_version,
            platform=platform,
            locale=request.locale,
            # Data from mobile devices
            manufacturer=data.get('manufacturer', ''),
            device=data.get('device', ''),
        )
        opinion.save()

        if data['email_ok'] and data['email']:
            e = models.ResponseEmail(email=data['email'], opinion=opinion)
            e.save()

        return HttpResponseRedirect(reverse('thanks')), form

    # The user did something wrong.
    return None, form
Beispiel #6
0
def _handle_feedback_post(request):
    form = ResponseForm(request.POST)
    if form.is_valid():
        data = form.cleaned_data

        # Most platforms aren't different enough between versions to care.
        # Windows is.
        platform = request.BROWSER.platform
        if platform == 'Windows':
            platform += ' ' + request.BROWSER.platform_version

        opinion = models.Response(
            # Data coming from the user
            happy=data['happy'],
            url=data['url'],
            description=data['description'],
            # Inferred data
            prodchan=_get_prodchan(request),
            user_agent=request.META.get('HTTP_USER_AGENT', ''),
            browser=request.BROWSER.browser,
            browser_version=request.BROWSER.browser_version,
            platform=platform,
            locale=request.locale,
            # Data from mobile devices
            manufacturer=data.get('manufacturer', ''),
            device=data.get('device', ''),
        )
        opinion.save()

        if data['email_ok'] and data['email']:
            e = models.ResponseEmail(email=data['email'], opinion=opinion)
            e.save()

        return HttpResponseRedirect(reverse('thanks')), form

    # The user did something wrong.
    return None, form
Beispiel #7
0
def android_feedback(request, locale=None, product=None, version=None,
                     channel=None):
    """Generic feedback form for desktop and mobile"""
    form = ResponseForm()

    if request.method == 'POST':
        return _handle_feedback_post(request, locale, product,
                                     version, channel)

    return render(request, 'feedback/android_feedback.html', {
        'form': form,
        'product': product,
        'version': version,
        'channel': channel,
        'TRUNCATE_LENGTH': TRUNCATE_LENGTH,
    })
Beispiel #8
0
def generic_feedback(request, locale=None, product=None, version=None,
                     channel=None):
    """Generic feedback form for desktop and mobile"""
    form = ResponseForm()

    if request.method == 'POST':
        return _handle_feedback_post(request, locale, product,
                                     version, channel)

    bd = product.collect_browser_data_for(request.BROWSER.browser)

    return render(request, 'feedback/generic_feedback.html', {
        'form': form,
        'product': product,
        'collect_browser_data': bd,
        'TRUNCATE_LENGTH': TRUNCATE_LENGTH,
    })
Beispiel #9
0
def _handle_feedback_post(request, locale=None, product=None,
                          version=None, channel=None):
    """Saves feedback post to db accounting for throttling

    :arg request: request we're handling the post for
    :arg locale: locale specified in the url
    :arg product: None or the Product
    :arg version: validated and sanitized version specified in the url
    :arg channel: validated and sanitized channel specified in the url

    """
    if getattr(request, 'limited', False):
        # If we're throttled, then return the thanks page, but don't
        # add the response to the db.
        return HttpResponseRedirect(reverse('thanks'))

    # Get the form and run is_valid() so it goes through the
    # validation and cleaning machinery. We don't really care if it's
    # valid, though, since we will take what we got and do the best we
    # can with it. Error validation is now in JS.
    form = ResponseForm(request.POST)
    form.is_valid()

    get_data = request.GET.copy()

    data = form.cleaned_data

    description = data.get('description', u'').strip()
    if not description:
        # If there's no description, then there's nothing to do here,
        # so thank the user and move on.
        return HttpResponseRedirect(reverse('thanks'))

    opinion = models.Response(
        # Data coming from the user
        happy=data['happy'],
        url=clean_url(data.get('url', u'').strip()),
        description=description,

        # Pulled from the form data or the url
        locale=data.get('locale', locale),

        # Data from mobile devices which is probably only
        # applicable to mobile devices
        manufacturer=data.get('manufacturer', ''),
        device=data.get('device', ''),
    )

    # Add user_agent and inferred data.
    user_agent = request.META.get('HTTP_USER_AGENT', '')
    if user_agent:
        browser = request.BROWSER

        opinion.user_agent = user_agent
        opinion.browser = browser.browser
        opinion.browser_version = browser.browser_version
        opinion.browser_platform = browser.platform
        if browser.platform == 'Windows':
            opinion.browser_platform += ' ' + browser.platform_version

    # source is src or utm_source
    source = (
        get_data.pop('src', [u''])[0] or
        get_data.pop('utm_source', [u''])[0]
    )
    if source:
        opinion.source = source[:100]

    campaign = get_data.pop('utm_campaign', [u''])[0]
    if campaign:
        opinion.campaign = campaign[:100]

    # If they sent "happy=1"/"happy=0" in the querystring, it will get
    # picked up by the javascript in the form and we can just drop it
    # here.
    get_data.pop('happy', None)

    platform = u''

    if product:
        # If we have a product at this point, then it came from the
        # url and it's a Product instance and we need to turn it into
        # the product.db_name which is a string.
        product_db_name = product.db_name
    else:
        # Check the POST data for the product.
        product_db_name = data.get('product', '')

    # For the version, we try the url data, then the POST data.
    version = version or data.get('version', '')

    # At this point, we have a bunch of values, but we might be
    # missing some values, too. We're going to cautiously infer data
    # from the user agent where we're very confident it's appropriate
    # to do so.
    if request.BROWSER != UNKNOWN:
        # If we don't have a product, try to infer that from the user
        # agent information.
        if not product_db_name:
            product_db_name = models.Response.infer_product(request.BROWSER)

        # If we have a product and it matches the user agent browser,
        # then we can infer the version and platform from the user
        # agent if they're missing.
        if product_db_name:
            product = models.Product.objects.get(db_name=product_db_name)
            if product.browser and product.browser == request.BROWSER.browser:
                if not version:
                    version = request.BROWSER.browser_version
                if not platform:
                    platform = models.Response.infer_platform(
                        product_db_name, request.BROWSER)

    # Make sure values are at least empty strings--no Nones.
    opinion.product = product_db_name or u''
    opinion.version = version or u''
    opinion.channel = channel or u''
    opinion.platform = platform or u''

    opinion.save()

    # If there was an email address, save that separately.
    if data.get('email_ok') and data.get('email'):
        e = models.ResponseEmail(email=data['email'], opinion=opinion)
        e.save()
        statsd.incr('feedback.emaildata.optin')

    # If there's browser data, save that separately.
    if data.get('browser_ok'):
        # This comes in as a JSON string. Because we're using
        # JSONObjectField, we need to convert it back to Python and
        # then save it. This is kind of silly, but it does guarantee
        # we have valid JSON.
        try:
            browser_data = data['browser_data']
            browser_data = json.loads(browser_data)

        except ValueError:
            # Handles empty string and any non-JSON value.
            statsd.incr('feedback.browserdata.badvalue')

        except KeyError:
            # Handles the case where it's missing from the data
            # dict. If it's missing, we don't want to do anything
            # including metrics.
            pass

        else:
            # If browser_data isn't an empty dict, then save it.
            if browser_data:
                rti = models.ResponsePI(
                    data=browser_data, opinion=opinion)
                rti.save()
                statsd.incr('feedback.browserdata.optin')

    if get_data:
        # There was extra context in the query string, so we grab that
        # with some restrictions and save it separately.
        slop = {}

        # We capture at most the first 20 key/val pairs
        get_data_items = sorted(get_data.items())[:20]

        for key, val in get_data_items:
            # Keys can be at most 20 characters long.
            key = key[:20]
            if len(val) == 1:
                val = val[0]

            # Values can be at most 20 characters long.
            val = val[:100]
            slop[key.encode('utf-8')] = val.encode('utf-8')

        context = models.ResponseContext(data=slop, opinion=opinion)
        context.save()
        statsd.incr('feedback.contextdata.optin')

    if data['happy']:
        statsd.incr('feedback.happy')
    else:
        statsd.incr('feedback.sad')

    request.session['response_id'] = opinion.id

    return HttpResponseRedirect(reverse('thanks'))
Beispiel #10
0
def _handle_feedback_post(request, locale=None, product=None, version=None, channel=None):
    if getattr(request, "limited", False):
        # If we're throttled, then return the thanks page, but don't
        # add the response to the db.
        return HttpResponseRedirect(reverse("thanks"))

    # Get the form and run is_valid() so it goes through the
    # validation and cleaning machinery. We don't really care if it's
    # valid, though, since we will take what we got and do the best we
    # can with it. Error validation is now in JS.
    form = ResponseForm(request.POST)
    form.is_valid()

    data = form.cleaned_data
    description = data.get("description", u"").strip()
    if not description:
        # If there's no description, then there's nothing to do here,
        # so thank the user and move on.
        return HttpResponseRedirect(reverse("thanks"))

    # Do some data validation of product, channel and version
    # coming from the url.
    product = models.Product.get_product_map().get(smart_str(product), u"")
    # FIXME - validate these better
    channel = smart_str(channel).lower()
    version = smart_str(version)

    # src, then source, then utm_source
    source = request.GET.get("src", u"")
    if not source:
        source = request.GET.get("utm_source", u"")

    campaign = request.GET.get("utm_campaign", u"")

    # Most platforms aren't different enough between versions to care.
    # Windows is.
    platform = request.BROWSER.platform
    if platform == "Windows":
        platform += " " + request.BROWSER.platform_version

    opinion = models.Response(
        # Data coming from the user
        happy=data["happy"],
        url=clean_url(data.get("url", u"")),
        description=data["description"].strip(),
        # Inferred data from user agent
        prodchan=_get_prodchan(request, product, channel),
        user_agent=request.META.get("HTTP_USER_AGENT", ""),
        browser=request.BROWSER.browser,
        browser_version=request.BROWSER.browser_version,
        platform=platform,
        # Pulled from the form data or the url
        locale=data.get("locale", locale),
        # Data from mobile devices which is probably only
        # applicable to mobile devices
        manufacturer=data.get("manufacturer", ""),
        device=data.get("device", ""),
    )

    if source:
        opinion.source = source[:100]

    if campaign:
        opinion.campaign = campaign[:100]

    if product:
        # If we picked up the product from the url, we use url
        # bits for everything.
        product = product or u""
        version = version or u""
        channel = channel or u""

    elif opinion.browser != UNKNOWN:
        # If we didn't pick up a product from the url, then we
        # infer as much as we can from the user agent.
        product = data.get("product", models.Response.infer_product(platform))
        version = data.get("version", request.BROWSER.browser_version)
        # Assume everything we don't know about is stable channel.
        channel = u"stable"

    else:
        product = channel = version = u""

    opinion.product = product or u""
    opinion.version = version or u""
    opinion.channel = channel or u""

    opinion.save()

    # If there was an email address, save that separately.
    if data.get("email_ok") and data.get("email"):
        e = models.ResponseEmail(email=data["email"], opinion=opinion)
        e.save()

    return HttpResponseRedirect(reverse("thanks"))
Beispiel #11
0
def _handle_feedback_post(request, locale=None, product=None,
                          version=None, channel=None):
    """Saves feedback post to db accounting for throttling

    :arg request: request we're handling the post for
    :arg locale: locale specified in the url
    :arg product: validated and sanitized product slug specified in
        the url
    :arg version: validated and sanitized version specified in the url
    :arg channel: validated and sanitized channel specified in the url

    """
    if getattr(request, 'limited', False):
        # If we're throttled, then return the thanks page, but don't
        # add the response to the db.
        return HttpResponseRedirect(reverse('thanks'))

    # Get the form and run is_valid() so it goes through the
    # validation and cleaning machinery. We don't really care if it's
    # valid, though, since we will take what we got and do the best we
    # can with it. Error validation is now in JS.
    form = ResponseForm(request.POST)
    form.is_valid()

    get_data = request.GET.copy()

    data = form.cleaned_data
    description = data.get('description', u'').strip()
    if not description:
        # If there's no description, then there's nothing to do here,
        # so thank the user and move on.
        return HttpResponseRedirect(reverse('thanks'))

    # Do some data validation of product, channel and version
    # coming from the url.
    if product:
        # If there was a product in the url, that's a product slug, so
        # we map it to a db_name which is what we want to save to the
        # db.
        product = models.Product.get_product_map()[product]

    # src, then source, then utm_source
    source = get_data.pop('src', [u''])[0]
    if not source:
        source = get_data.pop('utm_source', [u''])[0]

    campaign = get_data.pop('utm_campaign', [u''])[0]

    # If the product came in on the url, then we only want to populate
    # the platfrom from the user agent data iff the product specified
    # by the url is the same as the browser product.
    platform = u''
    if product is None or product == request.BROWSER.browser:
        # Most platforms aren't different enough between versions to care.
        # Windows is.
        platform = request.BROWSER.platform
        if platform == 'Windows':
            platform += ' ' + request.BROWSER.platform_version

    product = product or u''

    opinion = models.Response(
        # Data coming from the user
        happy=data['happy'],
        url=clean_url(data.get('url', u'')),
        description=data['description'].strip(),

        # Inferred data from user agent
        user_agent=request.META.get('HTTP_USER_AGENT', ''),
        browser=request.BROWSER.browser,
        browser_version=request.BROWSER.browser_version,
        platform=platform,

        # Pulled from the form data or the url
        locale=data.get('locale', locale),

        # Data from mobile devices which is probably only
        # applicable to mobile devices
        manufacturer=data.get('manufacturer', ''),
        device=data.get('device', ''),
    )

    if source:
        opinion.source = source[:100]

    if campaign:
        opinion.campaign = campaign[:100]

    if product:
        # If we picked up the product from the url, we use url
        # bits for everything.
        product = product or u''
        version = version or u''
        channel = channel or u''

    elif opinion.browser != UNKNOWN:
        # If we didn't pick up a product from the url, then we
        # infer as much as we can from the user agent.
        product = data.get(
            'product', models.Response.infer_product(platform))
        version = data.get(
            'version', request.BROWSER.browser_version)
        # Assume everything we don't know about is stable channel.
        channel = u'stable'

    else:
        product = channel = version = u''

    opinion.product = product or u''
    opinion.version = version or u''
    opinion.channel = channel or u''

    opinion.save()

    # If there was an email address, save that separately.
    if data.get('email_ok') and data.get('email'):
        e = models.ResponseEmail(email=data['email'], opinion=opinion)
        e.save()

    if get_data:
        # There was extra context in the query string, so we grab that
        # with some restrictions and save it separately.
        slop = {}

        # We capture at most the first 20 key/val pairs
        get_data_items = sorted(get_data.items())[:20]

        for key, val in get_data_items:
            # Keys can be at most 20 characters long.
            key = key[:20]
            if len(val) == 1:
                val = val[0]

            # Values can be at most 20 characters long.
            val = val[:100]
            slop[key.encode('utf-8')] = val.encode('utf-8')

        context = models.ResponseContext(data=slop, opinion=opinion)
        context.save()

    if data['happy']:
        statsd.incr('feedback.happy')
    else:
        statsd.incr('feedback.sad')

    return HttpResponseRedirect(reverse('thanks'))
Beispiel #12
0
def _handle_feedback_post(request,
                          locale=None,
                          product=None,
                          version=None,
                          channel=None):
    """Saves feedback post to db accounting for throttling

    :arg request: request we're handling the post for
    :arg locale: locale specified in the url
    :arg product: None or the Product
    :arg version: validated and sanitized version specified in the url
    :arg channel: validated and sanitized channel specified in the url

    """
    if getattr(request, 'limited', False):
        # If we're throttled, then return the thanks page, but don't
        # add the response to the db.
        return HttpResponseRedirect(reverse('thanks'))

    # Get the form and run is_valid() so it goes through the
    # validation and cleaning machinery. We don't really care if it's
    # valid, though, since we will take what we got and do the best we
    # can with it. Error validation is now in JS.
    form = ResponseForm(request.POST)
    form.is_valid()

    get_data = request.GET.copy()

    data = form.cleaned_data

    description = data.get('description', u'').strip()
    if not description:
        # If there's no description, then there's nothing to do here,
        # so thank the user and move on.
        return HttpResponseRedirect(reverse('thanks'))

    opinion = models.Response(
        # Data coming from the user
        happy=data['happy'],
        url=clean_url(data.get('url', u'').strip()),
        description=description,

        # Pulled from the form data or the url
        locale=data.get('locale', locale),

        # Data from mobile devices which is probably only
        # applicable to mobile devices
        manufacturer=data.get('manufacturer', ''),
        device=data.get('device', ''),
    )

    # Add user_agent and inferred data.
    user_agent = request.META.get('HTTP_USER_AGENT', '')
    if user_agent:
        browser = request.BROWSER

        opinion.user_agent = user_agent
        opinion.browser = browser.browser
        opinion.browser_version = browser.browser_version
        opinion.browser_platform = browser.platform
        if browser.platform == 'Windows':
            opinion.browser_platform += ' ' + browser.platform_version

    # source is src or utm_source
    source = (get_data.pop('src', [u''])[0]
              or get_data.pop('utm_source', [u''])[0])
    if source:
        opinion.source = source[:100]

    campaign = get_data.pop('utm_campaign', [u''])[0]
    if campaign:
        opinion.campaign = campaign[:100]

    platform = u''

    if product:
        # If we have a product at this point, then it came from the
        # url and it's a Product instance and we need to turn it into
        # the product.db_name which is a string.
        product_db_name = product.db_name
    else:
        # Check the POST data for the product.
        product_db_name = data.get('product', '')

    # For the version, we try the url data, then the POST data.
    version = version or data.get('version', '')

    # At this point, we have a bunch of values, but we might be
    # missing some values, too. We're going to cautiously infer data
    # from the user agent where we're very confident it's appropriate
    # to do so.
    if request.BROWSER != UNKNOWN:
        # If we don't have a product, try to infer that from the user
        # agent information.
        if not product_db_name:
            product_db_name = models.Response.infer_product(request.BROWSER)

        # If we have a product and it matches the user agent browser,
        # then we can infer the version and platform from the user
        # agent if they're missing.
        if product_db_name:
            product = models.Product.objects.get(db_name=product_db_name)
            if product.browser and product.browser == request.BROWSER.browser:
                if not version:
                    version = request.BROWSER.browser_version
                if not platform:
                    platform = models.Response.infer_platform(
                        product_db_name, request.BROWSER)

    # Make sure values are at least empty strings--no Nones.
    opinion.product = product_db_name or u''
    opinion.version = version or u''
    opinion.channel = channel or u''
    opinion.platform = platform or u''

    opinion.save()

    # If there was an email address, save that separately.
    if data.get('email_ok') and data.get('email'):
        e = models.ResponseEmail(email=data['email'], opinion=opinion)
        e.save()
        statsd.incr('feedback.emaildata.optin')

    # If there's browser data, save that separately.
    if data.get('browser_ok'):
        # This comes in as a JSON string. Because we're using
        # JSONObjectField, we need to convert it back to Python and
        # then save it. This is kind of silly, but it does guarantee
        # we have valid JSON.
        try:
            browser_data = data['browser_data']
            browser_data = json.loads(browser_data)

        except ValueError:
            # Handles empty string and any non-JSON value.
            statsd.incr('feedback.browserdata.badvalue')

        except KeyError:
            # Handles the case where it's missing from the data
            # dict. If it's missing, we don't want to do anything
            # including metrics.
            pass

        else:
            # If browser_data isn't an empty dict, then save it.
            if browser_data:
                rti = models.ResponsePI(data=browser_data, opinion=opinion)
                rti.save()
                statsd.incr('feedback.browserdata.optin')

    if get_data:
        # There was extra context in the query string, so we grab that
        # with some restrictions and save it separately.
        slop = {}

        # We capture at most the first 20 key/val pairs
        get_data_items = sorted(get_data.items())[:20]

        for key, val in get_data_items:
            # Keys can be at most 20 characters long.
            key = key[:20]
            if len(val) == 1:
                val = val[0]

            # Values can be at most 20 characters long.
            val = val[:100]
            slop[key.encode('utf-8')] = val.encode('utf-8')

        context = models.ResponseContext(data=slop, opinion=opinion)
        context.save()
        statsd.incr('feedback.contextdata.optin')

    if data['happy']:
        statsd.incr('feedback.happy')
    else:
        statsd.incr('feedback.sad')

    request.session['opinion_id'] = opinion.id

    return HttpResponseRedirect(reverse('thanks'))
Beispiel #13
0
def _handle_feedback_post(request, locale=None, product=None,
                          version=None, channel=None):
    """Saves feedback post to db accounting for throttling

    :arg request: request we're handling the post for
    :arg locale: locale specified in the url
    :arg product: validated and sanitized product slug specified in
        the url
    :arg version: validated and sanitized version specified in the url
    :arg channel: validated and sanitized channel specified in the url

    """
    if getattr(request, 'limited', False):
        # If we're throttled, then return the thanks page, but don't
        # add the response to the db.
        return HttpResponseRedirect(reverse('thanks'))

    # Get the form and run is_valid() so it goes through the
    # validation and cleaning machinery. We don't really care if it's
    # valid, though, since we will take what we got and do the best we
    # can with it. Error validation is now in JS.
    form = ResponseForm(request.POST)
    form.is_valid()

    data = form.cleaned_data
    description = data.get('description', u'').strip()
    if not description:
        # If there's no description, then there's nothing to do here,
        # so thank the user and move on.
        return HttpResponseRedirect(reverse('thanks'))

    # Do some data validation of product, channel and version
    # coming from the url.
    if product:
        # If there was a product in the url, that's a product slug, so
        # we map it to a db_name which is what we want to save to the
        # db.
        product = models.Product.get_product_map()[product]

    # src, then source, then utm_source
    source = request.GET.get('src', u'')
    if not source:
        source = request.GET.get('utm_source', u'')

    campaign = request.GET.get('utm_campaign', u'')

    # If the product came in on the url, then we only want to populate
    # the platfrom from the user agent data iff the product specified
    # by the url is the same as the browser product.
    platform = u''
    if product is None or product == request.BROWSER.browser:
        # Most platforms aren't different enough between versions to care.
        # Windows is.
        platform = request.BROWSER.platform
        if platform == 'Windows':
            platform += ' ' + request.BROWSER.platform_version

    product = product or u''

    opinion = models.Response(
        # Data coming from the user
        happy=data['happy'],
        url=clean_url(data.get('url', u'')),
        description=data['description'].strip(),

        # Inferred data from user agent
        prodchan=_get_prodchan(request, product, channel),
        user_agent=request.META.get('HTTP_USER_AGENT', ''),
        browser=request.BROWSER.browser,
        browser_version=request.BROWSER.browser_version,
        platform=platform,

        # Pulled from the form data or the url
        locale=data.get('locale', locale),

        # Data from mobile devices which is probably only
        # applicable to mobile devices
        manufacturer=data.get('manufacturer', ''),
        device=data.get('device', ''),
    )

    if source:
        opinion.source = source[:100]

    if campaign:
        opinion.campaign = campaign[:100]

    if product:
        # If we picked up the product from the url, we use url
        # bits for everything.
        product = product or u''
        version = version or u''
        channel = channel or u''

    elif opinion.browser != UNKNOWN:
        # If we didn't pick up a product from the url, then we
        # infer as much as we can from the user agent.
        product = data.get(
            'product', models.Response.infer_product(platform))
        version = data.get(
            'version', request.BROWSER.browser_version)
        # Assume everything we don't know about is stable channel.
        channel = u'stable'

    else:
        product = channel = version = u''

    opinion.product = product or u''
    opinion.version = version or u''
    opinion.channel = channel or u''

    opinion.save()

    # If there was an email address, save that separately.
    if data.get('email_ok') and data.get('email'):
        e = models.ResponseEmail(email=data['email'], opinion=opinion)
        e.save()

    if data['happy']:
        statsd.incr('feedback.happy')
    else:
        statsd.incr('feedback.sad')

    return HttpResponseRedirect(reverse('thanks'))
Beispiel #14
0
def _handle_feedback_post(request, locale=None, product=None,
                          version=None, channel=None):
    if getattr(request, 'limited', False):
        # If we're throttled, then return the thanks page, but don't
        # add the response to the db.
        return HttpResponseRedirect(reverse('thanks')), None

    form = ResponseForm(request.POST)
    if form.is_valid():
        # Do some data validation of product, channel and version
        # coming from the url.
        product = config.PRODUCT_MAP.get(smart_str(product), u'')
        # FIXME - validate these better
        channel = smart_str(channel).lower()
        version = smart_str(version)

        data = form.cleaned_data

        # Most platforms aren't different enough between versions to care.
        # Windows is.
        platform = request.BROWSER.platform
        if platform == 'Windows':
            platform += ' ' + request.BROWSER.platform_version

        opinion = models.Response(
            # Data coming from the user
            happy=data['happy'],
            url=data['url'],
            description=data['description'],

            # Inferred data from user agent
            prodchan=_get_prodchan(request, product, channel),
            user_agent=request.META.get('HTTP_USER_AGENT', ''),
            browser=request.BROWSER.browser,
            browser_version=request.BROWSER.browser_version,
            platform=platform,

            # Pulled from the form data or the url
            locale=data.get('locale', locale),

            # Data from mobile devices which is probably only
            # applicable to mobile devices
            manufacturer=data.get('manufacturer', ''),
            device=data.get('device', ''),
        )

        if product:
            # If we picked up the product from the url, we use url
            # bits for everything.
            product = product or u''
            version = version or u''
            channel = channel or u''

        elif opinion.browser != UNKNOWN:
            # If we didn't pick up a product from the url, then we
            # infer as much as we can from the user agent.
            product = data.get(
                'product', models.Response.infer_product(platform))
            version = data.get(
                'version', request.BROWSER.browser_version)
            # Assume everything we don't know about is stable channel.
            channel = u'stable'

        else:
            product = channel = version = u''

        opinion.product = product or u''
        opinion.version = version or u''
        opinion.channel = channel or u''

        opinion.save()

        # If there was an email address, save that separately.
        if data['email_ok'] and data['email']:
            e = models.ResponseEmail(email=data['email'], opinion=opinion)
            e.save()

        return HttpResponseRedirect(reverse('thanks')), form

    # The user did something wrong.
    return None, form
Beispiel #15
0
def _handle_feedback_post(request, locale=None, product=None,
                          version=None, channel=None):
    """Saves feedback post to db accounting for throttling

    :arg request: request we're handling the post for
    :arg locale: locale specified in the url
    :arg product: None or the Product
    :arg version: validated and sanitized version specified in the url
    :arg channel: validated and sanitized channel specified in the url

    """
    if getattr(request, 'limited', False):
        # If we're throttled, then return the thanks page, but don't
        # add the response to the db.
        return HttpResponseRedirect(reverse('thanks'))

    # Get the form and run is_valid() so it goes through the
    # validation and cleaning machinery. We don't really care if it's
    # valid, though, since we will take what we got and do the best we
    # can with it. Error validation is now in JS.
    form = ResponseForm(request.POST)
    form.is_valid()

    get_data = request.GET.copy()

    data = form.cleaned_data

    description = data.get('description', u'').strip()
    if not description:
        # If there's no description, then there's nothing to do here,
        # so thank the user and move on.
        return HttpResponseRedirect(reverse('thanks'))

    opinion = models.Response(
        # Data coming from the user
        happy=data['happy'],
        url=clean_url(data.get('url', u'')),
        description=description,

        # Pulled from the form data or the url
        locale=data.get('locale', locale),

        # Data from mobile devices which is probably only
        # applicable to mobile devices
        manufacturer=data.get('manufacturer', ''),
        device=data.get('device', ''),
    )

    # Add user_agent and inferred data.
    user_agent = request.META.get('HTTP_USER_AGENT', '')
    if user_agent:
        browser = request.BROWSER

        opinion.user_agent = user_agent
        opinion.browser = browser.browser
        opinion.browser_version = browser.browser_version
        opinion.browser_platform = browser.platform
        if browser.platform == 'Windows':
            opinion.browser_platform += ' ' + browser.platform_version

    # source is src or utm_source
    source = (
        get_data.pop('src', [u''])[0] or
        get_data.pop('utm_source', [u''])[0]
    )
    if source:
        opinion.source = source[:100]

    campaign = get_data.pop('utm_campaign', [u''])[0]
    if campaign:
        opinion.campaign = campaign[:100]

    platform = u''

    # Figure out product, version, channel and platform. We either get
    # them from the url, from the user_agent, or we don't set them at
    # all.
    if product:
        # If there was a product in the url, then we get a Product
        # instance as an argument and we want the db_name from that.
        product = product.db_name

        # FIXME: We should be able to "match" the product with the
        # user agent browser and if they're the same, then set
        # platform == browser_platform. However, it's tricky since
        # our "products" are "interesting".

    elif opinion.browser != UNKNOWN:
        # If we didn't pick up a product from the url, then we
        # infer as much as we can from the user agent.
        product = data.get(
            'product', models.Response.infer_product(opinion.browser_platform))
        version = data.get(
            'version', request.BROWSER.browser_version)
        # Assume everything we don't know about is stable channel.
        channel = u'stable'

    # Try to infer the platform from the product.
    if product and not platform:
        platform = models.Response.infer_platform(product, request.BROWSER)

    opinion.product = product or u''
    opinion.version = version or u''
    opinion.channel = channel or u''
    opinion.platform = platform or u''

    opinion.save()

    # If there was an email address, save that separately.
    if data.get('email_ok') and data.get('email'):
        e = models.ResponseEmail(email=data['email'], opinion=opinion)
        e.save()

    if get_data:
        # There was extra context in the query string, so we grab that
        # with some restrictions and save it separately.
        slop = {}

        # We capture at most the first 20 key/val pairs
        get_data_items = sorted(get_data.items())[:20]

        for key, val in get_data_items:
            # Keys can be at most 20 characters long.
            key = key[:20]
            if len(val) == 1:
                val = val[0]

            # Values can be at most 20 characters long.
            val = val[:100]
            slop[key.encode('utf-8')] = val.encode('utf-8')

        context = models.ResponseContext(data=slop, opinion=opinion)
        context.save()

    if data['happy']:
        statsd.incr('feedback.happy')
    else:
        statsd.incr('feedback.sad')

    return HttpResponseRedirect(reverse('thanks'))
Beispiel #16
0
def _handle_feedback_post(request):
    form = ResponseForm(request.POST)
    if form.is_valid():
        data = form.cleaned_data

        # Most platforms aren't different enough between versions to care.
        # Windows is.
        platform = request.BROWSER.platform
        if platform == 'Windows':
            platform += ' ' + request.BROWSER.platform_version

        opinion = models.Response(
            # Data coming from the user
            happy=data['happy'],
            url=data['url'],
            description=data['description'],

            # Inferred data from user agent
            prodchan=_get_prodchan(request),
            user_agent=request.META.get('HTTP_USER_AGENT', ''),
            browser=request.BROWSER.browser,
            browser_version=request.BROWSER.browser_version,
            platform=platform,

            # Data that comes form the user or is inferred
            locale=data.get('locale', request.locale),

            # Data from mobile devices
            manufacturer=data.get('manufacturer', ''),
            device=data.get('device', ''),
        )

        # This data is coming from the web form where we infer many
        # things from the user agent. If the user agent is bogus, then
        # the browser is UNKNOWN. If that's the case, then we can't
        # infer anything useful for product, channel or version so we
        # set them to empty strings.
        if opinion.browser == UNKNOWN:
            opinion.product = u''
            opinion.channel = u''
            opinion.version = u''

        else:
            opinion.product = data.get(
                'product', models.Response.infer_product(platform))
            # For now, we assume everything is stable because we don't
            # know otherwise.
            opinion.channel = u'stable'
            opinion.version = data.get(
                'version', request.BROWSER.browser_version)

        opinion.save()

        if data['email_ok'] and data['email']:
            e = models.ResponseEmail(email=data['email'], opinion=opinion)
            e.save()

        return HttpResponseRedirect(reverse('thanks')), form

    # The user did something wrong.
    return None, form