summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLeif Johansson <leifj@sunet.se>2011-06-19 23:39:34 +0200
committerLeif Johansson <leifj@sunet.se>2011-06-19 23:39:34 +0200
commita987b0cbf7874c4c547536270b80e432561ddfd8 (patch)
tree416546b83a5c591d65f55b72a49024122ae55a33
parent3cb58a9e9c9aae32ae1b22c3d8e83528df766764 (diff)
initial opensocial people service
-rw-r--r--coip/apps/api/__init__.py6
-rw-r--r--coip/apps/api/resources.py16
-rw-r--r--coip/apps/membership/models.py4
-rw-r--r--coip/apps/opensocial/__init__.py6
-rw-r--r--coip/apps/opensocial/people.py77
-rw-r--r--coip/apps/opensocial/serializer.py90
-rw-r--r--coip/settings.py7
-rw-r--r--coip/urls.py10
-rw-r--r--coip/uuidfield.py61
9 files changed, 270 insertions, 7 deletions
diff --git a/coip/apps/api/__init__.py b/coip/apps/api/__init__.py
new file mode 100644
index 0000000..3dd6dd2
--- /dev/null
+++ b/coip/apps/api/__init__.py
@@ -0,0 +1,6 @@
+from tastypie.api import Api
+from coip.apps.api.resources import UserResource, NameResource
+
+v1_api = Api(api_name="1.0")
+v1_api.register(UserResource())
+v1_api.register(NameResource()) \ No newline at end of file
diff --git a/coip/apps/api/resources.py b/coip/apps/api/resources.py
new file mode 100644
index 0000000..974b146
--- /dev/null
+++ b/coip/apps/api/resources.py
@@ -0,0 +1,16 @@
+'''
+Created on Jun 18, 2011
+
+@author: leifj
+'''
+from tastypie.resources import ModelResource
+from django.contrib.auth.models import User
+from coip.apps.name.models import Name
+
+class UserResource(ModelResource):
+ class Meta:
+ queryset = User.objects.all()
+
+class NameResource(ModelResource):
+ class Meta:
+ queryset = Name.objects.all() \ No newline at end of file
diff --git a/coip/apps/membership/models.py b/coip/apps/membership/models.py
index 0884cd1..b19c04a 100644
--- a/coip/apps/membership/models.py
+++ b/coip/apps/membership/models.py
@@ -17,8 +17,8 @@ class Membership(models.Model):
'''
Membership in a namespace/group
'''
- user = models.ForeignKey(User,blank=True,null=True,related_name='user')
- entity = models.ForeignKey(Entity,blank=True,null=True,related_name='entity')
+ user = models.ForeignKey(User,blank=True,null=True,related_name='memberships')
+ entity = models.ForeignKey(Entity,blank=True,null=True,related_name='entity') # XXX should this be memberships too?
name = models.ForeignKey(Name,related_name='memberships')
enabled = models.BooleanField()
hidden = models.BooleanField()
diff --git a/coip/apps/opensocial/__init__.py b/coip/apps/opensocial/__init__.py
new file mode 100644
index 0000000..289f68c
--- /dev/null
+++ b/coip/apps/opensocial/__init__.py
@@ -0,0 +1,6 @@
+from tastypie.api import Api
+from coip.apps.opensocial.people import PersonResource, MembershipResource
+
+opensocial_v1 = Api(api_name = "1.0")
+opensocial_v1.register(PersonResource())
+opensocial_v1.register(MembershipResource()) \ No newline at end of file
diff --git a/coip/apps/opensocial/people.py b/coip/apps/opensocial/people.py
new file mode 100644
index 0000000..128b2c7
--- /dev/null
+++ b/coip/apps/opensocial/people.py
@@ -0,0 +1,77 @@
+'''
+Created on Jun 19, 2011
+
+@author: leifj
+'''
+from tastypie.resources import ModelResource
+from coip.apps.userprofile.models import UserProfile, last_used_profile
+from django.contrib.auth.models import User
+from coip.apps.opensocial.serializer import OpenSocialSerializer
+from django.conf.urls.defaults import url
+from coip.apps.membership.models import Membership
+from tastypie.fields import ToManyField, ToOneField
+from tastypie.utils.urls import trailing_slash
+from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
+from tastypie.http import HttpGone, HttpMultipleChoices
+import logging
+from tastypie.serializers import Serializer
+from pprint import pformat
+
+class MembershipResource(ModelResource):
+
+ user = ToOneField("coip.apps.opensocial.people.PersonResource",'user',full=True)
+
+ class Meta:
+ queryset = Membership.objects.all()
+ serializer = OpenSocialSerializer()
+ resource_name = 'membership'
+ fields = ['user']
+
+ def dehydrate(self,bundle):
+ bundle = super(MembershipResource,self).dehydrate(bundle)
+ del bundle.data['resource_uri']
+ return bundle
+
+class PersonResource(ModelResource):
+
+ #memberships = ToManyField(MembershipResource,'memberships',full=True)
+
+ class Meta:
+ queryset = User.objects.all()
+ fields = ['username']
+ resource_name = 'people'
+ serializer = OpenSocialSerializer()
+
+ def override_urls(self):
+ return [
+ url(r"^(?P<resource_name>%s)/(?P<username>[\@\w\d_.-:]+)/(?P<pk>[\d]+)%s?" % (self._meta.resource_name,trailing_slash()),
+ self.wrap_view('list_memberships'), name="api_list_memberships"),
+ url(r"^(?P<resource_name>%s)/(?P<username>[\@\w\d_.-:]+)(?:/\@self)?%s" % (self._meta.resource_name,trailing_slash()),
+ self.wrap_view('dispatch_detail'), name="api_dispatch_detail"),
+ ]
+
+ def list_memberships(self, request, **kwargs):
+ logging.debug(pformat(kwargs))
+ try:
+ obj = self.cached_obj_get(request=request, username=kwargs['username'])
+ logging.debug(pformat(obj))
+ except ObjectDoesNotExist:
+ return HttpGone()
+ except MultipleObjectsReturned:
+ return HttpMultipleChoices("More than one resource is found at this URI.")
+
+ membership_resource = MembershipResource()
+ return membership_resource.get_list(request, group__owner_id=obj.pk)
+
+ def dispatch(self, request_type, request, **kwargs):
+ if kwargs.has_key('username') and kwargs['username'] == '@me':
+ kwargs['username'] = request.user.username
+ return super(PersonResource, self).dispatch(request_type, request, **kwargs)
+
+ def dehydrate(self,bundle):
+ bundle = super(PersonResource,self).dehydrate(bundle)
+ bundle.data['id'] = bundle.data['username']
+ bundle.data['displayName'] = last_used_profile(bundle.obj).display_name
+ del bundle.data['resource_uri']
+ del bundle.data['username']
+ return bundle \ No newline at end of file
diff --git a/coip/apps/opensocial/serializer.py b/coip/apps/opensocial/serializer.py
new file mode 100644
index 0000000..14ac2df
--- /dev/null
+++ b/coip/apps/opensocial/serializer.py
@@ -0,0 +1,90 @@
+'''
+Created on Jun 19, 2011
+
+@author: leifj
+'''
+from tastypie.serializers import Serializer, get_type_string
+from lxml.etree import Element
+from tastypie.bundle import Bundle
+from tastypie.fields import ApiField, ToOneField, ToManyField
+from django.utils.encoding import force_unicode
+import logging
+from pprint import pformat
+
+class OpenSocialSerializer(Serializer):
+
+ def __init__(self, formats=None, content_types=None, datetime_formatting=None):
+ super(OpenSocialSerializer,self).__init__(formats,content_types,datetime_formatting)
+
+ def to_etree(self, data, options=None, name=None, depth=0):
+
+ #logging.debug("--------")
+ #logging.debug(name)
+ #logging.debug(depth)
+ #logging.debug(pformat(data))
+ """
+ Given some data, converts that data to an ``etree.Element`` suitable
+ for use in the XML output.
+ """
+
+ if isinstance(data, (list, tuple)):
+ element = Element(name or 'objects')
+ if name:
+ element = Element(name)
+ #element.set('type', 'list')
+ else:
+ element = Element('objects')
+ for item in data:
+ element.append(self.to_etree(item, options, depth=depth+1))
+ elif isinstance(data, dict):
+ if depth == 0:
+ element = Element(name or 'response',attrib={'xmlns': "http://ns.opensocial.org/2008/opensocial"} )
+ else:
+ element = Element(name or 'object')
+ element.set('type', 'hash')
+
+ if data.has_key('objects'):
+ if len(data['objects']) == 1:
+ return self.to_etree(data['objects'][0], options, name=None, depth=depth)
+ else:
+ for v in data['objects']:
+ element.append(self.to_etree(v, options, name='entry', depth=depth+1))
+ else:
+ for (key, value) in data.iteritems():
+ keyname = key
+ if keyname == 'user':
+ keyname = 'person'
+ element.append(self.to_etree(value, options, name=keyname, depth=depth+1))
+ elif isinstance(data, Bundle):
+ element = Element(name or 'object')
+ for field_name, field_object in data.data.items():
+ keyname = field_name
+ if keyname == 'user':
+ keyname = 'person'
+ element.append(self.to_etree(field_object, options, name=keyname, depth=depth+1))
+ elif isinstance(data, ApiField):
+ if isinstance(data, ToOneField):
+ if data.full:
+ return self.to_etree(data.fk_resource, options, name, depth+1)
+ else:
+ return self.to_etree(data.value, options, name, depth+1)
+ elif isinstance(data, ToManyField):
+ if data.full:
+ element = Element(name or 'objects')
+ for bundle in data.m2m_bundles:
+ element.append(self.to_etree(bundle, options, bundle.resource_name, depth+1))
+ else:
+ element = Element(name or 'objects')
+ for value in data.value:
+ element.append(self.to_etree(value, options, name, depth=depth+1))
+ else:
+ return self.to_etree(data.value, options, name)
+ else:
+ element = Element(name or 'value')
+ simple_data = self.to_simple(data, options)
+ data_type = get_type_string(simple_data)
+ if data_type != 'string':
+ element.set('type', get_type_string(simple_data))
+ if data_type != 'null':
+ element.text = force_unicode(simple_data)
+ return element \ No newline at end of file
diff --git a/coip/settings.py b/coip/settings.py
index 8c43689..2a8297f 100644
--- a/coip/settings.py
+++ b/coip/settings.py
@@ -58,6 +58,7 @@ ADMIN_MEDIA_PREFIX = '/admin-media/'
# Make this unique, and don't share it with anybody.
SECRET_KEY = '!=ren*@$dklhfm$3#$h=a2g4r3)ra#+al)9kwi4&rpylr$3xnf'
+SESSION_ENGINE = "django.contrib.sessions.backends.cache"
# List of callables that know how to import templates from various sources.
TEMPLATE_LOADERS = (
@@ -67,13 +68,13 @@ TEMPLATE_LOADERS = (
)
MIDDLEWARE_CLASSES = (
- 'django.middleware.cache.UpdateCacheMiddleware',
+ #'django.middleware.cache.UpdateCacheMiddleware',
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'coip.middleware.UserMappingMiddleware',
'django.contrib.auth.middleware.RemoteUserMiddleware',
- 'django.middleware.cache.FetchFromCacheMiddleware'
+ #'django.middleware.cache.FetchFromCacheMiddleware'
)
AUTHENTICATION_BACKENDS = (
@@ -99,6 +100,8 @@ INSTALLED_APPS = (
'django.contrib.humanize',
'django_extensions',
'tagging',
+ 'tastypie',
+ 'oauth_provider',
'coip.extensions',
'coip.apps.name',
'coip.apps.membership',
diff --git a/coip/urls.py b/coip/urls.py
index 637bfa8..4b1bc6f 100644
--- a/coip/urls.py
+++ b/coip/urls.py
@@ -1,12 +1,13 @@
-from django.conf.urls.defaults import *
-
-# Uncomment the next two lines to enable the admin:
+from django.conf.urls.defaults import patterns,include
from django.contrib import admin
from django.contrib.auth.views import login
from settings import ADMIN_MEDIA_ROOT
from settings import MEDIA_ROOT
from django.http import HttpResponseRedirect
from coip.apps.auth.views import logout
+from coip.apps.opensocial import opensocial_v1
+from coip.apps.api import v1_api
+
admin.autodiscover()
def welcome(request):
@@ -64,4 +65,7 @@ urlpatterns = patterns('',
(r'^ctree/(?P<id>[0-9]+).json$', 'coip.apps.name.views.ctree'),
(r'^rtree.json$', 'coip.apps.name.views.rtree'),
(r'^rtree/(?P<id>[0-9]+).json$', 'coip.apps.name.views.rtree'),
+ # APIs
+ (r'^api/', include(v1_api.urls)),
+ (r'^opensocial/', include(opensocial_v1.urls))
)
diff --git a/coip/uuidfield.py b/coip/uuidfield.py
new file mode 100644
index 0000000..44a789c
--- /dev/null
+++ b/coip/uuidfield.py
@@ -0,0 +1,61 @@
+'''
+Created on Jun 19, 2011
+
+@author: leifj
+'''
+
+from django.db.models import CharField
+
+try:
+ import uuid
+except ImportError:
+ from django.utils import uuid
+
+class UUIDVersionError(Exception):
+ pass
+
+class UUIDField(CharField):
+ """ UUIDField for Django, supports all uuid versions which are natively
+ suported by the uuid python module.
+ """
+
+ def __init__(self, verbose_name=None, name=None, auto=True, version=1, node=None, clock_seq=None, namespace=None, **kwargs):
+ kwargs['maxlength'] = 36
+ if auto:
+ kwargs['blank'] = True
+ kwargs['editable'] = kwargs.get('editable', False)
+ self.version = version
+ if version==1:
+ self.node, self.clock_seq = node, clock_seq
+ elif version==3 or version==5:
+ self.namespace, self.name = namespace, name
+ CharField.__init__(self, verbose_name, name, **kwargs)
+
+ def get_internal_type(self):
+ return "UUIDField"
+
+ def create_uuid(self):
+ if not self.version or self.version==4:
+ return uuid.uuid4()
+ elif self.version==1:
+ return uuid.uuid1(self.node, self.clock_seq)
+ elif self.version==2:
+ raise UUIDVersionError("UUID version 2 is not supported.")
+ elif self.version==3:
+ return uuid.uuid3(self.namespace, self.name)
+ elif self.version==5:
+ return uuid.uuid5(self.namespace, self.name)
+ else:
+ raise UUIDVersionError("UUID version %s is not valid." % self.version)
+
+ def pre_save(self, model_instance, add):
+ if self.auto and add:
+ value = unicode(self.create_uuid())
+ setattr(model_instance, self.attname, value)
+ return value
+ else:
+ value = super(UUIDField, self).pre_save(model_instance, add)
+ if self.auto and not value:
+ value = unicode(self.create_uuid())
+ setattr(model_instance, self.attname, value)
+ return value \ No newline at end of file