Пример #1
0
    def main(self):
        console('Generating new Application Framework project ...')

        dirpath = self.options.dir
        while not dirpath:
            default = os.path.join(os.getcwd(), 'appfwk_project')
            dirpath = prompt('\nEnter path for project files',
                             default=default)

        dirpath = os.path.abspath(dirpath)
        if os.path.exists(dirpath):
            console('Project directory already exists, aborting.')
            return

        self.create_project_directory(dirpath)
        self.create_local_settings(dirpath)
        self.collectreports(dirpath)

        if self.options.offline_js:
            self.get_offline_js(dirpath)

        if not self.options.no_git:
            self.initialize_git(dirpath)

        if not self.options.no_init:
            self.initialize_project(dirpath)

        console('\n*****\n')
        console('App Framework project created.')

        if self.options.no_init:
            console("Change to that directory and run "
                    "'steel appfwk init' to initialize the project.")
Пример #2
0
    def main(self):
        console('Generating new Application Framework project ...')

        dirpath = self.options.dir
        while not dirpath:
            default = os.path.join(os.getcwd(), 'appfwk_project')
            dirpath = prompt('\nEnter path for project files', default=default)

        dirpath = os.path.abspath(dirpath)
        if os.path.exists(dirpath):
            console('Project directory already exists, aborting.')
            return

        self.create_project_directory(dirpath)
        self.create_local_settings(dirpath)
        self.collectreports(dirpath)

        if self.options.offline_js:
            self.get_offline_js(dirpath)

        if not self.options.no_git:
            self.initialize_git(dirpath)

        if not self.options.no_init:
            self.initialize_project(dirpath)

        console('\n*****\n')
        console('App Framework project created.')

        if self.options.no_init:
            console("Change to that directory and run "
                    "'steel appfwk init' to initialize the project.")
Пример #3
0
    def validate_args(self):
        if self.options.rest_server and not self.options.authfile:
            console('Must specify an authfile to use with rest-server.')
            sys.exit(1)

        if self.options.authfile and not self.options.rest_server:
            console('Must specify a rest-server for use with an authfile.')
            sys.exit(1)
Пример #4
0
    def validate_args(self):
        if self.options.rest_server and not self.options.authfile:
            console('Must specify an authfile to use with rest-server.')
            sys.exit(1)

        if self.options.authfile and not self.options.rest_server:
            console('Must specify a rest-server for use with an authfile.')
            sys.exit(1)
Пример #5
0
    def create_workspace_directory(self, dirpath):
        """Creates a workspace directory with a readme and management script"""
        # Make directory
        console('Creating project directory %s ...' % dirpath)
        self.mkdir(dirpath)

        self.create_file(dirpath, 'README.md', README_CONTENT)
        self.create_file(dirpath, 'collect_examples.py', COLLECT_EXAMPLES_CONTENT)
Пример #6
0
    def main(self):
        cwd = os.getcwd()
        if not os.path.exists('manage.py'):
            console('This command must be run inside the project directory to initialize.')
            return

        shell('python manage.py initialize --trace',
              msg='Initializing project using default settings',
              cwd=cwd)
Пример #7
0
 def signal_handler(self, signum, frame):
     if signum == signal.SIGHUP:
         console('Received signal %s, reloading config' % signum)
         self.reload_config()
     else:
         console('Received signal %s, shutting down gracefully.' % signum)
         if self.scheduler.running:
             self.scheduler.shutdown()
         sys.exit()
Пример #8
0
 def signal_handler(self, signum, frame):
     if signum == signal.SIGHUP:
         console('Received signal %s, reloading config' % signum)
         self.reload_config()
     else:
         console('Received signal %s, shutting down gracefully.' % signum)
         if self.scheduler.running:
             self.scheduler.shutdown()
         sys.exit()
Пример #9
0
    def main(self):
        cwd = os.getcwd()
        if not os.path.exists('manage.py'):
            console('This command must be run inside the project directory.')
            return

        shell('python manage.py reload --trace',
              msg='Reloading app framework reports',
              cwd=cwd)
Пример #10
0
    def create_workspace_directory(self, dirpath):
        """Create a workspace directory with a readme and management script."""
        # Make directory
        console('Creating project directory %s ...' % dirpath)
        self.mkdir(dirpath)

        self.create_file(dirpath, 'README.md', README_CONTENT)
        self.create_file(dirpath, 'collect_examples.py',
                         COLLECT_EXAMPLES_CONTENT)
