translate.services.tmserver

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