82e86cd7456abcf8c7434c1f66f73b7ed0697eb7
[progfou.git] / partageweb2 / partage-compta.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 """
4 Outil de partage de fichiers.
5
6 Depends: libapache2-mod-wsgi
7
8 Attention : le code n'est pas encore “thread-safe”...
9 """
10 import os
11 import re
12 import urllib
13 import tarfile
14
15 ROOT_PATH = '/srv/www/compta'
16 PAGE_TITLE = u'Rapport financier du BAP <em>(%s)</em>'
17 BACKGROUND_URL = 'Bamboo2.png'
18 EXCLUDED_FILES = [ 'description.txt' ]
19 INVALID_NAME = u'%s <span class="alert">(NOM INVALIDE)</span>'
20
21 html_template_filename = os.path.abspath(__file__.rstrip('.py') + '.tpl')
22 dir_description_filename = 'description.txt'
23
24 dir_link_template = u"""%s<li class="dir"><a href="javascript:void(0)" onclick="sw('l%s')">%s</a> <em>(%s) <a href="%s">archive du dossier</a></em> %s</li>"""
25 dir_description_template = u""" <em>%s</em>"""
26 begin_content_template = u"""%s<div id="l%s" class="hidden"><ul>"""
27 end_content_template = u"""%s</ul></div>"""
28 file_link_template = u"""%s<li><a href="%s">%s</a> <em>(%s)</em></li>"""
29
30 def valid_path(path):
31 if os.path.islink('.' + path):
32 return False
33 if not os.path.isdir('.' + path):
34 return False
35 return True
36
37 def human_size(size):
38 if size <= 1: return "0 octet"
39 if size < 1024: return "%s octets" % size
40 size /= 1024
41 if size < 1024: return "%s Kio" % size
42 size /= 1024
43 if size < 1024: return "%s Mio" % size
44 size /= 1024
45 if size < 1024: return "%s Gio" % size
46 size /= 1024
47 return "%s Tio" % size
48
49 def my_cmp(name1, name2):
50 m1 = re.match('[0-9]+', name1)
51 m2 = re.match('[0-9]+', name2)
52 if not m1 or not m2:
53 return cmp(name1, name2)
54 n1 = int(m1.group())
55 n2 = int(m2.group())
56 if n1 == n2:
57 return cmp(name1[m1.end():], name2[m2.end():])
58 if n1 < n2:
59 return -1
60 return 1
61
62 def my_listdir(path):
63 path = '.' + path
64 try:
65 names = os.listdir(path)
66 except OSError:
67 return [], []
68 dirs = []
69 files = []
70 for n in names:
71 p = path + '/' + n
72 if not os.path.islink(p):
73 if os.path.isdir(p):
74 dirs.append(n)
75 if os.path.isfile(p):
76 files.append(n)
77 dirs.sort(cmp=my_cmp)
78 files.sort(cmp=my_cmp)
79 return dirs, files
80
81
82 id_number = 0
83
84 def dir_content(prefix, root, level=0):
85 dirs, files = my_listdir(root)
86 if not len(dirs) and not len(files):
87 return [], 0
88 space = u" " * level
89 content = []
90 file_size_total = 0
91 for d in dirs:
92 path = root.rstrip('/') + '/' + d
93 sub_dir_content, sub_dir_size = dir_content(prefix, path, level + 1)
94 file_size_total += sub_dir_size
95 if sub_dir_content:
96 global id_number
97 id_number += 1
98 name = d.replace('_',' ')
99 try:
100 name = name.decode('utf-8')
101 except UnicodeDecodeError:
102 name = INVALID_NAME % name.decode('latin-1')
103 desc_file = '.' + path + '/' + dir_description_filename
104 if os.path.isfile(desc_file):
105 desc = file(desc_file).read().strip().replace('\n',' ')
106 desc = dir_description_template % desc.decode('utf-8')
107 else:
108 desc = ''
109 archive_path = (prefix + path + '.tar').decode('utf-8')
110 archive_size = human_size(sub_dir_size)
111 content.append(dir_link_template % \
112 (space, id_number, name, archive_size, archive_path, desc))
113 content.append(begin_content_template % (space, id_number))
114 content.extend(sub_dir_content)
115 content.append(end_content_template % space)
116 for f in files:
117 path = root.rstrip('/') + '/' + f
118 file_size = os.path.getsize('.' + path)
119 file_size_total += file_size
120 if file_size > 0 and f not in EXCLUDED_FILES:
121 name = f.replace('_',' ')
122 try:
123 name = name.decode('utf-8')
124 except UnicodeDecodeError:
125 name = INVALID_NAME % name.decode('latin-1')
126 content.append( file_link_template % (space,
127 urllib.quote(prefix + path), name, human_size(file_size)) )
128 return content, file_size_total
129
130 class FileBuffer():
131 def __init__(self):
132 self.reset()
133 def reset(self):
134 self._data = []
135 def write(self, data):
136 self._data.append(data)
137 def read(self):
138 return ''.join(self._data)
139
140 def tar_generator(path, mode='w|', bufsize=65536):
141 buffer = FileBuffer()
142 #yield 'open()\n'
143 tar = tarfile.open(mode=mode, fileobj=buffer, bufsize=bufsize)
144 for root, dirs, files in os.walk(path):
145 for name in files:
146 tar.add(os.path.join(root, name), recursive=False)
147 #yield '+ file: %s\n' % os.path.join(root, name)
148 yield buffer.read()
149 buffer.reset()
150 tar.close()
151 #yield 'close()\n'
152 yield buffer.read()
153
154 def application(environ, start_response):
155 global id_number
156 id_number = 0
157 os.chdir(ROOT_PATH)
158 if False and not environ.get('HTTP_USER'):
159 headers = [('Content-Type', 'text/plain; charset=utf-8'), ]
160 start_response('401 Authorization Required', headers)
161 return ['Authorization Required']
162 path = environ['PATH_INFO']
163 if not valid_path(os.path.dirname(path)):
164 headers = [('Content-Type', 'text/plain; charset=utf-8'), ]
165 start_response('404 Not Found', headers)
166 return ['Not Found']
167 if os.path.isfile('.' + path):
168 if path.endswith('.txt'):
169 mime_type = 'text/plain; charset=utf-8'
170 elif path.endswith('.pdf'):
171 mime_type = 'application/pdf'
172 else:
173 mime_type = 'application/octet-stream'
174 length = str(os.path.getsize('.' + path))
175 headers = [('Content-Type', mime_type), ('Content-Length', length), ]
176 start_response('200 OK', headers)
177 return file('.' + path)
178 if path.endswith('.tar') and os.path.isdir('.' + path[:-4]):
179 #headers = [('Content-Type', 'text/plain; charset=utf-8'), ]
180 headers = [('Content-Type', 'application/octet-stream'), ]
181 start_response('200 OK', headers)
182 return tar_generator('.' + path[:-4])
183 content, size = dir_content(environ['SCRIPT_NAME'], environ['PATH_INFO'])
184 environ.update({
185 'page_title': PAGE_TITLE % human_size(size),
186 'background_url': BACKGROUND_URL,
187 'content': '<ul>\n' + '\n'.join(content) + '\n</ul>',
188 })
189 html_template = file(html_template_filename).read().decode('utf-8')
190 content = (html_template % environ).encode('utf-8')
191 headers = [('Content-Type', 'text/html; charset=utf-8'), ]
192 start_response('200 OK', headers)
193 return [content]
194
195 if __name__ == '__main__':
196 # this runs when script is started directly from commandline
197 try:
198 # create a simple WSGI server and run the application
199 from wsgiref import simple_server
200 print "Running test application - point your browser at http://localhost:8000/ ..."
201 httpd = simple_server.WSGIServer(('', 8000), simple_server.WSGIRequestHandler)
202 httpd.set_app(application)
203 httpd.serve_forever()
204 except ImportError:
205 # wsgiref not installed, just output html to stdout
206 for content in application({}, lambda status, headers: None):
207 print content
208