Add dirtag.xul -> web.py data transfer.
[dirtag.git] / dirtag / web.py
1 #!/usr/bin/env python
2
3 from optparse import OptionParser
4 from os import path
5 from urllib import urlencode
6
7 import cherrypy
8 from jinja2 import Environment, FileSystemLoader
9
10 from dirtag import Dirtag, Tree, dir_tree
11
12
13 class WebInterface:
14     """The web interface to Dirtag.
15     """
16     def __init__(self, dirtag, template_dir, repository_name='Dirtag'):
17         """Initialize the bug repository for this web interface."""
18         self.dirtag = dirtag
19         self.env = Environment(loader=FileSystemLoader(template_dir))
20         self.repository_name = repository_name
21         self.rdf_root = 'http://dirtag.com/'
22
23     @cherrypy.expose
24     def index(self):
25         template = self.env.get_template('dirtag.xul')
26         cherrypy.response.headers['Content-Type'] = \
27             'application/vnd.mozilla.xul+xml'
28         return template.render(repository_name=self.repository_name)
29
30     @cherrypy.expose
31     def raw_rdf(self):
32         return self.tree_rdf(self.dirtag.raw_dir, 'raw')
33
34     @cherrypy.expose
35     def tag_rdf(self):
36         return self.tree_rdf(self.dirtag.tag_dir, 'tag', with_files=False)
37
38     def tree_rdf(self, root_dir, name, with_files=True):
39         cherrypy.response.headers['Content-Type'] = \
40             'application/rdf+xml'
41         tree = dir_tree(root_dir)
42         lines = [
43             '<?xml version="1.0"?>',
44             '',
45             '<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"',
46             '         xmlns:FILES="%srdf#">' % self.rdf_root,
47             ]
48         if with_files == True:
49             nodes = tree.traverse(depth=1, dirtag=self.dirtag)
50         else:
51             nodes = tree.traverse(depth=1, type='dirs', dirtag=self.dirtag)
52         for node in nodes:
53             lines.extend([
54                     '  <RDF:Description RDF:about="%s%s">'
55                     % (self.rdf_root, '/'.join([name]+node)),
56                     '    <FILES:name>%s</FILES:name>' % node[-1],
57                     ])
58             if node.type == 'file':
59                 lines.append('    <FILES:tags>%s</FILES:tags>'
60                              % ','.join(['/'.join(t) for t in node.tags]))
61             lines.append('  </RDF:Description>')
62
63         lines.append('  <RDF:Seq RDF:about="%s%s/files">'
64                      % (self.rdf_root, name))
65         stack = []
66         for node in tree.traverse(depth=1,):
67             while (len(stack) > 0 and
68                    not self.list_startswith(stack[-1], node[:-1])):
69                 lines.extend([
70                         ' '*(4*len(stack)+2) + '</RDF:Seq>',
71                         ' '*(4*len(stack)) + '</RDF:li>',
72                         ])
73                 stack.pop()
74             assert len(node) == len(stack)+1, (
75                 'Floating %s -> %s (%d -> %d)'
76                 % (stack[-1], node, len(stack), len(node)))
77             stack.append(node)
78             if node.type == 'file':
79                 raw_node = self.dirtag.raw_node(node)
80                 lines.append(' '*(4*len(stack))
81                              + '<RDF:li RDF:resource="%s%s"/>'
82                              % (self.rdf_root, '/'.join(['raw']+raw_node)))
83                 stack.pop()
84             else:
85                 lines.extend([
86                         ' '*(4*len(stack)) + '<RDF:li>',
87                         ' '*(4*len(stack)+2) + '<RDF:Seq RDF:about="%s%s">'
88                         % (self.rdf_root, '/'.join([name]+node)),
89                         ])
90         while len(stack) > 0:
91             lines.extend([
92                     ' '*(4*len(stack)+2) + '</RDF:Seq>',
93                     ' '*(4*len(stack)) + '</RDF:li>',
94                     ])
95             stack.pop()
96         lines.extend([
97                 '  </RDF:Seq>',
98                 '',
99                 '</RDF:RDF>',
100                 '',
101                 ])
102         return '\n'.join(lines)
103
104     def list_startswith(self, a, b):
105         """
106         >>> x = WebInterface(None, 'template')
107         >>> x.list_startswith([1, 2, 3], [1, 2, 3, 4])
108         True
109         >>> x.list_startswith([1, 2, 3], [1, 2, 3])
110         True
111         >>> x.list_startswith([1, 2, 3], [1, 2])
112         False
113         >>> x.list_startswith([1, 2, 3], [2, 2, 3])
114         False
115         """
116         return len([x for x,y in zip(a,b) if x==y]) == len(a)
117
118     @cherrypy.expose
119     def new_tag(self, tag):
120         self.dirtag.new_tag(tag.split('/'))
121         return '<p>New tag %s added</p>' % tag
122
123     @cherrypy.expose
124     def add_tag(self, path, tag):
125         self.dirtag.add_tag(path.split('/'), tag.split('/'))
126         return '<p>Added tag %s to %s</p>' % (tag, path)
127
128     @cherrypy.expose
129     def remove_tag(self, path, tag):
130         self.dirtag.remove_tag(path.split('/'), tag.split('/'))
131         return '<p>Removed tag %s from %s</p>' % (tag, path)
132
133
134 if __name__ == '__main__':
135     parser = OptionParser('%prog [options]', epilog=WebInterface.__doc__)
136     parser.add_option('-r', '--raw-dir', dest='raw_dir', metavar='DIR',
137                       default='raw',
138                       help='Location of raw files (%default)')
139     parser.add_option('-t', '--tag-dir', dest='tag_dir', metavar='DIR',
140                       default='tag',
141                       help='Location of tag links (%default)')
142     parser.add_option('-T', '--template-dir', dest='template_dir',
143                       metavar='DIR', default='template',
144                       help='Location of template files (%default)')
145     parser.add_option('-s', '--static-dir', dest='static_dir', metavar='DIR',
146                       default='static',
147                       help='Location of static files (%default)')
148     parser.add_option('-n', '--name', dest='name', metavar='STRING',
149                       default='dirtag',
150                       help='Name of the dirtag repository')
151     parser.add_option('--test', dest='test', default=False,
152                       action='store_true',
153                       help='Run unittests and exit')
154     options,args = parser.parse_args()
155
156     if options.test:
157         import doctest, sys
158         doctest.testmod()
159         sys.exit(0)
160
161     d = Dirtag(raw_dir=options.raw_dir, tag_dir=options.tag_dir)
162     w = WebInterface(
163         d, template_dir=options.template_dir, repository_name=options.name)
164     cherrypy.config.update({
165             'tools.encode.on': True,
166             'tools.encode.encoding': 'utf8',
167             })
168     app_config = {
169         '/static': {
170             'tools.staticdir.on': True,
171             'tools.staticdir.dir': path.abspath(options.static_dir),
172             }
173         }
174     cherrypy.quickstart(w, '/', app_config)