This repository has been archived by the owner on Aug 26, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 681
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #842 from groovecoder/metric-model-834313
fix bug 834313 - add kpi app with metric model
- Loading branch information
Showing
11 changed files
with
331 additions
and
0 deletions.
There are no files selected for viewing
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
from django.contrib import admin | ||
|
||
from kpi.models import Metric, MetricKind | ||
|
||
|
||
class MetricAdmin(admin.ModelAdmin): | ||
list_display = ['kind', 'start', 'end', 'value'] | ||
list_filter = ['kind'] | ||
|
||
|
||
class MetricKindAdmin(admin.ModelAdmin): | ||
pass | ||
|
||
|
||
admin.site.register(Metric, MetricAdmin) | ||
admin.site.register(MetricKind, MetricKindAdmin) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
from datetime import date, timedelta | ||
|
||
import cronjobs | ||
|
||
from kpi.models import Metric, MetricKind, L10N_METRIC_CODE | ||
from wiki.models import Document | ||
|
||
|
||
@cronjobs.register | ||
def update_l10n_metric(): | ||
"""Calculate new l10n coverage numbers and save. | ||
We don't enforce l10n completeness, so coverage is the % of translated docs | ||
that are modified AFTER the most recent edit to their en-US source. | ||
""" | ||
up_to_date_translations = 0 | ||
translations = Document.objects.exclude(locale='en-US') | ||
for translation in translations: | ||
if translation.modified > translation.parent.modified: | ||
up_to_date_translations += 1 | ||
|
||
coverage = up_to_date_translations / float(translations.count()) | ||
|
||
# Save the value to Metric table. | ||
metric_kind = MetricKind.objects.get(code=L10N_METRIC_CODE) | ||
start = date.today() | ||
end = start + timedelta(days=1) | ||
metric, created = Metric.objects.get_or_create(kind=metric_kind, | ||
start=start, | ||
end=end) | ||
metric.value = int(coverage*100) # store as a % int | ||
metric.save() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
# encoding: utf-8 | ||
import datetime | ||
from south.db import db | ||
from south.v2 import SchemaMigration | ||
from django.db import models | ||
|
||
class Migration(SchemaMigration): | ||
|
||
def forwards(self, orm): | ||
|
||
# Adding model 'MetricKind' | ||
db.create_table('kpi_metrickind', ( | ||
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), | ||
('code', self.gf('django.db.models.fields.CharField')(unique=True, max_length=255)), | ||
)) | ||
db.send_create_signal('kpi', ['MetricKind']) | ||
|
||
# Adding model 'Metric' | ||
db.create_table('kpi_metric', ( | ||
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), | ||
('kind', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['kpi.MetricKind'])), | ||
('start', self.gf('django.db.models.fields.DateField')()), | ||
('end', self.gf('django.db.models.fields.DateField')()), | ||
('value', self.gf('django.db.models.fields.PositiveIntegerField')()), | ||
)) | ||
db.send_create_signal('kpi', ['Metric']) | ||
|
||
# Adding unique constraint on 'Metric', fields ['kind', 'start', 'end'] | ||
db.create_unique('kpi_metric', ['kind_id', 'start', 'end']) | ||
|
||
|
||
def backwards(self, orm): | ||
|
||
# Removing unique constraint on 'Metric', fields ['kind', 'start', 'end'] | ||
db.delete_unique('kpi_metric', ['kind_id', 'start', 'end']) | ||
|
||
# Deleting model 'MetricKind' | ||
db.delete_table('kpi_metrickind') | ||
|
||
# Deleting model 'Metric' | ||
db.delete_table('kpi_metric') | ||
|
||
|
||
models = { | ||
'kpi.metric': { | ||
'Meta': {'unique_together': "[('kind', 'start', 'end')]", 'object_name': 'Metric'}, | ||
'end': ('django.db.models.fields.DateField', [], {}), | ||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
'kind': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['kpi.MetricKind']"}), | ||
'start': ('django.db.models.fields.DateField', [], {}), | ||
'value': ('django.db.models.fields.PositiveIntegerField', [], {}) | ||
}, | ||
'kpi.metrickind': { | ||
'Meta': {'object_name': 'MetricKind'}, | ||
'code': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), | ||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) | ||
} | ||
} | ||
|
||
complete_apps = ['kpi'] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
# encoding: utf-8 | ||
import datetime | ||
from south.db import db | ||
from south.v2 import DataMigration | ||
from django.db import models | ||
|
||
from kpi.models import L10N_METRIC_CODE | ||
|
||
class Migration(DataMigration): | ||
|
||
def forwards(self, orm): | ||
"Write your forwards methods here." | ||
l10n_metric = orm.MetricKind(code=L10N_METRIC_CODE) | ||
l10n_metric.save() | ||
|
||
|
||
def backwards(self, orm): | ||
"Write your backwards methods here." | ||
l10n_metric = orm.MetricKind.get(code=L10N_METRIC_CODE) | ||
l10n_metric.delete() | ||
|
||
|
||
|
||
models = { | ||
'kpi.metric': { | ||
'Meta': {'unique_together': "[('kind', 'start', 'end')]", 'object_name': 'Metric'}, | ||
'end': ('django.db.models.fields.DateField', [], {}), | ||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
'kind': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['kpi.MetricKind']"}), | ||
'start': ('django.db.models.fields.DateField', [], {}), | ||
'value': ('django.db.models.fields.PositiveIntegerField', [], {}) | ||
}, | ||
'kpi.metrickind': { | ||
'Meta': {'object_name': 'MetricKind'}, | ||
'code': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), | ||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) | ||
} | ||
} | ||
|
||
complete_apps = ['kpi'] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
# encoding: utf-8 | ||
import datetime | ||
from south.db import db | ||
from south.v2 import SchemaMigration | ||
from django.db import models | ||
|
||
class Migration(SchemaMigration): | ||
|
||
def forwards(self, orm): | ||
|
||
# Changing field 'Metric.value' | ||
db.alter_column('kpi_metric', 'value', self.gf('django.db.models.fields.PositiveIntegerField')(null=True)) | ||
|
||
|
||
def backwards(self, orm): | ||
|
||
# User chose to not deal with backwards NULL issues for 'Metric.value' | ||
raise RuntimeError("Cannot reverse this migration. 'Metric.value' and its values cannot be restored.") | ||
|
||
|
||
models = { | ||
'kpi.metric': { | ||
'Meta': {'unique_together': "[('kind', 'start', 'end')]", 'object_name': 'Metric'}, | ||
'end': ('django.db.models.fields.DateField', [], {}), | ||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
'kind': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['kpi.MetricKind']"}), | ||
'start': ('django.db.models.fields.DateField', [], {}), | ||
'value': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}) | ||
}, | ||
'kpi.metrickind': { | ||
'Meta': {'object_name': 'MetricKind'}, | ||
'code': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), | ||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) | ||
} | ||
} | ||
|
||
complete_apps = ['kpi'] |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
from django.db.models import (CharField, DateField, ForeignKey, | ||
PositiveIntegerField) | ||
|
||
from sumo.models import ModelBase | ||
|
||
|
||
L10N_METRIC_CODE = 'general wiki:l10n:coverage' | ||
KB_L10N_CONTRIBUTORS_METRIC_CODE = 'general wiki:l10n:contributors' | ||
|
||
|
||
class MetricKind(ModelBase): | ||
"""A programmer-readable identifier of a metric, like 'clicks: search'""" | ||
code = CharField(max_length=255, unique=True) | ||
|
||
def __unicode__(self): | ||
return self.code | ||
|
||
|
||
class Metric(ModelBase): | ||
"""A single numeric measurement aggregated over a span of time. | ||
For example, the number of hits to a page during a specific week. | ||
""" | ||
# If we need to (and I would prefer to avoid this, because it wrecks the | ||
# consistent semantics of rows--some will be aggregations and others will | ||
# not), we can lift the unique constraint on kind/start/end for things that | ||
# are collected in realtime and can't be immediately bucketed. However, in | ||
# such cases it would probably be nicer to our future selves to put them in | ||
# a separate store (table or whatever) until bucketing. | ||
|
||
# In the design of this table, we trade off constraints for generality. | ||
# There's no way to have the DB prove, for example, that both halves of a | ||
# clickthrough rate ratio will always exist, but the app can make sure it's | ||
# true upon inserting them. | ||
|
||
kind = ForeignKey(MetricKind) | ||
start = DateField() | ||
|
||
# Not useful yet. Present metrics have spans of known length. | ||
end = DateField() | ||
|
||
# Ints should be good enough for all the currently wish-listed metrics. | ||
# Percents can be (even better) represented by 2 separate metrics: one for | ||
# numerator, one for denominator. | ||
value = PositiveIntegerField(blank=True, null=True) | ||
|
||
class Meta(object): | ||
unique_together = [('kind', 'start', 'end')] | ||
|
||
def __unicode__(self): | ||
return '%s (%s thru %s): %s' % ( | ||
self.kind, self.start, self.end, self.value) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
from datetime import date | ||
|
||
from kpi.models import MetricKind, Metric | ||
|
||
|
||
def metric_kind(**kwargs): | ||
mk = MetricKind(code=kwargs.get('code', 'something')) | ||
mk.save() | ||
return mk | ||
|
||
def metric(**kwargs): | ||
defaults = {'start': date(1980, 02, 16), | ||
'end': date(1980, 02, 23), | ||
'value': 33} | ||
if 'kind' not in kwargs: | ||
defaults['kind'] = metric_kind(save=True) | ||
defaults.update(kwargs) | ||
m = Metric(**defaults) | ||
return m |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
from datetime import date | ||
import time | ||
|
||
from nose.tools import eq_ | ||
|
||
from kpi.cron import update_l10n_metric | ||
from kpi.models import Metric, L10N_METRIC_CODE | ||
from kpi.tests import metric_kind | ||
from sumo.tests import TestCase | ||
from wiki.tests import document, revision | ||
|
||
|
||
class CronJobTests(TestCase): | ||
|
||
fixtures = ['test_users.json'] | ||
|
||
def test_update_l10n_metric_cron(self): | ||
"""Verify the cron job creates the correct metric.""" | ||
l10n_kind = metric_kind(code=L10N_METRIC_CODE, save=True) | ||
|
||
# Create the en-US document with an approved revision. | ||
doc = document(save=True) | ||
rev = revision( | ||
document=doc, | ||
is_approved=True, | ||
save=True) | ||
|
||
time.sleep(1) | ||
|
||
# Create an es translation | ||
es_doc = document(parent=doc, locale='es', save=True) | ||
revision( | ||
document=es_doc, | ||
is_approved=True, | ||
based_on=rev, | ||
save=True) | ||
|
||
# Run it and verify results. | ||
# Value should be 100% | ||
update_l10n_metric() | ||
metrics = Metric.objects.filter(kind=l10n_kind) | ||
eq_(1, len(metrics)) | ||
eq_(100, metrics[0].value) | ||
|
||
# Update the en-US document | ||
rev2 = revision( | ||
document=doc, | ||
is_approved=True, | ||
save=True) | ||
|
||
# Run it and verify results. | ||
# Value should be 0% | ||
update_l10n_metric() | ||
metrics = Metric.objects.filter(kind=l10n_kind) | ||
eq_(1, len(metrics)) | ||
eq_(0, metrics[0].value) | ||
|
||
time.sleep(1) | ||
|
||
# Create a pt-BR translation | ||
ptBR_doc = document(parent=doc, locale='pt-BR', save=True) | ||
revision( | ||
document=ptBR_doc, | ||
is_approved=True, | ||
based_on=rev, | ||
save=True) | ||
|
||
# Run it and verify results. | ||
# Value should be 50% | ||
update_l10n_metric() | ||
metrics = Metric.objects.filter(kind=l10n_kind) | ||
eq_(1, len(metrics)) | ||
eq_(50, metrics[0].value) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters