Skip to content
This repository has been archived by the owner on Nov 3, 2021. It is now read-only.

Commit

Permalink
Added release themes page. Bug 623360.
Browse files Browse the repository at this point in the history
  • Loading branch information
Fred Wenzel committed Feb 28, 2011
1 parent 1bf5e51 commit b1ed59d
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 58 deletions.
66 changes: 42 additions & 24 deletions apps/themes/cron.py
Expand Up @@ -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
Expand Down Expand Up @@ -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 CHANNEL_USAGE:
for prod in PRODUCT_USAGE:
version = get_version(channel.short, prod)
log.debug('Clustering %s (%s: %s)' %
(unicode(prod.pretty), channel.short, version))
qs_product = qs.filter(product=prod.id, version=version)
cluster_by_feeling(qs_product, channel.short, 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_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 opinion_type in cluster_by[channel]:
type_qs = qs.filter(type=opinion_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, opinion_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 = {}
Expand Down
4 changes: 3 additions & 1 deletion apps/themes/models.py
Expand Up @@ -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)

Expand Down
28 changes: 15 additions & 13 deletions apps/themes/templates/themes/index.html
Expand Up @@ -12,27 +12,29 @@ <h3>{{ _('Product') }}</h3>
{{ 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() }}
Expand Down
68 changes: 51 additions & 17 deletions apps/themes/views.py
Expand Up @@ -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
Expand All @@ -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()

Expand All @@ -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()

Expand All @@ -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:
Expand Down Expand Up @@ -116,10 +118,42 @@ def index(request):
return jingo.render(request, 'themes/index.html', args)


@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

Expand Down
1 change: 1 addition & 0 deletions migrations/15-themes-channel.sql
@@ -0,0 +1 @@
ALTER TABLE `theme` ADD `channel` VARCHAR( 20 ) NOT NULL AFTER `product`, ADD INDEX ( `channel` ) ;
4 changes: 1 addition & 3 deletions templates/base.html
Expand Up @@ -51,10 +51,8 @@ <h1><a href="{{ url('dashboard', channel='release') }}">

<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" %}
Expand Down

0 comments on commit b1ed59d

Please sign in to comment.