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))
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) })
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)
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!'})
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', '') })
def test_str(self): eq_('a', smart_str('a')) eq_(u'a', smart_str(u'a'))
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"))
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 })
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)
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