Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
147 changes: 147 additions & 0 deletions src/imgtests/reporting/cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import argparse
import sys
from pathlib import Path
from typing import TYPE_CHECKING, Final

from imgtests.constant import DISTRIBUTION_DESCRIPTIONS
from imgtests.reporting.distro_comparison_export import (
add_distro_comparison_arguments,
)
from imgtests.reporting.distro_comparison_status_export import (
DEFAULT_EPSILON_PERCENT,
)

if TYPE_CHECKING:
from collections.abc import Sequence

EXPORT_TABLES: Final = ("experiment", "util_run_result")
DATABASE_COMMAND: Final = "database"
DISTRO_COMPARISON_COMMAND: Final = "distro-comparison"
DISTRO_COMPARISON_STATUS_COMMAND: Final = "comparison-status"


def parse_args(argv: Sequence[str] | None = None) -> argparse.Namespace:
raw_args = list(sys.argv[1:] if argv is None else argv)

parser = argparse.ArgumentParser(
description="Export imgtests database data and report comparisons to XLSX workbooks.",
)
subparsers = parser.add_subparsers(dest="command", required=True)

parser = subparsers.add_parser(
DATABASE_COMMAND,
help="export database tables to an XLSX workbook",
)
add_database_arguments(parser)
parser.set_defaults(command=DATABASE_COMMAND)

parser = subparsers.add_parser(
DISTRO_COMPARISON_COMMAND,
help="build Poky/SUSE comparison tables and charts from an exported report.xlsx",
)
add_distro_comparison_arguments(parser)
parser.set_defaults(command=DISTRO_COMPARISON_COMMAND)

parser = subparsers.add_parser(
DISTRO_COMPARISON_STATUS_COMMAND,
help="build Poky/SUSE PASS/FAIL status tables from an exported report.xlsx",
)
parser.set_defaults(command=DISTRO_COMPARISON_STATUS_COMMAND)

return parser.parse_args(raw_args)


def add_database_arguments(parser: argparse.ArgumentParser) -> None:
parser.add_argument(
"output",
type=Path,
help="Path to the output .xlsx file.",
)
parser.add_argument(
"--db-url",
required=True,
help="SQLAlchemy database URL.",
)
parser.add_argument(
"--tables",
nargs="+",
choices=EXPORT_TABLES,
default=list(EXPORT_TABLES),
help="Tables to export. Defaults to all project result tables.",
)
parser.add_argument(
"--configuration-id",
dest="configuration_ids",
action="append",
required=True,
metavar="DISTRO=ID",
type=_parse_configuration_id_override,
help=(
"Configuration id to write for a distribution. "
"Must be passed for each supported distribution, for example: "
"--configuration-id poky=10 --configuration-id suse=20."
),
)


def add_comparison_report_arguments(parser: argparse.ArgumentParser) -> None:
parser.add_argument(
"input",
type=Path,
help="Existing report.xlsx generated by the database export.",
)
parser.add_argument(
"--output",
type=Path,
help=(
"Output .xlsx file or output directory. "
"Default: input filename with _comparison suffix."
),
)
parser.add_argument(
"--experiment-ids",
nargs="+",
required=True,
help=(
"Experiment IDs to compare. With two IDs, one Poky/SUSE group is created. "
"With many IDs, comparable Poky/SUSE pairs are built inside the selected IDs."
),
)


def add_distro_comparison_status_arguments(parser: argparse.ArgumentParser) -> None:
add_comparison_report_arguments(parser)
parser.add_argument(
"--epsilon-percent",
type=float,
default=DEFAULT_EPSILON_PERCENT,
help="Relative tolerance for status decisions, in percent. Default: 1.0.",
)


def _parse_configuration_id_override(value: str) -> tuple[str, int]:
distribution_name, separator, raw_id = value.partition("=")

if not separator:
msg = "Expected configuration id in DISTRO=ID format."
raise argparse.ArgumentTypeError(msg)

distribution_name = distribution_name.strip().lower()
raw_id = raw_id.strip()

if distribution_name not in DISTRIBUTION_DESCRIPTIONS:
valid_names = ", ".join(DISTRIBUTION_DESCRIPTIONS)
msg = f"Unsupported distribution '{distribution_name}'. Available: {valid_names}."
raise argparse.ArgumentTypeError(msg)

try:
configuration_id = int(raw_id)
except ValueError as exc:
msg = f"Configuration id for '{distribution_name}' must be an integer."
raise argparse.ArgumentTypeError(msg) from exc

if configuration_id <= 0:
msg = f"Configuration id for '{distribution_name}' must be positive."
raise argparse.ArgumentTypeError(msg)

return distribution_name, configuration_id
Loading
Loading