Package translate :: Package storage :: Module project

[hide private]
[frames] | no frames]

# Source Code for Module translate.storage.project

      1  #!/usr/bin/env python
      2  # -*- coding: utf-8 -*-
      3  #
      4  # Copyright 2010 Zuza Software Foundation
      5  #
      6  # This file is part of the Translate Toolkit.
      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  import os
     22  import shutil
     23
     24  from translate.convert import factory as convert_factory
     25
     26  from projstore import ProjectStore
     27
     28  __all__ = ['Project']
     29
     30
     31  # FIXME: Isn't there a better place for this function?
     32  # XXX: Make sure that all extensions supported in translate.convert.factory
     33  #      that are not 3 letters long are added to the first "if" statement in
     34  #      split_extensions() below.
     35 -def split_extensions(filename):
     36      """Split the given filename into a name and extensions part.
     37          The extensions part is defined by any sequence of extensions, where an
     38          extension is a 3-letter, .-separated string or one of "po" or
     39          "properties". If the file name consists entirely out of extensions, the
     40          first part is assumed to be the file name and the rest extensions."""
     41      filename_parts = filename.split(os.extsep)
     42      extensions = []
     43      for part in reversed(filename_parts):
     44          if len(part) != 3 and part not in ('po', 'properties'):
     45              break
     46          extensions.append(part)
     47      if not extensions:
     48          return filename, ''
     49      extensions = [x for x in reversed(extensions)]
     50
     51      if len(extensions) == len(filename_parts):
     52          extensions = extensions[1:]
     53      return os.extsep.join(filename_parts[:-len(extensions)]), os.extsep.join(extensions)
     54
     55
     56 -class Project(object):
     57      """Manages a project store as well as the processes involved in a project
     58          workflow."""
     59
     60      # INITIALIZERS #
     61 -    def __init__(self, projstore=None):
     62          if projstore is None:
     63              projstore = ProjectStore()
     64          self.store = projstore
     65
     66 -    def __del__(self):
     67          if self.store:
     68              del self.store
     69
     70      # METHODS #
     71 -    def add_source(self, srcfile, src_fname=None):
     72          """Proxy for C{self.store.append_sourcefile()}."""
     73          return self.store.append_sourcefile(srcfile, src_fname)
     74
     75 -    def add_source_convert(self, srcfile, src_fname=None, convert_options=None, extension=None):
     76          """Convenience method that calls L{add_source} and L{convert_forward}
     77              and returns the results from both."""
     78          srcfile, srcfname = self.add_source(srcfile, src_fname)
     79          transfile, transfname = self.convert_forward(srcfname, convert_options=convert_options)
     80          return srcfile, srcfname, transfile, transfname
     81
     82 -    def close(self):
     83          """Proxy for C{self.store.close()}."""
     84          self.store.close()
     85
     86 -    def convert_forward(self, input_fname, template=None, output_fname=None, **options):
     87          """Convert the given input file to the next type in the process:
     88              Source document (eg. ODT) -> Translation file (eg. XLIFF) ->
     89              Translated document (eg. ODT).
     90
     91              @type  input_fname: basestring
     92              @param input_fname: The project name of the file to convert
     93              @type  convert_options: dict (optional)
     94              @param convert_options: Passed as-is to
     95                                      C{translate.convert.factory.convert()}.
     96              @returns 2-tuple: the converted file object and it's project name."""
     97          inputfile = self.get_file(input_fname)
     98          input_type = self.store.get_filename_type(input_fname)
     99
    100          if input_type == 'tgt':
    101              raise ValueError('Cannot convert a target document further: %s' % (input_fname))
    102
    103          templ_fname = None
    104          if isinstance(template, basestring):
    105              template, templ_fname = self.get_file(template)
    106
    107          if template and not templ_fname:
    108              templ_fname = template.name
    109
    110          # Check if we can determine a template from the conversion map
    111          if template is None:
    112              convert_map = self.store.convert_map
    113              if input_fname in convert_map:
    114                  templ_fname = convert_map[input_fname][1]
    115                  template = self.get_file(templ_fname)
    116              elif input_type == 'trans':
    117                  # inputfile is a translatable file, so it needed to be converted
    118                  # from some input document. Let's try and use that document as a
    119                  # template for this conversion.
    120                  for in_name, (out_name, tmpl_name) in self.store.convert_map.items():
    121                      if input_fname == out_name:
    122                          template, templ_fname = self.get_file(in_name), in_name
    123                          break
    124
    125          # Populate the conv_options dict with the options we can detect
    126          conv_options = dict(in_fname=input_fname)
    127
    128          if input_fname in self.store.convert_map:
    129              out_name, tmpl_name = self.store.convert_map[input_fname]
    130              if out_name in self.store._files and options.get('overwrite_output', True):
    131                  self.remove_file(out_name)
    132
    133          converted_file, converted_ext = convert_factory.convert(
    134              inputfile,
    135              template=template,
    136              options=conv_options,
    137              convert_options=options.get('convert_options', None)
    138          )
    139
    140          # Determine the file name and path where the output should be moved.
    141          if not output_fname:
    142              _dir, fname = os.path.split(input_fname)
    143              directory = ''
    144              if hasattr(inputfile, 'name'):
    145                  # Prefer to put it in the same directory as the input file
    146                  directory, _fn = os.path.split(inputfile.name)
    147              else:
    148                  # Otherwise put it in the current working directory
    149                  directory = os.getcwd()
    150              output_fname = os.path.join(directory, fname)
    151          output_fname, output_ext = split_extensions(output_fname)
    152          output_ext_parts = output_ext.split(os.extsep)
    153
    154          # Add the output suffix, if supplied
    155          if 'output_suffix' in options:
    156              output_fname += options['output_suffix']
    157
    158          # Check if we are in the situation where the output has an extension
    159          # of, for example, .odt.xlf.odt. If so, we want to change that to only
    160          # .odt.
    161          if len(output_ext_parts) >= 2 and output_ext_parts[-2] == converted_ext:
    162              output_ext_parts = output_ext_parts[:-1]
    163          else:
    164              output_ext_parts.append(converted_ext)
    165          output_fname += os.extsep.join([''] + output_ext_parts)
    166
    167          if os.path.isfile(output_fname):
    168              # If the output file already exist, we can't assume that it's safe
    169              # to overwrite it.
    170              os.unlink(converted_file.name)
    171              raise IOError("Output file already exists: %s" % (output_fname))
    172
    173          os.rename(converted_file.name, output_fname)
    174
    175          output_type = self.store.TYPE_INFO['next_type'][input_type]
    176          outputfile, output_fname = self.store.append_file(
    177              output_fname, None, ftype=output_type, delete_orig=True
    178          )
    179          self.store.convert_map[input_fname] = (output_fname, templ_fname)
    180
    181          return outputfile, output_fname
    182
    183 -    def export_file(self, fname, destfname):
    184          """Export the file with the specified filename to the given destination.
    185              This method will raise L{FileNotInProjectError} via the call to
    186              L{ProjectStore.get_file()} if C{fname} is not found in the project."""
    187          open(destfname, 'w').write(self.store.get_file(fname).read())
    188
    189 -    def get_file(self, fname):
    190          """Proxy for C{self.store.get_file()}."""
    191          return self.store.get_file(fname)
    192
    193 -    def get_proj_filename(self, realfname):
    194          """Proxy for C{self.store.get_proj_filename()}."""
    195          return self.store.get_proj_filename(realfname)
    196
    197 -    def get_real_filename(self, projfname):
    198          """Try and find a real file name for the given project file name."""
    199          projfile = self.get_file(projfname)
    200          rfname = getattr(projfile, 'name', getattr(projfile, 'filename', None))
    201          if rfname is None:
    202              raise ValueError('Project file has no real file: %s' % (projfname))
    203          return rfname
    204
    205 -    def remove_file(self, projfname, ftype=None):
    206          """Proxy for C{self.store.remove_file()}."""
    207          self.store.remove_file(projfname, ftype)
    208
    209 -    def save(self, filename=None):
    210          """Proxy for C{self.store.save()}."""
    211          self.store.save(filename)
    212
    213 -    def update_file(self, proj_fname, infile):
    214          """Proxy for C{self.store.update_file()}."""
    215          self.store.update_file(proj_fname, infile)
    216