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