"""Annotation storage.
TODO:
1. Move to templates for annotater store forms (?)
"""
import os
import logging
import paste.request
from routes import *
# annotater stuff
import annotater.model
class AnnotaterStore(object):
"Application to provide 'annotation' store."
DEBUG = True
def __init__(self, service_path='annotation'):
"""Create the WSGI application.
@param service_path: offset url where this application is mounted.
Should be specified without leading or trailing '/'.
"""
self.service_path = service_path
self.logger = self.setup_logging()
def get_routes_mapper(self):
# some extra additions to standard layout from map.resource
# help to make the url layout a bit nicer for those using the web ui
map = Mapper()
map.connect(self.service_path + '/delete/:id',
controller='annotation',
action='delete',
conditions=dict(method=['GET']))
map.connect(self.service_path + '/edit/:id',
controller='annotation',
action='edit',
conditions=dict(method=['GET']))
map.resource(self.service_path, self.service_path)
# map.resource assumes PUT for update but marginalias uses POST the
# exact mappings for REST seems a hotly contested matter see e.g.
# http://www.megginson.com/blogs/quoderat/archives/2005/04/03/post-in-rest-create-update-or-action/
# must have this *after* map.resource as otherwise overrides the create
# action
map.connect(self.service_path + '/:id',
controller='annotation',
action='update',
conditions=dict(method=['POST']))
return map
def setup_logging(self):
level = logging.DEBUG
logger = logging.getLogger('annotater')
logger.setLevel(level)
log_file_path = 'annotater-debug.log'
fh = logging.FileHandler(log_file_path, 'w')
fh.setLevel(level)
logger.addHandler(fh)
logger.info('START LOGGING')
return logger
def __call__(self, environ, start_response):
self.environ = environ
self.map = self.get_routes_mapper()
self.map.environ = environ
self.start_response = start_response
self.path = environ['PATH_INFO']
self.mapdict = self.map.match(self.path)
action = self.mapdict['action']
self.anno_schema = annotater.model.AnnotationSchema()
if action != 'delete':
self.query_vals = paste.request.parse_formvars(self.environ)
else:
# DELETE from js causes paste.request.parse_formvars to hang since
# we do not need it for delete if using clean urls just skip
# TODO: find out why this is and fix it.
self.query_vals = {}
if self.DEBUG:
self.logger.debug('** CALL TO ANNOTATE')
self.logger.debug('path: %s' % self.path)
self.logger.debug('environ: %s' % environ)
self.logger.debug('mapdict: %s' % self.mapdict)
self.logger.debug('query_vals: %s' % self.query_vals)
if action == 'index':
return self.index()
elif action == 'new':
return self.new()
elif action == 'create':
return self.create()
elif action == 'edit':
return self.edit()
elif action == 'update':
return self.update()
elif action == 'delete':
return self.delete()
else:
status = '404 Not Found'
response_headers = [ ('Content-type', 'text/plain'), ]
self.start_response(status, response_headers)
return ['Not found or method not allowed']
def _make_annotate_form(self, form_name, action_url, form_defaults):
from formencode import htmlfill
keys = [ 'url' , 'range', 'note' ]
vals = {}
for key in keys:
vals[key] = form_defaults.get(key, '')
formfields = ''
for key in keys:
formfields += \
'''
''' % (key, key, key, key)
form = \
'''
No such annotation #%s
' % id return [msg] anno = annotater.model.Annotation.get(id) posturl = self.map.generate(controller='annotation', action='update', id=anno.id, method='POST') print 'Post url:', posturl form_defaults = self.anno_schema.from_python(anno) form = self._make_annotate_form('annotate_edit', posturl, form_defaults) status = '200 OK' response_headers = [ ('Content-type', 'text/html'), ] self.start_response(status, response_headers) return [ form ] def update(self): id = self.mapdict['id'] try: id = int(id) except: status = '400 Bad Request' response_headers = [ ('Content-type', 'text/html'), ] self.start_response(status, response_headers) msg = 'No such annotation #%s
' % id return [msg] new_values = dict(self.query_vals) new_values['id'] = id if new_values.has_key('submission'): # comes from post del new_values['submission'] else: # comes direct from js # as comes from js need to add in the existing values for to_python # this is a bit of a hack but it the easiest way i can think of to # merge the values current = annotater.model.Annotation.get(id) current_defaults = self.anno_schema.from_python(current) for key in current_defaults.keys(): if not new_values.has_key(key): new_values[key] = current_defaults[key] # remove attributes coming from js that we do not yet support del new_values['access'] anno_edited = self.anno_schema.to_python(new_values) status = '204 Updated' response_headers = [] self.start_response(status, response_headers) return [''] def delete(self): id = self.mapdict['id'] response_headers = [ ('Content-type', 'text/html'), ] if id is None: status = '400 Bad Request' response_headers = [ ('Content-type', 'text/html'), ] self.start_response(status, response_headers) return ['Bad ID
'] else: status = '204 Deleted' try: id = int(id) annotater.model.Annotation.delete(id) self.start_response(status, response_headers) return [] except: status = '500 Internal server error' self.start_response(status, response_headers) return ['