<NME> deploy.py
<BEF> """A shortcut to deploy a fresh modoboa instance."""

import getpass
import os
from os.path import isfile
import shutil
import subprocess
import sys

import dj_database_url

import django
from django.core import management
from django.template import Context, Template
from django.utils.encoding import smart_str

from modoboa.core.commands import Command
from modoboa.lib.api_client import ModoAPIClient
from modoboa.lib.sysutils import exec_cmd

DBCONN_TPL = """
    },
"""

class DeployCommand(Command):
    help = "Create a fresh django project (calling startproject) and apply Modoboa specific settings."

        'ATOMIC_REQUESTS': True,
        super(DeployCommand, self).__init__(*args, **kwargs)
        self._parser.add_argument('name', type=str,
                                  help='The name of your Modoboa instance')
        self._parser.add_argument('--with-amavis', action='store_true', default=False,
                                  help='Include amavis configuration')
        self._parser.add_argument('--syncdb', action='store_true', default=False,
                                  help='Run django syncdb command')
        self._parser.add_argument('--collectstatic', action='store_true', default=False,
                                  help='Run django collectstatic command')

    def _exec_django_command(self, name, cwd, *args):
        """Run a django command for the freshly created project
        self._parser.add_argument("name", type=str,
                                  help="The name of your Modoboa instance")
        self._parser.add_argument(
        """
        cmd = 'python manage.py %s %s' % (name, " ".join(args))
        if not self._verbose:
            p = subprocess.Popen(cmd,
                                 stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
                                 shell=True, cwd=cwd)
            output = p.communicate()
        else:
            p = subprocess.Popen(cmd, shell=True, cwd=cwd)
            "--lang", type=str, default="en",
            output = None
        if p.returncode:
            if output:
                print >>sys.stderr, "\n".join(filter(lambda l: l is not None, output))
            print >>sys.stderr, "%s failed, check your configuration" % cmd

    def ask_db_info(self, name='default'):
        """Prompt the user for database information
            help="Create a development instance"
        )
        self._parser.add_argument(
            "--extensions", type=str, nargs="*",
        :param name: the connection name
        """
        print "Configuring database connection: %s" % name
        info = {'conn_name': name, 'dbtype': raw_input('Database type (mysql or postgres): ')}
        if info['dbtype'] not in ['mysql', 'postgres']:
            info['dbtype'] = 'mysql'
        if info['dbtype'] == 'postgres':
            info['dbtype'] = 'postgresql_psycopg2'
        default_host = 'localhost' if info['dbtype'] == 'mysql' else ''
        info['dbhost'] = raw_input("Database host (default: '%s'): " % default_host)
        if info['dbhost'] == '':
            info['dbhost'] = default_host
        info['dbname'] = raw_input('Database name: ')
        :param cwd: the directory where the command must be executed
        """
        return info

    def handle(self, parsed_args):
        management.call_command('startproject', parsed_args.name, verbosity=False)
        if os.path.exists("%(name)s/%(name)s" % {'name' : parsed_args.name}):
            # Django 1.4+
            path = "%(name)s/%(name)s" % {'name' : parsed_args.name}
            sys.path.append(parsed_args.name)
            django14 = True
        else:
            if output:
                print(
                    "\n".join([l.decode() for l in output if l is not None]),

        t = Template(dbconn_tpl)
        default_conn = t.render(Context(self.ask_db_info()))
        amavis_conn = t.render(Context(self.ask_db_info('amavis'))) if parsed_args.with_amavis \
            else None

        allowed_host = raw_input('Under which domain do you want to deploy modoboa? ')

        mod = __import__(parsed_args.name, globals(), locals(), ['settings'])
        tpl = self._render_template("%s/settings.py" % self._templates_dir, {
            'default_conn' : default_conn, 'amavis_conn' : amavis_conn,
            'secret_key' : mod.settings.SECRET_KEY,
            'name' : parsed_args.name, 'django14' : django14,
            'allowed_host': allowed_host
            })
        fp = open("%s/settings.py" % path, "w")
        fp.write(tpl)
        fp.close()
        shutil.copyfile("%s/urls.py" % self._templates_dir, "%s/urls.py" % path)
        os.mkdir("%s/media" % path)

        if parsed_args.syncdb:
            self._exec_django_command("syncdb", parsed_args.name, '--migrate', '--noinput')
            self._exec_django_command("loaddata", parsed_args.name, 'initial_users.json')

        if parsed_args.collectstatic:
            self._exec_django_command("collectstatic", parsed_args.name, '--noinput')
            info["PORT"] = ""
        info["NAME"] = input("Database name: ")
        info["USER"] = input("Username: ")
        info["PASSWORD"] = getpass.getpass("Password: ")
        return info

    def _get_extension_list(self):
        """Ask the API to get the list of all extensions.

        We hardcode the API url here to avoid a loading of
        django's settings since they are not available yet...
        """
        url = "http://api.modoboa.org/"
        official_exts = ModoAPIClient(url).list_extensions()
        return [extension["name"] for extension in official_exts]

    def find_extra_settings(self, extensions):
        """Install one or more extensions.

        Return the list of extensions providing settings we must
        include in the final configuration.

        """
        extra_settings = []
        for extension in extensions:
            module = __import__(extension[1], locals(), globals(), [])
            basedir = os.path.dirname(module.__file__)
            if not os.path.exists("{0}/settings.py".format(basedir)):
                continue
            extra_settings.append(extension[1])
        return extra_settings

    def handle(self, parsed_args):
        django.setup()
        management.call_command(
            "startproject", parsed_args.name, verbosity=False
        )
        path = "%(name)s/%(name)s" % {"name": parsed_args.name}
        sys.path.append(parsed_args.name)

        conn_tpl = Template(DBCONN_TPL)
        connections = {}
        if parsed_args.dburl:
            for dburl in parsed_args.dburl:
                conn_name, url = dburl.split(":", 1)
                info = dj_database_url.config(default=url)
                # In case the user fails to supply a valid database url,
                # fallback to manual mode
                if not info:
                    print("There was a problem with your database-url. \n")
                    info = self.ask_db_info(conn_name)
                # If we set this earlier, our fallback method will never
                # be triggered
                info["conn_name"] = conn_name
                connections[conn_name] = conn_tpl.render(Context(info))
        else:
            connections["default"] = conn_tpl.render(
                Context(self.ask_db_info()))

        if parsed_args.domain:
            allowed_host = parsed_args.domain
        else:
            allowed_host = input(
                "What will be the hostname used to access Modoboa? ")
            if not allowed_host:
                allowed_host = "localhost"
        extra_settings = []
        extensions = parsed_args.extensions
        if extensions:
            if "all" in extensions:
                extensions = self._get_extension_list()
            extensions = [(extension, extension.replace("-", "_"))
                          for extension in extensions]
            if not parsed_args.dont_install_extensions:
                cmd = (
                    sys.executable +
                    " -m pip install " +
                    " ".join([extension[0] for extension in extensions])
                )
                exec_cmd(cmd, capture_output=False)
            extra_settings = self.find_extra_settings(extensions)
            extensions = [extension[1] for extension in extensions]

        bower_components_dir = os.path.realpath(
            os.path.join(os.path.dirname(__file__), "../../bower_components")
        )

        mod = __import__(
            parsed_args.name, globals(), locals(), [smart_str("settings")]
        )
        tpl = self._render_template(
            "%s/settings.py.tpl" % self._templates_dir, {
                "db_connections": connections,
                "secret_key": mod.settings.SECRET_KEY,
                "name": parsed_args.name,
                "allowed_host": allowed_host,
                "lang": parsed_args.lang,
                "timezone": parsed_args.timezone,
                "bower_components_dir": bower_components_dir,
                "devmode": parsed_args.devel,
                "extensions": extensions,
                "extra_settings": extra_settings
            }
        )
        with open("%s/settings.py" % path, "w") as fp:
            fp.write(tpl)
        shutil.copyfile(
            "%s/urls.py.tpl" % self._templates_dir, "%s/urls.py" % path
        )
        os.mkdir("%s/media" % parsed_args.name)

        if isfile("%s/settings.pyc" % path):
            os.unlink("%s/settings.pyc" % path)
        self._exec_django_command(
            "migrate", parsed_args.name, "--noinput"
        )
        self._exec_django_command(
            "load_initial_data", parsed_args.name,
            "--admin-username", parsed_args.admin_username
        )
        if parsed_args.collectstatic:
            self._exec_django_command(
                "collectstatic", parsed_args.name, "--noinput"
            )
        self._exec_django_command(
            "set_default_site", parsed_args.name, allowed_host
        )

        base_frontend_dir = os.path.join(
            os.path.dirname(__file__), "../../frontend_dist/")
        if not os.path.exists(base_frontend_dir):
            return
        frontend_target_dir = "{}/frontend".format(parsed_args.name)
        shutil.copytree(base_frontend_dir, frontend_target_dir)

        with open("{}/config.json".format(frontend_target_dir), "w") as fp:
            fp.write("""{
    "API_BASE_URL": "https://%s/api/v2"
}
""" % allowed_host)

<MSG> PEP8 compliance and unit test fix.

<DFF> @@ -22,6 +22,7 @@ dbconn_tpl = """
     },
 """
 
+
 class DeployCommand(Command):
     help = "Create a fresh django project (calling startproject) and apply Modoboa specific settings."
 
@@ -29,12 +30,18 @@ class DeployCommand(Command):
         super(DeployCommand, self).__init__(*args, **kwargs)
         self._parser.add_argument('name', type=str,
                                   help='The name of your Modoboa instance')
-        self._parser.add_argument('--with-amavis', action='store_true', default=False,
-                                  help='Include amavis configuration')
-        self._parser.add_argument('--syncdb', action='store_true', default=False,
-                                  help='Run django syncdb command')
-        self._parser.add_argument('--collectstatic', action='store_true', default=False,
-                                  help='Run django collectstatic command')
+        self._parser.add_argument(
+            '--with-amavis', action='store_true', default=False,
+            help='Include amavis configuration'
+        )
+        self._parser.add_argument(
+            '--syncdb', action='store_true', default=False,
+            help='Run django syncdb command'
+        )
+        self._parser.add_argument(
+            '--collectstatic', action='store_true', default=False,
+            help='Run django collectstatic command'
+        )
 
     def _exec_django_command(self, name, cwd, *args):
         """Run a django command for the freshly created project
