RESTfulisation
[restcoda.git] / rest.wsgi
1 #!/usr/bin/python
2 # -*- coding: utf-8 -*-
3
4 import sys, traceback
5
6 # configuration (codes d'accès à la base MS-SQL)
7 sys.path.append('/home/thomas/public_html/')
8 import rest_config
9
10 # pour savoir quel objet interroger : Routes
11 from routes import Mapper
12 from routes.middleware import RoutesMiddleware
13 # et analyse de query_string (TODO: etudier parse au lieu de cgi)
14 from cgi import parse_qs
15
16 # pour chercher les données sur MS-SQL
17 from pymssql import connect
18
19 # pour afficher le résultat : jinja
20 from jinja import Environment, FileSystemLoader
21 from jinja.filters import stringfilter
22
23 # TODO systeme de cache : beaker
24 # from beaker.middleware import CacheMiddleware
25
26 # formats de sortie autorisés, et content-type correspondant
27 # formats = { 'xml': 'application/xml', 'html': 'text/html', 'txt': 'text/plain', 'json': 'application/json', 'rss': 'application/rss+xml' }
28 formats = { 'xml': 'application/xml', 'html': 'text/html', 'txt': 'text/plain', 'json': 'text/plain', 'rss': 'application/rss+xml' }
29
30 # les routes RESTful (cf http://routes.groovie.org/manual.html#restful-services)
31 mapper = Mapper()
32 mapper.resource('demlog','demlog')
33 mapper.resource('comlog','comlog')
34 mapper.resource('demdep','demdep')
35 mapper.resource('comdep','comdep')
36 mapper.resource('dempub','dempub')
37 mapper.resource('compub','compub')
38 # TODO à étudier pour permettre les comarexxx
39 mapper.resource('comare','comare')
40 mapper.resource('comsra','comsre')
41
42 class objetsql(object):
43     """objet de base : dispose d'un accès à MS-SQL (lire les données) et d'un accès à jinja (rendu des données)"""
44     def __init__(self, environ):
45         self.bd = connect(host=rest_config.host,user=rest_config.user,password=rest_config.password,database=rest_config.database)
46         self.cursor = self.bd.cursor()
47         self.jinja = Environment(loader=FileSystemLoader('/home/thomas/public_html/'))
48         self.environ = environ
49         self.outputformat = environ['wsgiorg.routing_args'][1].get('format','xml')
50
51 class document(objetsql):
52     """objet document CODA (demlog, comlog, demdep... ils ont tous le même format)"""
53     def __init__(self, environ, code_document='%', basename_template='document'):
54         super(document, self).__init__(environ)
55         self.code_document = code_document
56         self.basename_template = basename_template
57
58     def index(self):
59         self.cursor.execute("select top 30 * from auf_v_acces_demcom where code like '%s' order by date_modif desc" % (self.code_document))
60         documents={}
61         documents['code'] = self.code_document
62         documents_liste=[]
63         while 1:
64             document = dict_fetchone(self.cursor)
65             if document == None: break
66             document['code_rest'] = coda2rest(document['code'])
67             documents_liste.append(document)
68         documents['documents'] = documents_liste
69         template = self.jinja.get_template('%s-index.%s' % (self.basename_template, self.outputformat))
70         output = template.render(documents)
71         return self.outputformat, output
72
73     def show(self):
74         id = int(self.environ['wsgiorg.routing_args'][1]['id'])
75         self.cursor.execute("select top 1 * from auf_v_acces_demcom where code like '%s' and numero = %d" % (self.code_document, id))
76         document = dict_fetchone(self.cursor)
77         if document == None:
78             raise "document inexistant"
79         document['code_rest'] = coda2rest(document['code'])
80         details = []
81         self.cursor.execute("select * from auf_v_acces_dtls_demcom where code like '%s' and numero = %d" % (self.code_document, id))
82         while 1:
83             detail = dict_fetchone(self.cursor)
84             if detail == None: break
85             details.append(detail)
86         document['details']=details
87         template = self.jinja.get_template('%s.%s' % (self.basename_template, self.outputformat))
88         output = template.render(document)
89         return self.outputformat, output
90
91 class demlog(document):
92     def __init__(self, environ):
93         super(demlog, self).__init__(environ, code_document = 'DEM-LOG-AUF')
94
95 class comlog(document):
96     def __init__(self, environ):
97         super(comlog, self).__init__(environ, code_document = 'COM-LOG-AUF')
98
99 class demdep(document):
100     def __init__(self, environ):
101         super(demdep, self).__init__(environ, code_document = 'DEM-DEP-AUF')
102
103 class comdep(document):
104     def __init__(self, environ):
105         super(comdep, self).__init__(environ, code_document = 'COM-DEP-AUF')
106
107 class dempub(document):
108     def __init__(self, environ):
109         super(dempub, self).__init__(environ, code_document = 'DEM-PUB-AUF')
110
111 class compub(document):
112     def __init__(self, environ):
113         super(compub, self).__init__(environ, code_document = 'COM-PUB-AUF')
114
115 # TODO : voir la possibilité d'appel de comsre/sn1.rss
116 class comsre(document):
117     def __init__(self, environ):
118         super(comsre, self).__init__(environ, code_document = 'COM-SRE-%')
119
120 class comare(document):
121     def __init__(self, environ):
122         super(comare, self).__init__(environ, code_document = 'COM-ARE-%')
123
124
125 def dispatcher(environ, start_response):
126     """sera lancée par mod_wsgi"""
127     if environ.has_key('QUERY_STRING'):
128         environ['org.auf.filters'] = parse_qs(environ['QUERY_STRING'])
129     else:
130         environ['org.auf.filters'] = {}
131     results = environ['wsgiorg.routing_args'][1]
132     try:
133         target_class = globals()[results['controller']]
134         method_name = results['action']
135         method = getattr(target_class,method_name)
136         type, output = method(target_class(environ))
137         start_response("200 OK", [('Content-type', formats[type])])
138         return output.encode('utf-8')
139     except:
140         start_response("404 NOT FOUND", [('Content-type', 'text/plain')])
141         return 'erreur lors du traitement\n%s: %s\n%s' % ( sys.exc_info()[0] , sys.exc_info()[1] , traceback.format_exc())
142
143 application = RoutesMiddleware( dispatcher, mapper)
144
145
146 #
147 # petits utilitaires
148 #
149 def dict_fetchone(cursor):
150     """Renvoie le resultat d'un fetchone dans un dictionnaire"""
151     result = cursor.fetchone()
152     if result == None: return None
153     result_dict = {}
154     for i in range(len(result)):
155         if isinstance( result[i], str ):
156             result_dict[cursor.description[i][0]] = result[i].decode('iso-8859-1')
157         else:
158             result_dict[cursor.description[i][0]] = result[i]
159     return result_dict
160
161 import re 
162 p = re.compile('(dem|com)-(...)-auf',re.IGNORECASE)
163 def coda2rest(value):
164     """Traduit un nom CODA en l'objet correspodant, par exemple DEM-LOG-AUF en demlog"""
165     m = p.search(value)
166     if m == None: return value
167     return m.group(1).lower() + m.group(2).lower()
168