Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/Pootle-2.0.0/local_apps/pootle_app/project_tree.py
blob: f529766d95b8a375d9011e33915d82aea0d43922 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright 2009 Zuza Software Foundation
#
# This file is part of Pootle.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, see <http://www.gnu.org/licenses/>.

import os
import cStringIO

from translate.lang    import data as langdata
from translate.convert import pot2po

from pootle_misc.util import deletefromcache
from pootle_store.models      import Store
from pootle_store.util import absolute_real_path, relative_real_path
from pootle_app.models.directory  import Directory
from pootle_app.models.signals import post_template_update

def language_match_filename(language_code, path_name):
    name, ext = os.path.splitext(os.path.basename(path_name))
    return langdata.languagematch(language_code, name)

def direct_language_match_filename(language_code, path_name):
    name, ext = os.path.splitext(os.path.basename(path_name))
    return language_code == name

def match_template_filename(project, path_name):
    """test if path_name might point at a template file for given
    project"""
    name, ext = os.path.splitext(os.path.basename(path_name))
    #FIXME: is the test for matching extension redundant?
    if ext == os.path.extsep + project.get_template_filtetype():
        if ext != os.path.extsep + project.localfiletype:
            # template extension is distinct, surely file is a template
            return True
        elif not langdata.langcode_re.match(name):
            # file name can't possibly match any language, assume it is a template
            return True
    return False

def get_matching_language_dirs(project_dir, language):
    return [lang_dir for lang_dir in os.listdir(project_dir)
            if language.code == lang_dir]

def get_non_existant_language_dir(project_dir, language, file_style, make_dirs):
    if file_style == "gnu":
        return project_dir
    else:
        if make_dirs:
            language_dir = os.path.join(project_dir, language.code)
            os.mkdir(language_dir)
            return language_dir
        else:
            raise IndexError("directory not found for language %s, project %s" % (language.code, project_dir))

def get_or_make_language_dir(project_dir, language, file_style, make_dirs):
    matching_language_dirs = get_matching_language_dirs(project_dir, language)
    if len(matching_language_dirs) == 0:
        # if no matching directories can be found, check if it is a GNU-style project
        return get_non_existant_language_dir(project_dir, language, file_style, make_dirs)
    else:
        return os.path.join(project_dir, matching_language_dirs[0])

def get_language_dir(project_dir, language, file_style, make_dirs):
    language_dir = os.path.join(project_dir, language.code)
    if not os.path.exists(language_dir):
        return get_or_make_language_dir(project_dir, language, file_style, make_dirs)
    else:
        return language_dir


def get_translation_project_dir(language, project_dir, file_style, make_dirs=False):
    """returns the base directory containing po files for the project

    If make_dirs is True, then we will create project and language
    directories as necessary.
    """
    if file_style == 'gnu':
        return project_dir
    else:
        return get_language_dir(project_dir, language, file_style, make_dirs)

def is_hidden_file(path):
    return path[0] == '.'

def split_files_and_dirs(ignored_files, ext, real_dir, file_filter):
    files = []
    dirs = []
    for child_path in [child_path for child_path in os.listdir(real_dir)
                       if child_path not in ignored_files and not is_hidden_file(child_path)]:
        full_child_path = os.path.join(real_dir, child_path)
        if os.path.isfile(full_child_path) and full_child_path.endswith(ext) and file_filter(full_child_path):
            files.append(child_path)
        elif os.path.isdir(full_child_path):
            dirs.append(child_path)
    return files, dirs

def add_items(fs_items, db_items, create_db_item):
    items = []
    fs_items_set = set(fs_items)
    db_items_set = set(db_items)

    items_to_delete = db_items_set - fs_items_set
    items_to_create = fs_items_set - db_items_set

    for name in items_to_delete:
        db_items[name].delete()

    for name in db_items_set - items_to_delete:
        items.append(db_items[name])

    for name in items_to_create:
        item = create_db_item(name)
        items.append(item)
        item.save()

    return items

def add_files(ignored_files, ext, real_dir, db_dir, file_filter=lambda _x: True):
    files, dirs = split_files_and_dirs(ignored_files, ext, real_dir, file_filter)
    existing_stores = dict((store.name, store) for store in db_dir.child_stores.all())
    existing_dirs = dict((dir.name, dir) for dir in db_dir.child_dirs.all())
    add_items(files, existing_stores,
              lambda name: Store(file = relative_real_path(os.path.join(real_dir, name)),
                                 parent    = db_dir,
                                 name      = name))

    db_subdirs = add_items(dirs, existing_dirs,
                           lambda name: Directory(name=name, parent=db_dir))

    for db_subdir in db_subdirs:
        fs_subdir = os.path.join(real_dir, db_subdir.name)
        add_files(ignored_files, ext, fs_subdir, db_subdir, file_filter)


