Package translate :: Package services :: Module tmserver
[hide private]
[frames] | no frames]

Source Code for Module translate.services.tmserver

  1  #!/usr/bin/env python 
  2  # -*- coding: utf-8 -*- 
  3  # 
  4  # Copyright 2008-2009 Zuza Software Foundation 
  5  # 
  6  # This file is part of translate. 
  7  # 
  8  # This program is free software; you can redistribute it and/or modify 
  9  # it under the terms of the GNU General Public License as published by 
 10  # the Free Software Foundation; either version 2 of the License, or 
 11  # (at your option) any later version. 
 12  # 
 13  # This program is distributed in the hope that it will be useful, 
 14  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 15  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 16  # GNU General Public License for more details. 
 17  # 
 18  # You should have received a copy of the GNU General Public License 
 19  # along with this program; if not, see <http://www.gnu.org/licenses/>. 
 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 
36 37 -class TMServer(object):
38 """A RESTful JSON TM server.""" 39
40 - class RequestHandler(simple_server.WSGIRequestHandler):
41 """Custom request handler, disables some inefficient defaults""" 42
43 - def address_string(self):
44 """Disable client reverse dns lookup.""" 45 return self.client_address[0]
46
47 - def log_message(self, format, *args):
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 #load files into db 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 #initialize url dispatcher 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
83 - def translate_unit(self, environ, start_response, uid, slang, tlang):
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 #uid = unicode(urllib.unquote_plus(uid), "utf-8") 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 #uid = unicode(urllib.unquote_plus(uid), "utf-8") 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
111 - def forget_unit(self, environ, start_response, uid):
112 #FIXME: implement me 113 start_response("200 OK", [('Content-type', 'text/plain')]) 114 #uid = unicode(urllib.unquote_plus(uid), "utf-8") 115 116 return [response]
117 118 @selector.opliant
119 - def get_store_stats(self, environ, start_response, sid):
120 #FIXME: implement me 121 start_response("200 OK", [('Content-type', 'text/plain')]) 122 #sid = unicode(urllib.unquote_plus(sid), "utf-8") 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
147 - def forget_store(self, environ, start_response, sid):
148 #FIXME: implement me 149 start_response("200 OK", [('Content-type', 'text/plain')]) 150 #sid = unicode(urllib.unquote_plus(sid), "utf-8") 151 152 return [response]
153
154 155 -def main():
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 #setup debugging 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