diff --git a/euscanwww/euscan/decorators.py b/euscanwww/euscan/decorators.py new file mode 100644 index 0000000..f44ced9 --- /dev/null +++ b/euscanwww/euscan/decorators.py @@ -0,0 +1,45 @@ +from django.db.models.query import QuerySet +from django.db.models import Model +from django.http import HttpResponse +from django.utils import simplejson +from django.core import serializers + +try: + from functools import wraps +except ImportError: + def wraps(wrapped, assigned=('__module__', '__name__', '__doc__'), + updated=('__dict__',)): + def inner(wrapper): + for attr in assigned: + setattr(wrapper, attr, getattr(wrapped, attr)) + for attr in updated: + getattr(wrapper, attr).update(getattr(wrapped, attr, {})) + return wrapper + return inner + + +class DjangoJSONEncoder(simplejson.JSONEncoder): + def default(self, obj): + if isinstance(obj, QuerySet): + # `default` must return a python serializable + # structure, the easiest way is to load the JSON + # string produced by `serialize` and return it + return simplejson.loads(serializers.serialize('json', obj)) + if isinstance(obj, Model): + # Must be iterable to be serilized + obj = [obj] + return simplejson.loads(serializers.serialize('json', obj)) + return simplejson.JSONEncoder.default(self, obj) + +def render_to_json(function): + @wraps(function) + def wrapper(request, *args, **kwargs): + output = function(request, *args, **kwargs) + if not isinstance(output, dict): + return output + + output = simplejson.dumps(output, cls=DjangoJSONEncoder) + return HttpResponse(mimetype='application/json', content=output) + return wrapper + + diff --git a/euscanwww/euscan/urls.py b/euscanwww/euscan/urls.py index fee3d16..fe3ab61 100644 --- a/euscanwww/euscan/urls.py +++ b/euscanwww/euscan/urls.py @@ -3,25 +3,29 @@ from feeds import * package_patterns = patterns('euscan.views', url(r'^(?P[\w+][\w+.-]*)/(?P[\w+][\w+.-]*)/feed/$', PackageFeed(), name='package_feed'), + (r'^(?P[\w+][\w+.-]*)/(?P[\w+][\w+.-]*)/json/$', 'package_json'), (r'^(?P[\w+][\w+.-]*)/(?P[\w+][\w+.-]*)/$', 'package'), ) categories_patterns = patterns('euscan.views', - (r'^(?P[\w+][\w+.-]*)/view/$', 'category'), + (r'^(?P[\w+][\w+.-]*)/(view/)?$', 'category'), + (r'^(?P[\w+][\w+.-]*)/json/$', 'category_json'), url(r'^(?P[\w+][\w+.-]*)/feed/$', CategoryFeed(), name='category_feed'), (r'^(?P[\w+][\w+.-]*)/charts/(?P[\w\-]+).png$', 'chart_category'), (r'^$', 'categories'), ) herds_patterns = patterns('euscan.views', - (r'^(?P[\@\{\}\w+.-]*)/view/$', 'herd'), + (r'^(?P[\@\{\}\w+.-]*)/(view/)?$', 'herd'), + (r'^(?P[\@\{\}\w+.-]*)/json/$', 'herd_json'), url(r'^(?P[\@\{\}\w+.-]*)/feed/$', HerdFeed(), name='herd_feed'), (r'^(?P[\@\{\}\w+.-]*)/charts/(?P[\w\-]+).png$', 'chart_herd'), (r'^$', 'herds'), ) maintainers_patterns = patterns('euscan.views', - (r'^(?P\d+)/view/$', 'maintainer'), + (r'^(?P\d+)/(view/)?$', 'maintainer'), + (r'^(?P\d+)/json/$', 'maintainer_json'), url(r'^(?P\d+)/feed/$', MaintainerFeed(), name='maintainer_feed'), (r'^(?P\d+)/charts/(?P[\w\-]+).png$', 'chart_maintainer'), (r'^$', 'maintainers'), diff --git a/euscanwww/euscan/views.py b/euscanwww/euscan/views.py index e22a242..0fae991 100644 --- a/euscanwww/euscan/views.py +++ b/euscanwww/euscan/views.py @@ -5,6 +5,7 @@ from django.db.models import Sum, Max from euscan.models import Version, Package, Herd, Maintainer, EuscanResult, VersionLog from euscan.forms import WorldForm, PackagesForm +from euscan.decorators import render_to_json import charts @@ -35,13 +36,21 @@ def categories(request): return { 'categories' : categories } -@render_to('euscan/category.html') -def category(request, category): + +def category_data(request, category): packages = Package.objects.filter(category=category) if not packages: raise Http404 return { 'category' : category, 'packages' : packages } +@render_to('euscan/category.html') +def category(request, category): + return category_data(request, category) + +@render_to_json +def category_json(request, category): + return category_data(request, category) + @render_to('euscan/herds.html') def herds(request): # FIXME: optimize the query, it uses 'LEFT OUTER JOIN' instead of 'INNER JOIN' @@ -51,12 +60,19 @@ def herds(request): n_versions=Sum('n_versions')) return { 'herds' : herds } -@render_to('euscan/herd.html') -def herd(request, herd): +def herd_data(request, herd): herd = get_object_or_404(Herd, herd=herd) packages = Package.objects.filter(herds__id=herd.id) return { 'herd' : herd, 'packages' : packages } +@render_to('euscan/herd.html') +def herd(request, herd): + return herd_data(request, herd) + +@render_to_json +def herd_json(request, herd): + return herd_data(request, herd) + @render_to('euscan/maintainers.html') def maintainers(request): maintainers = Package.objects.filter(maintainers__isnull=False) @@ -67,14 +83,21 @@ def maintainers(request): return { 'maintainers' : maintainers } -@render_to('euscan/maintainer.html') -def maintainer(request, maintainer_id): + +def maintainer_data(request, maintainer_id): maintainer = get_object_or_404(Maintainer, id=maintainer_id) packages = Package.objects.filter(maintainers__id=maintainer.id) return { 'maintainer' : maintainer, 'packages' : packages } -@render_to('euscan/package.html') -def package(request, category, package): +@render_to('euscan/maintainer.html') +def maintainer(request, maintainer_id): + return maintainer_data(request, maintainer_id) + +@render_to_json +def maintainer_json(request, maintainer_id): + return maintainer_data(request, maintainer_id) + +def package_data(request, category, package): package = get_object_or_404(Package, category=category, name=package) package.homepages = package.homepage.split(' ') packaged = Version.objects.filter(package=package, packaged=True) @@ -85,6 +108,14 @@ def package(request, category, package): return { 'package' : package, 'packaged' : packaged, 'upstream' : upstream, 'log' : log, 'vlog' : vlog } +@render_to('euscan/package.html') +def package(request, category, package): + return package_data(request, category, package) + +@render_to_json +def package_json(request, category, package): + return package_data(request, category, package) + @render_to('euscan/world.html') def world(request): world_form = WorldForm()