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