From 9b2bbfd447c3fd7b71b325cbcfbc902861045f07 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Sat, 17 Jul 2010 11:34:50 -0400 Subject: [PATCH] Move dirtag.xul and *.rdf generation into CherryPy. Now the intial data displayed by dirtag.xul matches the on-disk raw/tag structure. I made a few changes in node handling so traversals would have all the information required (Node.root, Node.type, Node.tags, and associated handlers). --- dirtag/__init__.py | 98 ++++++++++++++----- dirtag/web.py | 160 ++++++++++++++++++++++++++++++++ static/raw-tree.rdf | 61 ------------ static/tag-tree.rdf | 38 -------- {static => template}/dirtag.xul | 12 +-- 5 files changed, 239 insertions(+), 130 deletions(-) create mode 100755 dirtag/web.py delete mode 100644 static/raw-tree.rdf delete mode 100644 static/tag-tree.rdf rename {static => template}/dirtag.xul (94%) diff --git a/dirtag/__init__.py b/dirtag/__init__.py index 406c286..cb69eeb 100644 --- a/dirtag/__init__.py +++ b/dirtag/__init__.py @@ -20,6 +20,24 @@ import os import os.path +class Node (list): + def __init__(self, root, type, tags, *args, **kwargs): + list.__init__(self, *args, **kwargs) + self.root = root + self.type = type + self.tags = tags + + def pre_extend(self, a): + """ + >>> n = Node(None, None, None, ['a', 'b', 'c']) + >>> n.pre_extend([1, 2, 3]) + >>> print n + [1, 2, 3, 'a', 'b', 'c'] + """ + for i,element in enumerate(a): + self.insert(i, element) + + class Tree (list): """ >>> t = Tree('a') @@ -36,25 +54,26 @@ class Tree (list): z|a|1 z|a|1|A z|a|2 - >>> print '\\n'.join(['|'.join(x) for x in t.traverse(depth=1)]) - 1 - 1|A - 2 """ - def __init__(self, data=None, *args, **kwargs): + def __init__(self, data=None, root=None, *args, **kwargs): self.data = data + self.root = root list.__init__(self, *args, **kwargs) - def traverse(self, prefix=[], depth=0): + def traverse(self, prefix=[], depth=0, type='both', dirtag=None): p = prefix + [self.data] - if depth < len(p): - yield p[depth:] + if depth < len(p) and type in ['both', 'dirs']: + yield Node(self.root, 'dir', [], p[depth:]) for child in self: if hasattr(child, 'traverse'): - for grandchild in child.traverse(p, depth=depth): + for grandchild in child.traverse( + p, depth=depth, type=type, dirtag=dirtag): yield grandchild - elif depth <= len(p): - yield (p + [child])[depth:] + elif depth <= len(p) and type in ['both', 'files']: + n = Node(self.root, 'file', [], (p + [child])[depth:]) + if dirtag != None: + n.tags = dirtag.node_tags(n) + yield n def dir_tree(root_dir): @@ -70,6 +89,16 @@ def dir_tree(root_dir): x|y|b1.dat x|a2.dat x|b3.dat + >>> print '\\n'.join(['|'.join(x) for x in t.traverse(depth=1, type='files')]) + 1|a1.dat + 1|b1.dat + x|y|b1.dat + x|a2.dat + x|b3.dat + >>> print '\\n'.join(['|'.join(x) for x in t.traverse(depth=1, type='dirs')]) + 1 + x + x|y """ if not os.path.exists(root_dir): raise ValueError(root_dir) @@ -77,11 +106,11 @@ def dir_tree(root_dir): for dirpath,dirnames,filenames in os.walk(root_dir, followlinks=True): t = trees.get( dirpath, - Tree(os.path.basename(dirpath))) + Tree(os.path.basename(dirpath), root=root_dir)) if dirpath == root_dir: root = t for d in sorted(dirnames): - t2 = Tree(d) + t2 = Tree(d, root=root_dir) trees[os.path.join(dirpath, d)] = t2 t.append(t2) t.extend(sorted(filenames)) @@ -91,15 +120,18 @@ def dir_tree(root_dir): class Dirtag (object): """ >>> d = Dirtag('test/raw', 'test/tag') - >>> print '\\n'.join(['|'.join(x) for x in d.elements()]) - a - a|a1.dat - a|a2.dat - a|a3.dat - b - b|b1.dat - b|b2.dat - b|b3.dat + >>> print '\\n'.join(['\t'.join([x.type, x.root, '|'.join(x), + ... ','.join(['/'.join(t) for t in x.tags])]) + ... for x in d.elements()] + ... ) # doctest: +NORMALIZE_WHITESPACE + dir test/raw a + file test/raw a|a1.dat 1 + file test/raw a|a2.dat x + file test/raw a|a3.dat + dir test/raw b + file test/raw b|b1.dat 1,x/y + file test/raw b|b2.dat + file test/raw b|b3.dat x >>> print '\\n'.join(['|'.join(x) for x in d.elements(['x'])]) x|y x|y|b1.dat @@ -131,13 +163,14 @@ class Dirtag (object): def elements(self, tag=None): if tag == None: tag = [] - nodes = dir_tree(self.raw_dir).traverse(depth=1) + nodes = dir_tree(self.raw_dir).traverse(depth=1, dirtag=self) else: p = [self.tag_dir]+tag nodes = dir_tree(os.path.join(*p)).traverse( - prefix=p[:-1], depth=len(p)) + prefix=p[:-1], depth=len(p), dirtag=self) for node in nodes: - yield tag+node + node.pre_extend(tag) + yield node def tags(self, target): for node in dir_tree(self.tag_dir).traverse(depth=1): @@ -170,6 +203,21 @@ class Dirtag (object): finally: os.chdir(cwd) + def raw_node(self, node): + """Return a node for the real file linked to by node. + """ + if node.root == self.raw_dir: + return node + path = os.path.realpath(os.path.join(node.root, *node)) + raw_path = os.path.relpath(path, self.raw_dir) + return raw_path.split(os.path.sep) + + def node_tags(self, node): + """Return a list of tags for a particular node. + """ + return self.tags(self.raw_node(node)) + + if __name__ == '__main__': import doctest doctest.testmod() diff --git a/dirtag/web.py b/dirtag/web.py new file mode 100755 index 0000000..fee3bb8 --- /dev/null +++ b/dirtag/web.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python + +from optparse import OptionParser +from os import path +from urllib import urlencode + +import cherrypy +from jinja2 import Environment, FileSystemLoader + +from dirtag import Dirtag, Tree, dir_tree + + +class WebInterface: + """The web interface to Dirtag. + """ + def __init__(self, dirtag, template_dir, repository_name='Dirtag'): + """Initialize the bug repository for this web interface.""" + self.dirtag = dirtag + self.env = Environment(loader=FileSystemLoader(template_dir)) + self.repository_name = repository_name + self.rdf_root = 'http://dirtag.com/' + + @cherrypy.expose + def index(self): + template = self.env.get_template('dirtag.xul') + cherrypy.response.headers['Content-Type'] = \ + 'application/vnd.mozilla.xul+xml' + return template.render(repository_name=self.repository_name) + + @cherrypy.expose + def raw_rdf(self): + return self.tree_rdf(self.dirtag.raw_dir, 'raw') + + @cherrypy.expose + def tag_rdf(self): + return self.tree_rdf(self.dirtag.tag_dir, 'tag', with_files=False) + + def tree_rdf(self, root_dir, name, with_files=True): + cherrypy.response.headers['Content-Type'] = \ + 'application/rdf+xml' + tree = dir_tree(root_dir) + lines = [ + '', + '', + '' % self.rdf_root, + ] + if with_files == True: + nodes = tree.traverse(depth=1, dirtag=self.dirtag) + else: + nodes = tree.traverse(depth=1, type='dirs', dirtag=self.dirtag) + for node in nodes: + lines.extend([ + ' ' + % (self.rdf_root, '/'.join([name]+node)), + ' %s' % node[-1], + ]) + if node.type == 'file': + lines.append(' %s' + % ','.join(['/'.join(t) for t in node.tags])) + lines.append(' ') + + lines.append(' ' + % (self.rdf_root, name)) + stack = [] + for node in tree.traverse(depth=1,): + while (len(stack) > 0 and + not self.list_startswith(stack[-1], node[:-1])): + lines.extend([ + ' '*(4*len(stack)+2) + '', + ' '*(4*len(stack)) + '', + ]) + stack.pop() + assert len(node) == len(stack)+1, ( + 'Floating %s -> %s (%d -> %d)' + % (stack[-1], node, len(stack), len(node))) + stack.append(node) + if node.type == 'file': + raw_node = self.dirtag.raw_node(node) + lines.append(' '*(4*len(stack)) + + '' + % (self.rdf_root, '/'.join(['raw']+raw_node))) + stack.pop() + else: + lines.extend([ + ' '*(4*len(stack)) + '', + ' '*(4*len(stack)+2) + '' + % (self.rdf_root, '/'.join([name]+node)), + ]) + while len(stack) > 0: + lines.extend([ + ' '*(4*len(stack)+2) + '', + ' '*(4*len(stack)) + '', + ]) + stack.pop() + lines.extend([ + ' ', + '', + '', + '', + ]) + return '\n'.join(lines) + + def list_startswith(self, a, b): + """ + >>> x = WebInterface(None, 'template') + >>> x.list_startswith([1, 2, 3], [1, 2, 3, 4]) + True + >>> x.list_startswith([1, 2, 3], [1, 2, 3]) + True + >>> x.list_startswith([1, 2, 3], [1, 2]) + False + >>> x.list_startswith([1, 2, 3], [2, 2, 3]) + False + """ + return len([x for x,y in zip(a,b) if x==y]) == len(a) + + #raise cherrypy.HTTPRedirect('/static/dirtag.xul', status=302) + +if __name__ == '__main__': + parser = OptionParser('%prog [options]', epilog=WebInterface.__doc__) + parser.add_option('-r', '--raw-dir', dest='raw_dir', metavar='DIR', + default='raw', + help='Location of raw files (%default)') + parser.add_option('-t', '--tag-dir', dest='tag_dir', metavar='DIR', + default='tag', + help='Location of tag links (%default)') + parser.add_option('-T', '--template-dir', dest='template_dir', + metavar='DIR', default='template', + help='Location of template files (%default)') + parser.add_option('-s', '--static-dir', dest='static_dir', metavar='DIR', + default='static', + help='Location of static files (%default)') + parser.add_option('-n', '--name', dest='name', metavar='STRING', + default='dirtag', + help='Name of the dirtag repository') + parser.add_option('--test', dest='test', default=False, + action='store_true', + help='Run unittests and exit') + options,args = parser.parse_args() + + if options.test: + import doctest, sys + doctest.testmod() + sys.exit(0) + + d = Dirtag(raw_dir=options.raw_dir, tag_dir=options.tag_dir) + w = WebInterface( + d, template_dir=options.template_dir, repository_name=options.name) + cherrypy.config.update({ + 'tools.encode.on': True, + 'tools.encode.encoding': 'utf8', + }) + app_config = { + '/static': { + 'tools.staticdir.on': True, + 'tools.staticdir.dir': path.abspath(options.static_dir), + } + } + cherrypy.quickstart(w, '/', app_config) diff --git a/static/raw-tree.rdf b/static/raw-tree.rdf deleted file mode 100644 index 4782d2e..0000000 --- a/static/raw-tree.rdf +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - a - - - - a1.dat - 1 - - - - a2.dat - x - - - - a3.dat - - - - - b - - - - b1.dat - 1,x/y - - - - b2.dat - - - - - b3.dat - x - - - - - - - - - - - - - - - - - - - - diff --git a/static/tag-tree.rdf b/static/tag-tree.rdf deleted file mode 100644 index 50d181e..0000000 --- a/static/tag-tree.rdf +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - 1 - - - - x - - - - y - - - - - - - - - - - - - - - - - - - - - - - diff --git a/static/dirtag.xul b/template/dirtag.xul similarity index 94% rename from static/dirtag.xul rename to template/dirtag.xul index cf5ac73..a6d6920 100644 --- a/static/dirtag.xul +++ b/template/dirtag.xul @@ -1,12 +1,12 @@ - + + @@ -28,7 +28,7 @@ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" > -