This commit is contained in:
Matt Claypotch 2011-04-07 16:05:28 -07:00
Родитель 39bfced727
Коммит 3885ab7d3d
10 изменённых файлов: 0 добавлений и 504 удалений

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

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

@ -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&Delta;</th>
<th>ADU(prev)</th>
<th>ADU(cur)</th>
<th>ADU&Delta;</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', 'extras',
'files', 'files',
'jingo_minify', 'jingo_minify',
'nick',
'pages', 'pages',
'perf', 'perf',
'reviews', 'reviews',

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

@ -56,9 +56,6 @@ urlpatterns = patterns('',
# Performance wall of shame. # Performance wall of shame.
('^performance/', include('perf.urls')), ('^performance/', include('perf.urls')),
# Nick's special pages.
('^nickspages/', include('nick.urls')),
# Localizable pages. # Localizable pages.
('', include('pages.urls')), ('', include('pages.urls')),