Пример #11
0
    def main(self):
        cwd = os.getcwd()
        if not os.path.exists('manage.py'):
            console('This command must be run inside the project directory.')
            return

        shell('python manage.py reload --trace',
              msg='Reloading app framework reports',
              cwd=cwd)
Пример #12
0
    def main(self):
        cwd = os.getcwd()
        if not os.path.exists('manage.py'):
            console('This command must be run inside the project directory.')
            return

        if self.options.interactive:
            yn = prompt_yn('This will delete and re-initialize the database from '
                           'scratch.  There is no undo.\nAre you sure?', False)
            if not yn:
                console('Aborting.')
                sys.exit()

        shell('python manage.py reset_appfwk --force --trace',
              msg='Resetting project database',
              cwd=cwd)
Пример #13
0
    def create_project_directory(self, dirpath):
        """Creates project directory and copies/links necessary files."""
        console('Creating project directory %s ...' % dirpath)
        self.mkdir(dirpath)

        # link manage.py and media directories
        # when symlink not available (windows) will copy files instead
        link_pkg_files('steelscript.appfwk.apps',
                       '../manage.py',
                       dirpath,
                       symlink=hasattr(os, 'symlink'),
                       buf=self.debug)

        for p in ('media', 'thirdparty'):
            link_pkg_dir('steelscript.appfwk.apps',
                         '../' + p,
                         os.path.join(dirpath, p),
                         symlink=hasattr(os, 'symlink'),
                         buf=self.debug)

        # copy and make folders
        self.mkdir(os.path.join(dirpath, 'logs'))

        datapath = os.path.join(dirpath, 'data')
        self.mkdir(datapath)
        link_pkg_dir('steelscript.appfwk.apps',
                     '../initial_data',
                     os.path.join(datapath, 'initial_data'),
                     symlink=False,
                     buf=self.debug)
        self.mkdir(os.path.join(datapath, 'datacache'))

        # copy example-configs
        link_pkg_files('steelscript.appfwk.apps',
                       '../project/example-configs/*',
                       os.path.join(dirpath, 'example-configs'),
                       symlink=False,
                       buf=self.debug)

        # copy sample locations
        link_pkg_files('steelscript.appfwk.apps',
                       'geolocation/sample_location*',
                       os.path.join(dirpath, 'example-configs'),
                       symlink=False,
                       buf=self.debug)
Пример #14
0
    def create_project_directory(self, dirpath):
        """Creates project directory and copies/links necessary files."""
        console('Creating project directory %s ...' % dirpath)
        self.mkdir(dirpath)

        # link manage.py and media directories
        # when symlink not available (windows) will copy files instead
        link_pkg_files('steelscript.appfwk.apps',
                       '../manage.py',
                       dirpath,
                       symlink=hasattr(os, 'symlink'),
                       buf=self.debug)

        for p in ('media', 'thirdparty'):
            link_pkg_dir('steelscript.appfwk.apps',
                         '../' + p,
                         os.path.join(dirpath, p),
                         symlink=hasattr(os, 'symlink'),
                         buf=self.debug)

        # copy and make folders
        self.mkdir(os.path.join(dirpath, 'logs'))

        datapath = os.path.join(dirpath, 'data')
        self.mkdir(datapath)
        link_pkg_dir('steelscript.appfwk.apps',
                     '../initial_data',
                     os.path.join(datapath, 'initial_data'),
                     symlink=False,
                     buf=self.debug)
        self.mkdir(os.path.join(datapath, 'datacache'))

        # copy example-configs
        link_pkg_files('steelscript.appfwk.apps',
                       '../project/example-configs/*',
                       os.path.join(dirpath, 'example-configs'),
                       symlink=False,
                       buf=self.debug)

        # copy sample locations
        link_pkg_files('steelscript.appfwk.apps',
                       'geolocation/sample_location*',
                       os.path.join(dirpath, 'example-configs'),
                       symlink=False,
                       buf=self.debug)
