porting /compatibility (bug 647023, 647029)
This commit is contained in:
Родитель
c48e179500
Коммит
b9d05898b0
|
@ -0,0 +1,62 @@
|
|||
import logging
|
||||
|
||||
from django.conf import settings
|
||||
from django.db.models import Sum, Max
|
||||
|
||||
import cronjobs
|
||||
import redisutils
|
||||
|
||||
import amo
|
||||
import versions.compare as vc
|
||||
from addons.models import Addon
|
||||
from stats.models import UpdateCount
|
||||
|
||||
log = logging.getLogger('z.compat')
|
||||
|
||||
|
||||
@cronjobs.register
|
||||
def compatibility_report():
|
||||
redis = redisutils.connections['master']
|
||||
|
||||
# for app in amo.APP_USAGE:
|
||||
for compat in settings.COMPAT:
|
||||
app = amo.APPS_ALL[compat['app']]
|
||||
version = compat['version']
|
||||
log.info(u'Making compat report for %s %s.' % (app.pretty, version))
|
||||
versions = (('latest', version), ('beta', version + 'b'),
|
||||
('alpha', compat['alpha']))
|
||||
|
||||
rv = dict((k, 0) for k in dict(versions))
|
||||
rv['other'] = 0
|
||||
|
||||
ignore = (amo.STATUS_NULL, amo.STATUS_DISABLED)
|
||||
qs = (Addon.objects.exclude(type=amo.ADDON_PERSONA, status__in=ignore)
|
||||
.filter(appsupport__app=app.id, name__locale='en-us'))
|
||||
|
||||
latest = UpdateCount.objects.aggregate(d=Max('date'))['d']
|
||||
qs = UpdateCount.objects.filter(addon__appsupport__app=app.id,
|
||||
date=latest)
|
||||
total = qs.aggregate(Sum('count'))['count__sum']
|
||||
addons = list(qs.values_list('addon', 'count', 'addon__appsupport__min',
|
||||
'addon__appsupport__max'))
|
||||
|
||||
# Count up the top 95% of addons by ADU.
|
||||
adus = 0
|
||||
for addon, count, minver, maxver in addons:
|
||||
# Don't count add-ons that weren't compatible with the previous
|
||||
# release
|
||||
if maxver < vc.version_int(compat['previous']):
|
||||
continue
|
||||
if adus < .95 * total:
|
||||
adus += count
|
||||
else:
|
||||
break
|
||||
for key, version in versions:
|
||||
if minver <= vc.version_int(version) <= maxver:
|
||||
rv[key] += 1
|
||||
break
|
||||
else:
|
||||
rv['other'] += 1
|
||||
log.info(u'Compat for %s %s: %s' % (app.pretty, version, rv))
|
||||
key = '%s:%s' % (app.id, version)
|
||||
redis.hmset('compat:' + key, rv)
|
|
@ -0,0 +1,43 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}{{ page_title(_('Compatibility Center')) }}{% endblock %}
|
||||
{% block bodyclass %}inverse{% endblock %}
|
||||
|
||||
{% macro percent(x, y) %}{{ (x / y|float * 100)|int }}{% endmacro %}
|
||||
|
||||
{% set app = request.APP.pretty %}
|
||||
{% set num = percent(versions['latest'], total) %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h1>{{ _('{app} {version} Add-on Compatibility Report')|f(app=app, version=version) }}</h1>
|
||||
<p>{% trans %}
|
||||
<b>{{ num }}%</b> of add-ons on addons.mozilla.org are compatible with {{ app }} {{ version }}.
|
||||
{% endtrans %}</p>
|
||||
|
||||
<div id="chart" class="primary"
|
||||
data-keys="{{ dict(keys)|json }}"
|
||||
data-data="{{ versions|json }}"
|
||||
data-total="{{ total }}"></div>
|
||||
<div class="secondary">
|
||||
<table id="compat">
|
||||
<tr>
|
||||
<th>{{ _('Version') }}</th>
|
||||
<th>{{ _('# of Add-ons') }}</th>
|
||||
</tr>
|
||||
{% for key, title in keys %}
|
||||
<tr>
|
||||
<td>{{ title }}</td>
|
||||
<td>{{ versions[key]|numberfmt }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
<p><a href="{{ url('compat.details', version) }}">{{ _('Detailed Report') }}</a></p>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block js %}
|
||||
<script src="{{ media('js/lib/highcharts.src.js') }}"></script>
|
||||
<script src="{{ media('js/zamboni/compat.js') }}"></script>
|
||||
{% endblock %}
|
|
@ -0,0 +1,9 @@
|
|||
from django.conf.urls.defaults import patterns, url
|
||||
|
||||
from . import views
|
||||
|
||||
|
||||
urlpatterns = patterns('',
|
||||
url('^(?P<version>[.\w]+)?$', views.index, name='compat.index'),
|
||||
url('^(?P<version>[.\w]+)/details$', views.details, name='compat.details'),
|
||||
)
|
|
@ -0,0 +1,32 @@
|
|||
from django import http
|
||||
from django.conf import settings
|
||||
|
||||
import jingo
|
||||
import redisutils
|
||||
from tower import ugettext_lazy as _lazy
|
||||
|
||||
KEYS = (
|
||||
('latest', _lazy('Latest')),
|
||||
('beta', _lazy('Beta')),
|
||||
('alpha', _lazy('Alpha')),
|
||||
('other', _lazy('Other')),
|
||||
)
|
||||
|
||||
|
||||
def index(request, version=None):
|
||||
version = version or settings.COMPAT[0]['version']
|
||||
if version not in [v['version'] for v in settings.COMPAT]:
|
||||
raise http.Http404()
|
||||
redis = redisutils.connections['master']
|
||||
compat = redis.hgetall('compat:%s:%s' % (request.APP.id, version))
|
||||
versions = dict((k, int(v)) for k, v in compat.items())
|
||||
print versions
|
||||
total = sum(versions.values())
|
||||
keys = [(k, unicode(v)) for k, v in KEYS]
|
||||
return jingo.render(request, 'compat/index.html',
|
||||
{'versions': versions, 'total': total,
|
||||
'version': version, 'keys': keys})
|
||||
|
||||
|
||||
def details(request, version):
|
||||
pass
|
|
@ -31,6 +31,10 @@ def dict_from_int(version_int):
|
|||
return d
|
||||
|
||||
|
||||
def num(vint):
|
||||
return '{major}.{minor1}.{minor2}.{minor3}'.format(**dict_from_int(vint))
|
||||
|
||||
|
||||
def version_dict(version):
|
||||
"""Turn a version string into a dict with major/minor/... info."""
|
||||
match = version_re.match(version or '')
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
$(function() {
|
||||
var chart = $('#chart'),
|
||||
data = JSON.parse(chart.attr('data-data')),
|
||||
total = chart.attr('data-total'),
|
||||
keys = JSON.parse(chart.attr('data-keys')),
|
||||
series = _.map(data, function(value, key) {
|
||||
return [keys[key], parseInt(value / total * 100)];
|
||||
});
|
||||
console.log(JSON.stringify(series));
|
||||
|
||||
var chart = new Highcharts.Chart({
|
||||
chart: {
|
||||
renderTo: 'chart'
|
||||
},
|
||||
title: '',
|
||||
plotOptions: {
|
||||
pie: { cursor: 'pointer' }
|
||||
},
|
||||
tooltip: {
|
||||
formatter: function() {
|
||||
return '<b>'+ this.point.name +'</b>: '+ this.y +' %';
|
||||
}
|
||||
},
|
||||
series: [{
|
||||
type: 'pie',
|
||||
data: series
|
||||
}]
|
||||
});
|
||||
});
|
12
settings.py
12
settings.py
|
@ -284,6 +284,7 @@ INSTALLED_APPS = (
|
|||
'bandwagon',
|
||||
'blocklist',
|
||||
'browse',
|
||||
'compat',
|
||||
'cronjobs',
|
||||
'csp',
|
||||
'devhub',
|
||||
|
@ -897,3 +898,14 @@ FILE_VIEWER_SIZE_LIMIT = 1048576
|
|||
|
||||
# How long to delay modify updates to cope with alleged NFS slowness.
|
||||
MODIFIED_DELAY = 120
|
||||
|
||||
# This is a list of dictionaries that we should generate compat info for.
|
||||
# app: should match amo.FIREFOX.id.
|
||||
# version: the app version we're generating compat info for.
|
||||
# alpha: the first version that should be considered alpha for :version.
|
||||
# previous: the major version before :version.
|
||||
COMPAT = (
|
||||
dict(app=1, version='4.0', alpha='3.7a', previous='3.6'),
|
||||
dict(app=1, version='5.0', alpha='5.0a', previous='4.0'),
|
||||
dict(app=1, version='6.0', alpha='6.0a', previous='5.0'),
|
||||
)
|
||||
|
|
2
urls.py
2
urls.py
|
@ -78,6 +78,8 @@ urlpatterns = patterns('',
|
|||
# SAMO/API
|
||||
('^api/', include('api.urls')),
|
||||
|
||||
('^compatibility/', include('compat.urls')),
|
||||
|
||||
# Review spam.
|
||||
url('^reviews/spam/$', 'reviews.views.spam', name='reviews.spam'),
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче