Removing the nick app
This commit is contained in:
Родитель
39bfced727
Коммит
3885ab7d3d
|
@ -1,146 +0,0 @@
|
|||
[
|
||||
{
|
||||
"pk": 2,
|
||||
"model": "addons.feature",
|
||||
"fields": {
|
||||
"application": 1,
|
||||
"end": "2010-12-01 00:00:00",
|
||||
"created": "2007-03-05 16:06:55",
|
||||
"locale": null,
|
||||
"modified": "2009-12-08 11:00:51",
|
||||
"start": "2008-11-01 00:00:00",
|
||||
"addon": 3615
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": 1,
|
||||
"model": "addons.addontype",
|
||||
"fields": {
|
||||
"description": 39111,
|
||||
"name_plural": 39112,
|
||||
"modified": "2009-02-06 08:42:28",
|
||||
"name": 39109,
|
||||
"created": "2006-08-21 23:53:19"
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": 1309,
|
||||
"model": "translations.translation",
|
||||
"fields": {
|
||||
"locale": "en-US",
|
||||
"created": "2007-03-05 13:09:26",
|
||||
"id": 1034,
|
||||
"modified": "2007-10-12 16:59:31",
|
||||
"localized_string": "Some of these add-ons will help you control and protect your personal data, while others help defend against some types of web attacks."
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": 1276,
|
||||
"model": "translations.translation",
|
||||
"fields": {
|
||||
"locale": "en-US",
|
||||
"created": "2007-03-05 13:09:26",
|
||||
"id": 1033,
|
||||
"modified": "2007-10-12 16:59:31",
|
||||
"localized_string": "Privacy & Security"
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": 12,
|
||||
"model": "addons.category",
|
||||
"fields": {
|
||||
"count": 556,
|
||||
"description": 1034,
|
||||
"weight": 0,
|
||||
"created": "2007-03-05 13:09:26",
|
||||
"modified": "2009-11-13 02:15:57",
|
||||
"application": 1,
|
||||
"type": 1,
|
||||
"slug": "privacy-security",
|
||||
"name": 1033
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": 122558,
|
||||
"model": "translations.translation",
|
||||
"fields": {
|
||||
"locale": "en-US",
|
||||
"created": "2007-12-19 21:48:37",
|
||||
"id": 88312,
|
||||
"modified": "2007-12-19 21:55:57",
|
||||
"localized_string": "These add-ons are related to tabs functionality and tabs management."
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": 122529,
|
||||
"model": "translations.translation",
|
||||
"fields": {
|
||||
"locale": "en-US",
|
||||
"created": "2007-12-19 21:48:37",
|
||||
"id": 88311,
|
||||
"modified": "2007-12-19 21:55:57",
|
||||
"localized_string": "Tabs"
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": 93,
|
||||
"model": "addons.category",
|
||||
"fields": {
|
||||
"count": 344,
|
||||
"description": 88312,
|
||||
"weight": 0,
|
||||
"created": "2007-12-19 21:48:37",
|
||||
"modified": "2009-11-13 02:15:58",
|
||||
"application": 1,
|
||||
"type": 1,
|
||||
"slug": "tabs",
|
||||
"name": 88311
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": 1564,
|
||||
"model": "translations.translation",
|
||||
"fields": {
|
||||
"locale": "en-US",
|
||||
"created": "2007-03-05 13:09:27",
|
||||
"id": 1044,
|
||||
"modified": "2007-03-21 13:32:15",
|
||||
"localized_string": "Do you have trouble remembering where you put the bookmark for that fantastic cookie recipe (no, the other one, with the pecans)? Are you tired of looking for a bookmark and then remembering it's on your other computer? Do you want to share your great Web finds with your friends, or see what they think is hot? These add-ons can help you with all of that and more."
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": 1531,
|
||||
"model": "translations.translation",
|
||||
"fields": {
|
||||
"locale": "en-US",
|
||||
"created": "2007-03-05 13:09:27",
|
||||
"id": 1043,
|
||||
"modified": "2007-03-21 13:32:15",
|
||||
"localized_string": "Bookmarks"
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": 22,
|
||||
"model": "addons.category",
|
||||
"fields": {
|
||||
"count": 824,
|
||||
"description": 1044,
|
||||
"weight": 0,
|
||||
"created": "2007-03-05 13:09:27",
|
||||
"modified": "2009-11-13 02:15:57",
|
||||
"application": 1,
|
||||
"type": 1,
|
||||
"slug": "bookmarks",
|
||||
"name": 1043
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": 1,
|
||||
"model": "addons.addoncategory",
|
||||
"fields": {
|
||||
"addon": 3615,
|
||||
"category": 12,
|
||||
"feature": 1
|
||||
}
|
||||
}
|
||||
]
|
|
@ -1 +0,0 @@
|
|||
# Django needs this before it loads fixtures for the app.
|
|
@ -1,89 +0,0 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block extrahead %}
|
||||
<link rel="stylesheet" href="{{ media('css/zamboni/nick.css') }}">
|
||||
<link rel="stylesheet"
|
||||
href="{{ media('css/zamboni/jquery-ui/custom-1.7.2.css') }}">
|
||||
{% endblock %}
|
||||
|
||||
{% block title %}
|
||||
Nick's Special Featured Add-ons Page for {{ request.APP.pretty }}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<ul class="listing-header">
|
||||
{% for name, url, title in sections %}
|
||||
<li {{ name|class_selected(section) }}>
|
||||
<a href="{{ url }}">{{ title }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</li>
|
||||
<li>
|
||||
<form id="params" method="get" action="" class="go">
|
||||
{{ form.as_p() }}
|
||||
</form>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<table id="rec-stats" title="holy tablular data batman">
|
||||
<thead>
|
||||
<th>ID</th>
|
||||
<th>Name</th>
|
||||
<th>Categories</th>
|
||||
<th>Rating</th>
|
||||
<th>Reviews</th>
|
||||
<th>Compatibility</th>
|
||||
<th>Featured?</th>
|
||||
<th>DL(prev)</th>
|
||||
<th>DL(cur)</th>
|
||||
<th>DLΔ</th>
|
||||
<th>ADU(prev)</th>
|
||||
<th>ADU(cur)</th>
|
||||
<th>ADUΔ</th>
|
||||
<th>SPARKLINES</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% cache query %}
|
||||
{% for addon in addons.object_list %}
|
||||
<tr>
|
||||
<td><a href="{{ url('addons.detail', addon.slug) }}"
|
||||
title="{{ addon.name }}">{{ addon.id }}</a></td>
|
||||
<td>{{ addon.name.localized_string|truncate(20, True) }}</td>
|
||||
<td>{{ addon.first_category }}</td>
|
||||
<td>{{ addon.average_rating }}</td>
|
||||
<td><a href="{{ url('reviews.list', addon.slug) }}">
|
||||
{{ addon.total_reviews}}</a></td>
|
||||
{% set supports = addon.current_version.compatible_apps %}
|
||||
{% if request.APP in supports %}
|
||||
<td>{{ supports[request.APP].max }}</td>
|
||||
{% else %}
|
||||
<td>---</td>
|
||||
{% endif %}
|
||||
<td>{% if addon.featured %}Y{% else %}N{% endif %}</td>
|
||||
<td>{{ addon.downloads.previous|numberfmt }}</td>
|
||||
<td>{{ addon.downloads.current|numberfmt }}</td>
|
||||
<td class="{{ addon.downloads.change }}">
|
||||
{{ addon.downloads.delta|numberfmt('+0%;-0%') }}
|
||||
</td>
|
||||
<td>{{ addon.adus.previous|round|numberfmt }}</td>
|
||||
<td>{{ addon.adus.current|round|numberfmt }}</td>
|
||||
<td class="{{ addon.adus.change }}">
|
||||
{{ addon.adus.delta|numberfmt('+0%;-0%') }}
|
||||
</td>
|
||||
<td class="sparklines">{{ addon.sparks|join(',') }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% endcache %}
|
||||
</tbody>
|
||||
</table>
|
||||
{{ addons|paginator }}
|
||||
{% endblock %}
|
||||
|
||||
{% block js %}
|
||||
<script src="{{ media('js/zamboni/jquery-ui/custom-1.7.2.min.js') }}"></script>
|
||||
<script src="{{ media('js/zamboni/jquery.sparkline.min.js') }}"></script>
|
||||
<script src="{{ media('js/zamboni/jquery.tablesorter.min.js') }}"></script>
|
||||
<script src="{{ media('js/zamboni/nick.js') }}"></script>
|
||||
<script>
|
||||
</script>
|
||||
{% endblock %}
|
|
@ -1,58 +0,0 @@
|
|||
from django.core.management import call_command
|
||||
from django.core.cache import cache
|
||||
|
||||
from nose.tools import eq_
|
||||
import test_utils
|
||||
|
||||
from amo.urlresolvers import reverse
|
||||
from applications.models import Application
|
||||
|
||||
|
||||
class TestViews(test_utils.TestCase):
|
||||
fixtures = ('base/users', 'base/addon_3615', 'base/addon_59',
|
||||
'nick/test_views',)
|
||||
|
||||
def setUp(self):
|
||||
assert self.client.login(username='admin@mozilla.com',
|
||||
password='password')
|
||||
cache.clear()
|
||||
|
||||
def test_basics(self):
|
||||
"""Make sure everything the template expects is available."""
|
||||
url = reverse('nick.featured')
|
||||
response = self.client.get(url, follow=True)
|
||||
|
||||
|
||||
def test_featured(self):
|
||||
url = reverse('nick.featured')
|
||||
response = self.client.get(url, follow=True)
|
||||
eq_(response.status_code, 200)
|
||||
|
||||
eq_('featured', response.context['section'])
|
||||
|
||||
addons = response.context['addons'].object_list
|
||||
eq_(len(addons), 1)
|
||||
|
||||
addon = addons[0]
|
||||
eq_(addon.id, 3615)
|
||||
assert hasattr(addon, 'downloads')
|
||||
assert hasattr(addon, 'adus')
|
||||
assert hasattr(addon, 'sparks')
|
||||
eq_(addon.first_category, addon.categories.all()[0])
|
||||
|
||||
def test_category_featured(self):
|
||||
url = reverse('nick.category_featured')
|
||||
response = self.client.get(url, follow=True)
|
||||
eq_(response.status_code, 200)
|
||||
|
||||
def test_combo(self):
|
||||
url = reverse('nick.combo')
|
||||
response = self.client.get(url, follow=True)
|
||||
eq_(response.status_code, 200)
|
||||
addons = response.context['addons'].object_list
|
||||
eq_(len(addons), 1)
|
||||
|
||||
def test_popular(self):
|
||||
url = reverse('nick.popular') + '?category=tabs'
|
||||
response = self.client.get(url, follow=True)
|
||||
eq_(response.status_code, 200)
|
|
@ -1,12 +0,0 @@
|
|||
from django.conf.urls.defaults import patterns, url
|
||||
|
||||
from . import views
|
||||
|
||||
|
||||
urlpatterns = patterns('',
|
||||
url('^featured$', views.featured, name='nick.featured'),
|
||||
url('^category_featured$', views.category_featured,
|
||||
name='nick.category_featured'),
|
||||
url('^featured\+categories$', views.combo, name='nick.combo'),
|
||||
url('^popular$', views.popular, name='nick.popular'),
|
||||
)
|
|
@ -1,186 +0,0 @@
|
|||
"""
|
||||
This set of views generates download and ADU statistics for featured,
|
||||
category-featured, and other popular add-ons. It's useful when you're
|
||||
analyzing the effectiveness of the featured list, and when picking new add-ons
|
||||
to feature.
|
||||
"""
|
||||
import collections
|
||||
from datetime import date, timedelta as td
|
||||
import time
|
||||
|
||||
from django import forms
|
||||
from django.contrib import admin
|
||||
from django.db.models import Sum, Avg
|
||||
|
||||
import jingo
|
||||
from caching.base import cached_with
|
||||
|
||||
import amo
|
||||
from amo.urlresolvers import reverse
|
||||
from addons.models import Addon, Category
|
||||
from stats.models import DownloadCount, UpdateCount
|
||||
|
||||
|
||||
# Helper functions, data structures, and forms.
|
||||
|
||||
def CategoryForm(request):
|
||||
"""Makes a form to select a category for the current app, and a date."""
|
||||
q = Category.objects.filter(application=request.APP.id,
|
||||
type=amo.ADDON_EXTENSION)
|
||||
choices = (('', '--------'),) + tuple((c.slug, c.name) for c in q)
|
||||
|
||||
class _CategoryForm(forms.Form):
|
||||
category = forms.ChoiceField(choices=choices, required=False)
|
||||
date = forms.DateField(initial=date.today, required=False)
|
||||
|
||||
return _CategoryForm(request.GET or None)
|
||||
|
||||
|
||||
class StatDelta(object):
|
||||
"""Compares two values and generates a delta."""
|
||||
|
||||
def __init__(self, current, previous):
|
||||
self.current = current
|
||||
self.previous = previous
|
||||
if previous != 0:
|
||||
self.delta = delta = float(current - previous) / previous
|
||||
else:
|
||||
self.delta = delta = 0
|
||||
# This might be useful as a css class.
|
||||
if delta > 0:
|
||||
self.change = 'positive'
|
||||
elif delta < 0:
|
||||
self.change = 'negative'
|
||||
else:
|
||||
self.change = 'neutral'
|
||||
|
||||
|
||||
def gather_stats(qs, key_name, aggregate, date_):
|
||||
"""Take a ValuesQuerySet and turn it into a dict of StatDeltas."""
|
||||
# Take the queryset and compare the aggregate
|
||||
# value between last week and 2 weeks ago.
|
||||
|
||||
# Some dates we'll be using: today - 1 week, today - 2 weeks.
|
||||
date_1w, date_2w = date_ - td(days=7), date_ - td(days=14)
|
||||
|
||||
rv, tmp = {}, {}
|
||||
tmp['cur'] = qs.filter(date__lte=date_, date__gte=date_1w)
|
||||
tmp['prev'] = qs.filter(date__lte=date_1w, date__gte=date_2w)
|
||||
for name, query in tmp.items():
|
||||
tmp[name] = dict((x[key_name], x[aggregate]) for x in query)
|
||||
for key, cur_value in tmp['cur'].items():
|
||||
rv[key] = StatDelta(cur_value, tmp['prev'].get(key, 0))
|
||||
return rv
|
||||
|
||||
|
||||
def attach_stats(request, addons, date_):
|
||||
"""
|
||||
Attach download and adu stats to each addon for the 2 weeks before date_.
|
||||
"""
|
||||
ids = [addon.id for addon in addons]
|
||||
|
||||
date_1w, date_2w = date_ - td(days=7), date_ - td(days=14)
|
||||
|
||||
# Gather download stats.
|
||||
q = (DownloadCount.stats.filter(addon__in=ids).values('addon')
|
||||
.annotate(Sum('count')))
|
||||
downloads = gather_stats(q, 'addon', 'count__sum', date_)
|
||||
|
||||
# Gather active daily user stats.
|
||||
q = (UpdateCount.stats.filter(addon__in=ids).values('addon')
|
||||
.annotate(Avg('count')))
|
||||
adus = gather_stats(q, 'addon', 'count__avg', date_)
|
||||
|
||||
# Download data for sparklines.
|
||||
q = (DownloadCount.stats.filter(addon__in=ids, date__gte=date_2w)
|
||||
.order_by('addon', 'date').values_list('addon', 'count'))
|
||||
sparks = collections.defaultdict(list)
|
||||
for addon_id, count in q:
|
||||
sparks[addon_id].append(count)
|
||||
|
||||
featured_ids = [a.id for a in Addon.objects.featured(request.APP)]
|
||||
|
||||
# Attach all the extra data to the addon.
|
||||
for addon in addons:
|
||||
addon.downloads = downloads.get(addon.id, StatDelta(0, 0))
|
||||
addon.adus = adus.get(addon.id, StatDelta(0, 0))
|
||||
addon.sparks = sparks[addon.id]
|
||||
addon.featured = addon.id in featured_ids
|
||||
try:
|
||||
addon.first_category = addon.categories.all()[0]
|
||||
except IndexError:
|
||||
pass
|
||||
|
||||
return list(addons)
|
||||
|
||||
|
||||
def get_sections():
|
||||
"""Get (name, url, title) for each function we expose."""
|
||||
return [(func.__name__, reverse(func), title)
|
||||
for func, title in _sections]
|
||||
|
||||
|
||||
@admin.site.admin_view
|
||||
def view(request, func):
|
||||
"""
|
||||
This isn't called directly by anything in urls.py. Since all the views in
|
||||
this module are quite similar, each function marked by @section just
|
||||
returns the queryset we should operate on. The rest of the structure is
|
||||
the same.
|
||||
"""
|
||||
qs = func(request).exclude(type=amo.ADDON_PERSONA).distinct()
|
||||
date_ = date.today()
|
||||
form = CategoryForm(request)
|
||||
|
||||
if form.is_valid():
|
||||
date_ = form.cleaned_data['date'] or date_
|
||||
category = form.cleaned_data['category']
|
||||
if category:
|
||||
qs = qs.filter(categories__slug=category)
|
||||
|
||||
addons = amo.utils.paginate(request, qs, per_page=75)
|
||||
q = addons.object_list
|
||||
cache_key = '%s%s' % (q.query, date_)
|
||||
f = lambda: attach_stats(request, q, date_)
|
||||
addons.object_list = cached_with(q, f, cache_key)
|
||||
|
||||
c = {'addons': addons, 'section': func.__name__,
|
||||
'query': q, 'form': form, 'sections': get_sections()}
|
||||
return jingo.render(request, 'nick/featured.html', c)
|
||||
|
||||
|
||||
# This will hold (function, title) pairs that are exposed by @section.
|
||||
_sections = []
|
||||
|
||||
|
||||
def section(title):
|
||||
"""
|
||||
Add the title and function to _sections and return a wrapper that calls
|
||||
``view`` with the decorated function.
|
||||
"""
|
||||
def decorator(func):
|
||||
v = lambda request: view(request, func)
|
||||
_sections.append((v, title))
|
||||
return v
|
||||
return decorator
|
||||
|
||||
|
||||
@section('Featured')
|
||||
def featured(request):
|
||||
return Addon.objects.featured(request.APP)
|
||||
|
||||
|
||||
@section('Category Featured')
|
||||
def category_featured(request):
|
||||
return Addon.objects.category_featured()
|
||||
|
||||
|
||||
@section('Featured + Category Featured')
|
||||
def combo(request):
|
||||
o = Addon.objects
|
||||
return o.featured(request.APP) | o.category_featured()
|
||||
|
||||
|
||||
@section('Popular')
|
||||
def popular(request):
|
||||
return Addon.objects.order_by('-weekly_downloads')
|
|
@ -1,8 +0,0 @@
|
|||
$(document).ready(function() {
|
||||
$('.sparklines').sparkline('html', {width: '8em', fillColor: false});
|
||||
$('#rec-stats').tablesorter();
|
||||
$('input[name=date]').datepicker({dateFormat: 'yy-mm-dd'})
|
||||
.change(function() {
|
||||
this.form.submit();
|
||||
});
|
||||
});
|
|
@ -287,7 +287,6 @@ INSTALLED_APPS = (
|
|||
'extras',
|
||||
'files',
|
||||
'jingo_minify',
|
||||
'nick',
|
||||
'pages',
|
||||
'perf',
|
||||
'reviews',
|
||||
|
|
3
urls.py
3
urls.py
|
@ -56,9 +56,6 @@ urlpatterns = patterns('',
|
|||
# Performance wall of shame.
|
||||
('^performance/', include('perf.urls')),
|
||||
|
||||
# Nick's special pages.
|
||||
('^nickspages/', include('nick.urls')),
|
||||
|
||||
# Localizable pages.
|
||||
('', include('pages.urls')),
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче