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-2010 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 logging 
 26  from cgi import parse_qs 
 27  from optparse import OptionParser 
 28  try: 
 29      import json #available since Python 2.6 
 30  except ImportError: 
 31      import simplejson as json #API compatible with the json module 
 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 
37 38 39 -class TMServer(object):
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 #initialize url dispatcher 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
64 - def _load_files(self, tmfiles):
65 from translate.storage import factory 66 if isinstance(tmfiles, list): 67 [self.tmdb.add_store(factory.getobject(tmfile), source_lang, target_lang) \ 68 for tmfile in tmfiles] 69 elif tmfiles: 70 self.tmdb.add_store(factory.getobject(tmfiles), source_lang, target_lang)
71 72 @selector.opliant
73 - def translate_unit(self, environ, start_response, uid, slang, tlang):
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 #uid = unicode(urllib.unquote_plus(uid), "utf-8") 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 #uid = unicode(urllib.unquote_plus(uid), "utf-8") 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
107 - def forget_unit(self, environ, start_response, uid):
108 #FIXME: implement me 109 start_response("200 OK", [('Content-type', 'text/plain')]) 110 #uid = unicode(urllib.unquote_plus(uid), "utf-8") 111 112 return [response]
113 114 @selector.opliant
115 - def get_store_stats(self, environ, start_response, sid):
116 #FIXME: implement me 117 start_response("200 OK", [('Content-type', 'text/plain')]) 118 #sid = unicode(urllib.unquote_plus(sid), "utf-8") 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 import StringIO 126 from translate.storage import factory 127 start_response("200 OK", [('Content-type', 'text/plain')]) 128 data = StringIO.StringIO(environ['wsgi.input'].read(int(environ['CONTENT_LENGTH']))) 129 data.name = sid 130 store = factory.getobject(data) 131 count = self.tmdb.add_store(store, slang, tlang) 132 response = "added %d units from %s" % (count, sid) 133 return [response]
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
145 - def forget_store(self, environ, start_response, sid):
146 #FIXME: implement me 147 start_response("200 OK", [('Content-type', 'text/plain')]) 148 #sid = unicode(urllib.unquote_plus(sid), "utf-8") 149 150 return [response]
151
152 153 -def main():
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 #setup debugging 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