Permalink
Browse files

Added release themes page. Bug 623360.

  • Loading branch information...
1 parent 2ac09f6 commit 6450b8901c28c62013e04f12c37cda06a8c81d30 @fwenzel fwenzel committed Feb 25, 2011
View
@@ -8,8 +8,9 @@
from textcluster import Corpus, search
from feedback.models import Opinion
-from input import (PRODUCT_USAGE, LATEST_BETAS, OPINION_PRAISE, OPINION_ISSUE,
- OPINION_IDEA, PLATFORM_USAGE)
+from input import (CHANNEL_USAGE, PLATFORM_USAGE, PRODUCT_USAGE,
+ LATEST_BETAS, LATEST_RELEASE, OPINION_PRAISE,
+ OPINION_ISSUE, OPINION_IDEA)
from themes.models import Theme, Item
SIM_THRESHOLD = settings.CLUSTER_SIM_THRESHOLD
@@ -49,39 +50,56 @@ def cluster():
base_qs = Opinion.objects.filter(locale='en-US', created__gte=week_ago)
log.debug('Beginning clustering')
- cluster_by_product(base_qs)
+ cluster_by_product_and_channel(base_qs)
-def cluster_by_product(qs):
- for prod in PRODUCT_USAGE:
- log.debug('Clustering %s(%s)' %
- (unicode(prod.pretty), LATEST_BETAS[prod]))
- qs_product = qs.filter(product=prod.id, version=LATEST_BETAS[prod])
- cluster_by_feeling(qs_product, prod)
+def cluster_by_product_and_channel(qs):
+ def get_version(channel, prod):
+ return LATEST_BETAS[prod] if channel == 'beta' else LATEST_RELEASE[prod]
+ for channel in (c.short for c in CHANNEL_USAGE):
+ for prod in PRODUCT_USAGE:
+ version = get_version(channel, prod)
+ log.debug('Clustering %s (%s: %s)' %
+ (unicode(prod.pretty), channel, version))
+ qs_product = qs.filter(product=prod.id, version=version)
+ cluster_beta_by_feeling(qs_product, channel, prod)
-def cluster_by_feeling(qs, prod):
- happy = qs.filter(type=OPINION_PRAISE.id)
- sad = qs.filter(type=OPINION_ISSUE.id)
- ideas = qs.filter(type=OPINION_IDEA.id)
- cluster_by_platform(happy, prod, 'happy')
- cluster_by_platform(sad, prod, 'sad')
- cluster_by_platform(ideas, prod, 'ideas')
+def cluster_beta_by_feeling(qs, channel, prod):
+ """Cluster all products by feeling."""
+ # Sentiments to be considered depend on channel.
+ cluster_by = {
+ 'beta': (OPINION_PRAISE, OPINION_ISSUE, OPINION_IDEA),
+ 'release': (OPINION_IDEA,),
+ }
+ for op_type in cluster_by[channel]:
+ type_qs = qs.filter(type=op_type.id)
-def cluster_by_platform(qs, prod, feeling):
- # We need to create corpii for each platform and manually inspect each
+ cluster_by_platform(type_qs, channel, prod, op_type.short)
+
+
+def cluster_by_platform(qs, channel, prod, feeling):
+ """
+ Cluster all products/feelings by platform ('all' as well as separate
+ platforms).
+ """
+ dimensions = dict(product=prod.id, channel=channel, feeling=feeling)
+ cluster_and_save(qs, dimensions)
+
+ # Beta only: Create corpora for each platform and inspect each
# opinion and put it in the right platform bucket.
+ if channel == 'beta':
+ for platform in PLATFORM_USAGE:
+ dimensions['platform'] = platform.short
+ cluster_and_save(qs.filter(platform=platform.short),
+ dimensions)
+
+def cluster_and_save(qs, dimensions):
result = cluster_queryset(qs)
- dimensions = dict(product=prod.id, feeling=feeling)
save_result(result, dimensions)
- for platform in PLATFORM_USAGE:
- result = cluster_queryset(qs.filter(platform=platform.short))
- dimensions['platform'] = platform.short
- save_result(result, dimensions)
-
def cluster_queryset(qs):
seen = {}
@@ -9,8 +9,10 @@ class Theme(ModelBase):
pivot = models.ForeignKey(Opinion, related_name='group')
opinions = models.ManyToManyField(Opinion, through='Item')
num_opinions = models.IntegerField(default=0, db_index=True)
- feeling = models.CharField(max_length=20, db_index=True) # happy or sad
+ feeling = models.CharField(max_length=20, db_index=True) # issue, praise,
+ # idea
product = models.PositiveSmallIntegerField()
+ channel = models.CharField(max_length=20) # beta, release
platform = models.CharField(max_length=255, db_index=True)
created = models.DateTimeField(auto_now_add=True, db_index=True)
@@ -12,27 +12,29 @@
{{ filter_list(products) }}
</div>
</div>
- <div class="choice" id="filter_type">
- <h3>{{ _('Type of Feedback') }}</h3>
- <div>
- {{ filter_list(sentiments) }}
+ {% if CHANNEL == 'beta' %}
+ <div class="choice" id="filter_type">
+ <h3>{{ _('Type of Feedback') }}</h3>
+ <div>
+ {{ filter_list(sentiments) }}
+ </div>
</div>
- </div>
- {# No need to show platforms if there is only one non-All platform #}
- {% if platforms|length > 2 %}
- <div class="choice" id="filter_platform">
- <h3>{{ _('Platform') }}</h3>
- <div>
- {{ filter_list(platforms) }}
+ {# No need to show platforms if there is only one non-All platform #}
+ {% if platforms|length > 2 %}
+ <div class="choice" id="filter_platform">
+ <h3>{{ _('Platform') }}</h3>
+ <div>
+ {{ filter_list(platforms) }}
+ </div>
</div>
- </div>
+ {% endif %}
{% endif %}
</form>
</div>
<div class="col middle wide">
<div id="messages" class="block">
- <h2>{{ _('Common Themes') }}</h2>
+ <h2>{{ _('Common Themes') if CHANNEL == 'beta' else _('Frequent Ideas') }}</h2>
{{ theme_list(themes) }}
{{ pager() }}
View
@@ -7,8 +7,11 @@
import jingo
from tower import ugettext as _
-from input import PRODUCTS, PLATFORMS, FIREFOX, PRODUCT_USAGE
-from input.decorators import cache_page
+from input import (CHANNEL_BETA, CHANNEL_RELEASE,
+ OPINION_PRAISE, OPINION_ISSUE, OPINION_IDEA,
+ PRODUCTS, PLATFORMS, FIREFOX, PRODUCT_USAGE,
+ get_channel)
+from input.decorators import cache_page, negotiate
from input.helpers import urlparams
from input.urlresolvers import reverse
from themes.models import Theme
@@ -18,33 +21,31 @@
def _get_sentiments(request, sentiment):
+ """Get available sentiment filters (beta channel only)."""
sentiments = []
url = request.get_full_path()
f = Filter(urlparams(url, s=None), _('All'), _('All feedback'),
not sentiment)
-
sentiments.append(f)
- f = Filter(urlparams(url, s='happy'), _('Praise'), _('Praise only'),
- (sentiment == 'happy'))
-
+ f = Filter(urlparams(url, s=OPINION_PRAISE.short), _('Praise'),
+ _('Praise only'), (sentiment == OPINION_PRAISE.short))
sentiments.append(f)
- f = Filter(urlparams(url, s='sad'), _('Issues'), _('Issues only'),
- (sentiment == 'sad'))
-
+ f = Filter(urlparams(url, s=OPINION_ISSUE.short), _('Issues'),
+ _('Issues only'), (sentiment == OPINION_ISSUE.short))
sentiments.append(f)
- f = Filter(urlparams(url, s='ideas'), _('Ideas'),
- _('Ideas only'),
- (sentiment == 'ideas'))
-
+ f = Filter(urlparams(url, s=OPINION_IDEA.short), _('Ideas'),
+ _('Ideas only'), (sentiment == OPINION_IDEA.short))
sentiments.append(f)
+
return sentiments
def _get_platforms(request, product, platform):
+ """Get platforms (beta channel only)."""
platforms = []
url = request.get_full_path()
@@ -66,6 +67,7 @@ def _get_platforms(request, product, platform):
def _get_products(request, product):
+ """Get product filters (all channels)."""
products = []
url = request.get_full_path()
@@ -78,10 +80,10 @@ def _get_products(request, product):
@cache_page(use_get=True)
-def index(request):
- """List the various clusters of data we have."""
+def beta_index(request):
+ """List the themes clusters for beta releases."""
- qs = Theme.objects.all()
+ qs = Theme.objects.filter(channel=CHANNEL_BETA.short)
product = request.GET.get('a', FIREFOX.short)
products = _get_products(request, product)
try:
@@ -117,9 +119,41 @@ def index(request):
@cache_page(use_get=True)
+def release_index(request):
+ """List the themes clusters for major releases."""
+
+ qs = Theme.objects.filter(channel=CHANNEL_RELEASE.short,
+ feeling=OPINION_IDEA.short)
+ product = request.GET.get('a', FIREFOX.short)
+ products = _get_products(request, product)
+ try:
+ qs = qs.filter(product=PRODUCTS[product].id)
+ except KeyError:
+ raise http.Http404
+
+ args = dict(products=products)
+ page = request.GET.get('page', 1)
+
+ if qs:
+ pp = settings.SEARCH_PERPAGE
+ pager = Paginator(qs.select_related(), pp)
+
+ try:
+ args['page'] = pager.page(page)
+ except (EmptyPage, InvalidPage):
+ args['page'] = pager.page(pager.num_pages)
+
+ args['themes'] = args['page'].object_list
+
+ return jingo.render(request, 'themes/index.html', args)
+
+index = negotiate(beta=beta_index, release=release_index)
+
+
+@cache_page(use_get=True)
def theme(request, theme_id):
try:
- theme = Theme.objects.get(id=theme_id)
+ theme = Theme.objects.get(id=theme_id, channel=get_channel())
except Theme.DoesNotExist:
raise http.Http404
@@ -0,0 +1,2 @@
+ALTER TABLE `theme` ADD `channel` VARCHAR( 20 ) NOT NULL AFTER `product` ;
+ALTER TABLE `theme` ADD INDEX ( `channel` ) ;
View
@@ -51,10 +51,8 @@
<ul>
<li><a class="dashboard" href="{{ url('dashboard', channel='release') }}"><span>{{ _('Experience') }}</span></a></li>
+ <li><a class="themes" href="{{ url('themes', channel='release') }}"> <span>{{ _('Ideas') }}</span></a></li>
<li><a class="issues" href="{{ url('website_issues', channel='release') }}"><span>{{ _('Broken Sites') }}</span></a></li>
- {# <li><a class="themes" href="{{ url('themes', channel='release') }}"> <span>{{ _('Ideas') }}</span> #}
- </a>
- </li>
</ul>
{% include "includes/channel_switcher.html" %}

0 comments on commit 6450b89

Please sign in to comment.