Ejemplo n.º 1
0
    def test_not_str(self):
        eq_(u'', smart_str(1))
        eq_(u'', smart_str(1.1))
        eq_(u'', smart_str(True))
        eq_(u'', smart_str(['a']))

        eq_(u'', smart_str(None))
Ejemplo n.º 2
0
    def get(self, request):
        events = get_product_details_history()

        products = smart_str(request.GET.get('products'))
        date_start = smart_str(request.GET.get('date_start'))
        date_end = smart_str(request.GET.get('date_end'))

        if products:
            products = [prod.strip() for prod in products.split(',')]
            events = [event for event in events
                      if event['product'] in products]

        if date_start:
            events = [event for event in events
                      if event['date'] >= date_start]

        if date_end:
            events = [event for event in events
                      if event['date'] <= date_end]

        return rest_framework.response.Response({
            'date_start': date_start if date_start else None,
            'date_end': date_end if date_end else None,
            'products': ','.join(products) if products else None,
            'count': len(events),
            'events': list(events)
        })
Ejemplo n.º 3
0
    def test_not_str(self):
        eq_(u'', smart_str(1))
        eq_(u'', smart_str(1.1))
        eq_(u'', smart_str(True))
        eq_(u'', smart_str(['a']))

        eq_(u'', smart_str(None))
Ejemplo n.º 4
0
def feedback_router_dev(request, product=None, version=None, channel=None,
                    *args, **kwargs):
    """DEV ONLY FEEDBACK ROUTER"""
    view = None

    if '_type' in request.POST:
        # Checks to see if `_type` is in the POST data and if so this
        # is coming from Firefox for Android which doesn't know
        # anything about csrf tokens. If that's the case, we send it
        # to a view specifically for FfA Otherwise we pass it to one
        # of the normal views, which enforces CSRF. Also, nix the
        # product just in case we're crossing the streams and
        # confusing new-style product urls with old-style backwards
        # compatability for the Android form.
        #
        # FIXME: Remove this hairbrained monstrosity when we don't need to
        # support the method that Firefox for Android currently uses to
        # post feedback which worked with the old input.mozilla.org.

        # This lets us measure how often this section of code kicks
        # off and thus how often old android stuff is happening. When
        # we're not seeing this anymore, we can nix all the old
        # android stuff.
        statsd.incr('feedback.oldandroid')

        return android_about_feedback(request, request.locale)

    product = smart_str(product, fallback=None)
    # FIXME - validate these better
    version = smart_str(version)
    channel = smart_str(channel).lower()

    if product == 'fxos' or request.BROWSER.browser == 'Firefox OS':
        # Firefox OS gets shunted to a different form which has
        # different Firefox OS specific questions.
        view = firefox_os_stable_feedback
        product = 'fxos'

    elif product in PRODUCT_OVERRIDE:
        # The "product" is really a specific form to use. So we None
        # out the product and let that form view deal with everything.
        view = PRODUCT_OVERRIDE[product]
        product = None

    elif product is None or product not in models.Product.get_product_map():
        # The product wasn't specified or doesn't exist, so we spit
        # out the product picker.
        template = 'feedback/picker.html'

        products = models.Product.objects.all()
        return render(request, template, {
            'products': products
        })

    view = view or generic_feedback_dev
        
    return view(request, request.locale, product, version, channel,
                *args, **kwargs)
