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 cgi import parse_qs
29 from optparse import OptionParser
30 try:
31 import json
32 except ImportError:
33 import simplejson as json
34 from wsgiref import simple_server
35
36 from translate.misc import selector
37 from translate.misc import wsgi
38 from translate.storage import factory
39 from translate.storage import base
40 from translate.storage import tmdb
43 """A RESTful JSON TM server."""
44
45 - def __init__(self, tmdbfile, tmfiles, max_candidates=3, min_similarity=75,
46 max_length=1000, prefix="", source_lang=None, target_lang=None):
47
48 self.tmdb = tmdb.TMDB(tmdbfile, max_candidates, min_similarity, max_length)
49
50
51 if isinstance(tmfiles, list):
52 [self.tmdb.add_store(factory.getobject(tmfile), source_lang, target_lang) \
53 for tmfile in tmfiles]
54 elif tmfiles:
55 self.tmdb.add_store(factory.getobject(tmfiles), source_lang, target_lang)
56
57
58 self.rest = selector.Selector(prefix=prefix)
59 self.rest.add("/{slang}/{tlang}/unit/{uid:any}",
60 GET=self.translate_unit,
61 POST=self.update_unit,
62 PUT=self.add_unit,
63 DELETE=self.forget_unit
64 )
65
66 self.rest.add("/{slang}/{tlang}/store/{sid:any}",
67 GET=self.get_store_stats,
68 PUT=self.upload_store,
69 POST=self.add_store,
70 DELETE=self.forget_store)
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):
124 """add units from uploaded file to tmdb"""
125 start_response("200 OK", [('Content-type', 'text/plain')])
126 data = StringIO.StringIO(environ['wsgi.input'].read(int(environ['CONTENT_LENGTH'])))
127 data.name = sid
128 store = factory.getobject(data)
129 count = self.tmdb.add_store(store, slang, tlang)
130 response = "added %d units from %s" % (count, sid)
131 return [response]
132
133 @selector.opliant
134 - def add_store(self, environ, start_response, sid, slang, tlang):
135 """Add unit from POST data to tmdb."""
136 start_response("200 OK", [('Content-type', 'text/plain')])
137 units = json.loads(environ['wsgi.input'].read(int(environ['CONTENT_LENGTH'])))
138 count = self.tmdb.add_list(units, slang, tlang)
139 response = "added %d units from %s" % (count, sid)
140 return [response]
141
142 @selector.opliant
144
145 start_response("200 OK", [('Content-type', 'text/plain')])
146
147
148 return [response]
149
152 parser = OptionParser()
153 parser.add_option("-d", "--tmdb", dest="tmdbfile", default=":memory:",
154 help="translation memory database file")
155 parser.add_option("-f", "--import-translation-file", dest="tmfiles", action="append",
156 help="translation file to import into the database")
157 parser.add_option("-t", "--import-target-lang", dest="target_lang",
158 help="target language of translation files")
159 parser.add_option("-s", "--import-source-lang", dest="source_lang",
160 help="source language of translation files")
161 parser.add_option("-b", "--bind", dest="bind", default="localhost",
162 help="adress to bind server to (default: localhost)")
163 parser.add_option("-p", "--port", dest="port", type="int", default=8888,
164 help="port to listen on (default: 8888)")
165 parser.add_option("--max-candidates", dest="max_candidates", type="int", default=3,
166 help="Maximum number of candidates")
167 parser.add_option("--min-similarity", dest="min_similarity", type="int", default=75,
168 help="minimum similarity")
169 parser.add_option("--max-length", dest="max_length", type="int", default=1000,
170 help="Maxmimum string length")
171 parser.add_option("--debug", action="store_true", dest="debug", default=False,
172 help="enable debugging features")
173
174 (options, args) = parser.parse_args()
175
176
177 format = '%(asctime)s %(levelname)s %(message)s'
178 level = options.debug and logging.DEBUG or logging.WARNING
179 if options.debug:
180 format = '%(levelname)7s %(module)s.%(funcName)s:%(lineno)d: %(message)s'
181 if sys.version_info[:2] < (2, 5):
182 format = '%(levelname)7s %(module)s [%(filename)s:%(lineno)d]: %(message)s'
183 else:
184 try:
185 import psyco
186 psyco.full()
187 except Exception:
188 pass
189
190 logging.basicConfig(level=level, format=format)
191
192 application = TMServer(options.tmdbfile, options.tmfiles, max_candidates=options.max_candidates,
193 min_similarity=options.min_similarity, max_length=options.max_length,
194 prefix="/tmserver", source_lang=options.source_lang, target_lang=options.target_lang)
195 wsgi.launch_server(options.bind, options.port, application.rest)
196
197
198 if __name__ == '__main__':
199 main()
200