Repository: translate/pootle

Issue:
Create an InitDB class
i think it would be cleaner to put the functions in pootle.core.initdb into a class



Discussion:
FWIW there's nothing wrong with plain functions in this context. Maybe you want to elaborate on the reasons why you propose the change?

for a few reasons:
- these functions logically belong together
- there are better conditions that could be written and these are similar in each of the tests - so they could probably use instance properties/attributes
- imho testing for these functions would be easier looking at them as logical group
- basically the generic reasons for OOP

...make it easier for endusers to customise initialisation behaviour

Initialization is a one-off thing, so there's probably something wrong if a deployment wants to customize it. And speaking of grouping, the module itself serves such a purpose.

Having said that, if you really want to make the change that's your call, however I don't see any great benefits to do so and personally I wouldn't put my time on it.

well there are some improvements i would like to make beyond changes in #4224 - and it would be a lot easier to do imho with these functions in a class

Which improvements are those?

off the top of my head:
- being able to do InitDB().has_run_previously() in mngmt command
- being able to specify which project/s to load at init
- being able to run methods selectively
- being able to write a deployment that extends init behaviour


Resolving patch:
diff --git a/pootle/apps/pootle_app/management/commands/initdb.py b/pootle/apps/pootle_app/management/commands/initdb.py
--- a/pootle/apps/pootle_app/management/commands/initdb.py
+++ b/pootle/apps/pootle_app/management/commands/initdb.py
@@ -15,7 +15,7 @@
 
 from django.core.management.base import NoArgsCommand
 
-from pootle.core.initdb import initdb
+from pootle.core.initdb import InitDB
 
 
 class Command(NoArgsCommand):
@@ -33,7 +33,7 @@ class Command(NoArgsCommand):
 
     def handle_noargs(self, **options):
         self.stdout.write('Populating the database.')
-        initdb(options["create_projects"])
+        InitDB().init_db(options["create_projects"])
         self.stdout.write('Successfully populated the database.')
         self.stdout.write("To create an admin user, use the `pootle "
                           "createsuperuser` command.")
diff --git a/pootle/core/initdb.py b/pootle/core/initdb.py
--- a/pootle/core/initdb.py
+++ b/pootle/core/initdb.py
@@ -25,277 +25,273 @@
 logger = logging.getLogger(__name__)
 
 