Пример #15
0
    def _cp_examples_from_src(cls, dirpath, overwrite):
        """Copy all examples from steelscript root directories"""
        # Get the paths of installed packages (ex /src/steelscript-netprofiler)
        pkg_paths = (os.path.dirname(p) for p in steelscript.__path__)
        # Get all the paths to the example files if the /examples folder exists
        example_paths = (p for p in pkg_paths if os.path.exists(os.path.join(p, 'examples')))
        new_dir = None
        for p in example_paths:
            new_dir = cls.path_leaf(p) + '-examples'
            # Remove 'steelscript-' prefix
            new_dir = new_dir.split('-', 1)[1]
            new_dir_path = os.path.join(dirpath, new_dir)
            cls.mkdir(new_dir_path)
            cls.copy_all(os.path.join(p, 'examples'), new_dir_path, overwrite)

        console("done")
        if new_dir is None:
            console("WARNING: No examples were found")
Пример #16
0
    def _cp_examples_from_src(cls, dirpath, overwrite):
        """Copy all examples from steelscript root directories."""
        # Get the paths of installed packages (ex /src/steelscript-netprofiler)
        pkg_paths = (os.path.dirname(p) for p in steelscript.__path__)
        # Get all the paths to the example files if the /examples folder exists
        example_paths = (p for p in pkg_paths
                         if os.path.exists(os.path.join(p, 'examples')))
        new_dir = None
        for p in example_paths:
            new_dir = cls.path_leaf(p) + '-examples'
            # Remove 'steelscript-' prefix
            new_dir = new_dir.split('-', 1)[1]
            new_dir_path = os.path.join(dirpath, new_dir)
            cls.mkdir(new_dir_path)
            cls.copy_all(os.path.join(p, 'examples'), new_dir_path, overwrite)

        console("done")
        if new_dir is None:
            console("WARNING: No examples were found")
Пример #17
0
    def create_local_settings(self, dirname):
        """Creates local settings configuration."""

        secret = ''.join(
            choice('abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)')
            for i in range(50)
        )

        fname = os.path.join(dirname, 'local_settings.py')
        if not os.path.exists(fname):
            console('Writing local settings %s ... ' % fname, newline=False)
            with open(fname, 'w') as f:
                f.write(LOCAL_CONTENT)
                if self.options.offline_js:
                    f.write("OFFLINE_JS = True\n")
                    f.write("STATICFILES_DIRS += (os.path.join(PROJECT_ROOT, "
                            "'offline'), )\n")
                else:
                    f.write("#OFFLINE_JS = True\n")
                    f.write("#STATICFILES_DIRS += (os.path.join(PROJECT_ROOT, "
                            "'offline'), )\n")
                f.write("SECRET_KEY = '%s'\n" % secret)
                f.write(LOCAL_FOOTER)
            console('done.')
        else:
            console('Skipping local settings generation.')
Пример #18
0
    def collect_examples(cls, dirpath, overwrite=False):
        """Copy examples from installed steelscript packages into workspace.

        :param dirpath: The absolute path to the directory the examples
        should be copied into.
        :param overwrite: If True, all edited examples will be overwritten with
        examples found in the installed packages /examples directories.
        """
        try:
            get_distribution('steelscript')
        except DistributionNotFound:
            console("Package not found: 'steelscript'")
            console("Check the installation")
            return

        console("Collecting examples from installed packages ... ",
                newline=False)

        examples_root = os.path.join(sys.prefix, 'share', 'doc',
                                     'steelscript', 'examples')
        # If the packages were installed normally, examples will be located in
        # the virtualenv/share/docs/steelscript/examples and if not they will
        # be in the packages root directories in /examples
        if os.path.exists(examples_root):
            cls._cp_examples_from_docs(dirpath, overwrite)
        else:
            cls._cp_examples_from_src(dirpath, overwrite)
Пример #19
0
    def create_local_settings(self, dirname):
        """Creates local settings configuration."""

        secret = ''.join(
            choice('abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)')
            for i in range(50))

        fname = os.path.join(dirname, 'local_settings.py')
        if not os.path.exists(fname):
            console('Writing local settings %s ... ' % fname, newline=False)
            with open(fname, 'w') as f:
                f.write(LOCAL_CONTENT)
                if self.options.offline_js:
                    f.write("OFFLINE_JS = True\n")
                    f.write("STATICFILES_DIRS += (os.path.join(PROJECT_ROOT, "
                            "'offline'), )\n")
                else:
                    f.write("#OFFLINE_JS = True\n")
                    f.write("#STATICFILES_DIRS += (os.path.join(PROJECT_ROOT, "
                            "'offline'), )\n")
                f.write("SECRET_KEY = '%s'\n" % secret)
                f.write(LOCAL_FOOTER)
            console('done.')
        else:
            console('Skipping local settings generation.')