Ejemplo n.º 5
0
def feedback_router_dev(request, product=None, version=None, channel=None,
                    *args, **kwargs):
    """DEV ONLY FEEDBACK ROUTER"""
    view = None

    if '_type' in request.POST:
        # Checks to see if `_type` is in the POST data and if so this
        # is coming from Firefox for Android which doesn't know
        # anything about csrf tokens. If that's the case, we send it
        # to a view specifically for FfA Otherwise we pass it to one
        # of the normal views, which enforces CSRF. Also, nix the
        # product just in case we're crossing the streams and
        # confusing new-style product urls with old-style backwards
        # compatability for the Android form.
        #
        # FIXME: Remove this hairbrained monstrosity when we don't need to
        # support the method that Firefox for Android currently uses to
        # post feedback which worked with the old input.mozilla.org.

        # This lets us measure how often this section of code kicks
        # off and thus how often old android stuff is happening. When
        # we're not seeing this anymore, we can nix all the old
        # android stuff.
        statsd.incr('feedback.oldandroid')

        return android_about_feedback(request, request.locale)

    product = smart_str(product, fallback=None)
    # FIXME - validate these better
    version = smart_str(version)
    channel = smart_str(channel).lower()

    if product == 'fxos' or request.BROWSER.browser == 'Firefox OS':
        # Firefox OS gets shunted to a different form which has
        # different Firefox OS specific questions.
        view = firefox_os_stable_feedback
        product = 'fxos'

    elif product in PRODUCT_OVERRIDE:
        # The "product" is really a specific form to use. So we None
        # out the product and let that form view deal with everything.
        view = PRODUCT_OVERRIDE[product]
        product = None

    elif product is None or product not in models.Product.get_product_map():
        # The product wasn't specified or doesn't exist, so we spit
        # out the product picker.
        template = 'feedback/picker.html'

        products = models.Product.objects.all()
        return render(request, template, {
            'products': products
        })

    view = view or generic_feedback_dev
        
    return view(request, request.locale, product, version, channel,
                *args, **kwargs)
Ejemplo n.º 6
0
    def post(self, request):
        """Handles posting a new heartbeat item"""
        post_data = dict(request.DATA)

        hb = {}

        # Figure out if there's any missing data.
        missing = []
        for key in ('locale', 'platform', 'product', 'version', 'channel',
                    'answer'):
            if key in post_data:
                hb[key] = smart_str(post_data.pop(key))
            else:
                missing.append(key)

        if 'poll' in post_data:
            poll_slug = smart_str(post_data.pop('poll'))
        else:
            missing.append('poll')

        if missing:
            return rest_framework.response.Response(
                status=400,
                data=({'msg': 'missing fields: ' + ', '.join(sorted(missing))})
            )

        # Figure out if the poll exists and is enabled.
        try:
            poll = Poll.objects.get(slug=poll_slug)
        except Poll.DoesNotExist:
            return rest_framework.response.Response(
                status=400,
                data=({'msg': 'poll "%s" does not exist' % poll_slug})
            )

        if not poll.enabled:
            return rest_framework.response.Response(
                status=400,
                data=({'msg': 'poll "%s" is not currently running' %
                       poll_slug})
            )

        hb['poll'] = poll

        # Add the extra POST data fields by tossing them in the
        # "extra" field.
        hb['extra'] = post_data

        hb = Answer(**hb)
        hb.save()

        return rest_framework.response.Response(
            status=201,
            data={'msg': 'success!'})
Ejemplo n.º 7
0
def translations_management_backfill_view(request):
    """Takes start and end dates and a model and backfills translations"""
    date_start = smart_date(request.POST.get('date_start'))
    date_end = smart_date(request.POST.get('date_end'))
    model = smart_str(request.POST.get('model'))

    if request.method == 'POST' and date_start and date_end and model:
        # NB: We just let the errors propagate because this is an
        # admin page. That way we get a traceback and all that detail.

        # We add one day to date_end so that it picks up the entire day of
        # date_end.
        #
        # FIXME: We should do this in a less goofy way.
        date_end = date_end + timedelta(days=1)

        model_cls = import_by_path(model)

        # FIXME: This assumes the model has a "created" field. If it
        # doesn't, then this breaks. When we have another model that we
        # want to translate, we can figure out how to generalize this
        # then.
        objects = model_cls.objects.filter(
            created__gte=date_start,
            created__lte=date_end
        )

        total_jobs = 0

        for instance in objects:
            total_jobs += len(create_translation_tasks(instance))

        messages.success(request, '%s jobs added' % total_jobs)
        return HttpResponseRedirect(request.path)

    from fjord.translations.tasks import REGISTERED_MODELS
    model_classes = [
        cls.__module__ + '.' + cls.__name__
        for cls in REGISTERED_MODELS
    ]

    return render(request, 'admin/translations_backfill.html', {
        'title': 'Translations - General Maintenance - Backfill',
        'settings': settings,
        'model_classes': model_classes,
        'date_start': request.POST.get('date_start', ''),
        'date_end': request.POST.get('date_end', ''),
        'model': request.POST.get('model', '')
    })
