summaryrefslogtreecommitdiff
path: root/coip
diff options
context:
space:
mode:
authorLeif Johansson <leifj@sunet.se>2010-07-05 21:37:04 +0200
committerLeif Johansson <leifj@sunet.se>2010-07-05 21:37:04 +0200
commit8f7f4ff3a35ddeacea7d5530c7dc3c296907ab67 (patch)
tree7d481dc7030ad191314aa2569cca9f8301e1f997 /coip
import
Diffstat (limited to 'coip')
-rw-r--r--coip/__init__.py0
-rw-r--r--coip/apps/__init__.py0
-rw-r--r--coip/apps/invitation/__init__.py0
-rw-r--r--coip/apps/invitation/admin.py4
-rw-r--r--coip/apps/invitation/forms.py11
-rw-r--r--coip/apps/invitation/models.py34
-rw-r--r--coip/apps/invitation/views.py33
-rw-r--r--coip/apps/membership/__init__.py0
-rw-r--r--coip/apps/membership/admin.py4
-rw-r--r--coip/apps/membership/forms.py11
-rw-r--r--coip/apps/membership/models.py23
-rw-r--r--coip/apps/membership/views.py13
-rw-r--r--coip/apps/name/__init__.py0
-rw-r--r--coip/apps/name/admin.py5
-rw-r--r--coip/apps/name/forms.py15
-rw-r--r--coip/apps/name/models.py73
-rw-r--r--coip/apps/name/views.py0
-rw-r--r--coip/mimeparse.py123
-rw-r--r--coip/multiresponse.py78
-rw-r--r--coip/urls.py17
20 files changed, 444 insertions, 0 deletions
diff --git a/coip/__init__.py b/coip/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/coip/__init__.py
diff --git a/coip/apps/__init__.py b/coip/apps/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/coip/apps/__init__.py
diff --git a/coip/apps/invitation/__init__.py b/coip/apps/invitation/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/coip/apps/invitation/__init__.py
diff --git a/coip/apps/invitation/admin.py b/coip/apps/invitation/admin.py
new file mode 100644
index 0000000..722b147
--- /dev/null
+++ b/coip/apps/invitation/admin.py
@@ -0,0 +1,4 @@
+from django.contrib import admin
+from coip.apps.invitation.models import Invitation
+
+admin.site.register(Invitation) \ No newline at end of file
diff --git a/coip/apps/invitation/forms.py b/coip/apps/invitation/forms.py
new file mode 100644
index 0000000..3370597
--- /dev/null
+++ b/coip/apps/invitation/forms.py
@@ -0,0 +1,11 @@
+'''
+Created on Jul 5, 2010
+
+@author: leifj
+'''
+from django import forms
+from coip.apps.invitation.models import Invitation
+
+class InvitationForm(forms.ModelForm):
+ class Meta:
+ model = Invitation \ No newline at end of file
diff --git a/coip/apps/invitation/models.py b/coip/apps/invitation/models.py
new file mode 100644
index 0000000..951fa59
--- /dev/null
+++ b/coip/apps/invitation/models.py
@@ -0,0 +1,34 @@
+'''
+Created on Jun 23, 2010
+
+@author: leifj
+'''
+
+from django.db import models
+from django.contrib.auth.models import User
+from coip.apps.membership.models import Membership
+from pprint import pprint
+from uuid import uuid4
+import datetime
+
+class Invitation(models.Model):
+ '''
+ Represents an invitation to an application
+ '''
+ sender = models.ForeignKey(User, unique=True)
+ membership = models.ForeignKey(Membership, unique=True)
+ timecreated = models.DateTimeField(auto_now_add=True)
+ lastupdated = models.DateTimeField(auto_now=True)
+ expires = models.DateTimeField()
+ token = models.TextField(unique=True)
+
+
+ def __init__(self):
+ self.token = uuid4()
+
+ def valid(self):
+ return datetime.date.today() > self.expires
+
+ def send_email(self):
+ pprint("sent email to "+self.to)
+ return \ No newline at end of file
diff --git a/coip/apps/invitation/views.py b/coip/apps/invitation/views.py
new file mode 100644
index 0000000..fe1c6af
--- /dev/null
+++ b/coip/apps/invitation/views.py
@@ -0,0 +1,33 @@
+'''
+Created on Jun 23, 2010
+
+@author: leifj
+'''
+from django.contrib.auth.decorators import login_required
+from apps.invitation.forms import InvitationForm
+from apps.invitation.models import Invitation
+from django.shortcuts import render_to_response
+from django.http import HttpResponseRedirect
+
+@login_required
+def create(request):
+ user = request.user
+ if request.method == 'POST':
+ form = InvitationForm(request.POST)
+ if form.is_valid():
+ to = form.cleaned_data["to"]
+ expires = form.cleaned_data["expires"]
+ invitation = Invitation(sender=user,to=to,expires=expires)
+ invitation.save()
+ invitation.send_email()
+ return HttpResponseRedirect("/user/home")
+ else:
+ form = InvitationForm({});
+
+ return render_to_response('apps/invitation/create.html')
+
+def accept(request,token):
+ user = request.user
+ invitation = Invitation.objects.get(token=token)
+
+ \ No newline at end of file
diff --git a/coip/apps/membership/__init__.py b/coip/apps/membership/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/coip/apps/membership/__init__.py
diff --git a/coip/apps/membership/admin.py b/coip/apps/membership/admin.py
new file mode 100644
index 0000000..a98efe7
--- /dev/null
+++ b/coip/apps/membership/admin.py
@@ -0,0 +1,4 @@
+from django.contrib import admin
+from coip.apps.membership.models import Membership
+
+admin.site.register(Membership) \ No newline at end of file
diff --git a/coip/apps/membership/forms.py b/coip/apps/membership/forms.py
new file mode 100644
index 0000000..9adab0d
--- /dev/null
+++ b/coip/apps/membership/forms.py
@@ -0,0 +1,11 @@
+'''
+Created on Jun 23, 2010
+
+@author: leifj
+'''
+from django import forms
+from coip.apps.membership.models import Membership
+
+class MembershipForm(forms.ModelForm):
+ class Meta:
+ model = Membership \ No newline at end of file
diff --git a/coip/apps/membership/models.py b/coip/apps/membership/models.py
new file mode 100644
index 0000000..df6e796
--- /dev/null
+++ b/coip/apps/membership/models.py
@@ -0,0 +1,23 @@
+'''
+Created on Jun 23, 2010
+
+@author: leifj
+'''
+from django.db import models
+from django.contrib.auth.models import User
+from coip.apps.name.models import Name
+import datetime
+
+class Membership(models.Model):
+ '''
+ Membership in a namespace/group
+ '''
+ user = models.ForeignKey(User,unique=True,blank=True)
+ enabled = models.BooleanField()
+ timecreated = models.DateTimeField(auto_now_add=True)
+ lastupdated = models.DateTimeField(auto_now=True)
+ expires = models.DateTimeField(blank=True)
+ name = models.ForeignKey(Name)
+
+ def valid(self):
+ return self.enabled and datetime.date.today() > self.expires
diff --git a/coip/apps/membership/views.py b/coip/apps/membership/views.py
new file mode 100644
index 0000000..7a06a21
--- /dev/null
+++ b/coip/apps/membership/views.py
@@ -0,0 +1,13 @@
+'''
+Created on Jun 23, 2010
+
+@author: leifj
+'''
+from django.contrib.auth.decorators import login_required
+from coip.apps.membership.models import Membership
+
+@login_required
+def memberships(request,name):
+
+ Membership.objects.get(name)
+ \ No newline at end of file
diff --git a/coip/apps/name/__init__.py b/coip/apps/name/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/coip/apps/name/__init__.py
diff --git a/coip/apps/name/admin.py b/coip/apps/name/admin.py
new file mode 100644
index 0000000..477da76
--- /dev/null
+++ b/coip/apps/name/admin.py
@@ -0,0 +1,5 @@
+from django.contrib import admin
+from coip.apps.name.models import Name, Attribute
+
+admin.site.register(Name)
+admin.site.register(Attribute) \ No newline at end of file
diff --git a/coip/apps/name/forms.py b/coip/apps/name/forms.py
new file mode 100644
index 0000000..0c96480
--- /dev/null
+++ b/coip/apps/name/forms.py
@@ -0,0 +1,15 @@
+'''
+Created on Jun 24, 2010
+
+@author: leifj
+'''
+from django import forms
+from coip.apps.name.models import Name, Attribute
+
+class NameForm(forms.ModelForm):
+ class Meta:
+ model = Name
+
+class AttributeForm(forms.ModelForm):
+ class Meta:
+ model = Attribute \ No newline at end of file
diff --git a/coip/apps/name/models.py b/coip/apps/name/models.py
new file mode 100644
index 0000000..ec86769
--- /dev/null
+++ b/coip/apps/name/models.py
@@ -0,0 +1,73 @@
+'''
+Created on Jun 24, 2010
+
+@author: leifj
+'''
+from django.db import models
+from django.contrib.auth.models import User
+import re
+
+class Attribute(models.Model):
+ name = models.CharField(unique=True,max_length=255)
+ description = models.TextField(blank=True)
+ timecreated = models.DateTimeField(auto_now_add=True)
+ lastupdated = models.DateTimeField(auto_now=True)
+
+ def __unicode__(self):
+ return self.name;
+
+class Name(models.Model):
+ '''
+ A name-space/authorization/right/group/collaboration/thing
+ '''
+ type = models.ForeignKey(Attribute, blank=True, null=True,related_name='names')
+ value = models.CharField(max_length=255)
+ parent = models.ForeignKey('self', blank=True, null=True,related_name='children')
+ partof = models.ForeignKey('self', blank=True, null=True,related_name='parts')
+ acl = models.TextField(blank=True) # fully-qualified-name '#' rights
+ description = models.TextField(blank=True)
+ creator = models.ForeignKey(User)
+ timecreated = models.DateTimeField(auto_now_add=True)
+ lastupdated = models.DateTimeField(auto_now=True)
+
+ def relative_name(self):
+ if self.type:
+ return "%s=%s" % (self.type.name,self.value)
+ else:
+ return self.value
+
+ def __unicode__(self):
+ n = self
+ str = ""
+ while n:
+ sep = ""
+ av = n.relative_name()
+
+ if n.parent:
+ if av.find("=") == -1:
+ sep = ':'
+ else:
+ sep = ';'
+
+ str = sep+av+str
+ n = n.parent
+
+ return str
+
+def walkto(root,nameparts):
+ name = None
+ for n in nameparts:
+ (a,eq,v) = n.partition('=')
+ if v:
+ attribute = Attribute.objects.get(name=a)
+ name = Name.objects.get(parent=root,type=attribute.id,value=v)
+ else:
+ name = Name.objects.get(parent=root,type=None,value=a)
+ return name
+
+def lookup(name):
+ return walkto(None,nameparts=re.compile('[;:]').split(name))
+
+def attribute(a):
+ Attribute.objects.get_or_create(name=a)
+ \ No newline at end of file
diff --git a/coip/apps/name/views.py b/coip/apps/name/views.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/coip/apps/name/views.py
diff --git a/coip/mimeparse.py b/coip/mimeparse.py
new file mode 100644
index 0000000..0fd91e7
--- /dev/null
+++ b/coip/mimeparse.py
@@ -0,0 +1,123 @@
+"""MIME-Type Parser
+
+This module provides basic functions for handling mime-types. It can handle
+matching mime-types against a list of media-ranges. See section 14.1 of
+the HTTP specification [RFC 2616] for a complete explanation.
+
+ http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
+
+Contents:
+ - parse_mime_type(): Parses a mime-type into its component parts.
+ - parse_media_range(): Media-ranges are mime-types with wild-cards and a 'q' quality parameter.
+ - quality(): Determines the quality ('q') of a mime-type when compared against a list of media-ranges.
+ - quality_parsed(): Just like quality() except the second parameter must be pre-parsed.
+ - best_match(): Choose the mime-type with the highest quality ('q') from a list of candidates.
+"""
+
+__version__ = "0.1.2"
+__author__ = 'Joe Gregorio'
+__email__ = "joe@bitworking.org"
+__credits__ = ""
+
+def parse_mime_type(mime_type):
+ """Carves up a mime-type and returns a tuple of the
+ (type, subtype, params) where 'params' is a dictionary
+ of all the parameters for the media range.
+ For example, the media range 'application/xhtml;q=0.5' would
+ get parsed into:
+
+ ('application', 'xhtml', {'q', '0.5'})
+ """
+ parts = mime_type.split(";")
+ params = dict([tuple([s.strip() for s in param.split("=")])\
+ for param in parts[1:] ])
+ full_type = parts[0].strip()
+ # Java URLConnection class sends an Accept header that includes a single "*"
+ # Turn it into a legal wildcard.
+ if full_type == '*': full_type = '*/*'
+ (type, subtype) = full_type.split("/")
+ return (type.strip(), subtype.strip(), params)
+
+def parse_media_range(range):
+ """Carves up a media range and returns a tuple of the
+ (type, subtype, params) where 'params' is a dictionary
+ of all the parameters for the media range.
+ For example, the media range 'application/*;q=0.5' would
+ get parsed into:
+
+ ('application', '*', {'q', '0.5'})
+
+ In addition this function also guarantees that there
+ is a value for 'q' in the params dictionary, filling it
+ in with a proper default if necessary.
+ """
+ (type, subtype, params) = parse_mime_type(range)
+ if not params.has_key('q') or not params['q'] or \
+ not float(params['q']) or float(params['q']) > 1\
+ or float(params['q']) < 0:
+ params['q'] = '1'
+ return (type, subtype, params)
+
+def fitness_and_quality_parsed(mime_type, parsed_ranges):
+ """Find the best match for a given mime-type against
+ a list of media_ranges that have already been
+ parsed by parse_media_range(). Returns a tuple of
+ the fitness value and the value of the 'q' quality
+ parameter of the best match, or (-1, 0) if no match
+ was found. Just as for quality_parsed(), 'parsed_ranges'
+ must be a list of parsed media ranges. """
+ best_fitness = -1
+ best_fit_q = 0
+ (target_type, target_subtype, target_params) =\
+ parse_media_range(mime_type)
+ for (type, subtype, params) in parsed_ranges:
+ if (type == target_type or type == '*' or target_type == '*') and \
+ (subtype == target_subtype or subtype == '*' or target_subtype == '*'):
+ param_matches = reduce(lambda x, y: x+y, [1 for (key, value) in \
+ target_params.iteritems() if key != 'q' and \
+ params.has_key(key) and value == params[key]], 0)
+ fitness = (type == target_type) and 100 or 0
+ fitness += (subtype == target_subtype) and 10 or 0
+ fitness += param_matches
+ if fitness > best_fitness:
+ best_fitness = fitness
+ best_fit_q = params['q']
+
+ return best_fitness, float(best_fit_q)
+
+def quality_parsed(mime_type, parsed_ranges):
+ """Find the best match for a given mime-type against
+ a list of media_ranges that have already been
+ parsed by parse_media_range(). Returns the
+ 'q' quality parameter of the best match, 0 if no
+ match was found. This function bahaves the same as quality()
+ except that 'parsed_ranges' must be a list of
+ parsed media ranges. """
+ return fitness_and_quality_parsed(mime_type, parsed_ranges)[1]
+
+def quality(mime_type, ranges):
+ """Returns the quality 'q' of a mime-type when compared
+ against the media-ranges in ranges. For example:
+
+ >>> quality('text/html','text/*;q=0.3, text/html;q=0.7, text/html;level=1, text/html;level=2;q=0.4, */*;q=0.5')
+ 0.7
+
+ """
+ parsed_ranges = [parse_media_range(r) for r in ranges.split(",")]
+ return quality_parsed(mime_type, parsed_ranges)
+
+def best_match(supported, header):
+ """Takes a list of supported mime-types and finds the best
+ match for all the media-ranges listed in header. The value of
+ header must be a string that conforms to the format of the
+ HTTP Accept: header. The value of 'supported' is a list of
+ mime-types.
+
+ >>> best_match(['application/xbel+xml', 'text/xml'], 'text/*;q=0.5,*/*; q=0.1')
+ 'text/xml'
+ """
+ parsed_header = [parse_media_range(r) for r in header.split(",")]
+ weighted_matches = [(fitness_and_quality_parsed(mime_type, parsed_header), mime_type)\
+ for mime_type in supported]
+ weighted_matches.sort()
+ return weighted_matches[-1][0][1] and weighted_matches[-1][1] or ''
diff --git a/coip/multiresponse.py b/coip/multiresponse.py
new file mode 100644
index 0000000..9a3d965
--- /dev/null
+++ b/coip/multiresponse.py
@@ -0,0 +1,78 @@
+import coip.mimeparse as mimeparse
+import re
+from django.conf import settings
+from django.shortcuts import render_to_response
+from django.core.exceptions import ObjectDoesNotExist
+from datetime import datetime
+from pprint import pprint
+from django.http import HttpResponse
+from django.core import serializers
+
+default_suffix_mapping = {"\.htm(l?)$": "text/html",
+ "\.json$": "application/json",
+ "\.rss$": "application/rss+xml",
+ "\.torrent$": "application/x-bittorrent"}
+
+def _accept_types(request, suffix):
+ for r in suffix.keys():
+ p = re.compile(r)
+ if p.search(request.path):
+ return suffix.get(r)
+ return None
+
+
+def timeAsrfc822 ( theTime ) :
+ import rfc822
+ return rfc822 . formatdate ( rfc822 . mktime_tz ( rfc822 . parsedate_tz ( theTime . strftime ( "%a, %d %b %Y %H:%M:%S" ) ) ) )
+
+def make_response_dict(request,d={}):
+
+ if request.user.is_authenticated():
+ d['user'] = request.user
+ profile = None
+ try:
+ profile = request.user.profile.get();
+ except ObjectDoesNotExist:
+ profile = UserProfile()
+ d['profile'] = profile
+
+ #d['stomp_host'] = STOMP_HOST
+ #d['stomp_port'] = STOMP_PORT
+ #d['orbited_prefix'] = ORBITED_PREFIX
+ #d['announce_url'] = ANNOUNCE_URL
+ #d['date'] = timeAsrfc822(datetime.now())
+ #if DEBUG is not None:
+ # d['debug'] = True
+
+ return d
+
+def json_response(data):
+ json_serializer = serializers.get_serializer("json")()
+ json_serializer.serialize()
+ json_serializer.serialize(data)
+
+ r = HttpResponse(data.getvalue(),content_type='application/json')
+ r['Cache-Control'] = 'no-cache, must-revalidate'
+ r['Pragma'] = 'no-cache'
+
+ return r
+
+def respond_to(request, template_mapping, dict={}, suffix_mapping=default_suffix_mapping):
+ accept = _accept_types(request, suffix_mapping)
+ if accept is None:
+ accept = (request.META['HTTP_ACCEPT'].split(','))[0]
+ content_type = mimeparse.best_match(template_mapping.keys(), accept)
+ template = None
+ if template_mapping.has_key(content_type):
+ template = template_mapping[content_type]
+ else:
+ template = template_mapping["text/html"]
+ if callable(template):
+ response = template(make_response_dict(request,dict))
+ elif isinstance(template, HttpResponse):
+ response = template
+ response['Content-Type'] = "%s; charset=%s" % (content_type, settings.DEFAULT_CHARSET)
+ else:
+ response = render_to_response(template,make_response_dict(request,dict))
+ response['Content-Type'] = "%s; charset=%s" % (content_type, settings.DEFAULT_CHARSET)
+ return response
diff --git a/coip/urls.py b/coip/urls.py
new file mode 100644
index 0000000..d24cc15
--- /dev/null
+++ b/coip/urls.py
@@ -0,0 +1,17 @@
+from django.conf.urls.defaults import *
+
+# Uncomment the next two lines to enable the admin:
+from django.contrib import admin
+admin.autodiscover()
+
+urlpatterns = patterns('',
+ # Example:
+ # (r'^coip/', include('coip.foo.urls')),
+
+ # Uncomment the admin/doc line below and add 'django.contrib.admindocs'
+ # to INSTALLED_APPS to enable admin documentation:
+ # (r'^admin/doc/', include('django.contrib.admindocs.urls')),
+
+ # Uncomment the next line to enable the admin:
+ (r'^admin/', include(admin.site.urls)),
+)