Permalink
Browse files

Get storage of pairs working

  • Loading branch information...
1 parent 02117d5 commit 6aa2776b8dc8c92092ebc735c62212db0d01b716 @harryf committed May 26, 2008
Showing with 184 additions and 21 deletions.
  1. +1 −1 dammit/request.py
  2. +48 −7 dammit/resource.py
  3. +135 −13 urldammit.py
View
@@ -47,7 +47,7 @@ def unpack_pairs(s):
next
if not is_scalar(v):
next
- out[k] = [v]
+ out[k] = v
return out
View
@@ -4,6 +4,7 @@
from couchdb.schema import *
from datetime import datetime
from dammit.couchutils import *
+import logging
alphanum = re.compile('^[a-zA-Z0-9]$')
@@ -17,12 +18,32 @@ class URI(Document):
status = IntegerField()
created = DateTimeField(default=datetime.now)
updated = DateTimeField(default=datetime.now)
- accessed = DateTimeField(default=datetime.now)
+ #accessed = DateTimeField(default=datetime.now)
tags = ListField(TextField())
- pairs = ListField(
- DictField(
- Schema.build(key = TextField(), value = TextField()))
- )
+ pairs = ListField(DictField(
+ Schema.build(k = TextField(), v = TextField())))
+
+
+def equal(a, b):
+ """
+ equality to handle quirks of python-couchdb Document
+ objects
+ """
+ logging.debug("Comparing %s with %s" % ( a, b ))
+ if type(a) == list:
+ return tuple(sorted(a)) == tuple(sorted(b))
+ if type(a) == dict:
+ return tuple([(k, a[k]) for k in sorted(a.keys())])\
+ == tuple([(k, b[k]) for k in sorted(b.keys())])
+ return a == b
+
+def expand_dict(d):
+ items = []
+ for k, v in d.items():
+ items.append({'k':k, 'v':v})
+
+ logging.debug("expanded dict - before '%s', after '%s'" % ( d, items ))
+ return items
def register_uri(db, uri, status = 200, **kwargs):
"""
@@ -31,9 +52,22 @@ def register_uri(db, uri, status = 200, **kwargs):
"""
id = uri_to_id(uri)
r = URI.load(db, id)
+
+ logging.debug("Looking for doc with id %s [%s]" % ( id, uri ))
if r:
+ logging.debug("Found it")
+
+ # once it's status is in this range, it becomes
+ # immutable
+ if 300 <= r.status < 400:
+ logging.debug("Redirect (immutable)")
+ return r
+
kwargs['status'] = status
+
+ if kwargs.has_key('pairs'):
+ kwargs['pairs'] = expand_dict(kwargs['pairs'])
now = datetime.now()
updated = False
@@ -50,10 +84,17 @@ def register_uri(db, uri, status = 200, **kwargs):
# r.accessed = now
if updated:
+ logging.debug("Storing changes")
r.store(db)
else:
- r = URI(id = id, uri = uri, status = status, **kwargs)
- put(db, r)
+ if 200 <= status < 300:
+ logging.debug("Not found - storing")
+ if kwargs.has_key('pairs'):
+ kwargs['pairs'] = expand_dict(kwargs['pairs'])
+ r = URI(id = id, uri = uri, status = status, **kwargs)
+ put(db, r)
+ else:
+ logging.debug("Not found but status is %s - ignoring" % status)
return r
View
@@ -1,67 +1,189 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import web
+import urllib2
+import logging
from dammit.couchutils import uri_to_id, put
from dammit.resource import *
+from dammit.lrucache import LRUCache
from couchdb import Database
+from dammit.http import status
+from dammit.request import unpack_tags, unpack_pairs, pack_response
urls = (
'/', 'urldammit',
'/_tools', 'tools',
'/(.*)', 'urldammit',
-
)
db = Database('http://localhost:5984/urldammit')
+# cache URIs we know about
+known = LRUCache(1000)
+
+# cache queries we know nothing about otherwise
+# we have to ask couch each time
+unknown = LRUCache(100)
+
class urldammit:
def HEAD(self, uri):
r = self._locate(uri)
if not r: return
if self._redirect(r): return
-
+ self._ok(r)
-
def GET(self, uri = None):
-
if not uri:
print "where's my url dammit?"
return
r = self._locate(uri)
+
if not r: return
if self._redirect(r): return
-
- print r
+
+ self._ok(r)
+ self._render(r)
def POST(self):
+ """
+ Update couch with status of uri
+ """
i = web.input()
- register_uri(db, i.uri)
+
+ uri = self._reqd(i, 'uri')
+ if uri is None: return
+
+ status = self._reqd(i, 'status')
+ if status is None: return
+ # todo validate numeric
+
+ try:
+ logging.debug('tags: %s', i.tags)
+ except:
+ logging.debug('tags not found')
+
+ tags = getattr(i, 'tags', [])
+ if tags:
+ tags = unpack_tags(tags)
+
+ logging.debug('tags unpacked: %s', tags)
+
+ try:
+ logging.debug('pairs: %s', i.tags)
+ except:
+ logging.debug('pairs not found')
+ pairs = getattr(i, 'pairs', [])
+ logging.debug(pairs)
+ if pairs:
+ pairs = unpack_pairs(pairs)
+
+ logging.debug('pairs unpacked: %s', pairs)
+
+ r = register_uri(db, uri = uri, status = int(status),\
+ tags = tags, pairs = pairs )
+ if r: known[id] = r
+ web.http.seeother("%s/%s" % ( web.ctx.home, urllib2.quote(i.uri)) )
+ self._render(r)
+
+
+ def DELETE(self):
+ i = web.input()
+ uri = self._reqd(i, 'uri')
+ if not uri: return
+ return
def _locate(self, uri):
- r = URI.load(db, uri_to_id(uri))
- if not r:
+ """
+ See what we know about this uri...
+ """
+ id = uri_to_id(uri)
+
+ def ithas(key, cache):
+ if key in cache:
+ return cache[key]
+ return None
+
+ u = ithas(id, unknown)
+ if u:
web.notfound()
return None
+
+ r = ithas(id, known)
+ if not r:
+ r = URI.load(db, id)
+
+ if not r:
+ unknown[id] = True
+ web.notfound()
+ return None
+
+ known[id] = r
+
return r
+ def _ok(self, r):
+ web.ctx.status = status[200]
+ web.http.lastmodified(r.updated)
+
def _redirect(self, r):
+ """
+ couch reports this url is now elsewhere
+ return a redirect response
+ """
if 300 <= r.status < 400:
- web.ctx.status = r.status
- web.header('Location', r.location)
- return True
+ if r.status in status:
+ web.ctx.status = status[r.status]
+ web.header('Location', r.location)
+ return True
return False
+ def _reqd(self, input, key):
+ val = None
+ try:
+ val = getattr(input,key)
+ except AttributeError:
+ web.ctx.status = status[406]
+ print "%s parameter required" % input
+ return val
+
+ def _render(self, r):
+ if not r:
+ return
+
+ response = {
+ 'uri': r.uri,
+ 'status': r.status,
+ 'created': str(r.created),
+ 'updated': str(r.updated),
+ 'tags': r.tags,
+ 'pairs': r.pairs.list,
+ }
+
+ response['id'] = uri_to_id(r.uri)
+
+ if r.location:
+ response['location'] = r.location
+ else:
+ response['location'] = r.uri
+
+ print pack_response(response)
+
class tools:
def GET(self):
print '<form action="/" method="POST">'
- print '<label for="uri">uri:</label><input name="uri" type="text">'
+ print '<label for="uri">uri:</label><input name="uri" type="text"><br>'
+ print '<label for="status">status:</label><input name="status" type="text"><br>'
+ print '<label for="tags">tags:</label><input name="tags" type="text"><br>'
+ print '<label for="pairs">pairs:</label><input name="pairs" type="text"><br>'
print '<input type="submit" value="submit">'
print '</form>'
if __name__ == '__main__':
+ import logging
+ logging.basicConfig(level=logging.DEBUG)
web.run(urls, globals() )

0 comments on commit 6aa2776

Please sign in to comment.