list2form : gestion des erreurs et des petits fichiers (merci cgi.py…)
[progfou.git] / wcs / wcs-dynexport
CommitLineData
20ae1ad6
P
1#!/usr/bin/python
2# -*- coding: utf-8 -*-
3"""
4Outil d'export dynamique de données w.c.s.
5
6Copyright : Agence universitaire de la Francophonie — www.auf.org
7Licence : GNU General Public Licence, version 2
8Auteur : Jean Christophe André
9Date de création : 13 mars 2013
10
11Depends: wcs, python-simplejson, python-magic
12
13URL d'accès :
14- /dynexport => liste des formulaires pour le domaine courant
15- /dynexport/domains.json => liste des domaines disponibles
16- /dynexport/formulaire => liste des options ci-dessous
17- /dynexport/formulaire/fields.json
18- /dynexport/formulaire/field-names.json
19- /dynexport/formulaire/field-names.txt
20- /dynexport/formulaire/data.json
21- /dynexport/formulaire/last-run.log
22- /dynexport/formulaire/liste-dossiers.json
23- /dynexport/formulaire/clear-cache => vide le cache
24- /dynexport/formulaire/data/nom-dossier.json
25- /dynexport/formulaire/data/nom-dossier_attachement-1.xxx
26- /dynexport/formulaire/data/nom-dossier_attachement-2.xxx
27- /dynexport/formulaire/data/nom-dossier_attachement-…
28"""
29import sys
30import os
31import os.path
32import logging
33import time # time, gmtime, strftime, strptime, struct_time
34import simplejson as json
35import magic
36import mimetypes
37import unicodedata
38from cStringIO import StringIO
39from gzip import GzipFile
40from re import match
41
da0c36c5 42EXPIRE_DELAY = 5 # maximum 5 secondes en cache web
20ae1ad6 43TIME_FORMAT = '%a, %d %b %Y %H:%M:%S GMT' # format date pour HTTP
da0c36c5 44#ETABLISSEMENT_FORMAT = r'^(\w+\s-\s.+)\s\(\d+\s-\s(Nord|Sud)\)$'
20ae1ad6
P
45
46WCS_ROOT_DIR = '/var/lib/wcs'
47WCS_DOMAIN_SUFFIX = '.auf.org'
20ae1ad6 48WCS_CACHE_DIR = '/var/tmp'
da0c36c5
P
49WCS_CACHE_DELAY_DEFAULT = 7*24*60*60 # 1 semaine
50WCS_CACHE_DELAY_FORMS = 5*60 # 5 minutes
20ae1ad6
P
51
52#--------------------------------------------------------------------------
f2de318d
P
53# variables globales
54#--------------------------------------------------------------------------
55
56pub = None
57
58#--------------------------------------------------------------------------
20ae1ad6
P
59# fonctions de traitement
60#--------------------------------------------------------------------------
61
62def http_redirect(location, code='302'):
63 headers = {}
64 headers['Content-Type'] = 'text/plain; charset=utf-8'
65 headers['Status'] = '302 Redirection'
66 headers['Location'] = location
67 data = """If you see this, it means the automatic redirection has failed.
68Please go to ${location}"""
69 # envoi de la réponse
70 headers = ''.join(map(lambda x: "%s: %s\r\n" % (x, headers[x]), headers))
71 f = open('/dev/stdout', 'wb')
72 f.write(headers + "\r\n")
73 if data:
74 f.write(data)
75 f.flush()
76 # arrêt du traitement
77 sys.exit(0)
78
79def http_reply_and_exit(data, mime_type='text/html', charset='utf-8'):
b4f12399 80 if data is None: data = ''
20ae1ad6
P
81 # références horaires
82 current_time = time.time()
83 mtime = time.gmtime(current_time)
da0c36c5 84 etime = time.gmtime(current_time + EXPIRE_DELAY)
20ae1ad6
P
85 if os.environ.has_key('HTTP_IF_MODIFIED_SINCE'):
86 try:
87 itime = time.strptime(os.environ['HTTP_IF_MODIFIED_SINCE'], TIME_FORMAT)
88 except ValueError:
89 itime = None
90 else:
91 itime = None
92 # préparation des en-têtes et données
93 headers = {}
94 headers['Content-Type'] = '%s; charset=%s' % (mime_type, charset)
95 headers['Last-Modified'] = time.strftime(TIME_FORMAT, mtime)
96 headers['Expires'] = time.strftime(TIME_FORMAT, etime)
97 if os.environ['REQUEST_METHOD'] == 'GET' and (not itime or mtime > itime):
98 # détermination de la version demandée (compressée ou non)
99 if os.environ.get('HTTP_ACCEPT_ENCODING','').split(',').count('gzip') > 0:
100 zdata = StringIO()
101 GzipFile('', 'w', 9, zdata).write(data)
102 data = zdata.getvalue()
103 headers['Content-Encoding'] = 'gzip'
104 headers['Vary'] = 'Content-Encoding'
105 headers['Content-Length'] = len(data)
106 else:
107 data = None
108 # envoi de la réponse
109 headers = ''.join(map(lambda x: "%s: %s\r\n" % (x, headers[x]), headers))
110 f = open('/dev/stdout', 'wb')
111 f.write(headers + "\r\n")
112 if data:
113 f.write(data)
114 f.flush()
115 # arrêt du traitement
116 sys.exit(0)
117
118
119def _reduce_to_alnum(s, replacement_char='-'):
120 """réduction d'une chaîne de caractères à de l'alpha-numérique"""
121
122 if type(s) is not unicode:
123 s = unicode(s, 'utf-8')
124 s = unicodedata.normalize('NFKD', s).encode('ASCII', 'ignore')
125 r = ''
126 for c in s:
127 if ('a' <= c.lower() <= 'z') or ('0' <= c <= '9'):
128 r += c
129 elif len(r) > 0 and r[-1] != replacement_char:
130 r += replacement_char
131 else: # r == '' or r[-1] == replacement_char
132 pass
133 return r.strip(replacement_char)
134
135def _make_wcs_cache_name(domain, form, name):
136 return 'wcs-%s-%s-%s' % (domain, form, name)
137
da0c36c5 138def set_wcs_cache(domain, form, name, data, delay=WCS_CACHE_DELAY_DEFAULT):
20ae1ad6
P
139 os.umask(0022)
140 cache_filename = _make_wcs_cache_name(domain, form, name)
da0c36c5
P
141 cache_filename = os.path.join(WCS_CACHE_DIR, cache_filename)
142 f = open(cache_filename, 'wb')
20ae1ad6
P
143 f.write(data)
144 f.close()
da0c36c5
P
145 # la date de modification est utilisée comme date d'expiration
146 atime = time.time()
147 mtime = atime + delay
148 os.utime(cache_filename, (atime, mtime))
20ae1ad6
P
149
150def get_wcs_cache(domain, form, name):
151 data = None
152 cache_filename = _make_wcs_cache_name(domain, form, name)
153 cache_filename = os.path.join(WCS_CACHE_DIR, cache_filename)
154 if os.path.exists(cache_filename):
da0c36c5
P
155 # la date de modification est utilisée comme date d'expiration
156 if time.time() < os.path.getmtime(cache_filename):
157 data = open(cache_filename, 'rb').read()
158 else:
159 os.unlink(cache_filename)
20ae1ad6
P
160 return data
161
162def clear_wcs_cache(domain, form):
163 cache_filename = _make_wcs_cache_name(domain, form, '')
164 for f in os.listdir(WCS_CACHE_DIR):
165 if f.startswith(cache_filename):
166 os.unlink(os.path.join(WCS_CACHE_DIR, f))
167
f2de318d
P
168def set_wcs_publisher(domain):
169 global pub
170 if pub is None:
171 from wcs import publisher
172 pub = publisher.WcsPublisher.create_publisher()
173 pub.app_dir = os.path.join(pub.app_dir, domain)
174 pub.set_config()
175
20ae1ad6
P
176def get_wcs_domains():
177 root = WCS_ROOT_DIR
178 suffix = WCS_DOMAIN_SUFFIX
179 try:
180 l = os.listdir(root)
181 except OSError:
182 return None
183 return [x for x in l if os.path.isdir(os.path.join(root, x)) and x.endswith(suffix)]
184
185def get_wcs_forms(domain):
da0c36c5
P
186 """extraction de la liste des formulaires"""
187 data = get_wcs_cache(domain, 'ALL', 'ALL.json')
188 if data is not None:
189 return json.loads(data, encoding='utf-8')
f2de318d
P
190 set_wcs_publisher(domain)
191 from wcs.formdef import FormDef
da0c36c5
P
192 forms = [f.url_name for i,f in FormDef.items()]
193 data = json.dumps(forms, ensure_ascii=False).encode('utf-8')
194 set_wcs_cache(domain, 'ALL', 'ALL.json', data, WCS_CACHE_DELAY_FORMS)
195 return forms
20ae1ad6
P
196
197def get_wcs_form_data(domain, form):
198 """extraction des données du formulaire"""
199 data = get_wcs_cache(domain, form, 'metadata.json')
200 if data is not None:
201 return json.loads(data, encoding='utf-8')
202 # dictionnaire des metadonnées (qui seront mises en cache)
203 metadata = {}
204
205 os.umask(0022)
206 logname = _make_wcs_cache_name(domain, form, 'last-run.log')
b4f12399
P
207 logger = logging.getLogger('wcs-dynexport')
208 logger.setLevel(logging.DEBUG)
209 log_formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
210 log_handler = logging.FileHandler(os.path.join(WCS_CACHE_DIR, logname))
211 log_handler.setLevel(logging.DEBUG)
212 log_handler.setFormatter(log_formatter)
213 logger.addHandler(log_handler)
20ae1ad6 214
b4f12399
P
215 logger.info('Début.')
216 log_handler.flush()
20ae1ad6 217
f2de318d 218 set_wcs_publisher(domain)
20ae1ad6
P
219 from wcs.formdef import FormDef
220 from wcs.fields import TitleField, CommentField, TextField, \
221 StringField, ItemField, ItemsField, EmailField, \
222 DateField, FileField, BoolField, TableField
20ae1ad6
P
223 formdef = FormDef.get_by_urlname(form)
224
225 # nommage des champs de façon unique
226 fields = {}
227 field_names = {}
228 field_names_duplicates = {}
33fcaa8a 229 for i, field in enumerate(formdef.fields):
20ae1ad6
P
230 if isinstance(field, TitleField) or isinstance(field, CommentField):
231 continue
232 if field.varname:
233 name = field.varname
234 else:
235 name = _reduce_to_alnum(field.label,'_').lower()
236 if name in field_names.values(): # duplicat
237 field_names_duplicates[name] = field_names_duplicates.get(name, 1) + 1
238 name = '%s_%d' % (name, field_names_duplicates[name])
239 field_names.update({field.id: name})
33fcaa8a 240 fields.update({field.id: {'index': i, 'name': field_names[field.id], 'label': field.label, 'varname': field.varname and field.varname or ''}})
20ae1ad6
P
241
242 data = json.dumps(fields, ensure_ascii=False).encode('utf-8')
243 set_wcs_cache(domain, form, 'fields.json', data)
244 metadata.update({'fields': fields})
245
246 # on charge la base des types MIME une fois pour toutes
247 #magicmime = magic.Magic(mime=True) => ce sera pour plus tard…
248 magicmime = magic.open(magic.MAGIC_MIME)
249 magicmime.load()
250
251 liste_dossiers = []
252 liste_attachements = {}
253 for object in formdef.data_class().select():
254 if object.user is None:
b4f12399 255 logger.warning("Dossier '%s' sans utilisateur associé ?!?"\
20ae1ad6
P
256 " On ignore...", object.id)
257 continue
258
e86adf99
P
259 try:
260 workflow_status = object.status.startswith('wf-') and \
261 object.get_workflow_status().name or None
262 except:
263 workflow_status = None
264
20ae1ad6
P
265 result = {
266 'num_dossier': object.id,
267 'wcs_status': object.status,
e86adf99 268 'wcs_workflow_status': workflow_status,
20ae1ad6
P
269 'wcs_user_email': object.user.email,
270 'wcs_user_display_name': object.user.display_name,
271 #'wcs_last_modified': time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime(object.last_modified())),
272 'wcs_comments': [],
273 }
274
275 if object.evolution is not None:
276 for e in object.evolution:
277 if e.comment is not None:
da0c36c5
P
278 try:
279 who = pub.user_class.get(e.who).display_name
280 except:
281 who = 'Inconnu(e)'
20ae1ad6
P
282 e_time = time.strftime('%Y-%m-%d %H:%M:%S', e.time)
283 comment = '%s -- %s %s' % (e.comment, who, e_time)
284 result['wcs_comments'].append(comment)
285
286 qfiles = { }
287 for field in formdef.fields:
288 field_id = str(field.id)
289 if not field_id in object.data:
290 continue
291 if isinstance(field, TitleField) or isinstance(field, CommentField):
292 continue
293 field_name = fields[field_id]['name']
294 data = object.data.get(field_id)
69251c41
P
295 # paliatif aux corrections de formulaires en cours de route
296 # (compensation nécessaire pour l'import depuis Sigma 2)
297 if data is None and field.required:
298 if isinstance(field, StringField) \
299 or isinstance(field, TextField) \
300 or isinstance(field, EmailField) \
301 or isinstance(field, ItemField):
302 result[field_name] = '(vide)'
303 elif isinstance(field, ItemsField) \
304 or isinstance(field, TableField):
305 result[field_name] = '(vide)'
306 elif isinstance(field, BoolField):
307 result[field_name] = False
308 elif isinstance(field, DateField):
309 result[field_name] = "9999-12-31"
310 continue
20ae1ad6
P
311 if data is None:
312 result[field_name] = None
313 continue
314 if isinstance(field, StringField) or isinstance(field, TextField) \
315 or isinstance(field, EmailField) or isinstance(field, ItemField):
da0c36c5
P
316 # nettoyage du nom d'établissement (suppression id et Nord/Sud)
317 #m = match(ETABLISSEMENT_FORMAT, data)
318 #if m is not None:
319 # data = m.groups()[0]
20ae1ad6
P
320 result[field_name] = data
321 elif isinstance(field, ItemsField) or isinstance(field, TableField):
322 result[field_name] = data # liste => peux-être joindre sur ';'
323 elif isinstance(field, BoolField):
324 result[field_name] = (data == 'True')
325 elif isinstance(field, DateField):
326 if isinstance(data, time.struct_time):
327 result[field_name] = '%04d-%02d-%02d' % (data.tm_year,
328 data.tm_mon, data.tm_mday)
329 else:
330 result[field_name] = data
331 elif isinstance(field, FileField):
332 if '.' in data.orig_filename:
333 extension = data.orig_filename.rpartition('.')[2].lower()
334 else: # il n'y a pas d'extension dans le nom de fichier
335 p = os.path.join(pub.app_dir, 'uploads', data.qfilename)
336 try:
337 #m = magicmime.from_file(p) => ce sera pour plus tard…
338 m = magicmime.file(p).split()[0].strip(';')
339 extension = mimetypes.guess_extension(m)
340 except:
b4f12399 341 logger.warning("Type de fichier inconnu pour '%s'.", p)
20ae1ad6
P
342 extension = None
343 if extension is not None:
344 extension = extension[1:]
345 else:
346 extension = 'unknown'
347 result[field_name] = "%s.%s" % (field_name, extension)
348 qfiles[field_name] = data.qfilename
349 else:
b4f12399 350 logger.warning("Type de champ inconnu '%s' pour '%s' (%s).",
20ae1ad6
P
351 field.__class__.__name__, field_name, field.label)
352
353 num_dossier = result['num_dossier']
354 nom = _reduce_to_alnum(result.get('nom','sans-nom')).upper()
355 prenom = _reduce_to_alnum(result.get('prenom','sans-prenom')).upper()
356 adel = result.get('adresse_electronique','sans-adel').replace('@','-').lower()
357
358 filename = "%04d-%s-%s-%s" % (num_dossier, nom, prenom, adel)
359 liste_dossiers.append(filename + '.json')
360
361 # sauvegarde des chemins d'accès aux fichiers joints
362 for f in qfiles:
363 dst = filename + '_' + result[f]
364 src = os.path.join(pub.app_dir, 'uploads', qfiles[f])
365 liste_attachements.update({dst: src})
904cd34f
P
366 # on renomme le fichier joint indiqué dans le dossier
367 result[f] = dst
20ae1ad6
P
368
369 # génération du fichier JSON
370 data = json.dumps(result, ensure_ascii=False).encode('utf-8')
371 set_wcs_cache(domain, form, 'data_%s.json' % filename, data)
372
b4f12399 373 logger.info("Dossier '%s' : %s.",
20ae1ad6
P
374 filename, result['wcs_workflow_status'])
375
376 data = json.dumps(liste_attachements, ensure_ascii=False).encode('utf-8')
377 set_wcs_cache(domain, form, 'data-files.json', data)
378 metadata.update({'attachements': liste_attachements})
379
380 liste_dossiers.sort()
381 data = json.dumps(liste_dossiers, ensure_ascii=False).encode('utf-8')
382 set_wcs_cache(domain, form, 'liste-dossiers.json', data)
383 metadata.update({'dossiers': liste_dossiers})
384
b4f12399
P
385 logger.info('Fin.')
386 log_handler.flush()
20ae1ad6
P
387
388 data = json.dumps(metadata, ensure_ascii=False).encode('utf-8')
389 set_wcs_cache(domain, form, 'metadata.json', data)
390
391#if __name__ == '__main__':
392# try:
393# extract_data(formdef, OUTPUT_DIRECTORY)
394# except:
b4f12399 395# logger.exception("Interruption du traitement pour cause d'erreur !")
20ae1ad6
P
396
397#--------------------------------------------------------------------------
398# gestion des requêtes web
399#--------------------------------------------------------------------------
400
401#l = []
402#for k in sorted(os.environ):
403# l.append('%s=%s\n' % (k, os.environ[k]))
404#data = ''.join(l)
405#http_reply_and_exit(data, 'text/plain')
406
407domain = os.environ.get('HTTP_HOST', '')
408if domain not in get_wcs_domains():
409 http_reply_and_exit("Domaine '%s' inconnu." % domain, 'text/plain')
410
411path_info = os.environ.get('PATH_INFO', '')
412
413path_prefix = os.environ.get('REQUEST_URI', '')
414if len(path_info) > 0:
415 path_prefix = path_prefix[:-len(path_info)]
416
417if path_info == '':
418 http_redirect(path_prefix + '/')
419
420if path_info == '/':
421 # liste des formulaires disponibles
422 l = sorted(get_wcs_forms(domain))
423 l = ['<li><a href="%s/">%s</a></li>' % (f, f) for f in l]
424 title = '<p>Liste des formulaires disponibles&nbsp;:</p>\n'
425 data = '<html>\n' + title + '<ul>\n' + '\n'.join(l) + '\n</ul>\n</html>'
426 http_reply_and_exit(data, 'text/html')
427
428if path_info == '/index.json':
429 # liste des formulaires disponibles
430 l = sorted(get_wcs_forms(domain))
431 data = json.dumps(l, ensure_ascii=False, indent=' ').encode('utf-8')
432 http_reply_and_exit(data, 'application/json')
433
434if path_info == '/domains.json':
435 # liste des domaines disponibles
436 l = get_wcs_domains()
437 data = json.dumps(l, ensure_ascii=False, indent=' ').encode('utf-8')
438 http_reply_and_exit(data, 'application/json')
439
440if match(r'^/[a-z0-9-]+$', path_info):
441 http_redirect(path_prefix + path_info + '/')
442
443if match(r'^/[a-z0-9-]+/$', path_info):
444 form = path_info.split('/')[1]
445 if form not in get_wcs_forms(domain):
446 http_reply_and_exit("Formulaire '%s' inconnu." % form, 'text/plain')
447 l = [ 'fields.json', 'field-names.json', 'field-names.txt', 'last-run.log', 'liste-dossiers.json' ]
448 l = ['<li><a href="%s">%s</a></li>' % (f, f) for f in l]
449 title = '<p>Liste des informations disponibles&nbsp;:</p>\n'
450 action1 = """<p><a href="data/">Export des données</a></p>\n"""
451 action2 = """<p><a href="clear-cache">Suppression du cache</a> (pour ré-export)</p>\n"""
452 data = '<html>\n' + title + '<ul>\n' + '\n'.join(l) + '\n</ul>\n' + action1 + action2 + '</html>'
453 http_reply_and_exit(data, 'text/html')
454
455if match(r'^/[a-z0-9-]+/index.json$', path_info):
456 form = path_info.split('/')[1]
457 if form not in get_wcs_forms(domain):
458 http_reply_and_exit("Formulaire '%s' inconnu." % form, 'text/plain')
459 l = [ 'fields.json', 'field-names.json', 'field-names.txt', 'last-run.log', 'liste-dossiers.json', 'data', 'clear-cache' ]
460 data = json.dumps(l, ensure_ascii=False, indent=' ').encode('utf-8')
461 http_reply_and_exit(data, 'application/json')
462
463if match(r'^/[a-z0-9-]+/clear-cache$', path_info):
464 form = path_info.split('/')[1]
465 if form not in get_wcs_forms(domain):
466 http_reply_and_exit("Formulaire '%s' inconnu." % form, 'text/plain')
467 clear_wcs_cache(domain, form)
468 http_reply_and_exit('Ok.', 'text/plain')
469
470if match(r'^/[a-z0-9-]+/fields.json$', path_info):
471 form = path_info.split('/')[1]
472 if form not in get_wcs_forms(domain):
473 http_reply_and_exit("Formulaire '%s' inconnu." % form, 'text/plain')
474 get_wcs_form_data(domain, form)
475 d = json.loads(get_wcs_cache(domain, form, 'fields.json'), encoding='utf-8')
476 data = json.dumps(d, ensure_ascii=False, indent=' ').encode('utf-8')
477 http_reply_and_exit(data, 'application/json')
478
479if match(r'^/[a-z0-9-]+/field-names.json$', path_info):
480 form = path_info.split('/')[1]
481 if form not in get_wcs_forms(domain):
482 http_reply_and_exit("Formulaire '%s' inconnu." % form, 'text/plain')
483 get_wcs_form_data(domain, form)
484 d = json.loads(get_wcs_cache(domain, form, 'fields.json'), encoding='utf-8')
485 d = dict([(k, d[k]['name']) for k in d])
486 data = json.dumps(d, ensure_ascii=False, indent=' ').encode('utf-8')
487 http_reply_and_exit(data, 'application/json')
488
489if match(r'^/[a-z0-9-]+/field-names.txt$', path_info):
490 form = path_info.split('/')[1]
491 if form not in get_wcs_forms(domain):
492 http_reply_and_exit("Formulaire '%s' inconnu." % form, 'text/plain')
493 get_wcs_form_data(domain, form)
494 d = json.loads(get_wcs_cache(domain, form, 'fields.json'), encoding='utf-8')
495 d = [(k, d[k]['name'], d[k]['label']) for k in d]
496 d = sorted(d, key=lambda x: int(x[0]))
497 text = u''.join([u'%s:%s:%s\n' % (x[0], x[1], x[2]) for x in d])
498 data = text.encode('utf-8')
499 http_reply_and_exit(data, 'text/plain')
500
501if match(r'^/[a-z0-9-]+/last-run.log$', path_info):
502 form = path_info.split('/')[1]
503 if form not in get_wcs_forms(domain):
504 http_reply_and_exit("Formulaire '%s' inconnu." % form, 'text/plain')
505 get_wcs_form_data(domain, form)
506 data = get_wcs_cache(domain, form, 'last-run.log')
507 http_reply_and_exit(data, 'text/plain')
508
509if match(r'^/[a-z0-9-]+/liste-dossiers.json$', path_info):
510 form = path_info.split('/')[1]
511 if form not in get_wcs_forms(domain):
512 http_reply_and_exit("Formulaire '%s' inconnu." % form, 'text/plain')
513 get_wcs_form_data(domain, form)
514 data = json.loads(get_wcs_cache(domain, form, 'liste-dossiers.json'), encoding='utf-8')
515 data = json.dumps(data, ensure_ascii=False, indent=' ').encode('utf-8')
516 http_reply_and_exit(data, 'application/json')
517
518if match(r'^/[a-z0-9-]+/data$', path_info):
519 http_redirect(path_prefix + path_info + '/')
520
521if match(r'^/[a-z0-9-]+/data/$', path_info):
522 form = path_info.split('/')[1]
523 if form not in get_wcs_forms(domain):
524 http_reply_and_exit("Formulaire '%s' inconnu." % form, 'text/plain')
525 get_wcs_form_data(domain, form)
526 dossiers = json.loads(get_wcs_cache(domain, form, 'liste-dossiers.json'), encoding='utf-8')
527 attachements = json.loads(get_wcs_cache(domain, form, 'data-files.json'), encoding='utf-8')
528 l = sorted(dossiers + attachements.keys())
529 if len(l) > 0:
530 l = ['<li><a href="%s">%s</a></li>' % (f, f) for f in l]
531 title = '<p>Liste des documents disponibles&nbsp;:</p>\n'
532 data = '<html>\n' + title + '<ul>\n' + '\n'.join(l) + '\n</ul>\n</html>'
533 else:
534 data = '<html>\n<p>Aucun document disponible.</p>\n</html>'
535 http_reply_and_exit(data, 'text/html')
536
537if match(r'^/[a-z0-9-]+/data/index.json$', path_info):
538 form = path_info.split('/')[1]
539 if form not in get_wcs_forms(domain):
540 http_reply_and_exit("Formulaire '%s' inconnu." % form, 'text/plain')
541 get_wcs_form_data(domain, form)
542 dossiers = json.loads(get_wcs_cache(domain, form, 'liste-dossiers.json'), encoding='utf-8')
543 attachements = json.loads(get_wcs_cache(domain, form, 'data-files.json'), encoding='utf-8')
544 l = sorted(dossiers + attachements.keys())
545 data = json.dumps(l, ensure_ascii=False, indent=' ').encode('utf-8')
546 http_reply_and_exit(data, 'application/json')
547
548if match(r'^/[a-z0-9-]+/data/[^/]+$', path_info):
549 form = path_info.split('/')[1]
550 if form not in get_wcs_forms(domain):
551 http_reply_and_exit("Formulaire '%s' inconnu." % form, 'text/plain')
552 get_wcs_form_data(domain, form)
553 doc = path_info.split('/')[3]
554 dossiers = json.loads(get_wcs_cache(domain, form, 'liste-dossiers.json'), encoding='utf-8')
555 if doc in dossiers:
556 data = get_wcs_cache(domain, form, 'data_' + doc)
557 data = json.loads(data, encoding='utf-8')
558 data = json.dumps(data, ensure_ascii=False, indent=' ').encode('utf-8')
559 http_reply_and_exit(data, 'application/json')
560 attachements = json.loads(get_wcs_cache(domain, form, 'data-files.json'), encoding='utf-8')
561 if doc in attachements:
562 data = open(attachements[doc], 'rb').read()
563 mime_type = mimetypes.guess_type(doc)[0]
564 if mime_type is None:
565 mime_type = 'application/octet-stream'
566 http_reply_and_exit(data, mime_type)
567 http_reply_and_exit("Document '%s' inconnu." % path_info, 'text/plain')
568
569http_reply_and_exit("Requête '%s' inconnue." % path_info, 'text/plain')