зеркало из https://github.com/mozilla/kitsune.git
[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:
Родитель
7ac3826382
Коммит
88cdfc6d07
|
@ -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
|
||||
|
|
Загрузка…
Ссылка в новой задаче