[bug 889913] Track l10n coverage % for all locale/product combos.

* Created new model for these metrics
* Added cronjob to calculate and store values
This commit is contained in:
Ricky Rosario 2013-07-17 16:07:08 -04:00
Родитель 7ac3826382
Коммит 88cdfc6d07
6 изменённых файлов: 202 добавлений и 6 удалений

Просмотреть файл

@ -0,0 +1,11 @@
from django.contrib import admin
from kitsune.dashboards.models import WikiMetric
class WikiMetricAdmin(admin.ModelAdmin):
list_display = ['code', 'date', 'locale', 'product', 'value']
list_filter = ['code', 'product', 'locale']
admin.site.register(WikiMetric, WikiMetricAdmin)

Просмотреть файл

@ -1,9 +1,14 @@
from datetime import date
from django.conf import settings
from django.db import transaction, connection
from django.db import connection
import cronjobs
from kitsune.dashboards.models import PERIODS, WikiDocumentVisits
from kitsune.dashboards.models import (
PERIODS, WikiDocumentVisits, WikiMetric, L10N_TOP20_CODE, L10N_ALL_CODE)
from kitsune.dashboards.readouts import overview_rows
from kitsune.products.models import Product
from kitsune.sumo.redis_utils import redis_client
from kitsune.wiki.models import Document
@ -19,6 +24,50 @@ def reload_wiki_traffic_stats():
WikiDocumentVisits.reload_period_from_analytics(period)
@cronjobs.register
def update_l10n_coverage_metrics():
"""Calculate and store the l10n metrics for each locale/product.
The metrics are:
* Percent localized of top 20 articles
* Percent localized of all articles
"""
today = date.today()
# Loop through all locales.
for locale in settings.SUMO_LANGUAGES:
# Skip en-US, it is always 100% localized.
if locale == settings.WIKI_DEFAULT_LANGUAGE:
continue
# Loop through all enabled products, including None (really All).
for product in [None] + list(Product.objects.filter(visible=True)):
# (Ab)use the overview_rows helper from the readouts.
rows = overview_rows(locale=locale, product=product)
# % of top 20 articles
top20 = rows['most-visited']
percent = 100.0 * float(top20['numerator']) / top20['denominator']
WikiMetric.objects.create(
code=L10N_TOP20_CODE,
locale=locale,
product=product,
date=today,
value=percent)
# % of all articles
all_ = rows['all']
percent = 100.0 * float(all_['numerator']) / all_['denominator']
WikiMetric.objects.create(
code=L10N_ALL_CODE,
locale=locale,
product=product,
date=today,
value=percent)
def _get_old_unhelpful():
"""
Gets the data from 2 weeks ago and formats it as output so that we can

Просмотреть файл

@ -4,9 +4,12 @@ from datetime import date, timedelta
from django.conf import settings
from django.db import models
from tower import ugettext_lazy as _lazy
from kitsune.dashboards import (LAST_7_DAYS, LAST_30_DAYS, LAST_90_DAYS,
ALL_TIME, PERIODS)
from kitsune.sumo.models import ModelBase
from kitsune.products.models import Product
from kitsune.sumo.models import ModelBase, LocaleField
from kitsune.sumo import googleanalytics
from kitsune.wiki.models import Document
@ -56,3 +59,31 @@ class WikiDocumentVisits(ModelBase):
# Don't erase interesting data if there's nothing to replace it:
log.warning('Google Analytics returned no interesting data,'
' so I kept what I had.')
L10N_TOP20_CODE = 'percent_localized_top20'
L10N_ALL_CODE = 'percent_localized_all'
METRIC_CODE_CHOICES = (
(L10N_TOP20_CODE, _lazy(u'Percent Localized: Top 20')),
(L10N_ALL_CODE, _lazy(u'Percent Localized: All')),
)
class WikiMetric(ModelBase):
"""A single numeric measurement for a locale, product and date.
For example, the percentage of all FxOS articles localized to Spanish."""
code = models.CharField(
db_index=True, max_length=255, choices=METRIC_CODE_CHOICES)
locale = LocaleField(db_index=True, null=True, blank=True)
product = models.ForeignKey(Product, null=True, blank=True)
date = models.DateField()
value = models.FloatField()
class Meta(object):
unique_together = ('code', 'product', 'locale', 'date')
def __unicode__(self):
return '[{date}][{locale}][{product}] {code}: {value}'.format(
date=self.date, code=self.code, locale=self.locale,
value=self.value, product=self.product)

Просмотреть файл

@ -8,11 +8,14 @@ from nose.tools import eq_
from kitsune.dashboards.cron import (
cache_most_unhelpful_kb_articles, _get_old_unhelpful,
_get_current_unhelpful)
_get_current_unhelpful, update_l10n_coverage_metrics)
from kitsune.dashboards.models import (
WikiMetric, L10N_TOP20_CODE, L10N_ALL_CODE)
from kitsune.products.tests import product
from kitsune.sumo.redis_utils import redis_client, RedisError
from kitsune.sumo.tests import TestCase
from kitsune.wiki.models import HelpfulVote
from kitsune.wiki.tests import revision
from kitsune.wiki.models import HelpfulVote, Revision
from kitsune.wiki.tests import revision, document
def _add_vote_in_past(rev, vote, days_back):
@ -235,3 +238,81 @@ class TopUnhelpfulArticlesCronTests(TestCase):
assert '%d::%.1f:' % (r2.id, 242.0) in result[0]
assert '%d::%.1f:' % (r3.id, 122.0) in result[1]
assert '%d::%.1f:' % (r.id, 102.0) in result[2]
class L10nCoverageMetricsTests(TestCase):
def test_update_l10n_coverage_metrics(self):
"""Test the cron job that updates l10n coverage metrics."""
p = product(save=True)
# Create en-US documents.
for i in range(20):
r = revision(
is_approved=True, is_ready_for_localization=True, save=True)
r.document.products.add(p)
r1 = Revision.objects.all()[0]
r2 = Revision.objects.all()[1]
# Translate one to es.
d = document(parent=r1.document, locale='es', save=True)
revision(document=d, based_on=r1, is_approved=True, save=True)
# Translate two to de.
d = document(parent=r1.document, locale='de', save=True)
revision(document=d, based_on=r1, is_approved=True, save=True)
d = document(parent=r2.document, locale='de', save=True)
revision(document=d, based_on=r2, is_approved=True, save=True)
# Translate all to ak.
for r in Revision.objects.filter(document__locale='en-US'):
d = document(parent=r.document, locale='ak', save=True)
revision(document=d, based_on=r, is_approved=True, save=True)
# Call the cronjob
update_l10n_coverage_metrics()
# Verify es metrics.
eq_(4, WikiMetric.objects.filter(locale='es').count())
eq_(5.0, WikiMetric.objects.get(
locale='es', product=p, code=L10N_TOP20_CODE).value)
eq_(5.0, WikiMetric.objects.get(
locale='es', product=p, code=L10N_ALL_CODE).value)
eq_(5.0, WikiMetric.objects.get(
locale='es', product=None, code=L10N_TOP20_CODE).value)
eq_(5.0, WikiMetric.objects.get(
locale='es', product=None, code=L10N_ALL_CODE).value)
# Verify de metrics.
eq_(4, WikiMetric.objects.filter(locale='de').count())
eq_(10.0, WikiMetric.objects.get(
locale='de', product=p, code=L10N_TOP20_CODE).value)
eq_(10.0, WikiMetric.objects.get(
locale='de', product=p, code=L10N_ALL_CODE).value)
eq_(10.0, WikiMetric.objects.get(
locale='de', product=None, code=L10N_TOP20_CODE).value)
eq_(10.0, WikiMetric.objects.get(
locale='de', product=None, code=L10N_ALL_CODE).value)
# Verify ak metrics.
eq_(4, WikiMetric.objects.filter(locale='de').count())
eq_(100.0, WikiMetric.objects.get(
locale='ak', product=p, code=L10N_TOP20_CODE).value)
eq_(100.0, WikiMetric.objects.get(
locale='ak', product=p, code=L10N_ALL_CODE).value)
eq_(100.0, WikiMetric.objects.get(
locale='ak', product=None, code=L10N_TOP20_CODE).value)
eq_(100.0, WikiMetric.objects.get(
locale='ak', product=None, code=L10N_ALL_CODE).value)
# Verify it metrics.
eq_(4, WikiMetric.objects.filter(locale='it').count())
eq_(0.0, WikiMetric.objects.get(
locale='it', product=p, code=L10N_TOP20_CODE).value)
eq_(0.0, WikiMetric.objects.get(
locale='it', product=p, code=L10N_ALL_CODE).value)
eq_(0.0, WikiMetric.objects.get(
locale='it', product=None, code=L10N_TOP20_CODE).value)
eq_(0.0, WikiMetric.objects.get(
locale='it', product=None, code=L10N_ALL_CODE).value)

Просмотреть файл

@ -0,0 +1,23 @@
CREATE TABLE `dashboards_wikimetric` (
`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
`code` varchar(255) NOT NULL,
`locale` varchar(7),
`product_id` integer,
`date` date NOT NULL,
`value` double precision NOT NULL,
UNIQUE (`code`, `product_id`, `locale`, `date`)
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
ALTER TABLE `dashboards_wikimetric` ADD CONSTRAINT `product_id_refs_id_7aaf7d8c` FOREIGN KEY (`product_id`) REFERENCES `products_product` (`id`);
CREATE INDEX `dashboards_wikimetric_65da3d2c` ON `dashboards_wikimetric` (`code`);
CREATE INDEX `dashboards_wikimetric_928541cb` ON `dashboards_wikimetric` (`locale`);
CREATE INDEX `dashboards_wikimetric_bb420c12` ON `dashboards_wikimetric` (`product_id`);
INSERT INTO `django_content_type` (`name`, `app_label`, `model`) VALUES ('wikimetric','dashboards','wikimetric');
SELECT (@id:=`id`) FROM `django_content_type` WHERE `name` = 'wikimetric';
INSERT INTO `auth_permission` (`name`, `content_type_id`, `codename`)
VALUES ('Can add wikimetric',@id,'add_wikimetric'),
('Can change wikimetric',@id,'change_wikimetric'),
('Can delete wikimetric',@id,'delete_wikimetric');

Просмотреть файл

@ -32,6 +32,7 @@ HOME = /tmp
0 2 * * * {{ cron }} update_search_ctr_metric
0 4 * * * {{ cron }} auto_lock_old_questions
0 5 * * * {{ cron }} reindex_kb
0 1 * * * {{ cron }} update_l10n_coverage_metrics
# Twice per week.
#05 01 * * 1,4 {{ cron }} update_weekly_votes