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 cgi import parse_qs 
 29  from optparse import OptionParser 
 30  try: 
 31      import json #available since Python 2.6 
 32  except ImportError: 
 33      import simplejson as json #API compatible with the json module 
 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 
41 42 -class TMServer(object):
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 #load files into db 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 #initialize url dispatcher 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
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 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
143 - def forget_store(self, environ, start_response, sid):
144 #FIXME: implement me 145 start_response("200 OK", [('Content-type', 'text/plain')]) 146 #sid = unicode(urllib.unquote_plus(sid), "utf-8") 147 148 return [response]
149
150 151 -def main():
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 #setup debugging 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