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 43 -class TMServer(object):
44 """A RESTful JSON TM server.""" 45
46 - def __init__(self, tmdbfile, tmfiles, max_candidates=3, min_similarity=75, 47 max_length=1000, prefix="", source_lang=None, target_lang=None):
48 49 self.tmdb = tmdb.TMDB(tmdbfile, max_candidates, min_similarity, max_length) 50 51 #load files into db 52 if isinstance(tmfiles, list): 53 [self.tmdb.add_store(factory.getobject(tmfile), source_lang, target_lang) \ 54 for tmfile in tmfiles] 55 elif tmfiles: 56 self.tmdb.add_store(factory.getobject(tmfiles), source_lang, target_lang) 57 58 #initialize url dispatcher 59 self.rest = selector.Selector(prefix=prefix) 60 self.rest.add("/{slang}/{tlang}/unit/{uid:any}", 61 GET=self.translate_unit, 62 POST=self.update_unit, 63 PUT=self.add_unit, 64 DELETE=self.forget_unit) 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