Пример #20
0
    def collect_examples(cls, dirpath, overwrite=False):
        """Copy examples from installed steelscript packages into workspace.

        :param dirpath: The absolute path to the directory the examples
        should be copied into.
        :param overwrite: If True, all edited examples will be overwritten with
        examples found in the installed packages /examples directories.
        """
        try:
            get_distribution('steelscript')
        except DistributionNotFound:
            console("Package not found: 'steelscript'")
            console("Check the installation")
            return

        console("Collecting examples from installed packages ... ",
                newline=False)

        examples_root = os.path.join(sys.prefix, 'share', 'doc', 'steelscript',
                                     'examples')
        # If the packages were installed normally, examples will be located in
        # the virtualenv/share/docs/steelscript/examples and if not they will
        # be in the packages root directories in /examples
        if os.path.exists(examples_root):
            cls._cp_examples_from_docs(dirpath, overwrite)
        else:
            cls._cp_examples_from_src(dirpath, overwrite)
Пример #21
0
    def _cp_examples_from_docs(cls, dirpath, overwrite):
        """Copy all examples from the virtual enviornment's installed packages"""
        examples_root = os.path.join(sys.prefix, 'share', 'doc',
                                     'steelscript', 'examples')
        e = AvailableDistributions()
        # Get packages with prefix steel (ex. steelscript.netshark)
        steel_pkgs = (x for x in e if x.startswith('steel'))
        # Remove the 'steelscript.' prefix
        no_prefix_pkgs = (x.split('.', 1)[1] if '.' in x else x for x in steel_pkgs )
        # Turn those package names (ex. 'netshark') into full paths
        example_paths = (os.path.join(examples_root, p) for p in no_prefix_pkgs
                         if os.path.exists(os.path.join(examples_root, p)))
        new_dir = None
        for p in example_paths:
            new_dir = cls.path_leaf(p) + '-examples'
            new_dir_path = os.path.join(dirpath, new_dir)
            cls.mkdir(new_dir_path)
            cls.copy_all(p, new_dir_path, overwrite)

        console("done")
        if new_dir is None:
            console("WARNING: No examples were found")
Пример #22
0
    def _cp_examples_from_docs(cls, dirpath, overwrite):
        """Copy all examples from the virtual environment's installed packages.
        """
        examples_root = os.path.join(sys.prefix, 'share', 'doc', 'steelscript',
                                     'examples')
        e = AvailableDistributions()
        # Get packages with prefix steel (ex. steelscript.netshark)
        steel_pkgs = (x for x in e if x.startswith('steel'))
        # Remove the 'steelscript.' prefix
        no_prefix_pkgs = (x.split('.', 1)[1] if '.' in x else x
                          for x in steel_pkgs)
        # Turn those package names (ex. 'netshark') into full paths
        example_paths = (os.path.join(examples_root, p) for p in no_prefix_pkgs
                         if os.path.exists(os.path.join(examples_root, p)))
        new_dir = None
        for p in example_paths:
            new_dir = cls.path_leaf(p) + '-examples'
            new_dir_path = os.path.join(dirpath, new_dir)
            cls.mkdir(new_dir_path)
            cls.copy_all(p, new_dir_path, overwrite)

        console("done")
        if new_dir is None:
            console("WARNING: No examples were found")
Пример #23
0
 def create_file(self, dirname, filename, content):
     """Create a file according to some basic specifications."""
     fname = os.path.join(dirname, filename)
     if not os.path.exists(fname):
         console('Writing {0} file {1} ... '.format(filename, fname),
                 newline=False)
         with open(fname, 'w') as f:
             f.write(content)
         console('done.')
     else:
         console('File already exists, skipping writing the file.')
Пример #24
0
 def create_file(self, dirname, filename, content):
     """Create a file according to some basic specifications."""
     fname = os.path.join(dirname, filename)
     if not os.path.exists(fname):
         console('Writing {0} file {1} ... '.format(filename, fname),
                 newline=False)
         with open(fname, 'w') as f:
             f.write(content)
         console('done.')
     else:
         console('File already exists, skipping writing the file.')