@@ -44,9 +51,10 @@ class DeployCommand(Command):
         """
         cmd = 'python manage.py %s %s' % (name, " ".join(args))
         if not self._verbose:
-            p = subprocess.Popen(cmd,
-                                 stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
-                                 shell=True, cwd=cwd)
+            p = subprocess.Popen(
+                cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
+                shell=True, cwd=cwd
+            )
             output = p.communicate()
         else:
             p = subprocess.Popen(cmd, shell=True, cwd=cwd)
@@ -54,8 +62,8 @@ class DeployCommand(Command):
             output = None
         if p.returncode:
             if output:
-                print >>sys.stderr, "\n".join(filter(lambda l: l is not None, output))
-            print >>sys.stderr, "%s failed, check your configuration" % cmd
+                print >> sys.stderr, "\n".join([l for l in output if l is not None])
+            print >> sys.stderr, "%s failed, check your configuration" % cmd
 
     def ask_db_info(self, name='default'):
         """Prompt the user for database information
@@ -66,13 +74,15 @@ class DeployCommand(Command):
         :param name: the connection name
         """
         print "Configuring database connection: %s" % name
-        info = {'conn_name': name, 'dbtype': raw_input('Database type (mysql or postgres): ')}
+        info = {'conn_name': name,
+                'dbtype': raw_input('Database type (mysql or postgres): ')}
         if info['dbtype'] not in ['mysql', 'postgres']:
             info['dbtype'] = 'mysql'
         if info['dbtype'] == 'postgres':
             info['dbtype'] = 'postgresql_psycopg2'
         default_host = 'localhost' if info['dbtype'] == 'mysql' else ''
-        info['dbhost'] = raw_input("Database host (default: '%s'): " % default_host)
+        info['dbhost'] = \
+            raw_input("Database host (default: '%s'): " % default_host)
         if info['dbhost'] == '':
             info['dbhost'] = default_host
         info['dbname'] = raw_input('Database name: ')
@@ -81,10 +91,12 @@ class DeployCommand(Command):
         return info
 
     def handle(self, parsed_args):
-        management.call_command('startproject', parsed_args.name, verbosity=False)
-        if os.path.exists("%(name)s/%(name)s" % {'name' : parsed_args.name}):
+        management.call_command(
+            'startproject', parsed_args.name, verbosity=False
+        )
+        if os.path.exists("%(name)s/%(name)s" % {'name': parsed_args.name}):
             # Django 1.4+
-            path = "%(name)s/%(name)s" % {'name' : parsed_args.name}
+            path = "%(name)s/%(name)s" % {'name': parsed_args.name}
             sys.path.append(parsed_args.name)
             django14 = True
         else:
@@ -94,27 +106,37 @@ class DeployCommand(Command):
 
         t = Template(dbconn_tpl)
         default_conn = t.render(Context(self.ask_db_info()))
-        amavis_conn = t.render(Context(self.ask_db_info('amavis'))) if parsed_args.with_amavis \
-            else None
+        amavis_conn = t.render(Context(self.ask_db_info('amavis'))) \
+            if parsed_args.with_amavis else None
 
-        allowed_host = raw_input('Under which domain do you want to deploy modoboa? ')
+        allowed_host = raw_input(
+            'Under which domain do you want to deploy modoboa? '
+        )
 
         mod = __import__(parsed_args.name, globals(), locals(), ['settings'])
         tpl = self._render_template("%s/settings.py" % self._templates_dir, {
-            'default_conn' : default_conn, 'amavis_conn' : amavis_conn,
-            'secret_key' : mod.settings.SECRET_KEY,
-            'name' : parsed_args.name, 'django14' : django14,
+            'default_conn': default_conn, 'amavis_conn': amavis_conn,
+            'secret_key': mod.settings.SECRET_KEY,
+            'name': parsed_args.name, 'django14': django14,
             'allowed_host': allowed_host
-            })
+        })
         fp = open("%s/settings.py" % path, "w")
         fp.write(tpl)
         fp.close()
-        shutil.copyfile("%s/urls.py" % self._templates_dir, "%s/urls.py" % path)
+        shutil.copyfile(
+            "%s/urls.py" % self._templates_dir, "%s/urls.py" % path
+        )
         os.mkdir("%s/media" % path)
 
         if parsed_args.syncdb:
-            self._exec_django_command("syncdb", parsed_args.name, '--migrate', '--noinput')
-            self._exec_django_command("loaddata", parsed_args.name, 'initial_users.json')
+            self._exec_django_command(
+                "syncdb", parsed_args.name, '--migrate', '--noinput'
+            )
+            self._exec_django_command(
+                "loaddata", parsed_args.name, 'initial_users.json'
+            )
 
         if parsed_args.collectstatic:
-            self._exec_django_command("collectstatic", parsed_args.name, '--noinput')
+            self._exec_django_command(
+                "collectstatic", parsed_args.name, '--noinput'
+            )

PEP8 compliance and unit test fix.