Ejemplo n.º 8
0
 def test_str(self):
     eq_('a', smart_str('a'))
     eq_(u'a', smart_str(u'a'))
Ejemplo n.º 9
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"))
Ejemplo n.º 10
0
def analytics_flagged(request):
    """View showing responses with flags

    NOTE: This is not permanent and might go away depending on how the
    spicedham prototype works.

    """
    template = 'analytics/analyzer/flags.html'

    # FIXME: Importing this here so all the changes are localized to
    # this function.  If we decide to go forward with this, we should
    # unlocalize it.

    from django.contrib import messages
    from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
    from django.http import HttpResponseRedirect

    from fjord.flags.models import Flag

    if request.method == 'POST':
        flag_action = request.POST.get('flag')
        if flag_action:
            # We do some shenanigans here to make sure we're fetching
            # and operating on a flag_set that's not
            # cached. django-cache-machine doesn't invalidate m2m
            # queries correctly. :(
            resp = get_object_or_404(
                Response, pk=smart_int(request.POST['id']))
            flag = get_object_or_404(Flag, name=flag_action)

            resp_flags = dict([(f.name, f)
                               for f in resp.flag_set.no_cache().all()])
            if flag.name in resp_flags:
                del resp_flags[flag.name]
                messages.success(request, 'removed %s flag from %d' % (
                    flag_action, resp.id))
            else:
                resp_flags[flag.name] = flag
                messages.success(request, 'added %s flag from %d' % (
                    flag_action, resp.id))

            resp.flag_set.clear()
            resp.flag_set.add(*resp_flags.values())
            return HttpResponseRedirect(request.get_full_path())

    resp_filter = smart_str(request.GET.get('filter'))

    # Only look at en-US locale responses since Monday September 8th,
    # 2014 we pushed the integration code out.
    response_list = (Response.uncached
                     .filter(locale=u'en-US')
                     .filter(created__gte='2014-09-08'))

    counts = {
        'total': response_list.count(),
        'abuse': response_list.filter(flag__name='abuse').count(),
        'abuse-wrong': response_list.filter(flag__name='abuse-wrong').count(),
        'false-positive': (response_list
                           .filter(flag__name='abuse')
                           .filter(flag__name='abuse-wrong').count()),
    }
    counts['false-negative'] = (
        counts['abuse-wrong'] - counts['false-positive']
    )

    if resp_filter:
        response_list = response_list.filter(flag__name=resp_filter)

    paginator = Paginator(response_list, 50)

    page = request.GET.get('page')
    try:
        responses = paginator.page(page)
    except PageNotAnInteger:
        responses = paginator.page(1)
    except EmptyPage:
        responses = paginator.page(paginator.num_pages)

    return render(request, template, {
        'counts': counts,
        'responses': responses
    })
