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 import urllib
25 import StringIO
26 import logging
27 import sys
28 from optparse import OptionParser
29 import simplejson as json
30 from wsgiref import simple_server
31
32 from translate.misc import selector
33 from translate.storage import factory
34 from translate.storage import base
35 from translate.storage import tmdb
38 """A RESTful JSON TM server."""
39
41 """Custom request handler, disables some inefficient defaults"""
42
44 """Disable client reverse dns lookup."""
45 return self.client_address[0]
46
48 """Log requests using logging instead of printing to
49 stderror."""
50 logging.info("%s - - [%s] %s" %
51 (self.address_string(),
52 self.log_date_time_string(),
53 format % args))
54
55 - def __init__(self, tmdbfile, tmfiles, max_candidates=3, min_similarity=75,
56 max_length=1000, prefix="", source_lang=None, target_lang=None):
57
58 self.tmdb = tmdb.TMDB(tmdbfile, max_candidates, min_similarity, max_length)
59
60
61 if isinstance(tmfiles, list):
62 [self.tmdb.add_store(factory.getobject(tmfile), source_lang, target_lang) \
63 for tmfile in tmfiles]
64 elif tmfiles:
65 self.tmdb.add_store(factory.getobject(tmfiles), source_lang, target_lang)
66
67
68 self.rest = selector.Selector(prefix=prefix)
69 self.rest.add("/{slang}/{tlang}/unit/{uid:any}",
70 GET=self.translate_unit,
71 POST=self.update_unit,
72 PUT=self.add_unit,
73 DELETE=self.forget_unit
74 )
75
76 self.rest.add("/{slang}/{tlang}/store/{sid:any}",
77 GET=self.get_store_stats,
78 PUT=self.upload_store,
79 POST=self.add_store,
80 DELETE=self.forget_store)
81
82 @selector.opliant
84 start_response("200 OK", [('Content-type', 'text/plain')])
85 candidates = self.tmdb.translate_unit(uid, slang, tlang)
86 logging.debug("candidates: %s", unicode(candidates))
87 response = json.dumps(candidates, indent=4)
88 return [response]
89
90 @selector.opliant
91 - def add_unit(self, environ, start_response, uid, slang, tlang):
92 start_response("200 OK", [('Content-type', 'text/plain')])
93
94 data = json.loads(environ['wsgi.input'].read(int(environ['CONTENT_LENGTH'])))
95 unit = base.TranslationUnit(data['source'])
96 unit.target = data['target']
97 self.tmdb.add_unit(unit, slang, tlang)
98 return [""]
99
100 @selector.opliant
101 - def update_unit(self, environ, start_response, uid, slang, tlang):
102 start_response("200 OK", [('Content-type', 'text/plain')])
103
104 data = json.loads(environ['wsgi.input'].read(int(environ['CONTENT_LENGTH'])))
105 unit = base.TranslationUnit(data['source'])
106 unit.target = data['target']
107 self.tmdb.add_unit(unit, slang, tlang)
108 return [""]
109
110 @selector.opliant
112
113 start_response("200 OK", [('Content-type', 'text/plain')])
114
115
116 return [response]
117
118 @selector.opliant
120
121 start_response("200 OK", [('Content-type', 'text/plain')])
122
123
124 return [response]
125
126 @selector.opliant
127 - def upload_store(self, environ, start_response, sid, slang, tlang):
128 """add units from uploaded file to tmdb"""
129 start_response("200 OK", [('Content-type', 'text/plain')])
130 data = StringIO.StringIO(environ['wsgi.input'].read(int(environ['CONTENT_LENGTH'])))
131 data.name = sid
132 store = factory.getobject(data)
133 count = self.tmdb.add_store(store, slang, tlang)
134 response = "added %d units from %s" % (count, sid)
135 return [response]
136
137 @selector.opliant
138 - def add_store(self, environ, start_response, sid, slang, tlang):
139 """Add unit from POST data to tmdb."""
140 start_response("200 OK", [('Content-type', 'text/plain')])
141 units = json.loads(environ['wsgi.input'].read(int(environ['CONTENT_LENGTH'])))
142 count = self.tmdb.add_list(units, slang, tlang)
143 response = "added %d units from %s" % (count, sid)
144 return [response]
145
146 @selector.opliant
148
149 start_response("200 OK", [('Content-type', 'text/plain')])
150
151
152 return [response]
153
156 parser = OptionParser()
157 parser.add_option("-d", "--tmdb", dest="tmdbfile", default=":memory:",
158 help="translation memory database file")
159 parser.add_option("-f", "--import-translation-file", dest="tmfiles", action="append",
160 help="translation file to import into the database")
161 parser.add_option("-t", "--import-target-lang", dest="target_lang",
162 help="target language of translation files")
163 parser.add_option("-s", "--import-source-lang", dest="source_lang",
164 help="source language of translation files")
165 parser.add_option("-b", "--bind", dest="bind", default="localhost",
166 help="adress to bind server to (default: localhost)")
167 parser.add_option("-p", "--port", dest="port", type="int", default=8888,
168 help="port to listen on (default: 8888)")
169 parser.add_option("--max-candidates", dest="max_candidates", type="int", default=3,
170 help="Maximum number of candidates")
171 parser.add_option("--min-similarity", dest="min_similarity", type="int", default=75,
172 help="minimum similarity")
173 parser.add_option("--max-length", dest="max_length", type="int", default=1000,
174 help="Maxmimum string length")
175 parser.add_option("--debug", action="store_true", dest="debug", default=False,
176 help="enable debugging features")
177
178 (options, args) = parser.parse_args()
179
180
181 format = '%(asctime)s %(levelname)s %(message)s'
182 level = options.debug and logging.DEBUG or logging.WARNING
183 if options.debug:
184 format = '%(levelname)7s %(module)s.%(funcName)s:%(lineno)d: %(message)s'
185 if sys.version_info[:2] < (2, 5):
186 format = '%(levelname)7s %(module)s [%(filename)s:%(lineno)d]: %(message)s'
187 else:
188 try:
189 import psyco
190 psyco.full()
191 except Exception:
192 pass
193
194 logging.basicConfig(level=level, format=format)
195
196 application = TMServer(options.tmdbfile, options.tmfiles, max_candidates=options.max_candidates,
197 min_similarity=options.min_similarity, max_length=options.max_length,
198 prefix="/tmserver", source_lang=options.source_lang, target_lang=options.target_lang)
199 httpd = simple_server.make_server(options.bind, options.port,
200 application.rest, handler_class=TMServer.RequestHandler)
201 httpd.serve_forever()
202
203
204 if __name__ == '__main__':
205 main()
206