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