diff --git a/fjord/analytics/analyzer_views.py b/fjord/analytics/analyzer_views.py index 640a9d58..28b6d519 100644 --- a/fjord/analytics/analyzer_views.py +++ b/fjord/analytics/analyzer_views.py @@ -19,13 +19,15 @@ from elasticsearch_dsl import F +from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage +from django.core.urlresolvers import reverse_lazy from django.http import HttpResponse -from django.shortcuts import get_object_or_404, render +from django.shortcuts import render from django.utils.encoding import force_bytes -from django.views.generic.edit import FormView +from django.views.generic.edit import CreateView, UpdateView from django.utils.decorators import method_decorator -from fjord.analytics.forms import ProductsUpdateForm +from fjord.analytics.forms import ProductsCreateForm from fjord.analytics.utils import counts_to_options, zero_fill from fjord.base.templatetags.jinja_helpers import locale_name from fjord.base.utils import ( @@ -384,8 +386,7 @@ def empty_to_unknown(text): (original_search.aggs .bucket('histogram', 'date_histogram', field='created', interval='day') - .bucket('per_sentiment', 'terms', field='happy') - ) + .bucket('per_sentiment', 'terms', field='happy')) results = original_search.execute() buckets = results.aggregations['histogram']['buckets'] @@ -424,48 +425,55 @@ def empty_to_unknown(text): }) -class ProductsUpdateView(FormView): - """An administrator view for showing, adding, and updating the products.""" +class ProductCreateView(CreateView): + model = Product template_name = 'analytics/analyzer/products.html' - form_class = ProductsUpdateForm - success_url = 'products' + success_url = reverse_lazy('analytics_products') + form_class = ProductsCreateForm @method_decorator(check_new_user) @method_decorator(analyzer_required) def dispatch(self, *args, **kwargs): - return super(ProductsUpdateView, self).dispatch(*args, **kwargs) - - def get(self, request, *args, **kwargs): - if 'pk' in request.GET: - self.object = get_object_or_404(Product, pk=request.GET['pk']) - return super(ProductsUpdateView, self).get(request, *args, **kwargs) + return super(ProductCreateView, self).dispatch(*args, **kwargs) def get_context_data(self, **kwargs): - context = super(ProductsUpdateView, self).get_context_data(**kwargs) - context['products'] = Product.objects.all() + context = super(ProductCreateView, self).get_context_data(**kwargs) + page = self.request.GET.get('page') + paginator = Paginator(Product.objects.order_by('id'), 25) + try: + products = paginator.page(page) + except PageNotAnInteger: + products = paginator.page(1) + except EmptyPage: + products = paginator.page(paginator.num_pages) + + context['products'] = products + context['update'] = False return context - def get_form_kwargs(self): - kwargs = super(ProductsUpdateView, self).get_form_kwargs() - if hasattr(self, 'object'): - kwargs['instance'] = self.object - return kwargs - def form_valid(self, form): +class ProductUpdateView(UpdateView): + model = Product + template_name = 'analytics/analyzer/products.html' + success_url = reverse_lazy('analytics_products') + form_class = ProductsCreateForm + + @method_decorator(check_new_user) + @method_decorator(analyzer_required) + def dispatch(self, *args, **kwargs): + return super(ProductUpdateView, self).dispatch(*args, **kwargs) + + def get_context_data(self, **kwargs): + context = super(ProductUpdateView, self).get_context_data(**kwargs) + page = self.request.GET.get('page') + paginator = Paginator(Product.objects.order_by('id'), 25) try: - instance = Product.objects.get(db_name=form.data.get('db_name')) - instance.slug = form.data.get('slug') or instance.slug - instance.display_name = (form.data.get('display_name') or - instance.display_name) - instance.notes = form.data.get('notes') or instance.notes - instance.enabled = form.data.get('enabled') or False - instance.on_dashboard = form.data.get('on_dashboard') or False - instance.on_picker = form.data.get('on_picker') or False - instance.browser = form.data.get('browser') or u'' - instance.browser_data_browser = ( - form.data.get('browser_data_browser') or u'' - ) - self.object = instance.save() - except Product.DoesNotExist: - self.object = form.save() - return super(ProductsUpdateView, self).form_valid(form) + products = paginator.page(page) + except PageNotAnInteger: + products = paginator.page(1) + except EmptyPage: + products = paginator.page(paginator.num_pages) + + context['products'] = products + context['update'] = True + return context diff --git a/fjord/analytics/forms.py b/fjord/analytics/forms.py index ba65012c..addd4aee 100644 --- a/fjord/analytics/forms.py +++ b/fjord/analytics/forms.py @@ -3,7 +3,7 @@ from fjord.feedback.models import Product -class ProductsUpdateForm(forms.ModelForm): +class ProductsCreateForm(forms.ModelForm): class Meta: model = Product fields = [ diff --git a/fjord/analytics/jinja2/analytics/analyzer/products.html b/fjord/analytics/jinja2/analytics/analyzer/products.html index 09cd67fe..38a2a5b8 100644 --- a/fjord/analytics/jinja2/analytics/analyzer/products.html +++ b/fjord/analytics/jinja2/analytics/analyzer/products.html @@ -1,52 +1,99 @@ {% extends "analytics/analyzer/dashboard.html" %} +{# Note: This is not l10n-ized since it's only available to analyzers for now. #} + {% block content_middle %}

Analytics: Products

- - - - - - - - - - - - - - - - {% for prod in products %} + {% if not update %} +

Total products: {{ products.paginator.count }}

+ +
+ {% if next_page %} + + Older + + {% else %} + Older + {% endif %} + | Page {{ products.number }} of {{ products.paginator.num_pages }} | + {% if prev_page %} + + Newer + + {% else %} + Newer + {% endif %} +
+ +
idenabled?on dashboard?on picker?display namedb nameslugautomatic translations?feedback product urlbrowserbrowser data browsernotes
- - - - - - - - - {# FIXME - URL is hard-coded so it doesn't include the locale. Need better way to do this. #} - - - - + + + + + + + + + + + + + - {% endfor %} -
- Edit - {{ prod.id }} - {{ prod.enabled }}{{ prod.on_dashboard }}{{ prod.on_picker }}{{ prod.display_name }}{{ prod.db_name }}{{ prod.slug }}{{ prod.translation_system }}https://input.mozilla.org/feedback/{{ prod.slug }}{{ prod.browser }}{{ prod.browser_data_browser }}{{ prod.notes }}idenabled?on dashboard?on picker?display namedb nameslugautomatic translations?feedback product urlbrowserbrowser data browsernotes
-

- Click on "Edit" above to edit an existing product or fill out - the fields below to create a new product. -

-

- Note: If you go to update an existing product and you change the - db_name, it will create a new product. You can't change the - db_name for an existing product since it'll orphan responses. -

+ {% for prod in products %} + + + {{ prod.id }} + + {{ prod.enabled }} + {{ prod.on_dashboard }} + {{ prod.on_picker }} + {{ prod.display_name }} + {{ prod.db_name }} + {{ prod.slug }} + {{ prod.translation_system }} + {# FIXME - URL is hard-coded so it doesn't include the locale. Need better way to do this. #} + https://input.mozilla.org/feedback/{{ prod.slug }} + {{ prod.browser }} + {{ prod.browser_data_browser }} + {{ prod.notes }} + + Update + + + {% endfor %} + + +
+ {% if next_page %} + + Older + + {% else %} + Older + {% endif %} + | Page {{ products.number }} of {{ products.paginator.num_pages }} | + {% if prev_page %} + + Newer + + {% else %} + Newer + {% endif %} +
+ {% endif %} + + {% if update %} +

+ Note: If you go to update an existing product and you change the + db_name, it will create a new product. You can't change the + db_name for an existing product since it'll orphan responses. +

+ {% endif %} + +

{% if update %}Update{% else %}Create{% endif %} Product

+

{% if update %}< Back to products{% endif %}

{% csrf_token %} {{ form.as_p() }} diff --git a/fjord/analytics/tests/test_analyzer_views.py b/fjord/analytics/tests/test_analyzer_views.py index 99e8b7a9..c81a62f6 100644 --- a/fjord/analytics/tests/test_analyzer_views.py +++ b/fjord/analytics/tests/test_analyzer_views.py @@ -395,7 +395,8 @@ def test_update_product(self): } resp = self.client.post( - reverse('analytics_products'), data, follow=True + reverse('analytics_products_update', args=(prod.id,)), + data, follow=True ) assert resp.status_code == 200 assert data['display_name'] in resp.content diff --git a/fjord/analytics/urls.py b/fjord/analytics/urls.py index a30d8e69..3c0ac37b 100644 --- a/fjord/analytics/urls.py +++ b/fjord/analytics/urls.py @@ -1,6 +1,6 @@ from django.conf.urls import patterns, url -from fjord.analytics.analyzer_views import ProductsUpdateView +from fjord.analytics.analyzer_views import ProductCreateView, ProductUpdateView urlpatterns = patterns( @@ -25,8 +25,12 @@ # Analytics dashboard url(r'^analytics/?$', 'analytics_dashboard', name='analytics_dashboard'), - url(r'^analytics/products/?$', ProductsUpdateView.as_view(), + url(r'^analytics/products/?$', + ProductCreateView.as_view(), name='analytics_products'), + url(r'^analytics/products/(?P\d+)/update/$', + ProductUpdateView.as_view(), + name='analytics_products_update'), url(r'^analytics/search/?$', 'analytics_search', name='analytics_search'), ) diff --git a/fjord/feedback/migrations/0008_auto_20151014_0820.py b/fjord/feedback/migrations/0008_auto_20151014_0820.py new file mode 100644 index 00000000..bdb8963f --- /dev/null +++ b/fjord/feedback/migrations/0008_auto_20151014_0820.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('feedback', '0007_auto_20150915_0813'), + ] + + operations = [ + migrations.AlterField( + model_name='product', + name='db_name', + field=models.CharField(help_text='Do not change this after you have created the product. You will orphan all the existing feedback!', max_length=50), + ), + ] diff --git a/fjord/feedback/models.py b/fjord/feedback/models.py index 3554c3ee..ce17d642 100644 --- a/fjord/feedback/models.py +++ b/fjord/feedback/models.py @@ -64,7 +64,11 @@ class Product(ModelBase): # We're not using foreign keys, so when we save something to the # database, we use this name - db_name = models.CharField(max_length=50) + db_name = models.CharField( + max_length=50, + help_text=(u'Do not change this after you have created the product. ' + u'You will orphan all the existing feedback!') + ) # This is the slug used in the feedback product urls; we don't use # the SlugField because we don't require slugs be unique