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