Change menu ``Report`` to ``Usage``
[astakos] / snf-astakos-app / astakos / im / api / __init__.py
index ba70e3a..754f34f 100644 (file)
 # interpreted as representing official policies, either expressed
 # or implied, of GRNET S.A.
 
+from functools import wraps
+from traceback import format_exc
+from urllib import quote, unquote
+
 from django.http import HttpResponse
 from django.utils import simplejson as json
+from django.conf import settings
+from django.core.urlresolvers import reverse
+
+from astakos.im.models import AstakosUser, GroupKind, Service, Resource
+from astakos.im.api.faults import Fault, ItemNotFound, InternalServerError, BadRequest
+from astakos.im.settings import INVITATIONS_ENABLED, COOKIE_NAME, EMAILCHANGE_ENABLED
 
-from astakos.im.models import AstakosUser
-from astakos.im.faults import ItemNotFound
+import logging
+logger = logging.getLogger(__name__)
 
 format = ('%a, %d %b %Y %H:%M:%S GMT')
 
+absolute = lambda request, url: request.build_absolute_uri(url)
+
+
+def render_fault(request, fault):
+    if isinstance(fault, InternalServerError) and settings.DEBUG:
+        fault.details = format_exc(fault)
+
+    request.serialization = 'text'
+    data = fault.message + '\n'
+    if fault.details:
+        data += '\n' + fault.details
+    response = HttpResponse(data, status=fault.code)
+    response['Content-Length'] = len(response.content)
+    return response
+
+
+def api_method(http_method=None):
+    """Decorator function for views that implement an API method."""
+    def decorator(func):
+        @wraps(func)
+        def wrapper(request, *args, **kwargs):
+            try:
+                if http_method and request.method != http_method:
+                    raise BadRequest('Method not allowed.')
+                response = func(request, *args, **kwargs)
+                return response
+            except Fault, fault:
+                return render_fault(request, fault)
+            except BaseException, e:
+                logger.exception('Unexpected error: %s' % e)
+                fault = InternalServerError('Unexpected error')
+                return render_fault(request, fault)
+        return wrapper
+    return decorator
+
+
 def _get_user_by_username(user_id):
     try:
-        user = AstakosUser.objects.get(username = user_id)
-    except AstakosUser.DoesNotExist, e:
+        user = AstakosUser.objects.get(username=user_id)
+    except AstakosUser.DoesNotExist:
         raise ItemNotFound('Invalid userid')
     else:
         response = HttpResponse()
-        response.status=200
-        user_info = {'id':user.id,
-                     'username':user.username,
-                     'email':[user.email],
-                     'name':user.realname,
-                     'auth_token_created':user.auth_token_created.strftime(format),
-                     'auth_token_expires':user.auth_token_expires.strftime(format),
-                     'has_credits':user.has_credits,
-                     'enabled':user.is_active,
-                     'groups':[g.name for g in user.groups.all()]}
+        response.status = 200
+        user_info = {'id': user.id,
+                     'username': user.username,
+                     'email': [user.email],
+                     'name': user.realname,
+                     'auth_token_created': user.auth_token_created.strftime(format),
+                     'auth_token_expires': user.auth_token_expires.strftime(format),
+                     'has_credits': user.has_credits,
+                     'enabled': user.is_active,
+                     'groups': [g.name for g in user.groups.all()]}
         response.content = json.dumps(user_info)
         response['Content-Type'] = 'application/json; charset=UTF-8'
         response['Content-Length'] = len(response.content)
         return response
 
+
 def _get_user_by_email(email):
     if not email:
         raise BadRequest('Email missing')
     try:
-        user = AstakosUser.objects.get(email = email)
-    except AstakosUser.DoesNotExist, e:
+        user = AstakosUser.objects.get(email__iexact=email)
+    except AstakosUser.DoesNotExist:
         raise ItemNotFound('Invalid email')
-    
+
     if not user.is_active:
         raise ItemNotFound('Inactive user')
     else:
         response = HttpResponse()
-        response.status=200
-        user_info = {'id':user.id,
-                     'username':user.username,
-                     'email':[user.email],
-                     'enabled':user.is_active,
-                     'name':user.realname,
-                     'auth_token_created':user.auth_token_created.strftime(format),
-                     'auth_token_expires':user.auth_token_expires.strftime(format),
-                     'has_credits':user.has_credits,
-                     'groups':[g.name for g in user.groups.all()],
-                     'user_permissions':[p.codename for p in user.user_permissions.all()]}
+        response.status = 200
+        user_info = {'id': user.id,
+                     'username': user.username,
+                     'email': [user.email],
+                     'enabled': user.is_active,
+                     'name': user.realname,
+                     'auth_token_created': user.auth_token_created.strftime(format),
+                     'auth_token_expires': user.auth_token_expires.strftime(format),
+                     'has_credits': user.has_credits,
+                     'groups': [g.name for g in user.groups.all()],
+                     'user_permissions': [p.codename for p in user.user_permissions.all()]}
         response.content = json.dumps(user_info)
         response['Content-Type'] = 'application/json; charset=UTF-8'
         response['Content-Length'] = len(response.content)
