Skip to content
This repository has been archived by the owner on Aug 26, 2022. It is now read-only.

Commit

Permalink
[603808] Built out infrastructure for the Localization (and Contribut…
Browse files Browse the repository at this point in the history
…or) Dashboards.

* Markup, CSS, templates, and queries for the Overview and Untranslated Articles localization summaries are in place, though there's still some optimization to do.
* Add a `number()` template function for localized thousands formatting, etc.
* Stub out Contributors Dash view just so we can reverse() to it.
* Add trivial tests. Perhaps horrific ones testing the accuracy of the queries will follow. Perhaps.
* To come: WebTrends integration and the rest of the dashboard readouts
  • Loading branch information
erikrose committed Oct 26, 2010
1 parent 7850fad commit 1d71650
Show file tree
Hide file tree
Showing 18 changed files with 474 additions and 18 deletions.
115 changes: 115 additions & 0 deletions apps/dashboards/readouts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
"""Data aggregators for dashboards"""

from django.conf import settings
from django.utils.timesince import timesince

import jingo
from tower import ugettext as _, ugettext_lazy as _lazy

from wiki.models import Document


def localizable_docs():
"""Return a Queryset of the Documents which are in the default locale,
approved, and allow translations."""
return Document.objects.exclude(current_revision=None).filter(
locale=settings.WIKI_DEFAULT_LANGUAGE, is_localizable=True)


def overview_rows(locale):
"""Return the iterable of dicts needed to draw the Overview table."""
# The Overview table is a special case: it has only a static number of
# rows, so it has no expanded, all-rows view, and thus needs no slug, no
# "max" kwarg on rows(), etc. It doesn't fit the Readout signature, so we
# don't shoehorn it in.

total = localizable_docs().count()
if locale == settings.WIKI_DEFAULT_LANGUAGE:
translated = total
else:
# How many approved documents are there in German that have parents?
# TODO: Make sure caching doesn't foil this.
translated = Document.objects.filter(locale=locale).exclude(
current_revision=None).exclude(parent=None).count()
return [dict(title=_('All Knowledge Base Articles'),
numerator=translated, denominator=total,
percent=(int(round(translated / float(total) * 100)) if total
else 100),
description=_('How many of the approved English '
'articles which allow translations '
'have an approved translation into this '
'language')),
# TODO: Enable after we integrate WebTrends stats:
# dict(title=_('Most Viewed Articles'),
# numerator=0, denominator=1,
# percent=0,
# description=_('These are the top 15-20 most visited English'
# ' articles, which in sum account for over 50%'
# ' of the total traffic to the English '
# 'Knowledge Base.'))
]


class Readout(object):
"""Abstract class representing one table on the Localization Dashboard
Describing these as atoms gives us the whole-page details views for free.
"""
#title = _lazy(u'Localized Title of Readout')
#slug = 'URL slug for detail page and CSS class for table'

def rows(self, max=None):
"""Return an iterable of dicts containing the data for the table.
Limit to `max` rows.
"""
raise NotImplementedError

def render(self, rows):
"""Return HTML table rows for the given data.
Default implementation renders a template named after the value of
self.slug in the dashboards/includes/localization directory.
"""
return jingo.render_to_string(
self.request,
'dashboards/includes/localization/%s.html' % self.slug,
{'rows': rows})


class UntranslatedReadout(Readout):
title = _lazy(u'Untranslated Articles')
slug = 'untranslated'

def __init__(self, request):
"""`request.locale` must not be the default locale."""
self.request = request