Пример #25
0
    def _cp_files(cls, kind, src_paths, dst_paths, overwrite):
        if src_paths:
            console("Collecting {} from installed packages ... ".format(kind),
                    newline=False)

            for src, dst in zip(src_paths, dst_paths):
                cls.mkdir(dst)
                cls.copy_all(src, dst, overwrite)

            console("done")
        else:
            console("WARNING: No {} were found".format(kind))
Пример #26
0
    def _cp_files(cls, kind, src_paths, dst_paths, overwrite):
        if src_paths:
            console("Collecting {} from installed packages ... ".format(kind),
                    newline=False)

            for src, dst in zip(src_paths, dst_paths):
                cls.mkdir(dst)
                cls.copy_all(src, dst, overwrite)

            console("done")
        else:
            console("WARNING: No {} were found".format(kind))
Пример #27
0
    def main(self):
        console('Generating new SteelScript workspace...')

        dirpath = self.options.dir
        while not dirpath:
            default = os.path.join(os.getcwd(), 'steelscript-workspace')
            dirpath = prompt('\nEnter path for workspace files',
                             default=default)

        dirpath = os.path.abspath(dirpath)
        if os.path.exists(dirpath):
            console('Workspace directory already exists, aborting.')
            return

        self.create_workspace_directory(dirpath)
        self.collect_examples(dirpath, True)
        if self.options.git:
            self.initialize_git(dirpath)

        console('\n*****\n')
        console('SteelScript workspace created.')
Пример #28
0
    def main(self):
        console('Generating new SteelScript workspace...')

        dirpath = self.options.dir
        while not dirpath:
            default = os.path.join(os.getcwd(), 'steelscript-workspace')
            dirpath = prompt('\nEnter path for workspace files',
                             default=default)

        dirpath = os.path.abspath(dirpath)
        if os.path.exists(dirpath):
            console('Workspace directory already exists, aborting.')
            return

        self.create_workspace_directory(dirpath)
        self.collect_examples(dirpath, True)
        if self.options.git:
            self.initialize_git(dirpath)

        console('\n*****\n')
        console('SteelScript workspace created.')
Пример #29
0
    def get_offline_js(self, dirpath):
        console("Downloading offline JavaScript files...")

        offline_js_dir = os.path.join(dirpath, 'offline')
        self.mkdir(offline_js_dir)

        failedurls = set()
        offline_files = settings.OFFLINE_JS_FILES + settings.OFFLINE_CSS_FILES
        for url, dirname in offline_files:
            filename = url.rsplit('/', 1)[1]

            console("Downloading {0}... ".format(url), newline=False)

            connectionfailed = False
            try:
                r = requests.get(url, stream=True, allow_redirects=True)
            except requests.exceptions.Timeout:
                console("failed: request timed out.".format(filename))
                connectionfailed = True
            except requests.exceptions.ConnectionError as e:
                console("failed with connection error: {0}".format(e))
                connectionfailed = True

            if connectionfailed:
                failedurls.add(url)
            elif r.status_code != requests.codes.ok:
                console("failed with HTTP status code {0}.".format(
                    filename, r.status_code))
                failedurls.add(url)
            else:
                if dirname is not None:
                    f = tempfile.NamedTemporaryFile(delete=False)
                    downloadpath = f.name
                else:
                    downloadpath = os.path.join(offline_js_dir, filename)
                    f = open(downloadpath, 'w')

                for chunk in r:
                    f.write(chunk)
                f.close()

                console("success.")

                # If dirname is not None, that means the file is a zip or tar
                # archive and should be extracted to that subdirectory.
                if dirname is not None:
                    finaldir = os.path.join(offline_js_dir, dirname)
                    console("Extracting to " + finaldir + "... ",
                            newline=False)
                    os.mkdir(finaldir)
                    try:
                        # when original url gets redirected to the cloud,
                        # the zip file would be moved to the middle
                        # hence search for string of '.zip' followed by
                        # Non-alphanumeric letters

                        if r.url.endswith('zip') or \
                                re.search('.zip[^a-zA-Z\d]', r.url):
                            # Unzip into temporary dir, then move the contents
                            # of the outermost dir where we want. (With tar we
                            # can just use --strip-components 1.)
                            unzipdir = tempfile.mkdtemp()
                            shell("unzip {0} -d {1}".format(
                                downloadpath, unzipdir))
                            shell("mv -v {0}/*/* {1}".format(
                                unzipdir, finaldir))
                            shell("rm -rf {0}".format(unzipdir))
                        else:  # Not a zip, assume tarball.
                            self.mkdir(finaldir)
                            shell(("tar xvf {0} --strip-components 1 "
                                   "--directory {1}").format(
                                       downloadpath, finaldir))
                    except Exception as e:
                        # This will probably be a ShellFailed exception, but
                        # we need to clean up the file no matter what.
                        raise e
                    finally:
                        os.remove(downloadpath)

                    console("success.")

        if failedurls:
            console("Warning: the following offline JavaScript files failed "
                    "to download. To complete this installation, you must "
                    "manually download these files to " + offline_js_dir + ".")

            for url, dirname in settings.OFFLINE_JS_FILES:
                if url in failedurls:
                    console("    {0}".format(url))
                    if dirname is not None:
                        console(
                            "   (this file is an archive -- extract to %s)" %
                            os.path.join(offline_js_dir, dirname))
        else:
            console("Done.")