Ejemplo n.º 11
0
def feedback_router(request, product=None, version=None, channel=None,
                    *args, **kwargs):
    """Determine a view to use, and call it.

    If product is given, reference `product_routes` to look up a view.
    If `product` is not passed, or isn't found in `product_routes`,
    asssume the user is either a stable desktop Firefox or a stable
    mobile Firefox based on the parsed UA, and serve them the
    appropriate page. This is to handle the old formname way of doing
    things. At some point P, we should measure usage of the old
    formnames and deprecate them.

    This also handles backwards-compatability with the old Firefox for
    Android form which can't have a CSRF token.

    Note: We never want to cache this view.

    """
    view = None

    if '_type' in request.POST:
        # Checks to see if `_type` is in the POST data and if so this
        # is coming from Firefox for Android which doesn't know
        # anything about csrf tokens. If that's the case, we send it
        # to a view specifically for FfA Otherwise we pass it to one
        # of the normal views, which enforces CSRF. Also, nix the
        # product just in case we're crossing the streams and
        # confusing new-style product urls with old-style backwards
        # compatability for the Android form.
        #
        # FIXME: Remove this hairbrained monstrosity when we don't need to
        # support the method that Firefox for Android currently uses to
        # post feedback which worked with the old input.mozilla.org.
        view = android_about_feedback
        product = None

        # This lets us measure how often this section of code kicks
        # off and thus how often old android stuff is happening. When
        # we're not seeing this anymore, we can nix all the old
        # android stuff.
        statsd.incr('feedback.oldandroid')

        return android_about_feedback(request, request.locale)


    # FIXME - validate these better
    version = smart_str(version)
    channel = smart_str(channel).lower()

    if product == 'fxos' or request.BROWSER.browser == 'Firefox OS':
        # Firefox OS gets shunted to a different form which has
        # different Firefox OS specific questions.
        view = firefox_os_stable_feedback
        product = 'fxos'

    elif product:
        product = smart_str(product)

        if product in PRODUCT_OVERRIDE:
            # If the product is really a form name, we use that
            # form specifically.
            view = PRODUCT_OVERRIDE[product]
            product = None

        elif product not in models.Product.get_product_map():
            # If they passed in a product and we don't know about
            # it, stop here.
            return render(request, 'feedback/unknownproduct.html', {
                'product': product
            })

    if view is None:
        view = generic_feedback

    return view(request, request.locale, product, version, channel,
                *args, **kwargs)
Ejemplo n.º 12
0
def feedback_router(request, product=None, version=None, channel=None,
                    *args, **kwargs):
    """Determine a view to use, and call it.

    If product is given, reference `product_routes` to look up a view.
    If `product` is not passed, or isn't found in `product_routes`,
    asssume the user is either a stable desktop Firefox or a stable
    mobile Firefox based on the parsed UA, and serve them the
    appropriate page. This is to handle the old formname way of doing
    things. At some point P, we should measure usage of the old
    formnames and deprecate them.

    This also handles backwards-compatability with the old Firefox for
    Android form which can't have a CSRF token.

    Note: We never want to cache this view.

    """
    view = None

    if '_type' in request.POST:
        # Checks to see if `_type` is in the POST data and if so this
        # is coming from Firefox for Android which doesn't know
        # anything about csrf tokens. If that's the case, we send it
        # to a view specifically for FfA Otherwise we pass it to one
        # of the normal views, which enforces CSRF. Also, nix the
        # product just in case we're crossing the streams and
        # confusing new-style product urls with old-style backwards
        # compatability for the Android form.
        #
        # FIXME: Remove this hairbrained monstrosity when we don't need to
        # support the method that Firefox for Android currently uses to
        # post feedback which worked with the old input.mozilla.org.
        view = android_about_feedback
        product = None

        # This lets us measure how often this section of code kicks
        # off and thus how often old android stuff is happening. When
        # we're not seeing this anymore, we can nix all the old
        # android stuff.
        statsd.incr('feedback.oldandroid')

        return android_about_feedback(request, request.locale)


    # FIXME - validate these better
    version = smart_str(version)
    channel = smart_str(channel).lower()

    if product == 'fxos' or request.BROWSER.browser == 'Firefox OS':
        # Firefox OS gets shunted to a different form which has
        # different Firefox OS specific questions.
        view = firefox_os_stable_feedback
        product = 'fxos'

    elif product:
        product = smart_str(product)

        if product in PRODUCT_OVERRIDE:
            # If the product is really a form name, we use that
            # form specifically.
            view = PRODUCT_OVERRIDE[product]
            product = None

        elif product not in models.Product.get_product_map():
            # If they passed in a product and we don't know about
            # it, stop here.
            return render(request, 'feedback/unknownproduct.html', {
                'product': product
            })

    if view is None:
        view = generic_feedback

    return view(request, request.locale, product, version, channel,
                *args, **kwargs)
Ejemplo n.º 13
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
Ejemplo n.º 14
0
 def test_str(self):
     eq_('a', smart_str('a'))
     eq_(u'a', smart_str(u'a'))