-        return response
\ No newline at end of file
+        return response
+
+
+@api_method(http_method='GET')
+def get_services(request):
+    callback = request.GET.get('callback', None)
+    services = Service.objects.all()
+    data = tuple({'id': s.pk, 'name': s.name, 'url': s.url, 'icon':
+                 s.icon} for s in services)
+    data = json.dumps(data)
+    mimetype = 'application/json'
+
+    if callback:
+        mimetype = 'application/javascript'
+        data = '%s(%s)' % (callback, data)
+
+    return HttpResponse(content=data, mimetype=mimetype)
+
+
+@api_method()
+def get_menu(request, with_extra_links=False, with_signout=True):
+    user = request.user
+    index_url = reverse('index')
+    l = [{'url': absolute(request, index_url), 'name': "Sign in"}]
+    if user.is_authenticated():
+        l = []
+        append = l.append
+        item = MenuItem
+        item.current_path = absolute(request, request.path)
+        append(item(
+               url=absolute(request, reverse('index')),
+               name=user.email))
+        append(item(url=absolute(request, reverse('edit_profile')),
+               name="My account"))
+        if with_extra_links:
+            if user.has_usable_password() and user.provider in ('local', ''):
+                append(item(
+                       url=absolute(request, reverse('password_change')),
+                       name="Change password"))
+            if EMAILCHANGE_ENABLED:
+                append(item(
+                       url=absolute(request, reverse('email_change')),
+                       name="Change email"))
+            if INVITATIONS_ENABLED:
+                append(item(
+                       url=absolute(request, reverse('invite')),
+                       name="Invitations"))
+            
+            append(item(
+                   url=absolute(request, reverse('group_list')),
+                   name="Projects",
+#                    submenu=(item(
+#                             url=absolute(request,
+#                                          reverse('group_list')),
+#                             name="Overview"),
+#                             item(
+#                                 url=absolute(request,
+#                                              reverse('group_create_list')),
+#                                 name="Create"),
+#                             item(
+#                                 url=absolute(request,
+#                                              reverse('group_search')),
+#                                 name="Join"),
+#                     )
+                )
+            )
+            append(item(
+                   url=absolute(request, reverse('resource_usage')),
+                   name="Usage"))
+            append(item(
+                   url=absolute(request, reverse('feedback')),
+                   name="Feedback"))
+#            append(item(
+#                   url=absolute(request, reverse('billing')),
+#                   name="Billing"))
+#            append(item(
+#                   url=absolute(request, reverse('timeline')),
+#                   name="Timeline"))
+        if with_signout:
+            append(item(
+                   url=absolute(request, reverse('logout')),
+                   name="Sign out"))
+
+    callback = request.GET.get('callback', None)
+    data = json.dumps(tuple(l))
+    mimetype = 'application/json'
+
+    if callback:
+        mimetype = 'application/javascript'
+        data = '%s(%s)' % (callback, data)
+
+    return HttpResponse(content=data, mimetype=mimetype)
+
+
+class MenuItem(dict):
+    current_path = ''
+
+    def __init__(self, *args, **kwargs):
+        super(MenuItem, self).__init__(*args, **kwargs)
+        if kwargs.get('url') or kwargs.get('submenu'):
+            self.__set_is_active__()
+
+    def __setitem__(self, key, value):
+        super(MenuItem, self).__setitem__(key, value)
+        if key in ('url', 'submenu'):
+            self.__set_is_active__()
+
+    def __set_is_active__(self):
+        if self.get('is_active'):
+            return
+        if self.current_path == self.get('url'):
+            self.__setitem__('is_active', True)
+        else:
+            submenu = self.get('submenu', ())
+            current = (i for i in submenu if i.get('url') == self.current_path)
+            try:
+                current_node = current.next()
+                if not current_node.get('is_active'):
+                    current_node.__setitem__('is_active', True)
+                self.__setitem__('is_active', True)
+            except StopIteration:
+                return
+
+    def __setattribute__(self, name, value):
+        super(MenuItem, self).__setattribute__(name, value)
+        if name == 'current_path':
+            self.__set_is_active__()