1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 """A translation memory server using tmdb for storage, communicates
22 with clients using JSON over HTTP."""
23
24
25 import logging
26 from cgi import parse_qs
27 from optparse import OptionParser
28 try:
29 import json
30 except ImportError:
31 import simplejson as json
32
33 from translate.misc import selector
34 from translate.misc import wsgi
35 from translate.storage import base
36 from translate.storage import tmdb
40 """A RESTful JSON TM server."""
41
42 - def __init__(self, tmdbfile, tmfiles, max_candidates=3, min_similarity=75,
43 max_length=1000, prefix="", source_lang=None, target_lang=None):
44
45 self.tmdb = tmdb.TMDB(tmdbfile, max_candidates, min_similarity, max_length)
46
47 if tmfiles:
48 self._load_files(tmfiles)
49
50
51 self.rest = selector.Selector(prefix=prefix)
52 self.rest.add("/{slang}/{tlang}/unit/{uid:any}",
53 GET=self.translate_unit,
54 POST=self.update_unit,
55 PUT=self.add_unit,
56 DELETE=self.forget_unit)
57
58 self.rest.add("/{slang}/{tlang}/store/{sid:any}",
59 GET=self.get_store_stats,
60 PUT=self.upload_store,
61 POST=self.add_store,
62 DELETE=self.forget_store)
63
71
72 @selector.opliant
74 start_response("200 OK", [('Content-type', 'text/plain')])
75 candidates = self.tmdb.translate_unit(uid, slang, tlang)
76 logging.debug("candidates: %s", unicode(candidates))
77 response = json.dumps(candidates, indent=4)
78 params = parse_qs(environ.get('QUERY_STRING', ''))
79 try:
80 callback = params.get('callback', [])[0]
81 response = "%s(%s)" % (callback, response)
82 except IndexError:
83 pass
84 return [response]
85
86 @selector.opliant
87 - def add_unit(self, environ, start_response, uid, slang, tlang):
88 start_response("200 OK", [('Content-type', 'text/plain')])
89
90 data = json.loads(environ['wsgi.input'].read(int(environ['CONTENT_LENGTH'])))
91 unit = base.TranslationUnit(data['source'])
92 unit.target = data['target']
93 self.tmdb.add_unit(unit, slang, tlang)
94 return [""]
95
96 @selector.opliant
97 - def update_unit(self, environ, start_response, uid, slang, tlang):
98 start_response("200 OK", [('Content-type', 'text/plain')])
99
100 data = json.loads(environ['wsgi.input'].read(int(environ['CONTENT_LENGTH'])))
101 unit = base.TranslationUnit(data['source'])
102 unit.target = data['target']
103 self.tmdb.add_unit(unit, slang, tlang)
104 return [""]
105
106 @selector.opliant
108
109 start_response("200 OK", [('Content-type', 'text/plain')])
110
111
112 return [response]
113
114 @selector.opliant
116
117 start_response("200 OK", [('Content-type', 'text/plain')])
118
119
120 return [response]
121
122 @selector.opliant
123 - def upload_store(self, environ, start_response, sid, slang, tlang):
134
135 @selector.opliant
136 - def add_store(self, environ, start_response, sid, slang, tlang):
137 """Add unit from POST data to tmdb."""
138 start_response("200 OK", [('Content-type', 'text/plain')])
139 units = json.loads(environ['wsgi.input'].read(int(environ['CONTENT_LENGTH'])))
140 count = self.tmdb.add_list(units, slang, tlang)
141 response = "added %d units from %s" % (count, sid)
142 return [response]
143
144 @selector.opliant
146
147 start_response("200 OK", [('Content-type', 'text/plain')])
148
149
150 return [response]
151
154 parser = OptionParser()
155 parser.add_option("-d", "--tmdb", dest="tmdbfile", default=":memory:",
156 help="translation memory database file")
157 parser.add_option("-f", "--import-translation-file", dest="tmfiles", action="append",
158 help="translation file to import into the database")
159 parser.add_option("-t", "--import-target-lang", dest="target_lang",
160 help="target language of translation files")
161 parser.add_option("-s", "--import-source-lang", dest="source_lang",
162 help="source language of translation files")
163 parser.add_option("-b", "--bind", dest="bind", default="localhost",
164 help="adress to bind server to (default: localhost)")
165 parser.add_option("-p", "--port", dest="port", type="int", default=8888,
166 help="port to listen on (default: 8888)")
167 parser.add_option("--max-candidates", dest="max_candidates", type="int", default=3,
168 help="Maximum number of candidates")
169 parser.add_option("--min-similarity", dest="min_similarity", type="int", default=75,
170 help="minimum similarity")
171 parser.add_option("--max-length", dest="max_length", type="int", default=1000,
172 help="Maxmimum string length")
173 parser.add_option("--debug", action="store_true", dest="debug", default=False,
174 help="enable debugging features")
175
176 (options, args) = parser.parse_args()
177
178
179 format = '%(asctime)s %(levelname)s %(message)s'
180 level = options.debug and logging.DEBUG or logging.WARNING
181 if options.debug:
182 format = '%(levelname)7s %(module)s.%(funcName)s:%(lineno)d: %(message)s'
183 import sys
184 if sys.version_info[:2] < (2, 5):
185 format = '%(levelname)7s %(module)s [%(filename)s:%(lineno)d]: %(message)s'
186 else:
187 try:
188 import psyco
189 psyco.full()
190 except Exception:
191 pass
192
193 logging.basicConfig(level=level, format=format)
194
195 application = TMServer(options.tmdbfile, options.tmfiles, max_candidates=options.max_candidates,
196 min_similarity=options.min_similarity, max_length=options.max_length,
197 prefix="/tmserver", source_lang=options.source_lang, target_lang=options.target_lang)
198 wsgi.launch_server(options.bind, options.port, application.rest)
199
200
201 if __name__ == '__main__':
202 main()
203