コード例 #1
0
class ReportGenerator:
    def __init__(self):
        pass


    def initialize(self, dep11_dir):
        dep11_dir = os.path.abspath(dep11_dir)

        conf = load_generator_config(dep11_dir)
        if not conf:
            return False

        self._archive_root = conf.get("ArchiveRoot")

        self._html_url = conf.get("HtmlBaseUrl")
        if not self._html_url:
            self._html_url = "."

        self._template_dir = os.path.join(get_data_dir(), "templates", "default")

        self._distro_name = conf.get("DistroName", "Debian")

        self._export_dir = os.path.join(dep11_dir, "export")
        if conf.get("ExportDir"):
            self._export_dir = conf.get("ExportDir")

        if not os.path.exists(self._export_dir):
            os.makedirs(self._export_dir)

        self._suites_data = conf['Suites']

        self._html_export_dir = os.path.join(self._export_dir, "html")

        self._dep11_url = conf.get("MediaBaseUrl")

        # load metadata cache
        cache_dir = os.path.join(dep11_dir, "cache")
        if conf.get("CacheDir"):
            cache_dir = conf.get("CacheDir")
        self._cache = DataCache(os.path.join(self._export_dir, "media"))
        self._cache.open(cache_dir)

        os.chdir(dep11_dir)
        return True


    def _get_packages_for(self, suite, component, arch):
        return read_packages_dict_from_file(self._archive_root, suite, component, arch).values()


    def render_template(self, name, out_dir, out_name = None, *args, **kwargs):
        if not out_name:
            out_path = os.path.join(out_dir, name)
        else:
            out_path = os.path.join(out_dir, out_name)
        # create subdirectories if necessary
        out_dir = os.path.dirname(os.path.realpath(out_path))
        if not os.path.exists(out_dir):
            os.makedirs(out_dir)

        j2_env = Environment(loader=FileSystemLoader(self._template_dir))

        template = j2_env.get_template(name)
        content = template.render(root_url = self._html_url,
                                    distro = self._distro_name,
                                    time = time.strftime("%Y-%m-%d %H:%M:%S %Z"),
                                    generator_version = __version__,
                                    *args, **kwargs)
        log.debug("Render: %s" % (out_path.replace(self._html_export_dir, "")))
        with open(out_path, 'wb') as f:
            f.write(bytes(content, 'utf-8'))


    def _highlight_yaml(self, yml_data):
        if not yml_data:
            return ""
        if not pygments:
            return yml_data.replace("\n", "<br/>\n")
        return pygments.highlight(yml_data, YamlLexer(), HtmlFormatter())


    def _expand_hint(self, hint_data):
        tag_name = hint_data['tag']
        tag = get_hint_tag_info(tag_name)

        desc = ""
        try:
            desc = tag['text'] % hint_data['params']
        except Exception as e:
            desc = "Error while expanding hint description: %s" % (str(e))

        severity = tag.get('severity')
        if not severity:
            log.error("Tag %s has no severity!", tag_name)
            severity = "info"

        return {'tag_name': tag_name, 'description': desc, 'severity': severity}


    def update_reports(self, suite_name):
        dep11_hintsdir = os.path.join(self._export_dir, "hints")
        if not os.path.exists(dep11_hintsdir):
            return
        dep11_minfodir = os.path.join(self._export_dir, "data")
        if not os.path.exists(dep11_minfodir):
            return

        suite = self._suites_data.get(suite_name)
        if not suite:
            log.error("Suite '%s' not found!" % (suite_name))
            return False

        export_dir_root = self._html_export_dir
        media_dir = os.path.join(self._export_dir, "media")
        noimage_url = os.path.join(self._html_url, "static", "img", "no-image.png")

        # Render archive suites index page
        self.render_template("suites_index.html", export_dir_root, "index.html", suites=self._suites_data.keys())
        export_dir = os.path.join(export_dir_root, suite_name)

        log.info("Collecting metadata and issue information for suite '%s'" % (suite_name))

        stats = StatsGenerator(self._cache)
        suite_error_count = 0
        suite_warning_count = 0
        suite_info_count = 0
        suite_metainfo_count = 0

        for component in suite['components']:
            issue_summaries = dict()
            mdata_summaries = dict()
            export_dir_section = os.path.join(self._export_dir, "html", suite_name, component)
            export_dir_issues = os.path.join(export_dir_section, "issues")
            export_dir_metainfo = os.path.join(export_dir_section, "metainfo")

            error_count = 0
            warning_count = 0
            info_count = 0
            metainfo_count = 0

            hint_pages = dict()
            cpt_pages = dict()

            for arch in suite['architectures']:
                pkglist = self._get_packages_for(suite_name, component, arch)

                for pkg in pkglist:
                    pkid = pkg.pkid

                    maintainer = None
                    if pkg:
                        maintainer = pkg.maintainer
                    if not maintainer:
                        maintainer = "Unknown"

                    #
                    # Data processing hints
                    #
                    hints_list = self._cache.get_hints(pkid)
                    if hints_list:
                        hints_list = yaml.safe_load_all(hints_list)
                        for hdata in hints_list:
                            pkg_name = hdata['Package']
                            pkg_id = hdata.get('PackageID')
                            if not pkg_id:
                                pkg_id = pkg_name
                            if not issue_summaries.get(maintainer):
                                issue_summaries[maintainer] = dict()

                            hints_raw = hdata.get('Hints', list())

                            # expand all hints to show long descriptions
                            errors = list()
                            warnings = list()
                            infos = list()

                            for hint in hints_raw:
                                ehint = self._expand_hint(hint)
                                severity = ehint['severity']
                                if severity == "info":
                                    infos.append(ehint)
                                elif severity == "warning":
                                    warnings.append(ehint)
                                else:
                                    errors.append(ehint)

                            if not hint_pages.get(pkg_name):
                                hint_pages[pkg_name] = list()

                            # we fold multiple architectures with the same issues into one view
                            pkid_noarch = pkg_id
                            if "/" in pkg_id:
                                pkid_noarch = pkg_id[:pkg_id.rfind("/")]

                            pcid = ""
                            if hdata.get('ID'):
                                pcid = "%s: %s" % (pkid_noarch, hdata.get('ID'))
                            else:
                                pcid = pkid_noarch

                            page_data = {'identifier': pcid, 'errors': errors, 'warnings': warnings, 'infos': infos, 'archs': [arch]}
                            try:
                                l = hint_pages[pkg_name]
                                index = next(i for i, v in enumerate(l) if equal_dicts(v, page_data, ['archs']))
                                hint_pages[pkg_name][index]['archs'].append(arch)
                            except StopIteration:
                                hint_pages[pkg_name].append(page_data)

                                # add info to global issue count
                                error_count += len(errors)
                                warning_count += len(warnings)
                                info_count += len(infos)

                                # add info for global index
                                if not issue_summaries[maintainer].get(pkg_name):
                                    issue_summaries[maintainer][pkg_name] = {'error_count': len(errors), 'warning_count': len(warnings), 'info_count': len(infos)}


                    #
                    # Component metadata
                    #
                    cptgids = self._cache.get_cpt_gids_for_pkg(pkid)
                    if cptgids:
                        for cptgid in cptgids:
                            mdata = self._cache.get_metadata(cptgid)
                            if not mdata:
                                log.error("Package '%s' refers to missing component with gid '%s'" % (pkid, cptgid))
                                continue
                            mdata = yaml.safe_load(mdata)

                            pkg_name = mdata.get('Package')
                            if not pkg_name:
                                # we probably hit the header
                                continue
                            if not mdata_summaries.get(maintainer):
                                mdata_summaries[maintainer] = dict()


                            # ugly hack to have the screenshot entries linked
                            #if mdata.get('Screenshots'):
                            #    sshot_baseurl = os.path.join(self._dep11_url, component)
                            #    for i in range(len(mdata['Screenshots'])):
                            #        url = mdata['Screenshots'][i]['source-image']['url']
                            #        url = "<a href=\"%s\">%s</a>" % (os.path.join(sshot_baseurl, url), url)
                            #        mdata['Screenshots'][i]['source-image']['url'] = Markup(url)
                            #        thumbnails = mdata['Screenshots'][i]['thumbnails']
                            #        for j in range(len(thumbnails)):
                            #            url = thumbnails[j]['url']
                            #            url = "<a href=\"%s\">%s</a>" % (os.path.join(sshot_baseurl, url), url)
                            #            thumbnails[j]['url'] = Markup(url)
                            #        mdata['Screenshots'][i]['thumbnails'] = thumbnails


                            mdata_yml = dict_to_dep11_yaml(mdata)
                            mdata_yml = self._highlight_yaml(mdata_yml)
                            cid = mdata.get('ID')

                            # try to find an icon for this component (if it's a GUI app)
                            icon_url = None
                            if mdata['Type'] == 'desktop-app' or mdata['Type'] == "web-app":
                                icon_name = mdata['Icon'].get("cached")
                                if icon_name:
                                    icon_fname = os.path.join(component, cptgid, "icons", "64x64", icon_name)
                                    if os.path.isfile(os.path.join(media_dir, icon_fname)):
                                        icon_url = os.path.join(self._dep11_url, icon_fname)
                                    else:
                                        icon_url = noimage_url
                                else:
                                    icon_url = noimage_url
                            else:
                                icon_url = os.path.join(self._html_url, "static", "img", "cpt-nogui.png")

                            if not cpt_pages.get(pkg_name):
                                cpt_pages[pkg_name] = list()

                            page_data = {'cid': cid, 'mdata': mdata_yml, 'icon_url': icon_url, 'archs': [arch]}
                            try:
                                l = cpt_pages[pkg_name]
                                index = next(i for i, v in enumerate(l) if equal_dicts(v, page_data, ['archs']))
                                cpt_pages[pkg_name][index]['archs'].append(arch)
                            except StopIteration:
                                cpt_pages[pkg_name].append(page_data)

                                # increase valid metainfo count
                                metainfo_count += 1

                            # check if we had this package, and add to summary
                            pksum = mdata_summaries[maintainer].get(pkg_name)
                            if not pksum:
                                pksum = dict()

                            if pksum.get('cids'):
                                if not cid in pksum['cids']:
                                    pksum['cids'].append(cid)
                            else:
                                pksum['cids'] = [cid]

                            mdata_summaries[maintainer][pkg_name] = pksum


            #
            # Summary and HTML writing
            #

            log.info("Rendering HTML pages for suite '%s/%s'" % (suite_name, component))

            # remove old HTML pages
            shutil.rmtree(export_dir_section, ignore_errors=True)

            # now write the HTML pages with the previously collected & transformed issue data
            for pkg_name, entry_list in hint_pages.items():
                # render issues page
                self.render_template("issues_page.html", export_dir_issues, "%s.html" % (pkg_name),
                        package_name=pkg_name, entries=entry_list, suite=suite_name, section=component)

            # render page with all components found in a package
            for pkg_name, cptlist in cpt_pages.items():
                # render metainfo page
                self.render_template("metainfo_page.html", export_dir_metainfo, "%s.html" % (pkg_name),
                        package_name=pkg_name, cpts=cptlist, suite=suite_name, section=component)

            # Now render our issue index page
            self.render_template("issues_index.html", export_dir_issues, "index.html",
                        package_summaries=issue_summaries, suite=suite_name, section=component)

            # ... and the metainfo index page
            self.render_template("metainfo_index.html", export_dir_metainfo, "index.html",
                        package_summaries=mdata_summaries, suite=suite_name, section=component)


            validate_result = "Validation was not performed."
            d_fname = os.path.join(dep11_minfodir, suite_name, component, "Components-%s.yml.gz" % (arch))
            if os.path.isfile(d_fname):
                # do format validation
                validator = DEP11Validator()
                ret = validator.validate_file(d_fname)
                if ret:
                    validate_result = "No errors found."
                else:
                    validate_result = ""
                    for issue in validator.issue_list:
                        validate_result += issue.replace("FATAL", "<strong>FATAL</strong>")+"<br/>\n"

            # sum up counts for suite statistics
            suite_metainfo_count += metainfo_count
            suite_error_count += error_count
            suite_warning_count += warning_count
            suite_info_count += info_count

            # add current statistics to the statistics database
            stats.add_data(suite_name, component, metainfo_count, error_count, warning_count, info_count)

            # calculate statistics for this component
            count = metainfo_count + error_count + warning_count + info_count
            valid_perc = 100/count*metainfo_count if count > 0 else 0
            error_perc = 100/count*error_count if count > 0 else 0
            warning_perc = 100/count*warning_count if count > 0 else 0
            info_perc = 100/count*info_count if count > 0 else 0

            # Render our overview page
            self.render_template("section_overview.html", export_dir_section, "index.html",
                        suite=suite_name, section=component, valid_percentage=valid_perc,
                        error_percentage=error_perc, warning_percentage=warning_perc, info_percentage=info_perc,
                        metainfo_count=metainfo_count, error_count=error_count, warning_count=warning_count,
                        info_count=info_count, validate_result=validate_result)


        # calculate statistics for this suite
        count = suite_metainfo_count + suite_error_count + suite_warning_count + suite_info_count
        valid_perc = 100/count*suite_metainfo_count if count > 0 else 0
        error_perc = 100/count*suite_error_count if count > 0 else 0
        warning_perc = 100/count*suite_warning_count if count > 0 else 0
        info_perc = 100/count*suite_info_count if count > 0 else 0

        # Render archive components index/overview page
        self.render_template("sections_index.html", export_dir, "index.html",
                        sections=suite['components'], suite=suite_name, valid_percentage=valid_perc,
                        error_percentage=error_perc, warning_percentage=warning_perc, info_percentage=info_perc,
                        metainfo_count=suite_metainfo_count, error_count=suite_error_count, warning_count=suite_warning_count,
                        info_count=suite_info_count)

        # plot graphs
        stats.plot_graphs(os.path.join(export_dir, "stats"))

        # Copy the static files
        target_static_dir = os.path.join(self._export_dir, "html", "static")
        shutil.rmtree(target_static_dir, ignore_errors=True)
        shutil.copytree(os.path.join(self._template_dir, "static"), target_static_dir)