Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JSON Formatter for Result file (C++ and Python versions). Adds py Text report and py GithubCI report #191

Open
wants to merge 29 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
360d2cc
Adds optional Json Format Report module
MatteoRagni Sep 25, 2024
65e1e54
Adds optional Json Format Report module
MatteoRagni Sep 25, 2024
038d151
Merge remote-tracking branch 'MatteoRagni/antemotion-mr/json-format' …
MatteoRagni Sep 26, 2024
9654bcd
Adds optional Json Format Report module
MatteoRagni Sep 25, 2024
a18be8d
Merge remote-tracking branch 'MatteoRagni/antemotion-mr/json-format' …
MatteoRagni Sep 26, 2024
14046b5
Adds JSON report tool (Python version)
MatteoRagni Sep 30, 2024
dc09b86
Optional to avoid automatic download of deps
MatteoRagni Oct 1, 2024
531e6c9
Base report class and application classes
MatteoRagni Oct 1, 2024
c9774df
Fixing mistype in CmakeFile preventing build
MatteoRagni Oct 1, 2024
f2ac6d6
Just for fun, adding GithubCI formatter
MatteoRagni Oct 1, 2024
9b17fb0
Removing build date, that will be removed in v1.0.0-rc1
MatteoRagni Oct 2, 2024
73a4419
Completing text reporter, fixing issue on parameter automatic parsing
MatteoRagni Oct 4, 2024
5d8c60a
Merge branch 'main' into antemotion-mr/json-format
MatteoRagni Oct 8, 2024
ef43499
Aligning report module with the new framework module directory structure
MatteoRagni Oct 8, 2024
69bb58d
Resolving reviews, testing required
MatteoRagni Oct 8, 2024
7eb784e
Framework definitions for python report modules
MatteoRagni Oct 30, 2024
c682cdb
Add quotation to all windows paths to handle spaces in manifests (#193)
hoangtungdinh Oct 4, 2024
3d1c574
Update esmini doc and add quotation to windows path in manifests (#192)
hoangtungdinh Oct 7, 2024
0519ec7
Update windows build instructions (#197)
romanodanilo Oct 7, 2024
c15bd32
Update documentation and version
hoangtungdinh Oct 8, 2024
6f3850b
Add PyPi README for qc_framework
hoangtungdinh Oct 8, 2024
3256f94
Build demo pipeline every week on Sunday at 2am
hoangtungdinh Oct 16, 2024
e493da9
Update xerces url
hoangtungdinh Oct 16, 2024
a612c32
Enforce rule uid in Cpp baselib and example checker bundle
hoangtungdinh Oct 10, 2024
657155b
Merging staged modifications to run the python report in unit tests
MatteoRagni Oct 30, 2024
35d840c
Restoring fix on windows manifest for tests
MatteoRagni Oct 30, 2024
81c5850
Unit test passes using current implementation of qc-baselib-py
MatteoRagni Oct 30, 2024
8df5556
Merge branch 'main' into antemotion-mr/json-format
MatteoRagni Oct 30, 2024
c9ea3f6
Including documentation for report utilities
MatteoRagni Oct 30, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -158,3 +158,6 @@ cython_debug/
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/

# VSCode
.vscode
5 changes: 5 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_STANDARD 17)

option(ASAM_QC_ENABLE_FETCH_CONTENT
"Allow usage of fetch content. If OFF, alla packages should be available via find_package" ON)
option(ASAM_QC_JSON_REPORT_FORMAT_ENABLE
"Compile Report Format JSON utility. Requires nlohmann-json as dependency" OFF)

file(STRINGS version QC4OPENX_VERSION LIMIT_COUNT 1)
project(qc4openx VERSION ${QC4OPENX_VERSION})

Expand Down
3 changes: 3 additions & 0 deletions runtime/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ black = "^24.4.2"

[tool.poetry.scripts]
qc_runtime = 'runtime.runtime:main'
qc_report_json = 'runtime.report.json.__main__:main'
qc_report_text = 'runtime.report.text.__main__:main'
qc_report_github_ci = 'runtime.report.github_ci.__main__:main'

[build-system]
requires = ["poetry-core"]
Expand Down
5 changes: 5 additions & 0 deletions runtime/runtime/report/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Copyright 2024, ASAM e.V.+
#
# This Source Code Form is subject to the terms of the Mozilla
# Public License, v. 2.0. If a copy of the MPL was not distributed
# with this file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 changes: 7 additions & 0 deletions runtime/runtime/report/base/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Copyright 2024, ASAM e.V.
#
# This Source Code Form is subject to the terms of the Mozilla
# Public License, v. 2.0. If a copy of the MPL was not distributed
# with this file, You can obtain one at https://mozilla.org/MPL/2.0/.
from .report_formatter import ReportFormatter
from .report_application import ReportApplication
265 changes: 265 additions & 0 deletions runtime/runtime/report/base/report_application.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,265 @@
# Copyright 2024, ASAM e.V.
#
# This Source Code Form is subject to the terms of the Mozilla
# Public License, v. 2.0. If a copy of the MPL was not distributed
# with this file, You can obtain one at https://mozilla.org/MPL/2.0/.
from argparse import ArgumentParser
from .report_formatter import ReportFormatter
import logging
import sys


class ReportApplication:
r"""
Class that wraps the common behavior for a ``ReportFormatter`` in order to create
a command line application wich is compatible with the desing specification of the
Quality Checker runtime.

Application created with this utility class have the following command line
arguments:

* For each formatter parameters, a custom automatic optional argument is created
programmatically, with declared default values.
* create a specific configuration file for the current formatter. The configuration
file will contain all the parameters supported. Default value for the parameter
can be changed with command line input. It is possible to define the output file
* report directly from a result file. It is possible to use the additional arguments
to define the path of the input file
* report from a configuration file. In this case, the precedence for the parameters
follows:

* lowest: default value
* configuration file
* highest: command line arguments

It is possible to define stdout / stderr output by using the ``__stdout__`` /
``__stderr__`` strings in command line arguments.

There are optional arguments for the current class, provided as ``kwargs``.

Use the ``main`` method to create the actual application.

Example usage::

def main():
app = ReportApplication(formatter)
return app.main()

if __name__ == "__main__":
main()

:param formatter: formatter object for application creation
:param prog: string that represent the name to be used in help
:param description: description to include in the program help
:param license: license epilogue to include in the program help
"""

class Constants:
DEFAULT_OUTPUT_CONFIG_FILE_NAME = f"Config.xml"
INPUT_FILE_IS_STDIN = "__stdin__"
OUTPUT_FILE_IS_STDERR = "__stderr__"
OUTPUT_FILE_IS_STDOUT = "__stdout__"

def __init__(self, formatter: ReportFormatter, *args, **kwargs):
self._formatter = formatter

_default_license = "Quality Framework, Copyright 2024, ASAM e.V.: License MPLv2"
_default_description = f"Formatter {self._formatter.module_name} implementation"
_default_prog = self._formatter.module_name

self._log = logging.getLogger(self._formatter.module_name)

self._args = ArgumentParser(
prog=kwargs.get("prog") or _default_prog,
description=kwargs.get("description") or _default_description,
epilog=kwargs.get("license") or _default_license,
)
self._define_arguments()

def main(self) -> int:
r"""
Main function. Executes the formatter.

:return: integer value as exit number
"""
try:
args, override_params = self._parse_args_and_params()

for name, value in override_params.items():
self._formatter.set_param(name, value)

if args.default_conf:
self.export_default_config(args.default_conf_filename)
return 0

if args.from_result:
self.report_from_result()
return 0

if args.from_config:
self.report_from_config(args.from_config, override_params)
return 0

except Exception as e:
self._log.exception(
"Error while executing the report tool module: %s - %s",
type(e).__name__,
e,
)
return -1
return 1

def _define_arguments(self):
r"""
Defines application arguments. Parameters specific for the formatter
are created as ``"param_{name of the parmaeter}"``
"""
group = self._args.add_mutually_exclusive_group(required=True)
group.add_argument(
"-c",
"--from-configuration",
dest="from_config",
type=str,
help="run the application with an input configuration file",
)
group.add_argument(
"-r",
"--from-result",
dest="from_result",
action="store_true",
help="run the application directly on an input result file",
)
group.add_argument(
"-x",
"--export-config",
dest="default_conf",
action="store_true",
help="write default (or modified) configuration",
)
self._args.add_argument(
"-xo",
"--export-config-output",
dest="default_conf_filename",
default=self.Constants.DEFAULT_OUTPUT_CONFIG_FILE_NAME,
help="optional name of file for default configuration",
)

# Automatically assigning parameters to command line argument
class ArgsDefaulter:
def __init__(self, default_type):
self.default_type = default_type
def __call__(self, arg):
if arg is None:
return None
return self.default_type(arg)

param_group = self._args.add_argument_group("Parameters")
for name in self._formatter.get_params_list():
value = self._formatter.get_param(name)
value_type = type(value)

param_group.add_argument(
f"--param-{name}",
default=None,
type=ArgsDefaulter(value_type),
dest=f"param_{name}",
help=f"formatter parameter {name} (default value: `{value}` [{value_type.__name__}])",
)

def _parse_args_and_params(self):
r"""
Evaluate command line parameters and extract the parameters override dictionary

:return: a tuple of namespace and dictionary with override params
"""
args = self._args.parse_args()

override_params = {}
for name in self._formatter.get_params_list():
param_name = f"param_{name}"
if param_name in args:
value = getattr(args, param_name)
if value:
override_params[name] = getattr(args, param_name)

return args, override_params

def export_default_config(self, output_filename):
r"""
Export the default configuration for the formatter. Command line
parameter values can be used to customize the created configuration file.

:param output_filename: name of the output file
"""
from qc_baselib import Configuration
from qc_baselib.models.config import Config, ReportModuleType
from qc_baselib.models.common import ParamType

parameters = [
ParamType(name=name, value=self._formatter.get_param(name))
for name in self._formatter.get_params_list()
]

config = Config(
params=[],
checker_bundles=[],
reports=[
ReportModuleType(
application=self._formatter.module_name,
params=parameters,
)
],
)
# FIXME: Fix this approach when public interface for reports is available
config_string = config.to_xml(
pretty_print=True, xml_declaration=True, standalone=False, encoding="utf-8"
).decode('utf-8')

if output_filename == self.Constants.OUTPUT_FILE_IS_STDOUT:
print(config_string, file=sys.stdout, flush=True)
return
if output_filename == self.Constants.OUTPUT_FILE_IS_STDERR:
print(config_string, file=sys.stderr, flush=True)
return
with open(output_filename, "w") as output_fp:
output_fp.write(config_string) # type: ignore

def report_from_result(self):
r"""
Runs the formatter by loading directly a result file
"""
input: str = self._formatter.get_param(self._formatter.default_input_file_param_name) # type: ignore
output: str = self._formatter.get_param(self._formatter.default_output_file_param_name) # type: ignore

self._formatter.dump(input, output)

def report_from_config(self, configuration_file, override_params):
r"""
Runs the formatter by loading a configuration file.

.. info:: command line parameters override configuration parmaeter values

:param configuration_file: path of the configuration file to load
:param override_param: command line override params
"""
from qc_baselib import Configuration
from qc_baselib.models.config import Config

configuration = Configuration()
if configuration_file == self.Constants.INPUT_FILE_IS_STDIN:
inner_config = Config.from_xml(sys.stdin.read().encode("utf-8"))
# FIXME: Fix this for update public interface of models
configuration._configuration = inner_config
else:
configuration.load_from_file(configuration_file)

for name in self._formatter.get_params_list():
if override_params.get(name) is not None:
continue
param_value = configuration.get_report_module_param(
self._formatter.module_name, name
)
if param_value:
self._formatter.set_param(name, param_value)

self.report_from_result()
Loading
Loading