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