From 0d5e1e0901b3a3dbedbfc7261b8dc4b22dbdf1c6 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Thu, 17 Nov 2011 12:06:48 +0100 Subject: [PATCH] euscanwww: add basic json/xml API Signed-off-by: Corentin Chary --- euscanwww/api/__init__.py | 0 euscanwww/api/handlers.py | 171 ++++++++++++++++++++++++++++++++++++++ euscanwww/api/urls.py | 26 ++++++ euscanwww/urls.py | 1 + 4 files changed, 198 insertions(+) create mode 100644 euscanwww/api/__init__.py create mode 100644 euscanwww/api/handlers.py create mode 100644 euscanwww/api/urls.py diff --git a/euscanwww/api/__init__.py b/euscanwww/api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/euscanwww/api/handlers.py b/euscanwww/api/handlers.py new file mode 100644 index 0000000..9e08915 --- /dev/null +++ b/euscanwww/api/handlers.py @@ -0,0 +1,171 @@ +from piston.handler import AnonymousBaseHandler, BaseHandler +from piston.utils import rc, HttpStatusCode + +from django.db.models import Sum, Max +from django.shortcuts import get_object_or_404 +from django.forms.models import model_to_dict + +from euscan.models import Version, Package, Herd, Maintainer, EuscanResult, VersionLog +from euscan.forms import WorldForm, PackagesForm + +def xint(i): + try: + return int(i) + except: + return 0 + +def renameFields(vqs, fields): + ret = [] + for n in vqs: + for tr in fields: + if tr[0] in n: + n[tr[1]] = n[tr[0]] + del n[tr[0]] + ret.append(n) + return ret + +# /api/1.0/ +class RootHandler(AnonymousBaseHandler): + allowed_methods = ('GET',) + + def read(self, request): + return {'api-version': '1.0'} + +# /api/1.0/statistics +class StatisticsHandler(AnonymousBaseHandler): + allowed_methods = ('GET',) + + def read(self, request): + print request + data = {} + data['n_packaged'] = xint(Package.objects.aggregate(Sum('n_packaged'))['n_packaged__sum']) + data['n_overlay'] = xint(Package.objects.aggregate(Sum('n_overlay'))['n_overlay__sum']) + data['n_versions'] = xint(Package.objects.aggregate(Sum('n_versions'))['n_versions__sum']) + data['n_upstream'] = data['n_versions'] - data['n_packaged'] - data['n_overlay'] + data['n_packages'] = Package.objects.count() + data['n_herds'] = Herd.objects.count() + data['n_maintainers'] = Maintainer.objects.count() + data['last_scan'] = EuscanResult.objects.get(id=EuscanResult.objects.aggregate(Max('id'))['id__max']).datetime + + return data + +# /api/1.0/maintainers +class MaintainersHandler(AnonymousBaseHandler): + allowed_methods = ('GET',) + + def read(self, request): + maintainers = Package.objects.filter(maintainers__isnull=False) + maintainers = maintainers.values('maintainers__id', 'maintainers__name', 'maintainers__email') + maintainers = maintainers.annotate(n_packaged=Sum('n_packaged'), + n_overlay=Sum('n_overlay'), + n_versions=Sum('n_versions')) + + maintainers = renameFields(maintainers, [('maintainers__id', 'id'), + ('maintainers__name', 'name'), + ('maintainers__email', 'email')]) + return maintainers + +# /api/1.0/herds +class HerdsHandler(AnonymousBaseHandler): + allowed_methods = ('GET',) + + def read(self, request): + # FIXME: optimize the query, it uses 'LEFT OUTER JOIN' instead of 'INNER JOIN' + herds = Package.objects.filter(herds__isnull=False) + herds = herds.values('herds__herd').annotate(n_packaged=Sum('n_packaged'), + n_overlay=Sum('n_overlay'), + n_versions=Sum('n_versions')) + + herds = renameFields(herds, [('herds__herd', 'herd')]) + return herds + +# /api/1.0/categories +class CategoriesHandler(AnonymousBaseHandler): + allowed_methods = ('GET',) + + def read(self, request): + categories = Package.objects.values('category') + categories = categories.annotate(n_packaged=Sum('n_packaged'), + n_overlay=Sum('n_overlay'), + n_versions=Sum('n_versions')) + + return { 'categories' : categories } + +# /api/1.0/packages/by-maintainer/ +# /api/1.0/packages/by-category/ +# /api/1.0/packages/by-herd/ +class PackagesHandler(AnonymousBaseHandler): + allowed_methods = ('GET',) + fields = ('category', 'name', 'n_packaged', 'n_overlay', 'n_versions') + model = Package + + def read(self, request, **kwargs): + data = {} + + if 'category' in kwargs: + packages = Package.objects.filter(category=kwargs['category']) + data = { 'category' : kwargs['category'], 'packages' : packages } + elif 'herd' in kwargs: + herd = get_object_or_404(Herd, herd=kwargs['herd']) + packages = Package.objects.filter(herds__id=herd.id) + data = { 'herd' : herd, 'packages' : packages } + elif 'maintainer' in kwargs: + maintainer = get_object_or_404(Maintainer, id=kwargs['maintainer']) + packages = Package.objects.filter(maintainers__id=maintainer.id) + data = { 'maintainer' : maintainer, 'packages' : packages } + + return data + +# /api/1.0/package/ +class PackageHandler(AnonymousBaseHandler): + allowed_methods = ('GET',) + + def read(self, request, category, package): + package = get_object_or_404(Package, category=category, name=package) + package.homepages = package.homepage.split(' ') + versions = Version.objects.filter(package=package) + log = EuscanResult.objects.filter(package=package).order_by('-datetime')[:1] + log = log[0] if log else None + vlog = VersionLog.objects.filter(package=package).order_by('-id') + + herds = [] + for herd in package.herds.all(): + print herd + herds.append(model_to_dict(herd, ['herd'])) + + + maintainers = [] + for maintainer in package.maintainers.all(): + maintainers.append(model_to_dict(maintainer, ['name', 'email'])) + + version_log = [] + for v in vlog: + v = model_to_dict(v, ['version', 'revision', 'slot', 'overlay', 'datetime', 'action']) + if v['action'] == VersionLog.VERSION_ADDED: + v['action'] = 'added' + if v['action'] == VersionLog.VERSION_REMOVED: + v['action'] = 'removed' + version_log.append(v) + + upstream = [] + packaged = [] + for version in versions: + unpackaged = not version.packaged + version = model_to_dict(version, ['version', 'revision', 'slot', 'overlay', 'urls']) + if unpackaged: + upstream.append(version) + else: + packaged.append(version) + + package = model_to_dict(package, ['category', 'name', 'description', + 'homepage']) + package['herds'] = herds + package['maintainers'] = maintainers + package['packaged'] = packaged + package['upstream'] = upstream + package['vlog'] = version_log + if log: + package['log'] = model_to_dict(log, ['result', 'datetime']) + + return package + diff --git a/euscanwww/api/urls.py b/euscanwww/api/urls.py new file mode 100644 index 0000000..04dec20 --- /dev/null +++ b/euscanwww/api/urls.py @@ -0,0 +1,26 @@ +from django.conf.urls.defaults import * + +from piston.resource import Resource +from api.handlers import * + +root_handler = Resource(handler=RootHandler) +statistics_handler = Resource(handler=StatisticsHandler) +herds_handler = Resource(handler=HerdsHandler) +categories_handler = Resource(handler=CategoriesHandler) +maintainers_handler = Resource(handler=MaintainersHandler) +packages_handler = Resource(handler=PackagesHandler) +package_handler = Resource(handler=PackageHandler) + +urlpatterns = patterns('api.views', + (r'^1.0/statistics\.(?P.+)$', statistics_handler), + (r'^1.0/herds\.(?P.+)$', herds_handler), + (r'^1.0/categories\.(?P.+)$', categories_handler), + (r'^1.0/maintainers\.(?P.+)$', maintainers_handler), + + (r'^1.0/packages/by-maintainer/(?P\d+)\.(?P.+)$', packages_handler), + (r'^1.0/packages/by-herd/(?P[\@\{\}\w+.-]*)\.(?P.+)?$', packages_handler), + (r'^1.0/packages/by-category/(?P[\w+][\w+.-]*)\.(?P.+)?$', packages_handler), + (r'^1.0/package/(?P[\w+][\w+.-]*)/(?P[\w+][\w+.-]*)\.(?P.+)$', package_handler), + + (r'^1.0/api\.(?P.+)$', root_handler), +) diff --git a/euscanwww/urls.py b/euscanwww/urls.py index d4cb99b..0ef0259 100644 --- a/euscanwww/urls.py +++ b/euscanwww/urls.py @@ -10,6 +10,7 @@ urlpatterns = patterns('', # (r'^admin/doc/', include('django.contrib.admindocs.urls')), (r'^admin/', include(admin.site.urls)), + (r'^api/', include('api.urls')), (r'^', include('euscan.urls')), )