We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Init from templates doesnt create directories correctly
### Steps to reproduce:

- create a new project which has subdirectories
- load templates from fs
- init a new tp from admin ui

### Results (Expected/Actual):

- new dirs/stores are created, but the dirs dont have the correct link to their tp

### Environment (i.e. 'pootle --version', DB, OS, Browser):

2.9


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `pootle/apps/pootle_translationproject/models.py`
Content:
```
1 # -*- coding: utf-8 -*-
2 #
3 # Copyright (C) Pootle contributors.
4 #
5 # This file is a part of the Pootle project. It is distributed under the GPL3
6 # or later license. See the LICENSE file for a copy of the license and the
7 # AUTHORS file for copyright and authorship information.
8 
9 import logging
10 import posixpath
11 from pathlib import PurePosixPath
12 
13 from django.contrib.contenttypes.fields import GenericRelation
14 from django.db import models
15 from django.urls import reverse
16 from django.utils.functional import cached_property
17 
18 from pootle.core.delegate import data_tool
19 from pootle.core.mixins import CachedTreeItem
20 from pootle.core.url_helpers import get_editor_filter, split_pootle_path
21 from pootle_app.models.directory import Directory
22 from pootle_checks.constants import EXCLUDED_FILTERS
23 from pootle_language.models import Language
24 from pootle_project.models import Project
25 from pootle_revision.models import Revision
26 from staticpages.models import StaticPage
27 
28 
29 logger = logging.getLogger(__name__)
30 
31 
32 class TranslationProjectManager(models.Manager):
33 
34     def get_terminology_project(self, language_id):
35         # FIXME: the code below currently uses the same approach to determine
36         # the 'terminology' kind of a project as 'Project.is_terminology()',
37         # which means it checks the value of 'checkstyle' field
38         # (see pootle_project/models.py:240).
39         #
40         # This should probably be replaced in the future with a dedicated
41         # project property.
42         return self.get(language=language_id,
43                         project__checkstyle='terminology')
44 
45     def live(self):
46         """Filters translation projects that have non-obsolete directories."""
47         return self.filter(directory__obsolete=False)
48 
49     def for_user(self, user, select_related=None):
50         """Filters translation projects for a specific user.
51 
52         - Admins always get all translation projects.
53         - Regular users only get enabled translation projects
54             accessible to them.
55 
56         :param user: The user for whom the translation projects need to be
57             retrieved for.
58         :return: A filtered queryset with `TranslationProject`s for `user`.
59         """
60         qs = self.live()
61         if select_related is not None:
62             qs = qs.select_related(*select_related)
63 
64         if user.is_superuser:
65             return qs
66 
67         return qs.filter(
68             project__disabled=False,
69             project__code__in=Project.accessible_by_user(user))
70 
71     def get_for_user(self, user, project_code, language_code,
72                      select_related=None):
73         """Gets a `language_code`/`project_code` translation project
74         for a specific `user`.
75 
76         - Admins can get the translation project even
77             if its project is disabled.
78         - Regular users only get a translation project
79             if its project isn't disabled and it is accessible to them.
80 
81         :param user: The user for whom the translation project needs
82             to be retrieved.
83         :param project_code: The code of a project for the TP to retrieve.
84         :param language_code: The code of the language fro the TP to retrieve.
85         :return: The `TranslationProject` matching the params, raises
86             otherwise.
87         """
88         return self.for_user(
89             user, select_related).get(
90                 project__code=project_code,
91                 language__code=language_code)
92 
93 
94 class TranslationProject(models.Model, CachedTreeItem):
95 
96     language = models.ForeignKey(
97         Language, db_index=False, on_delete=models.CASCADE)
98     project = models.ForeignKey(
99         Project, db_index=True, on_delete=models.CASCADE)
100     directory = models.OneToOneField(
101         Directory, db_index=True, editable=False, on_delete=models.CASCADE)
102     pootle_path = models.CharField(max_length=255, null=False, unique=True,
103                                    db_index=True, editable=False)
104     creation_time = models.DateTimeField(auto_now_add=True, db_index=True,
105                                          editable=False, null=True)
106     revisions = GenericRelation(Revision)
107 
108     objects = TranslationProjectManager()
109 
110     class Meta(object):
111         unique_together = (
112             ('language', 'project'),
113             ('project', 'language'))
114         db_table = 'pootle_app_translationproject'
115         # disabled objects are hidden for related objects too
116         base_manager_name = 'objects'
117 
118     @cached_property
119     def code(self):
120         return u'-'.join([self.language.code, self.project.code])
121 
122     @cached_property
123     def data_tool(self):
124         return data_tool.get(self.__class__)(self)
125 
126     # # # # # # # # # # # # # #  Properties # # # # # # # # # # # # # # # # # #
127 
128     @property
129     def name(self):
130         # TODO: See if `self.fullname` can be removed
131         return self.fullname
132 
133     @property
134     def fullname(self):
135         return "%s [%s]" % (self.project.fullname, self.language.name)
136 
137     @property
138     def checker(self):
139         from translate.filters import checks
140         checkerclasses = [
141             checks.projectcheckers.get(
142                 self.project.checkstyle,
143                 checks.StandardChecker)]
144         return checks.TeeChecker(checkerclasses=checkerclasses,
145                                  excludefilters=EXCLUDED_FILTERS,
146                                  errorhandler=self.filtererrorhandler,
147                                  languagecode=self.language.code)
148 
149     @property
150     def disabled(self):
151         return self.project.disabled
152 
153     @cached_property
154     def templates_tp(self):
155         return self.project.get_template_translationproject()
156 
157     @property
158     def is_template_project(self):
159         return self == self.templates_tp
160 
161     # # # # # # # # # # # # # #  Methods # # # # # # # # # # # # # # # # # # #
162 
163     def __unicode__(self):
164         return self.pootle_path
165 
166     def __init__(self, *args, **kwargs):
167         super(TranslationProject, self).__init__(*args, **kwargs)
168 
169     def save(self, *args, **kwargs):
170         self.directory = (
171             self.language.directory.get_or_make_subdir(self.project.code))
172         self.pootle_path = self.directory.pootle_path
173         super(TranslationProject, self).save(*args, **kwargs)
174         if self.directory.tp_id != self.pk:
175             self.directory.tp = self
176             self.directory.save()
177 
178     def delete(self, *args, **kwargs):
179         directory = self.directory
180 
181         super(TranslationProject, self).delete(*args, **kwargs)
182         directory.delete()
183 
184     def get_absolute_url(self):
185         return reverse(
186             'pootle-tp-browse',
187             args=split_pootle_path(self.pootle_path)[:-1])
188 
189     def get_translate_url(self, **kwargs):
190         return u''.join(
191             [reverse("pootle-tp-translate",
192                      args=split_pootle_path(self.pootle_path)[:-1]),
193              get_editor_filter(**kwargs)])
194 
195     def get_announcement(self, user=None):
196         """Return the related announcement, if any."""
197         return StaticPage.get_announcement_for(self.pootle_path, user)
198 
199     def filtererrorhandler(self, functionname, str1, str2, e):
200         logger.error(
201             u"Error in filter %s: %r, %r, %s",
202             functionname,
203             str1,
204             str2, e)
205         return False
206 
207     def is_accessible_by(self, user):
208         """Returns `True` if the current translation project is accessible
209         by `user`.
210         """
211         if user.is_superuser:
212             return True
213 
214         return self.project.code in Project.accessible_by_user(user)
215 
216     def can_be_inited_from_templates(self):
217         """Returns `True` if the current translation project hasn't been
218         saved yet and can be initialized from templates.
219         """
220 
221         # This method checks if the current translation project directory
222         # doesn't exist. So it won't work if the translation project is already
223         # saved the database because the translation project directory is
224         # auto-created in `save()` method.
225         return (
226             not self.is_template_project
227             and self.templates_tp is not None)
228 
229     def create_parent_dirs(self, pootle_path):
230         parent = self.directory
231         dirs_to_create = []
232         for path in PurePosixPath(pootle_path).parents:
233             path = posixpath.join(str(path), "")
234             if path == self.pootle_path:
235                 break
236             dirs_to_create.append(path)
237         for path in reversed(dirs_to_create):
238             parent, __ = Directory.objects.get_or_create(
239                 pootle_path=path,
240                 parent=parent,
241                 name=posixpath.basename(path.rstrip("/")))
242         return parent
243 
244     def init_store_from_template(self, template_store):
245         """Initialize a new file for `self` using `template_store`.
246         """
247         pootle_path = posixpath.join(
248             self.pootle_path.rstrip("/"),
249             template_store.tp_path.lstrip("/"))
250         pootle_path = ".".join(
251             [posixpath.splitext(pootle_path)[0],
252              template_store.filetype.extension.name])
253         name = posixpath.basename(pootle_path)
254         if name in ["template.po", "templates.po"]:
255             # gnu-style layout
256             # use language code instead of template name
257             name = ".".join(
258                 [self.language.code,
259                  template_store.filetype.extension.name])
260             dirname = posixpath.dirname(pootle_path)
261             pootle_path = posixpath.join(dirname, name)
262         if not self.stores.filter(pootle_path=pootle_path).exists():
263             return self.stores.create(
264                 parent=self.create_parent_dirs(pootle_path),
265                 pootle_path=pootle_path,
266                 name=name)
267 
268     def init_from_templates(self):
269         """Initializes the current translation project files using
270         the templates TP ones.
271         """
272         template_stores = self.templates_tp.stores.live().select_related(
273             "filetype__template_extension",
274             "filetype__extension").order_by("creation_time")
275         for template_store in template_stores.iterator():
276             new_store = self.init_store_from_template(template_store)
277             if new_store:
278                 new_store.update(
279                     new_store.deserialize(template_store.serialize()))
280 
281     # # # TreeItem
282     def get_children(self):
283         return self.directory.children
284 
285     def get_parents(self):
286         return [self.project]
287
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/pootle/apps/pootle_translationproject/models.py b/pootle/apps/pootle_translationproject/models.py
--- a/pootle/apps/pootle_translationproject/models.py
+++ b/pootle/apps/pootle_translationproject/models.py
@@ -238,6 +238,7 @@
             parent, __ = Directory.objects.get_or_create(
                 pootle_path=path,
                 parent=parent,
+                tp=self,
                 name=posixpath.basename(path.rstrip("/")))
         return parent

{"golden_diff": "diff --git a/pootle/apps/pootle_translationproject/models.py b/pootle/apps/pootle_translationproject/models.py\n--- a/pootle/apps/pootle_translationproject/models.py\n+++ b/pootle/apps/pootle_translationproject/models.py\n@@ -238,6 +238,7 @@\n             parent, __ = Directory.objects.get_or_create(\n                 pootle_path=path,\n                 parent=parent,\n+                tp=self,\n                 name=posixpath.basename(path.rstrip(\"/\")))\n         return parent\n", "issue": "Init from templates doesnt create directories correctly\n### Steps to reproduce:\r\n\r\n- create a new project which has subdirectories\r\n- load templates from fs\r\n- init a new tp from admin ui\r\n\r\n### Results (Expected/Actual):\r\n\r\n- new dirs/stores are created, but the dirs dont have the correct link to their tp\r\n\r\n### Environment (i.e. 'pootle --version', DB, OS, Browser):\r\n\r\n2.9\r\n\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\n#\n# Copyright (C) Pootle contributors.\n#\n# This file is a part of the Pootle project. It is distributed under the GPL3\n# or later license. See the LICENSE file for a copy of the license and the\n# AUTHORS file for copyright and authorship information.\n\nimport logging\nimport posixpath\nfrom pathlib import PurePosixPath\n\nfrom django.contrib.contenttypes.fields import GenericRelation\nfrom django.db import models\nfrom django.urls import reverse\nfrom django.utils.functional import cached_property\n\nfrom pootle.core.delegate import data_tool\nfrom pootle.core.mixins import CachedTreeItem\nfrom pootle.core.url_helpers import get_editor_filter, split_pootle_path\nfrom pootle_app.models.directory import Directory\nfrom pootle_checks.constants import EXCLUDED_FILTERS\nfrom pootle_language.models import Language\nfrom pootle_project.models import Project\nfrom pootle_revision.models import Revision\nfrom staticpages.models import StaticPage\n\n\nlogger = logging.getLogger(__name__)\n\n\nclass TranslationProjectManager(models.Manager):\n\n    def get_terminology_project(self, language_id):\n        # FIXME: the code below currently uses the same approach to determine\n        # the 'terminology' kind of a project as 'Project.is_terminology()',\n        # which means it checks the value of 'checkstyle' field\n        # (see pootle_project/models.py:240).\n        #\n        # This should probably be replaced in the future with a dedicated\n        # project property.\n        return self.get(language=language_id,\n                        project__checkstyle='terminology')\n\n    def live(self):\n        \"\"\"Filters translation projects that have non-obsolete directories.\"\"\"\n        return self.filter(directory__obsolete=False)\n\n    def for_user(self, user, select_related=None):\n        \"\"\"Filters translation projects for a specific user.\n\n        - Admins always get all translation projects.\n        - Regular users only get enabled translation projects\n            accessible to them.\n\n        :param user: The user for whom the translation projects need to be\n            retrieved for.\n        :return: A filtered queryset with `TranslationProject`s for `user`.\n        \"\"\"\n        qs = self.live()\n        if select_related is not None:\n            qs = qs.select_related(*select_related)\n\n        if user.is_superuser:\n            return qs\n\n        return qs.filter(\n            project__disabled=False,\n            project__code__in=Project.accessible_by_user(user))\n\n    def get_for_user(self, user, project_code, language_code,\n                     select_related=None):\n        \"\"\"Gets a `language_code`/`project_code` translation project\n        for a specific `user`.\n\n        - Admins can get the translation project even\n            if its project is disabled.\n        - Regular users only get a translation project\n            if its project isn't disabled and it is accessible to them.\n\n        :param user: The user for whom the translation project needs\n            to be retrieved.\n        :param project_code: The code of a project for the TP to retrieve.\n        :param language_code: The code of the language fro the TP to retrieve.\n        :return: The `TranslationProject` matching the params, raises\n            otherwise.\n        \"\"\"\n        return self.for_user(\n            user, select_related).get(\n                project__code=project_code,\n                language__code=language_code)\n\n\nclass TranslationProject(models.Model, CachedTreeItem):\n\n    language = models.ForeignKey(\n        Language, db_index=False, on_delete=models.CASCADE)\n    project = models.ForeignKey(\n        Project, db_index=True, on_delete=models.CASCADE)\n    directory = models.OneToOneField(\n        Directory, db_index=True, editable=False, on_delete=models.CASCADE)\n    pootle_path = models.CharField(max_length=255, null=False, unique=True,\n                                   db_index=True, editable=False)\n    creation_time = models.DateTimeField(auto_now_add=True, db_index=True,\n                                         editable=False, null=True)\n    revisions = GenericRelation(Revision)\n\n    objects = TranslationProjectManager()\n\n    class Meta(object):\n        unique_together = (\n            ('language', 'project'),\n            ('project', 'language'))\n        db_table = 'pootle_app_translationproject'\n        # disabled objects are hidden for related objects too\n        base_manager_name = 'objects'\n\n    @cached_property\n    def code(self):\n        return u'-'.join([self.language.code, self.project.code])\n\n    @cached_property\n    def data_tool(self):\n        return data_tool.get(self.__class__)(self)\n\n    # # # # # # # # # # # # # #  Properties # # # # # # # # # # # # # # # # # #\n\n    @property\n    def name(self):\n        # TODO: See if `self.fullname` can be removed\n        return self.fullname\n\n    @property\n    def fullname(self):\n        return \"%s [%s]\" % (self.project.fullname, self.language.name)\n\n    @property\n    def checker(self):\n        from translate.filters import checks\n        checkerclasses = [\n            checks.projectcheckers.get(\n                self.project.checkstyle,\n                checks.StandardChecker)]\n        return checks.TeeChecker(checkerclasses=checkerclasses,\n                                 excludefilters=EXCLUDED_FILTERS,\n                                 errorhandler=self.filtererrorhandler,\n                                 languagecode=self.language.code)\n\n    @property\n    def disabled(self):\n        return self.project.disabled\n\n    @cached_property\n    def templates_tp(self):\n        return self.project.get_template_translationproject()\n\n    @property\n    def is_template_project(self):\n        return self == self.templates_tp\n\n    # # # # # # # # # # # # # #  Methods # # # # # # # # # # # # # # # # # # #\n\n    def __unicode__(self):\n        return self.pootle_path\n\n    def __init__(self, *args, **kwargs):\n        super(TranslationProject, self).__init__(*args, **kwargs)\n\n    def save(self, *args, **kwargs):\n        self.directory = (\n            self.language.directory.get_or_make_subdir(self.project.code))\n        self.pootle_path = self.directory.pootle_path\n        super(TranslationProject, self).save(*args, **kwargs)\n        if self.directory.tp_id != self.pk:\n            self.directory.tp = self\n            self.directory.save()\n\n    def delete(self, *args, **kwargs):\n        directory = self.directory\n\n        super(TranslationProject, self).delete(*args, **kwargs)\n        directory.delete()\n\n    def get_absolute_url(self):\n        return reverse(\n            'pootle-tp-browse',\n            args=split_pootle_path(self.pootle_path)[:-1])\n\n    def get_translate_url(self, **kwargs):\n        return u''.join(\n            [reverse(\"pootle-tp-translate\",\n                     args=split_pootle_path(self.pootle_path)[:-1]),\n             get_editor_filter(**kwargs)])\n\n    def get_announcement(self, user=None):\n        \"\"\"Return the related announcement, if any.\"\"\"\n        return StaticPage.get_announcement_for(self.pootle_path, user)\n\n    def filtererrorhandler(self, functionname, str1, str2, e):\n        logger.error(\n            u\"Error in filter %s: %r, %r, %s\",\n            functionname,\n            str1,\n            str2, e)\n        return False\n\n    def is_accessible_by(self, user):\n        \"\"\"Returns `True` if the current translation project is accessible\n        by `user`.\n        \"\"\"\n        if user.is_superuser:\n            return True\n\n        return self.project.code in Project.accessible_by_user(user)\n\n    def can_be_inited_from_templates(self):\n        \"\"\"Returns `True` if the current translation project hasn't been\n        saved yet and can be initialized from templates.\n        \"\"\"\n\n        # This method checks if the current translation project directory\n        # doesn't exist. So it won't work if the translation project is already\n        # saved the database because the translation project directory is\n        # auto-created in `save()` method.\n        return (\n            not self.is_template_project\n            and self.templates_tp is not None)\n\n    def create_parent_dirs(self, pootle_path):\n        parent = self.directory\n        dirs_to_create = []\n        for path in PurePosixPath(pootle_path).parents:\n            path = posixpath.join(str(path), \"\")\n            if path == self.pootle_path:\n                break\n            dirs_to_create.append(path)\n        for path in reversed(dirs_to_create):\n            parent, __ = Directory.objects.get_or_create(\n                pootle_path=path,\n                parent=parent,\n                name=posixpath.basename(path.rstrip(\"/\")))\n        return parent\n\n    def init_store_from_template(self, template_store):\n        \"\"\"Initialize a new file for `self` using `template_store`.\n        \"\"\"\n        pootle_path = posixpath.join(\n            self.pootle_path.rstrip(\"/\"),\n            template_store.tp_path.lstrip(\"/\"))\n        pootle_path = \".\".join(\n            [posixpath.splitext(pootle_path)[0],\n             template_store.filetype.extension.name])\n        name = posixpath.basename(pootle_path)\n        if name in [\"template.po\", \"templates.po\"]:\n            # gnu-style layout\n            # use language code instead of template name\n            name = \".\".join(\n                [self.language.code,\n                 template_store.filetype.extension.name])\n            dirname = posixpath.dirname(pootle_path)\n            pootle_path = posixpath.join(dirname, name)\n        if not self.stores.filter(pootle_path=pootle_path).exists():\n            return self.stores.create(\n                parent=self.create_parent_dirs(pootle_path),\n                pootle_path=pootle_path,\n                name=name)\n\n    def init_from_templates(self):\n        \"\"\"Initializes the current translation project files using\n        the templates TP ones.\n        \"\"\"\n        template_stores = self.templates_tp.stores.live().select_related(\n            \"filetype__template_extension\",\n            \"filetype__extension\").order_by(\"creation_time\")\n        for template_store in template_stores.iterator():\n            new_store = self.init_store_from_template(template_store)\n            if new_store:\n                new_store.update(\n                    new_store.deserialize(template_store.serialize()))\n\n    # # # TreeItem\n    def get_children(self):\n        return self.directory.children\n\n    def get_parents(self):\n        return [self.project]\n", "path": "pootle/apps/pootle_translationproject/models.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\n#\n# Copyright (C) Pootle contributors.\n#\n# This file is a part of the Pootle project. It is distributed under the GPL3\n# or later license. See the LICENSE file for a copy of the license and the\n# AUTHORS file for copyright and authorship information.\n\nimport logging\nimport posixpath\nfrom pathlib import PurePosixPath\n\nfrom django.contrib.contenttypes.fields import GenericRelation\nfrom django.db import models\nfrom django.urls import reverse\nfrom django.utils.functional import cached_property\n\nfrom pootle.core.delegate import data_tool\nfrom pootle.core.mixins import CachedTreeItem\nfrom pootle.core.url_helpers import get_editor_filter, split_pootle_path\nfrom pootle_app.models.directory import Directory\nfrom pootle_checks.constants import EXCLUDED_FILTERS\nfrom pootle_language.models import Language\nfrom pootle_project.models import Project\nfrom pootle_revision.models import Revision\nfrom staticpages.models import StaticPage\n\n\nlogger = logging.getLogger(__name__)\n\n\nclass TranslationProjectManager(models.Manager):\n\n    def get_terminology_project(self, language_id):\n        # FIXME: the code below currently uses the same approach to determine\n        # the 'terminology' kind of a project as 'Project.is_terminology()',\n        # which means it checks the value of 'checkstyle' field\n        # (see pootle_project/models.py:240).\n        #\n        # This should probably be replaced in the future with a dedicated\n        # project property.\n        return self.get(language=language_id,\n                        project__checkstyle='terminology')\n\n    def live(self):\n        \"\"\"Filters translation projects that have non-obsolete directories.\"\"\"\n        return self.filter(directory__obsolete=False)\n\n    def for_user(self, user, select_related=None):\n        \"\"\"Filters translation projects for a specific user.\n\n        - Admins always get all translation projects.\n        - Regular users only get enabled translation projects\n            accessible to them.\n\n        :param user: The user for whom the translation projects need to be\n            retrieved for.\n        :return: A filtered queryset with `TranslationProject`s for `user`.\n        \"\"\"\n        qs = self.live()\n        if select_related is not None:\n            qs = qs.select_related(*select_related)\n\n        if user.is_superuser:\n            return qs\n\n        return qs.filter(\n            project__disabled=False,\n            project__code__in=Project.accessible_by_user(user))\n\n    def get_for_user(self, user, project_code, language_code,\n                     select_related=None):\n        \"\"\"Gets a `language_code`/`project_code` translation project\n        for a specific `user`.\n\n        - Admins can get the translation project even\n            if its project is disabled.\n        - Regular users only get a translation project\n            if its project isn't disabled and it is accessible to them.\n\n        :param user: The user for whom the translation project needs\n            to be retrieved.\n        :param project_code: The code of a project for the TP to retrieve.\n        :param language_code: The code of the language fro the TP to retrieve.\n        :return: The `TranslationProject` matching the params, raises\n            otherwise.\n        \"\"\"\n        return self.for_user(\n            user, select_related).get(\n                project__code=project_code,\n                language__code=language_code)\n\n\nclass TranslationProject(models.Model, CachedTreeItem):\n\n    language = models.ForeignKey(\n        Language, db_index=False, on_delete=models.CASCADE)\n    project = models.ForeignKey(\n        Project, db_index=True, on_delete=models.CASCADE)\n    directory = models.OneToOneField(\n        Directory, db_index=True, editable=False, on_delete=models.CASCADE)\n    pootle_path = models.CharField(max_length=255, null=False, unique=True,\n                                   db_index=True, editable=False)\n    creation_time = models.DateTimeField(auto_now_add=True, db_index=True,\n                                         editable=False, null=True)\n    revisions = GenericRelation(Revision)\n\n    objects = TranslationProjectManager()\n\n    class Meta(object):\n        unique_together = (\n            ('language', 'project'),\n            ('project', 'language'))\n        db_table = 'pootle_app_translationproject'\n        # disabled objects are hidden for related objects too\n        base_manager_name = 'objects'\n\n    @cached_property\n    def code(self):\n        return u'-'.join([self.language.code, self.project.code])\n\n    @cached_property\n    def data_tool(self):\n        return data_tool.get(self.__class__)(self)\n\n    # # # # # # # # # # # # # #  Properties # # # # # # # # # # # # # # # # # #\n\n    @property\n    def name(self):\n        # TODO: See if `self.fullname` can be removed\n        return self.fullname\n\n    @property\n    def fullname(self):\n        return \"%s [%s]\" % (self.project.fullname, self.language.name)\n\n    @property\n    def checker(self):\n        from translate.filters import checks\n        checkerclasses = [\n            checks.projectcheckers.get(\n                self.project.checkstyle,\n                checks.StandardChecker)]\n        return checks.TeeChecker(checkerclasses=checkerclasses,\n                                 excludefilters=EXCLUDED_FILTERS,\n                                 errorhandler=self.filtererrorhandler,\n                                 languagecode=self.language.code)\n\n    @property\n    def disabled(self):\n        return self.project.disabled\n\n    @cached_property\n    def templates_tp(self):\n        return self.project.get_template_translationproject()\n\n    @property\n    def is_template_project(self):\n        return self == self.templates_tp\n\n    # # # # # # # # # # # # # #  Methods # # # # # # # # # # # # # # # # # # #\n\n    def __unicode__(self):\n        return self.pootle_path\n\n    def __init__(self, *args, **kwargs):\n        super(TranslationProject, self).__init__(*args, **kwargs)\n\n    def save(self, *args, **kwargs):\n        self.directory = (\n            self.language.directory.get_or_make_subdir(self.project.code))\n        self.pootle_path = self.directory.pootle_path\n        super(TranslationProject, self).save(*args, **kwargs)\n        if self.directory.tp_id != self.pk:\n            self.directory.tp = self\n            self.directory.save()\n\n    def delete(self, *args, **kwargs):\n        directory = self.directory\n\n        super(TranslationProject, self).delete(*args, **kwargs)\n        directory.delete()\n\n    def get_absolute_url(self):\n        return reverse(\n            'pootle-tp-browse',\n            args=split_pootle_path(self.pootle_path)[:-1])\n\n    def get_translate_url(self, **kwargs):\n        return u''.join(\n            [reverse(\"pootle-tp-translate\",\n                     args=split_pootle_path(self.pootle_path)[:-1]),\n             get_editor_filter(**kwargs)])\n\n    def get_announcement(self, user=None):\n        \"\"\"Return the related announcement, if any.\"\"\"\n        return StaticPage.get_announcement_for(self.pootle_path, user)\n\n    def filtererrorhandler(self, functionname, str1, str2, e):\n        logger.error(\n            u\"Error in filter %s: %r, %r, %s\",\n            functionname,\n            str1,\n            str2, e)\n        return False\n\n    def is_accessible_by(self, user):\n        \"\"\"Returns `True` if the current translation project is accessible\n        by `user`.\n        \"\"\"\n        if user.is_superuser:\n            return True\n\n        return self.project.code in Project.accessible_by_user(user)\n\n    def can_be_inited_from_templates(self):\n        \"\"\"Returns `True` if the current translation project hasn't been\n        saved yet and can be initialized from templates.\n        \"\"\"\n\n        # This method checks if the current translation project directory\n        # doesn't exist. So it won't work if the translation project is already\n        # saved the database because the translation project directory is\n        # auto-created in `save()` method.\n        return (\n            not self.is_template_project\n            and self.templates_tp is not None)\n\n    def create_parent_dirs(self, pootle_path):\n        parent = self.directory\n        dirs_to_create = []\n        for path in PurePosixPath(pootle_path).parents:\n            path = posixpath.join(str(path), \"\")\n            if path == self.pootle_path:\n                break\n            dirs_to_create.append(path)\n        for path in reversed(dirs_to_create):\n            parent, __ = Directory.objects.get_or_create(\n                pootle_path=path,\n                parent=parent,\n                tp=self,\n                name=posixpath.basename(path.rstrip(\"/\")))\n        return parent\n\n    def init_store_from_template(self, template_store):\n        \"\"\"Initialize a new file for `self` using `template_store`.\n        \"\"\"\n        pootle_path = posixpath.join(\n            self.pootle_path.rstrip(\"/\"),\n            template_store.tp_path.lstrip(\"/\"))\n        pootle_path = \".\".join(\n            [posixpath.splitext(pootle_path)[0],\n             template_store.filetype.extension.name])\n        name = posixpath.basename(pootle_path)\n        if name in [\"template.po\", \"templates.po\"]:\n            # gnu-style layout\n            # use language code instead of template name\n            name = \".\".join(\n                [self.language.code,\n                 template_store.filetype.extension.name])\n            dirname = posixpath.dirname(pootle_path)\n            pootle_path = posixpath.join(dirname, name)\n        if not self.stores.filter(pootle_path=pootle_path).exists():\n            return self.stores.create(\n                parent=self.create_parent_dirs(pootle_path),\n                pootle_path=pootle_path,\n                name=name)\n\n    def init_from_templates(self):\n        \"\"\"Initializes the current translation project files using\n        the templates TP ones.\n        \"\"\"\n        template_stores = self.templates_tp.stores.live().select_related(\n            \"filetype__template_extension\",\n            \"filetype__extension\").order_by(\"creation_time\")\n        for template_store in template_stores.iterator():\n            new_store = self.init_store_from_template(template_store)\n            if new_store:\n                new_store.update(\n                    new_store.deserialize(template_store.serialize()))\n\n    # # # TreeItem\n    def get_children(self):\n        return self.directory.children\n\n    def get_parents(self):\n        return [self.project]\n", "path": "pootle/apps/pootle_translationproject/models.py"}]}