addons-server/apps/discovery/views.py

178 строки
6.3 KiB
Python
Исходник Обычный вид История

2010-05-03 22:34:30 +04:00
import json
import uuid
2010-05-03 22:34:30 +04:00
2010-05-03 22:31:37 +04:00
from django import http
2010-05-05 09:07:31 +04:00
from django.contrib import admin
from django.forms.models import modelformset_factory
from django.shortcuts import redirect
2010-05-03 22:31:37 +04:00
from django.views.decorators.csrf import csrf_exempt
2010-04-21 07:08:27 +04:00
import jingo
2010-05-04 19:44:59 +04:00
import amo.utils
import api.utils
2010-05-10 21:35:08 +04:00
import api.views
2010-05-03 22:42:36 +04:00
from addons.models import Addon
from bandwagon.models import Collection, SyncedCollection, CollectionToken
2010-05-10 21:35:08 +04:00
from stats.models import GlobalStat
2010-05-05 09:07:31 +04:00
from .models import DiscoveryModule
from .forms import DiscoveryModuleForm
from .modules import registry as module_registry
2010-05-03 22:42:36 +04:00
def pane(request, version, platform):
2010-05-10 21:35:08 +04:00
def from_api(list_type):
r = api_view(request, platform, version, list_type)
return json.loads(r.content)
try:
qs = GlobalStat.objects.filter(name='addon_total_downloads')
addon_downloads = qs.latest().count
except GlobalStat.DoesNotExist:
addon_downloads = None
2010-05-10 21:35:08 +04:00
return jingo.render(request, 'discovery/pane.html',
{'modules': get_modules(request, platform, version),
'addon_downloads': addon_downloads,
'top_addons': from_api('by_adu'),
'featured': from_api('featured')})
def get_modules(request, platform, version):
lang = request.LANG
qs = DiscoveryModule.objects.filter(app=request.APP.id)
# Remove any modules without a registered backend or an ordering.
modules = [m for m in qs if m.module in module_registry
and m.ordering is not None]
# Remove modules that specify a locales string we're not part of.
modules = [m for m in modules if not m.locales
or lang in m.locales.split()]
modules = sorted(modules, key=lambda x: x.ordering)
return [module_registry[m.module](request, platform, version)
for m in modules]
def api_view(request, platform, version, list_type,
api_version=1.5, format='json', mimetype='application/json'):
"""Wrapper for calling an API view."""
view = api.views.ListView()
view.request, view.version = request, api_version
view.format, view.mimetype = format, mimetype
return view.process_request(list_type, platform=platform,
version=version)
2010-05-03 22:31:37 +04:00
2010-05-05 09:07:31 +04:00
@admin.site.admin_view
def module_admin(request):
APP = request.APP
# Custom sorting to drop ordering=NULL objects to the bottom.
qs = DiscoveryModule.uncached.raw("""
SELECT * from discovery_modules WHERE app_id = %s
ORDER BY ordering IS NULL, ordering""", [APP.id])
qs.ordered = True # The formset looks for this.
_sync_db_and_registry(qs, APP)
Form = modelformset_factory(DiscoveryModule, form=DiscoveryModuleForm,
can_delete=True, extra=0)
formset = Form(request.POST or None, queryset=qs)
if request.method == 'POST' and formset.is_valid():
formset.save()
return redirect('discovery.module_admin')
return jingo.render(request, 'discovery/module_admin.html',
{'formset': formset})
def _sync_db_and_registry(qs, app):
"""Match up the module registry and DiscoveryModule rows in the db."""
existing = dict((m.module, m) for m in qs)
add = [m for m in module_registry if m not in existing]
delete = [m for m in existing if m not in module_registry]
for m in add:
DiscoveryModule.objects.create(module=m, app_id=app.id)
for m in delete:
DiscoveryModule.objects.get(module=m, app=app.id).delete()
if add or delete:
qs._result_cache = None
2010-05-03 22:31:37 +04:00
@csrf_exempt
def recommendations(request, limit=5):
"""
Figure out recommended add-ons for an anonymous user based on POSTed guids.
POST body looks like {"guids": [...]} with an optional "token" key if
they've been here before.
"""
if request.method != 'POST':
return http.HttpResponseNotAllowed(['POST'])
2010-05-03 22:34:30 +04:00
try:
POST = json.loads(request.raw_post_data)
guids = POST['guids']
except (ValueError, TypeError, KeyError):
# Errors: invalid json, didn't get a dict, didn't find "guids".
2010-05-03 22:34:30 +04:00
return http.HttpResponseBadRequest()
2010-05-03 22:42:36 +04:00
addon_ids = get_addon_ids(guids)
token = POST['token'] if 'token' in POST else get_random_token()
if 'token' in POST:
q = SyncedCollection.objects.filter(token_set__token=token)
if q:
# We've seen this user before.
synced = q[0]
if synced.addon_index == Collection.make_index(addon_ids):
# Their add-ons didn't change, get out quick.
recs = synced.get_recommendations()
return _recommendations(request, limit, token, recs)
else:
# Remove the link to the current sync, make a new one below.
synced.token_set.get(token=token).delete()
2010-05-04 19:44:59 +04:00
synced = get_synced_collection(addon_ids, token)
recs = synced.get_recommendations()
return _recommendations(request, limit, token, recs)
def _recommendations(request, limit, token, recs):
"""Return a JSON response for the recs view."""
2010-05-04 19:44:59 +04:00
ids = list(recs.addons.order_by('collectionaddon__ordering')
.values_list('id', flat=True))[:limit]
data = {'token': token, 'recommendations': recs.get_url_path(),
'addons': [api.utils.addon_to_dict(Addon.objects.get(pk=pk))
for pk in ids]}
content = json.dumps(data, cls=amo.utils.JSONEncoder)
return http.HttpResponse(content, content_type='application/json')
2010-05-03 22:42:36 +04:00
def get_addon_ids(guids):
return Addon.objects.filter(guid__in=guids).values_list('id', flat=True)
def get_synced_collection(addon_ids, token):
"""
Get a synced collection for these addons. May reuse an existing collection.
The token is associated with the collection.
"""
index = Collection.make_index(addon_ids)
try:
c = (SyncedCollection.objects.no_cache()
.filter(addon_index=index))[0]
except IndexError:
c = SyncedCollection.objects.create(listed=False)
c.set_addons(addon_ids)
c.token_set.create(token=token)
return c
def get_random_token():
"""Get a random token for the user, make sure it's unique."""
while 1:
token = unicode(uuid.uuid4())
if CollectionToken.objects.filter(token=token).count() == 0:
return token