def verify_pod_links(self, pod):
        """Check for missing files linked to from POD's html page.

        See documentation for :class:`~verify_links.LinkVerifier`. This method
        calls LinkVerifier to check existence of all files linked to from the 
        POD's own top-level html page (after templating). If any files are
        missing, an error message listing them is written to the run's index.html 
        (located in src/html/pod_missing_snippet.html).
        """
        _log.info('Checking linked output files for %s', pod.name)
        verifier = verify_links.LinkVerifier(
            self.POD_HTML(pod),  # root HTML file to start search at
            self.WK_DIR,  # root directory to resolve relative paths
            verbose=False)
        missing_out = verifier.verify_pod_links(pod.name)
        if missing_out:
            _log.error('POD %s has missing output files:\n%s', pod.name,
                       '    \n'.join(missing_out))
            template_d = html_templating_dict(pod)
            template_d['missing_output'] = '<br>'.join(missing_out)
            util.append_html_template(
                self.html_src_file('pod_missing_snippet.html'),
                self.CASE_TEMP_HTML, template_d)
            pod.exceptions.log(
                util.MDTFFileNotFoundError(
                    f'Missing {len(missing_out)} files.'))
        else:
            _log.info('\tNo files are missing.')
Exemple #2
0
    def verify_pod_links(self, pod):
        """Check for missing files linked to from POD's html page.

        See documentation for :class:`~src.verify_links.LinkVerifier`. This method
        calls :class:`~src.verify_links.LinkVerifier` to check existence of all
        files linked to from the POD's own top-level html page (after templating).
        If any files are missing, an error message listing them is written to
        the run's ``index.html`` page (located in ``src/html/pod_missing_snippet.html``).
        """
        pod.log.info('Checking linked output files for %s.', pod.full_name)
        verifier = verify_links.LinkVerifier(
            self.POD_HTML(pod),  # root html file to start search at
            self.WK_DIR,  # root directory to resolve relative paths
            verbose=False,
            log=pod.log)
        missing_out = verifier.verify_pod_links(pod.name)
        if missing_out:
            pod.deactivate(
                util.MDTFFileNotFoundError(
                    f'Missing {len(missing_out)} files.'))
        else:
            pod.log.info('\tNo files are missing.')
    def make_pod_html(self, pod):
        """Perform templating on POD's html results page(s).

        A wrapper for :func:`~util.append_html_template`. Looks for all 
        html files in POD_CODE_DIR, templates them, and copies them to 
        POD_WK_DIR, respecting subdirectory structure (see doc for
        :func:`~util.recursive_copy`).
        """
        test_path = os.path.join(pod.POD_CODE_DIR,
                                 self.pod_html_template_file_name(pod))
        if not os.path.isfile(test_path):
            # POD's top-level HTML template needs to exist
            raise util.MDTFFileNotFoundError(test_path)
        template_d = html_templating_dict(pod)
        # copy and template all .html files, since PODs can make sub-pages
        source_files = util.find_files(pod.POD_CODE_DIR, '*.html')
        util.recursive_copy(
            source_files,
            pod.POD_CODE_DIR,
            pod.POD_WK_DIR,
            copy_function=(lambda src, dest: util.append_html_template(
                src, dest, template_dict=template_d, append=False)),
            overwrite=True)
    def convert_pod_figures(self, pod, src_subdir, dest_subdir):
        """Convert all vector graphics in `POD_WK_DIR/subdir` to .png files using
        ghostscript.

        All vector graphics files (identified by extension) in any subdirectory 
        of `POD_WK_DIR/src_subdir` are converted to .png files by running 
        `ghostscript <https://www.ghostscript.com/>`__ in a subprocess.
        Ghostscript is included in the _MDTF_base conda environment. Afterwards,
        any bitmap files (identified by extension) in any subdirectory of
        `POD_WK_DIR/src_subdir` are moved to `POD_WK_DIR/dest_subdir`, preserving
        and subdirectories (see doc for :func:`~util.recursive_copy`.)

        Args:
            src_subdir: Subdirectory tree of `POD_WK_DIR` to search for vector
                graphics files.
            dest_subdir: Subdirectory tree of `POD_WK_DIR` to move converted 
                bitmap files to.
        """
        # Flags to pass to ghostscript for PS -> PNG conversion (in particular
        # bitmap resolution.)
        eps_convert_flags = (
            "-dSAFER -dBATCH -dNOPAUSE -dEPSCrop -r150 "
            "-sDEVICE=png16m -dTextAlphaBits=4 -dGraphicsAlphaBits=4")

        abs_src_subdir = os.path.join(pod.POD_WK_DIR, src_subdir)
        abs_dest_subdir = os.path.join(pod.POD_WK_DIR, dest_subdir)
        files = util.find_files(
            abs_src_subdir,
            ['*.ps', '*.PS', '*.eps', '*.EPS', '*.pdf', '*.PDF'])
        for f in files:
            f_stem, _ = os.path.splitext(f)
            # Append "_MDTF_TEMP" + page number to output files ("%d" = ghostscript's
            # template for multi-page output). If input .ps/.pdf file has multiple
            # pages, this will generate 1 png per page, counting from 1.
            f_out = f_stem + '_MDTF_TEMP_%d.png'
            try:
                util.run_shell_command(
                    f'gs {eps_convert_flags} -sOutputFile="{f_out}" {f}')
            except Exception as exc:
                _log.error("%s produced malformed plot: %s", pod.name,
                           f[len(abs_src_subdir):])
                if isinstance(exc, util.MDTFCalledProcessError):
                    _log.debug(
                        "gs error encountered when converting %s for %s:\n%s",
                        pod.name, f[len(abs_src_subdir):],
                        getattr(exc, "output", ""))
                continue
            # gs ran successfully; check how many files it created:
            out_files = glob.glob(f_stem + '_MDTF_TEMP_?.png')
            if not out_files:
                raise util.MDTFFileNotFoundError(
                    f"No .png generated from {f}.")
            elif len(out_files) == 1:
                # got one .png, so remove suffix.
                os.rename(out_files[0], f_stem + '.png')
            else:
                # Multiple .pngs. Drop the MDTF_TEMP suffix and renumber starting
                # from zero (forget which POD requires this.)
                for n in range(len(out_files)):
                    os.rename(f_stem + f'_MDTF_TEMP_{n+1}.png',
                              f_stem + f'-{n}.png')
        # move converted figures and any figures that were saved directly as bitmaps
        files = util.find_files(abs_src_subdir,
                                ['*.png', '*.gif', '*.jpg', '*.jpeg'])
        util.recursive_copy(files,
                            abs_src_subdir,
                            abs_dest_subdir,
                            copy_function=shutil.move,
                            overwrite=False)