def rows(self, max=None):
# TODO: Optimize so there isn't another query per doc to get the
# current_revision. Use the method from
# http://www.caktusgroup.com/blog/2009/09/28/custom-joins-with-djangos-
# queryjoin/.
rows = Document.objects.raw('SELECT english.* FROM wiki_document '
'english RIGHT OUTER JOIN wiki_revision ON '
'english.current_revision_id=wiki_revision.id LEFT OUTER JOIN '
'wiki_document translated ON english.id=translated.parent_id AND '
'translated.locale=%s WHERE translated.id IS NULL AND '
'english.current_revision_id IS NOT NULL AND '
'english.is_localizable AND english.locale=%s ORDER BY '
'wiki_revision.reviewed DESC',
params=[self.request.locale, settings.WIKI_DEFAULT_LANGUAGE])
for d in rows[:max] if max else rows:
# TODO: i18nize better. Show only 1 unit of time: for example,
# weeks instead of weeks+days or months instead of months+weeks.
#
# Not ideal but free:
ago = (d.current_revision.reviewed and
_('%s ago') % timesince(d.current_revision.reviewed))
yield (dict(title=d.title,
url=d.get_absolute_url(),
visits=0, percent=0,
updated=ago))
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<tr>
<th>Article</th>
<th>Visits</th>
<th></th>
<th>Updated</th>
</tr>
{% for row in rows %}
<tr>
<td><a href="{{ row.url }}">{{ row.title }}</a></td>
<td>
{{ number(row.visits) }}
</td>
<td>
<div class="absolute-graph" style="width: {{ row.percent }}%"></div>
</td>
<td>
{{ row.updated }}
</td>
</tr>
{% endfor %}
89 changes: 89 additions & 0 deletions apps/dashboards/templates/dashboards/localization.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
{# vim: set ts=2 et sts=2 sw=2: #}
{% extends "wiki/base.html" %}
{% from "wiki/includes/sidebar_modules.html" import for_contributors, quick_links %}
{% set title = _('Localizer Dashboard') %}

{% block content %}
<article id="localize" class="main dashboards">
<div id="breadcrumbs">
{{ _('You are here:') }}
{{ breadcrumbs([(None, _(title))]) }}
</div>

<h1>{{ _(title) }}</h1>

<div class="choice-list">
<label>{{ _('Show information for:') }}</label>
<ul>
<li>
<a href="{{ url('dashboards.contributors') }}">{{ default_locale_name }}</a>
</li>
<li>
{{ current_locale_name }}
</li>
</ul>
</div>

<details class="h2" open="open">
<summary>{{ _('Overview') }}</summary>
<table class="overview">
{% for row in overview_rows() %}
<tr>
<td><a href="#">{{ row.title }}</a></td>
<td>
{{ number(row.numerator) }}
<div class="denominator">of {{ number(row.denominator) }}</div>
</td>
<td>
{{ row.percent }}%
<div class="percent-graph">
<div style="width: {{ row.percent }}%"></div>
</div>
</td>
<td>
{{ row.description }}
</td>
</tr>
{% endfor %}
<tr>
<td>{{ _('User Interface') }}</td>
<td></td>
<td></td>
<td>
{% trans url='https://localize.mozilla.org/' %}
A fully translated user interface improves the experience for
site visitors in your language. Help
<a href="{{ url }}">translate the interface</a>.
{% endtrans %}
</td>
</tr>
</table>
<div id="overview-options" class="choice-list">
<label>{{ _('More options:') }}</label>
<ul>
<li><a href="#untranslated">{{ _('Untranslated') }}</a></li>
<li><a href="#out-of-date">{{ _('Out-of-Date') }}</a></li>
<li><a href="#updates-needed">{{ _('Updates Needed') }}</a></li>
<li><a href="#unreviewed-changes">{{ _('Unreviewed Changes') }}</a></li>
</ul>
</div>
</details>

<details class="h2" open="open">
{% with readout = readouts['untranslated'] %}
<summary><a id="untranslated">{{ readout.title }}</a></summary>
<table class="{{ readout.slug }}">
{{ readout.render(readout.rows(10))|safe }}
</table>
<div class="table-footer">
<a href="{{ url('dashboards.localization_detail', readout.slug) }}">{{ _('All untranslated articles...') }}</a>
</div>
{% endwith %}
</details>
</article>
{% endblock %}

{% block side %}
{{ for_contributors() }}
{{ quick_links() }}
{% endblock %}
24 changes: 24 additions & 0 deletions apps/dashboards/templates/dashboards/localization_detail.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{# vim: set ts=2 et sts=2 sw=2: #}
{% extends "wiki/base.html" %}
{% from "wiki/includes/sidebar_modules.html" import for_contributors, quick_links %}
{% set title = readout.title %}

{% block content %}
<article id="localize" class="main dashboards">
<div id="breadcrumbs">
{{ _('You are here:') }}
{{ breadcrumbs([(url('dashboards.localization'), _('Localization Dashboard')), (None, readout.title)]) }}
</div>

<h1>{{ readout.title }}</h2>

<table class="{{ readout.slug }}">
{{ readout.render(readout.rows())|safe }}
</table>
</article>
{% endblock %}

{% block side %}
{{ for_contributors() }}
{{ quick_links() }}
{% endblock %}
Empty file.
22 changes: 22 additions & 0 deletions apps/dashboards/tests/test_templates.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from nose.tools import eq_

from sumo.tests import TestCase
from sumo.urlresolvers import reverse


class LocalizationDashTests(TestCase):
def test_render(self):
"""Assert the main dash renders and doesn't crash."""
response = self.client.get(reverse('dashboards.localization',
locale='de'),
follow=False)
eq_(200, response.status_code)

def test_untranslated_detail(self):
"""Assert the whole-page Untranslated Articles view works."""
# We shouldn't have to write tests for every whole-page view: just
# enough to cover all the different kinds of table templates.
response = self.client.get(reverse('dashboards.localization_detail',
args=['untranslated'],
locale='de'))
eq_(200, response.status_code)
12 changes: 12 additions & 0 deletions apps/dashboards/tests/test_views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from sumo.tests import TestCase
from sumo.urlresolvers import reverse


class LocalizationDashTests(TestCase):
def test_redirect_to_contributor_dash(self):
"""Should redirect to Contributor Dash if the locale is the default"""
response = self.client.get(reverse('dashboards.localization',
locale='en-US'),
follow=True)
self.assertRedirects(response, reverse('dashboards.contributors',
locale='en-US'))
8 changes: 6 additions & 2 deletions apps/dashboards/urls.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
from django.conf.urls.defaults import patterns, url
from django.views.generic.simple import redirect_to


urlpatterns = patterns('dashboards.views',
url(r'^$', redirect_to, {'url': 'home'}),
url(r'^home/$', 'home', name='home'),
# TODO: mobile home page
# TODO: live chat page
# TODO: contributor dashboard
# TODO: l10n dashboard (may be part of wiki app instead)
url(r'^contributors$', 'contributors', name='dashboards.contributors'),
# TODO: more contributor dashboard
url(r'^localization$', 'localization', name='dashboards.localization'),
url(r'^localization/(?P<readout>[^/]+)', 'localization_detail',
name='dashboards.localization_detail'),
)
44 changes: 44 additions & 0 deletions apps/dashboards/views.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
from functools import partial

from django.conf import settings
from django.http import Http404, HttpResponseRedirect, HttpResponse
from django.views.decorators.http import require_GET

import jingo
from tower import ugettext_lazy as _lazy

from dashboards.readouts import overview_rows, UntranslatedReadout
from sumo_locales import LOCALES
from sumo.parser import get_object_fallback
from sumo.urlresolvers import reverse
from wiki.models import Document
from wiki.views import SHOWFOR_DATA

Expand All @@ -18,3 +27,38 @@ def home(request):

data.update(SHOWFOR_DATA)
return jingo.render(request, 'dashboards/home.html', data)


@require_GET
def localization(request):
"""Render localizer dashboard."""
if request.locale == settings.WIKI_DEFAULT_LANGUAGE:
return HttpResponseRedirect(reverse('dashboards.contributors'))

return jingo.render(request, 'dashboards/localization.html',
{'overview_rows': partial(overview_rows, request.locale),
'readouts':
{'untranslated': UntranslatedReadout(request)},
'default_locale_name': LOCALES[settings.WIKI_DEFAULT_LANGUAGE].native,
'default_locale': settings.WIKI_DEFAULT_LANGUAGE,
'current_locale_name': LOCALES[request.locale].native,
})


# Tables that have their own whole-page views
READOUTS_WITH_DETAILS = dict((t.slug, t) for t in [UntranslatedReadout])


@require_GET
def localization_detail(request, readout):
"""Show all the rows for the given localizer dashboard table."""
if readout not in READOUTS_WITH_DETAILS:
raise Http404

return jingo.render(request, 'dashboards/localization_detail.html',
{'readout': READOUTS_WITH_DETAILS[readout](request)})


def contributors(request):
return HttpResponse('<html><head><title>Hello</title></head>'
'<body>World</body></html>')
Loading

0 comments on commit 1d71650

Please sign in to comment.