# GNAThub (GNATdashboard)
# Copyright (C) 2016-2017, AdaCore
#
# This is free software; you can redistribute it and/or modify it under
# terms of the GNU General Public License as published by the Free Soft-
# ware Foundation; either version 3, or (at your option) any later ver-
# sion. This software is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN-
# TABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
# License for more details. You should have received a copy of the GNU
# General Public License distributed with this software; see file
# COPYING3. If not, go to http://www.gnu.org/licenses for a complete copy
# of the license.
"""GNAThub plug-in for the generation of a standalone rich HTML report.
It exports the HTMLReport class which implements the :class:`GNAThub.Plugin`
interface. This allows GNAThub's plug-in scanner to automatically find this
module and load it as part of the GNAThub default execution.
"""
import GNAThub
from GNAThub import Console, Plugin, Reporter
from _report import ReportBuilder, write_js_header
import inspect
import os
from enum import Enum
from pathlib import Path
from shutil import copy2, copytree, rmtree, which
import _codepeer
[docs]class HTMLReport(Plugin, Reporter):
"""HTMLReport plugin for GNAThub."""
@property
def name(self):
return 'html-report'
@property
def webapp_dir(self):
"""Return the path to the webapp directory.
This directory contains the generic parts of the web application.
:rtype: str
"""
this = inspect.getfile(inspect.currentframe())
return os.path.join(os.path.dirname(os.path.dirname(this)), 'webui')
@property
def output_dir(self):
"""Return the path to the directory where to generate the HTML report.
:return: the full path to the output directory
:rtype: str
"""
return os.path.join(GNAThub.root(), self.name)
[docs] def copy_contents(self, src, dstfile):
with open(src, 'r') as srcfile:
dstfile.write(srcfile.read())
[docs] def output_xml(self, src, dst):
with open(dst, 'w') as dstfile:
write_js_header(dst, dstfile)
dstfile.write("`")
self.copy_contents(src, dstfile)
dstfile.write('`')
[docs] def output_json(self, src, dst):
with open(dst, 'w') as dstfile:
write_js_header(dst, dstfile)
self.copy_contents(src, dstfile)
[docs] def verbose_info(self, message):
if (GNAThub.verbose()):
self.info(message)
[docs] def report(self):
"""Generate JSON-encoded representation of the data collected."""
# The output directory for the JSON-encoded report data
data_output_dir = os.path.join(self.output_dir, 'json')
src_output_dir = os.path.join(self.output_dir, 'source')
try:
self.info('generate JSON-encoded report')
# Create directory structure if needed
if not os.path.exists(self.output_dir):
os.makedirs(self.output_dir)
else:
self.log.warn('%s: already exists', self.output_dir)
self.log.warn('existing report may be overriden')
# Copy the generic web application files
for entry in os.listdir(self.webapp_dir):
path = os.path.join(self.webapp_dir, entry)
dest = os.path.join(self.output_dir, entry)
if os.path.isdir(path):
self.log.debug('rm -r "%s"', dest)
if os.path.isdir(dest):
rmtree(dest)
self.log.debug('cp -r "%s" "%s"', path, dest)
copytree(path, dest)
else:
self.log.debug('cp "%s" "%s"', path, dest)
copy2(path, dest)
# Create the report output directory
for directory in (data_output_dir, src_output_dir):
if not os.path.exists(directory):
os.makedirs(directory)
# The report builder initially starts empty. The more sources
# processed, the more complete the report.
report = ReportBuilder()
# Generate the JSON-representation of each source of the project.
for count, source in enumerate(report.iter_sources(), start=1):
dest = '{}.js'.format(
os.path.join(src_output_dir, source.filename))
source.save_as(dest)
self.log.debug('%s: saved as %s', source.filename, dest)
Console.progress(count, report.index.source_file_count, False)
# Generate the JSON-encoded report for message navigation.
dest = os.path.join(data_output_dir, 'message.js')
report.index.message_to_json(dest)
self.log.debug('message index saved as %s', dest)
self.verbose_info('HTML report message generated in ' + dest)
# Generate the JSON-encoded report for filter panel.
dest = os.path.join(data_output_dir, 'filter.js')
report.index.filter_to_json(dest)
self.log.debug('filter index saved as %s', dest)
self.verbose_info('HTML report filter generated in ' + dest)
# Generate the JSON-encoded report for code navigation.
dest = os.path.join(data_output_dir, 'code.js')
report.index.code_to_json(dest)
self.log.debug('code index saved as %s', dest)
self.verbose_info('HTML report code generated in ' + dest)
# Generate the JSON-encoded report for custom review status.
dest = os.path.join(data_output_dir, 'custom_status.js')
report.index.custom_review_to_json(dest)
self.log.debug('custom review status saved as %s', dest)
self.verbose_info(
'HTML report custom review status generated in ' + dest)
# See codepeer.py:output_dir for the code below
codepeer_obj_dir_suffix = "gnatsas" if _codepeer.version == _codepeer.GNATSAS else "codepeer"
codepeer_obj_dir = os.path.join(GNAThub.Project.artifacts_dir(), codepeer_obj_dir_suffix)
if os.path.isdir(codepeer_obj_dir):
# Extract reviews
review_file = os.path.join(codepeer_obj_dir, "codepeer_review_csv.json")
review_js = os.path.join(data_output_dir, 'codepeer_review.js')
review_js_csv = os.path.join(data_output_dir, 'codepeer_review_csv.js')
if _codepeer.version != _codepeer.LEGACY:
# Reviews have been generated from the CSV
self.output_json(review_file, review_js_csv)
self.verbose_info(review_file + ' file copied as js in ' + review_js_csv)
# Remove the other kind of review file
Path(review_js).unlink(missing_ok=True)
else:
# Call codepeer_bridge to extract the reviews
self.log.debug("Export info from codepeer_bridge")
dest = os.path.join(data_output_dir, 'codepeer_review.xml')
name = 'codepeer_bridge'
cmd = ['codepeer_bridge',
'--output-dir=' + GNAThub.output_dir(),
'--db-dir=' + GNAThub.db_dir(),
'--export-reviews=' + dest]
self.log.debug('Codepeer_bridge file generated in %s', dest)
self.verbose_info('Codepeer_bridge file generated in ' + dest)
GNAThub.Run(name, cmd)
self.verbose_info('Converting Codepeer_bridge file into' +
'js file ' + review_js)
self.output_xml(dest, review_js)
# Remove the other kind of review file
Path(review_js_csv).unlink(missing_ok=True)
legacy_race_dir = os.path.join(
'codepeer',
GNAThub.Project.name().lower() + '.output',
)
gnatsas_race_dir = os.path.join(
'gnatsas',
GNAThub.Project.name().lower() + '.inspector',
'output',
)
race_dir = gnatsas_race_dir if _codepeer.version == _codepeer.GNATSAS else legacy_race_dir
# Get race_condition file
race_file = os.path.join(GNAThub.Project.artifacts_dir(),
race_dir,
'race_conditions.xml')
if os.path.isfile(race_file):
dest = os.path.join(data_output_dir, 'race_conditions.js')
self.output_xml(race_file, dest)
self.verbose_info(race_file + ' file copied as js in ' + dest)
except Exception as why:
self.log.exception('failed to generate the HTML report')
self.error(str(why))
return GNAThub.EXEC_FAILURE
except IOError as why:
self.log.exception('failed to generate the HTML report')
self.error(str(why))
return GNAThub.EXEC_FAILURE
else:
return GNAThub.EXEC_SUCCESS