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  try: 
 30      import json #available since Python 2.6 
 31  except ImportError: 
 32      import simplejson as json #API compatible with the json module 
 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 
40 41 -class TMServer(object):
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 #load files into db 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 #initialize url dispatcher 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
72 - def translate_unit(self, environ, start_response, uid, slang, tlang):
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 #uid = unicode(urllib.unquote_plus(uid), "utf-8") 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 #uid = unicode(urllib.unquote_plus(uid), "utf-8") 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
100 - def forget_unit(self, environ, start_response, uid):
101 #FIXME: implement me 102 start_response("200 OK", [('Content-type', 'text/plain')]) 103 #uid = unicode(urllib.unquote_plus(uid), "utf-8") 104 105 return [response]
106 107 @selector.opliant
108 - def get_store_stats(self, environ, start_response, sid):
109 #FIXME: implement me 110 start_response("200 OK", [('Content-type', 'text/plain')]) 111 #sid = unicode(urllib.unquote_plus(sid), "utf-8") 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
136 - def forget_store(self, environ, start_response, sid):
137 #FIXME: implement me 138 start_response("200 OK", [('Content-type', 'text/plain')]) 139 #sid = unicode(urllib.unquote_plus(sid), "utf-8") 140 141 return [response]
142
143 144 -def main():
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 #setup debugging 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