diff options
author | Leif Johansson <leifj@sunet.se> | 2010-07-05 21:37:04 +0200 |
---|---|---|
committer | Leif Johansson <leifj@sunet.se> | 2010-07-05 21:37:04 +0200 |
commit | 8f7f4ff3a35ddeacea7d5530c7dc3c296907ab67 (patch) | |
tree | 7d481dc7030ad191314aa2569cca9f8301e1f997 /coip |
import
Diffstat (limited to 'coip')
-rw-r--r-- | coip/__init__.py | 0 | ||||
-rw-r--r-- | coip/apps/__init__.py | 0 | ||||
-rw-r--r-- | coip/apps/invitation/__init__.py | 0 | ||||
-rw-r--r-- | coip/apps/invitation/admin.py | 4 | ||||
-rw-r--r-- | coip/apps/invitation/forms.py | 11 | ||||
-rw-r--r-- | coip/apps/invitation/models.py | 34 | ||||
-rw-r--r-- | coip/apps/invitation/views.py | 33 | ||||
-rw-r--r-- | coip/apps/membership/__init__.py | 0 | ||||
-rw-r--r-- | coip/apps/membership/admin.py | 4 | ||||
-rw-r--r-- | coip/apps/membership/forms.py | 11 | ||||
-rw-r--r-- | coip/apps/membership/models.py | 23 | ||||
-rw-r--r-- | coip/apps/membership/views.py | 13 | ||||
-rw-r--r-- | coip/apps/name/__init__.py | 0 | ||||
-rw-r--r-- | coip/apps/name/admin.py | 5 | ||||
-rw-r--r-- | coip/apps/name/forms.py | 15 | ||||
-rw-r--r-- | coip/apps/name/models.py | 73 | ||||
-rw-r--r-- | coip/apps/name/views.py | 0 | ||||
-rw-r--r-- | coip/mimeparse.py | 123 | ||||
-rw-r--r-- | coip/multiresponse.py | 78 | ||||
-rw-r--r-- | coip/urls.py | 17 |
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)), +) |