def translation_project_should_exist(language, project):
    """tests if there are translation files corresponding to given
    language and project"""
    if project.get_treestyle() == "gnu":
        # GNU style projects are tricky

        if language.code == 'templates':
            # language is template look for template files
            for dirpath, dirnames, filenames in os.walk(project.get_real_path()):
                for filename in filenames:
                    if filename.endswith(os.path.extsep + project.get_template_filtetype()):
                        if project.get_template_filtetype() != project.localfiletype:
                            # templates and translation files have a
                            # different extension, easy to detect
                            # templates
                            return True
                        elif not langdata.langcode_re.match(os.path.splitext(filename)[0]):
                            # can't tell templates by their extension,
                            # assume any translation file that can't
                            # be a language name is a template
                            return True
        else:
            # find files with the language name in the project dir
            for dirpath, dirnames, filenames in os.walk(project.get_real_path()):
                for filename in filenames:
                    if project.file_belongs_to_project(filename, match_templates=False) and \
                           os.path.splitext(filename)[0] == language.code:
                        return True
    else:
        # find directory with the language name in the project dir
        dirpath, dirnames, filename = os.walk(project.get_real_path()).next()
        if language.code in dirnames:
            return True

    return False

def scan_translation_project_files(translation_project):
    """returns a list of po files for the project and language"""
    project       = translation_project.project
    real_path     = translation_project.abs_real_path
    directory     = translation_project.directory
    ignored_files = set(p.strip() for p in project.ignoredfiles.split(','))
    ext           = os.extsep + project.localfiletype

    # scan for pots if template project
    if translation_project.is_template_project:
        ext = os.extsep + project.get_template_filtetype()

    if translation_project.file_style == 'gnu':
        if translation_project.is_template_project:
            add_files(ignored_files, ext, real_path, directory,
                      lambda filename: match_template_filename(project, filename))
        else:
            add_files(ignored_files, ext, real_path, directory,
                      lambda filename: direct_language_match_filename(translation_project.language.code, filename))
    else:
        add_files(ignored_files, ext, real_path, directory)
    for store in translation_project.stores.all():
        store.file._delete_store_cache()


def get_extension(language, project):
    """file extension used for this project, returns pot if it's a po
    project and language is templates"""
    ext = project.localfiletype
    if language.code == 'templates' and ext == 'po':
        return 'pot'
    else:
        return ext

def ensure_target_dir_exists(target_path):
    target_dir = os.path.dirname(target_path)
    if not os.path.exists(target_dir):
        os.makedirs(target_dir)

def read_original_target(target_path):
    try:
        return open(target_path, "rb")
    except:
        return None

def convert_template(template_path, target_path):
    ensure_target_dir_exists(target_path)
    template_file = open(template_path, "rb")
    target_file   = cStringIO.StringIO()
    original_file = read_original_target(target_path)
    pot2po.convertpot(template_file, target_file, original_file)
    try:
        output_file = open(target_path, "wb")
        output_file.write(target_file.getvalue())
    finally:
        output_file.close()

def get_translated_name_gnu(translation_project, store):
    path_parts = store.file.path.split(os.sep)
    path_parts[-1] =  "%s.%s" % (translation_project.language.code,
                                 translation_project.project.localfiletype)
    return os.sep.join(path_parts)

def get_translated_name(translation_project, store):
    name, ext = os.path.splitext(store.name)
    path_parts = store.file.name.split(os.sep)

    # replace language code
    path_parts[1] = translation_project.language.code
    # replace extension
    path_parts[-1] = name + '.' + translation_project.project.localfiletype

    return absolute_real_path(os.sep.join(path_parts))

def convert_templates(template_translation_project, translation_project):
    oldstats = translation_project.getquickstats()
    for store in template_translation_project.stores.all():
        if translation_project.file_style == 'gnu':
            new_store_path = get_translated_name_gnu(translation_project, store)
        else:
            new_store_path = get_translated_name(translation_project, store)
        convert_template(store.file.path, new_store_path)
    scan_translation_project_files(translation_project)
    newstats = translation_project.getquickstats()
    post_template_update.send(sender=translation_project, oldstats=oldstats, newstats=newstats)