Пример #30
0
    def get_offline_js(self, dirpath):
        console("Downloading offline JavaScript files...")

        offline_js_dir = os.path.join(dirpath, 'offline')
        self.mkdir(offline_js_dir)

        failedurls = set()
        offline_files = settings.OFFLINE_JS_FILES + settings.OFFLINE_CSS_FILES
        for url, dirname in offline_files:
            filename = url.rsplit('/', 1)[1]

            console("Downloading {0}... ".format(url), newline=False)

            connectionfailed = False
            try:
                r = requests.get(url, stream=True, allow_redirects=True)
            except requests.exceptions.Timeout:
                console("failed: request timed out.".format(filename))
                connectionfailed = True
            except requests.exceptions.ConnectionError as e:
                console("failed with connection error: {0}".format(e))
                connectionfailed = True

            if connectionfailed:
                failedurls.add(url)
            elif r.status_code != requests.codes.ok:
                console("failed with HTTP status code {0}.".format(filename,
                        r.status_code))
                failedurls.add(url)
            else:
                if dirname is not None:
                    f = tempfile.NamedTemporaryFile(delete=False)
                    downloadpath = f.name
                else:
                    downloadpath = os.path.join(offline_js_dir, filename)
                    f = open(downloadpath, 'w')

                for chunk in r:
                    f.write(chunk)
                f.close()

                console("success.")

                # If dirname is not None, that means the file is a zip or tar
                # archive and should be extracted to that subdirectory.
                if dirname is not None:
                    finaldir = os.path.join(offline_js_dir, dirname)
                    console("Extracting to " + finaldir + "... ",
                            newline=False)
                    os.mkdir(finaldir)
                    try:
                        # when original url gets redirected to the cloud,
                        # the zip file would be moved to the middle
                        # hence search for string of '.zip' followed by
                        # Non-alphanumeric letters

                        if r.url.endswith('zip') or \
                                re.search('.zip[^a-zA-Z\d]', r.url):
                            # Unzip into temporary dir, then move the contents
                            # of the outermost dir where we want. (With tar we
                            # can just use --strip-components 1.)
                            unzipdir = tempfile.mkdtemp()
                            shell("unzip {0} -d {1}".format(downloadpath,
                                                            unzipdir))
                            shell("mv -v {0}/*/* {1}".format(unzipdir,
                                                             finaldir))
                            shell("rm -rf {0}".format(unzipdir))
                        else:  # Not a zip, assume tarball.
                            self.mkdir(finaldir)
                            shell(("tar xvf {0} --strip-components 1 "
                                  "--directory {1}").format(downloadpath,
                                                            finaldir))
                    except Exception as e:
                        # This will probably be a ShellFailed exception, but
                        # we need to clean up the file no matter what.
                        raise e
                    finally:
                        os.remove(downloadpath)

                    console("success.")

        if failedurls:
            console("Warning: the following offline JavaScript files failed "
                    "to download. To complete this installation, you must "
                    "manually download these files to " + offline_js_dir + ".")

            for url, dirname in settings.OFFLINE_JS_FILES:
                if url in failedurls:
                    console("    {0}".format(url))
                    if dirname is not None:
                        console("   (this file is an archive -- extract to %s)"
                                % os.path.join(offline_js_dir, dirname))
        else:
            console("Done.")