-def initdb(create_projects=True):
-    """Populate the database with default initial data.
-
-    This creates the default database to get a working Pootle installation.
-    """
-    create_revision()
-    create_essential_users()
-    create_root_directories()
-    create_template_languages()
-    if create_projects:
-        create_terminology_project()
-    create_pootle_permissions()
-    create_pootle_permission_sets()
-    if create_projects:
-        create_default_projects()
-    create_default_languages()
-
-
-def _create_object(model_klass, **criteria):
-    instance, created = model_klass.objects.get_or_create(**criteria)
-    if created:
-        logger.debug("Created %s: '%s'", instance.__class__.__name__, instance)
-    else:
-        logger.debug("%s already exists - skipping: '%s'",
-                     instance.__class__.__name__, instance)
-    return instance, created
-
-
-def _create_pootle_user(**criteria):
-    user, created = _create_object(get_user_model(), **criteria)
-    if created:
-        user.set_unusable_password()
-        user.save()
-    return user
-
-
-def _create_pootle_permission_set(permissions, **criteria):
-    permission_set, created = _create_object(PermissionSet, **criteria)
-    if created:
-        permission_set.positive_permissions = permissions
-        permission_set.save()
-    return permission_set
-
-
-def create_revision():
-    Revision.initialize()
-
-
-def create_essential_users():
-    """Create the 'default' and 'nobody' User instances.
-
-    These users are required for Pootle's permission system.
-    """
-    # The nobody user is used to represent an anonymous user in cases where
-    # we need to associate model information with such a user. An example is
-    # in the permission system: we need a way to store rights for anonymous
-    # users; thus we use the nobody user.
-    criteria = {
-        'username': u"nobody",
-        'full_name': u"any anonymous user",
-        'is_active': True,
-    }
-    _create_pootle_user(**criteria)
-
-    # The 'default' user represents any valid, non-anonymous user and is used
-    # to associate information any such user. An example is in the permission
-    # system: we need a way to store default rights for users. We use the
-    # 'default' user for this.
-    #
-    # In a future version of Pootle we should think about using Django's
-    # groups to do better permissions handling.
-    criteria = {
-        'username': u"default",
-        'full_name': u"any authenticated user",
-        'is_active': True,
-    }
-    _create_pootle_user(**criteria)
-
-    # The system user represents a system, and is used to
-    # associate updates done by bulk commands as update_stores.
-    criteria = {
-        'username': u"system",
-        'full_name': u"system user",
-        'is_active': True,
-    }
-    _create_pootle_user(**criteria)
-
-
-def create_pootle_permissions():
-    """Create Pootle's directory level permissions."""
-
-    args = {
-        'app_label': "pootle_app",
-        'model': "directory",
-    }
-
-    pootle_content_type, created = _create_object(ContentType, **args)
-    pootle_content_type.name = 'pootle'
-    pootle_content_type.save()
-
-    # Create the permissions.
-    permissions = [
-        {
-            'name': _("Can access a project"),
-            'codename': "view",
-        },
-        {
-            'name': _("Cannot access a project"),
-            'codename': "hide",
-        },
-        {
-            'name': _("Can make a suggestion for a translation"),
-            'codename': "suggest",
-        },
-        {
-            'name': _("Can submit a translation"),
-            'codename': "translate",
-        },
-        {
-            'name': _("Can review suggestions"),
-            'codename': "review",
-        },
-        {
-            'name': _("Can administrate a translation project"),
-            'codename': "administrate",
-        },
-    ]
-
-    criteria = {
-        'content_type': pootle_content_type,
-    }
-
-    for permission in permissions:
-        criteria.update(permission)
-        _create_object(Permission, **criteria)
-
-
-def create_pootle_permission_sets():
-    """Create the default permission set for the 'nobody' and 'default' users.
-
-    'nobody' is the anonymous (non-logged in) user, and 'default' is the logged
-    in user.
-    """
-    User = get_user_model()
-
-    nobody = User.objects.get(username='nobody')
-    default = User.objects.get(username='default')
-
-    view = get_pootle_permission('view')
-    suggest = get_pootle_permission('suggest')
-    translate = get_pootle_permission('translate')
-
-    # Default permissions for tree root.
-    criteria = {
-        'user': nobody,
-        'directory': Directory.objects.root,
-    }
-    _create_pootle_permission_set([view, suggest], **criteria)
-
-    criteria['user'] = default
-    _create_pootle_permission_set([view, suggest, translate], **criteria)
-
-    # Default permissions for templates language.
-    # Override with no permissions for templates language.
-    criteria = {
-        'user': nobody,
-        'directory': Directory.objects.get(pootle_path="/templates/"),
-    }
-    _create_pootle_permission_set([], **criteria)
-
-    criteria['user'] = default
-    _create_pootle_permission_set([], **criteria)
-
-
-def require_english():
-    """Create the English Language item."""
-    criteria = {
-        'code': "en",
-        'fullname': u"English",
-        'nplurals': 2,
-        'pluralequation': "(n != 1)",
-    }
-    en, created = _create_object(Language, **criteria)
-    return en
-
-
-def create_root_directories():
-    """Create the root Directory items."""
-    root, created = _create_object(Directory, **dict(name=""))
-    _create_object(Directory, **dict(name="projects", parent=root))
-
-
-def create_template_languages():
-    """Create the 'templates' and English languages.
-
-    The 'templates' language is used to give users access to the untranslated
-    template files.
-    """
-    _create_object(Language, **dict(code="templates", fullname="Templates"))
-    require_english()
-
-
-def create_terminology_project():
-    """Create the terminology project.
-
-    The terminology project is used to display terminology suggestions while
-    translating.
-    """
-    criteria = {
-        'code': "terminology",
-        'fullname': u"Terminology",
-        'source_language': require_english(),
-        'checkstyle': "terminology",
-    }
-    _create_object(Project, **criteria)
-
-
-def create_default_projects():
-    """Create the default projects that we host.
-
-    You might want to add your projects here, although you can also add things
-    through the web interface later.
-    """
-    from pootle_project.models import Project
-
-    en = require_english()
-
-    criteria = {
-        'code': u"tutorial",
-        'source_language': en,
-        'fullname': u"Tutorial",
-        'checkstyle': "standard",
-        'localfiletype': "po",
-        'treestyle': "auto",
-    }
-    tutorial, created = _create_object(Project, **criteria)
-
-    criteria = {
-        'active': True,
-        'title': "Project instructions",
-        'body': ('<div dir="ltr" lang="en">Tutorial project where users can '
-                 'play with Pootle and learn more about translation and '
-                 'localisation.<br />For more help on localisation, visit the '
-                 '<a href="http://docs.translatehouse.org/projects/'
-                 'localization-guide/en/latest/guide/start.html">localisation '
-                 'guide</a>.</div>'),
-        'virtual_path': "announcements/projects/"+tutorial.code,
-    }
-    _create_object(Announcement, **criteria)
-
-
-def create_default_languages():
-    """Create the default languages."""
-    from translate.lang import data, factory
-
-    from pootle_language.models import Language
-
-    # import languages from toolkit
-    for code in data.languages.keys():
-        try:
-            tk_lang = factory.getlanguage(code)
-            criteria = {
-                'code': code,
-                'fullname': tk_lang.fullname,
-                'nplurals': tk_lang.nplurals,
-                'pluralequation': tk_lang.pluralequation,
-            }
+class InitDB(object):
+
+    def init_db(self, create_projects=True):
+        """Populate the database with default initial data.
+
+        This creates the default database to get a working Pootle installation.
+        """
+        self.create_revision()
+        self.create_essential_users()
+        self.create_root_directories()
+        self.create_template_languages()
+        if create_projects:
+            self.create_terminology_project()
+        self.create_pootle_permissions()
+        self.create_pootle_permission_sets()
+        if create_projects:
+            self.create_default_projects()
+        self.create_default_languages()
+
+    def _create_object(self, model_klass, **criteria):
+        instance, created = model_klass.objects.get_or_create(**criteria)
+        if created:
+            logger.debug(
+                "Created %s: '%s'"
+                % (instance.__class__.__name__, instance))
+        else:
+            logger.debug(
+                "%s already exists - skipping: '%s'"
+                % (instance.__class__.__name__, instance))
+        return instance, created
+
+    def _create_pootle_user(self, **criteria):
+        user, created = self._create_object(get_user_model(), **criteria)
+        if created:
+            user.set_unusable_password()
+            user.save()
+        return user
+
+    def _create_pootle_permission_set(self, permissions, **criteria):
+        permission_set, created = self._create_object(PermissionSet,
+                                                      **criteria)
+        if created:
+            permission_set.positive_permissions = permissions
+            permission_set.save()
+        return permission_set
+
+    def create_revision(self):
+        Revision.initialize()
+
+    def create_essential_users(self):
+        """Create the 'default' and 'nobody' User instances.
+
+        These users are required for Pootle's permission system.
+        """
+        # The nobody user is used to represent an anonymous user in cases
+        # where we need to associate model information with such a user. An
+        # example is in the permission system: we need a way to store rights
+        # for anonymous users; thus we use the nobody user.
+        criteria = {
+            'username': u"nobody",
+            'full_name': u"any anonymous user",
+            'is_active': True,
+        }
+        self._create_pootle_user(**criteria)
+
+        # The 'default' user represents any valid, non-anonymous user and is
+        # used to associate information any such user. An example is in the
+        # permission system: we need a way to store default rights for users.
+        # We use the 'default' user for this.
+        #
+        # In a future version of Pootle we should think about using Django's
+        # groups to do better permissions handling.
+        criteria = {
+            'username': u"default",
+            'full_name': u"any authenticated user",
+            'is_active': True,
+        }
+        self._create_pootle_user(**criteria)
+
+        # The system user represents a system, and is used to
+        # associate updates done by bulk commands as update_stores.
+        criteria = {
+            'username': u"system",
+            'full_name': u"system user",
+            'is_active': True,
+        }
+        self._create_pootle_user(**criteria)
+
+    def create_pootle_permissions(self):
+        """Create Pootle's directory level permissions."""
+
+        args = {
+            'app_label': "pootle_app",
+            'model': "directory",
+        }
+
+        pootle_content_type, created = self._create_object(ContentType, **args)
+        pootle_content_type.name = 'pootle'
+        pootle_content_type.save()
+
+        # Create the permissions.
+        permissions = [
+            {
+                'name': _("Can access a project"),
+                'codename': "view",
+            },
+            {
+                'name': _("Cannot access a project"),
+                'codename': "hide",
+            },
+            {
+                'name': _("Can make a suggestion for a translation"),
+                'codename': "suggest",
+            },
+            {
+                'name': _("Can submit a translation"),
+                'codename': "translate",
+            },
+            {
+                'name': _("Can review suggestions"),
+                'codename': "review",
+            },
+            {
+                'name': _("Can administrate a translation project"),
+                'codename': "administrate",
+            },
+        ]
+
+        criteria = {
+            'content_type': pootle_content_type,
+        }
+
+        for permission in permissions:
+            criteria.update(permission)
+            self._create_object(Permission, **criteria)
+
+    def create_pootle_permission_sets(self):
+        """Create the default permission set for the 'nobody' and 'default' users.
+
+        'nobody' is the anonymous (non-logged in) user, and 'default' is the
+        logged in user.
+        """
+        User = get_user_model()
+
+        nobody = User.objects.get(username='nobody')
+        default = User.objects.get(username='default')
+
+        view = get_pootle_permission('view')
+        suggest = get_pootle_permission('suggest')
+        translate = get_pootle_permission('translate')
+
+        # Default permissions for tree root.
+        criteria = {
+            'user': nobody,
+            'directory': Directory.objects.root,
+        }
+        self._create_pootle_permission_set([view, suggest], **criteria)
+
+        criteria['user'] = default
+        self._create_pootle_permission_set(
+            [view, suggest, translate], **criteria)
+
+        # Default permissions for templates language.
+        # Override with no permissions for templates language.
+        criteria = {
+            'user': nobody,
+            'directory': Directory.objects.get(pootle_path="/templates/"),
+        }
+        self._create_pootle_permission_set([], **criteria)
+
+        criteria['user'] = default
+        self._create_pootle_permission_set([], **criteria)
+
+    def require_english(self):
+        """Create the English Language item."""
+        criteria = {
+            'code': "en",
+            'fullname': u"English",
+            'nplurals': 2,
+            'pluralequation': "(n != 1)",
+        }
+        en, created = self._create_object(Language, **criteria)
+        return en
+
+    def create_root_directories(self):
+        """Create the root Directory items."""
+        root, created = self._create_object(Directory, **dict(name=""))
+        self._create_object(Directory, **dict(name="projects", parent=root))
+
+    def create_template_languages(self):
+        """Create the 'templates' and English languages.
+
+        The 'templates' language is used to give users access to the
+        untranslated template files.
+        """
+        self._create_object(
+            Language, **dict(code="templates", fullname="Templates"))
+        self.require_english()
+
+    def create_terminology_project(self):
+        """Create the terminology project.
+
+        The terminology project is used to display terminology suggestions
+        while translating.
+        """
+        criteria = {
+            'code': "terminology",
+            'fullname': u"Terminology",
+            'source_language': self.require_english(),
+            'checkstyle': "terminology",
+        }
+        self._create_object(Project, **criteria)
+
+    def create_default_projects(self):
+        """Create the default projects that we host.
+
+        You might want to add your projects here, although you can also add
+        things through the web interface later.
+        """
+        from pootle_project.models import Project
+
+        en = self.require_english()
+
+        criteria = {
+            'code': u"tutorial",
+            'source_language': en,
+            'fullname': u"Tutorial",
+            'checkstyle': "standard",
+            'localfiletype': "po",
+            'treestyle': "auto",
+        }
+        tutorial, created = self._create_object(Project, **criteria)
+
+        criteria = {
+            'active': True,
+            'title': "Project instructions",
+            'body': (
+                '<div dir="ltr" lang="en">Tutorial project where users can '
+                'play with Pootle and learn more about translation and '
+                'localisation.<br />For more help on localisation, visit the '
+                '<a href="http://docs.translatehouse.org/projects/'
+                'localization-guide/en/latest/guide/start.html">localisation '
+                'guide</a>.</div>'),
+            'virtual_path': "announcements/projects/"+tutorial.code,
+        }
+        self._create_object(Announcement, **criteria)
+
+    def create_default_languages(self):
+        """Create the default languages."""
+        from translate.lang import data, factory
+
+        from pootle_language.models import Language
+
+        # import languages from toolkit
+        for code in data.languages.keys():
             try:
-                criteria['specialchars'] = tk_lang.specialchars
-            except AttributeError:
+                tk_lang = factory.getlanguage(code)
+                criteria = {
+                    'code': code,
+                    'fullname': tk_lang.fullname,
+                    'nplurals': tk_lang.nplurals,
+                    'pluralequation': tk_lang.pluralequation,
+                }
+                try:
+                    criteria['specialchars'] = tk_lang.specialchars
+                except AttributeError:
+                    pass
+                self._create_object(Language, **criteria)
+            except:
                 pass
-            _create_object(Language, **criteria)
-        except:
-            pass


Test patch: