Skip to content

Commit 14406e4

Browse files
committed
feat: add report generation from templates
Signed-off-by: James McCorrie <[email protected]>
1 parent 02b05fe commit 14406e4

File tree

4 files changed

+310
-322
lines changed

4 files changed

+310
-322
lines changed

src/dvsim/flow/base.py

Lines changed: 44 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,11 @@
2121
from dvsim.job.data import CompletedJobStatus
2222
from dvsim.launcher.factory import get_launcher_cls
2323
from dvsim.logging import log
24-
from dvsim.report.data import IPMeta, ResultsSummary
24+
from dvsim.report.data import FlowResults, IPMeta, ResultsSummary
25+
from dvsim.report.generate import gen_block_report, gen_summary_report
2526
from dvsim.scheduler import Scheduler
2627
from dvsim.utils import (
2728
find_and_substitute_wildcards,
28-
md_results_to_html,
29-
mk_path,
3029
rm_path,
3130
subst_wildcards,
3231
)
@@ -90,7 +89,7 @@ def __init__(self, flow_cfg_file, hjson_data, args, mk_config) -> None:
9089
self.overrides = []
9190

9291
# List of cfgs if the parsed cfg is a primary cfg list
93-
self.cfgs = []
92+
self.cfgs: Sequence[FlowCfg] = []
9493

9594
# Add a notion of "primary" cfg - this is indicated using
9695
# a special key 'use_cfgs' within the hjson cfg.
@@ -448,125 +447,67 @@ def deploy_objects(self) -> Sequence[CompletedJobStatus]:
448447
interactive=self.interactive,
449448
).run()
450449

451-
@abstractmethod
452-
def _gen_results(self, results: Sequence[CompletedJobStatus]) -> str:
453-
"""Generate flow results.
454-
455-
The function is called after the flow has completed. It collates
456-
the status of all run targets and generates a dict. It parses the log
457-
to identify the errors, warnings and failures as applicable. It also
458-
prints the full list of failures for debug / triage to the final
459-
report, which is in markdown format.
460-
461-
Args:
462-
results: completed job status objects.
463-
464-
Returns:
465-
Results as a formatted string
466-
467-
"""
468-
469450
def gen_results(self, results: Sequence[CompletedJobStatus]) -> None:
470451
"""Generate flow results.
471452
472453
Args:
473454
results: completed job status objects.
474455
475456
"""
457+
reports_dir = Path(self.scratch_base_path) / "reports"
458+
all_flow_results: Mapping[str, FlowResults] = {}
459+
476460
for item in self.cfgs:
477461
project = item.name
478462
item_results = [r for r in results if r.project == project]
479463

480-
json_str = (
481-
item._gen_json_results(item_results) if hasattr(item, "_gen_json_results") else None
482-
)
483-
result = item._gen_results(item_results)
484-
485-
log.info("[results]: [%s]:\n%s\n", project, result)
486-
log.info("[scratch_path]: [%s] [%s]", project, item.scratch_path)
464+
flow_results: FlowResults = item._gen_json_results(item_results)
465+
all_flow_results[project] = flow_results
487466

488-
item.write_results(
489-
self.results_html_name,
490-
item.results_md,
491-
json_str=json_str,
467+
# Write results to the report area.
468+
gen_block_report(
469+
results=flow_results,
470+
path=reports_dir,
492471
)
493472

494-
log.verbose("[report]: [%s] [%s/report.html]", project, item.results_dir)
495-
496473
self.errors_seen |= item.errors_seen
497474

498475
if self.is_primary_cfg:
499-
json_str = self._gen_json_results_summary()
500-
self.gen_results_summary()
501-
502-
self.write_results(
503-
html_filename=self.results_html_name,
504-
text_md=self.results_summary_md,
505-
json_str=json_str,
476+
# The timestamp for this run has been taken with `utcnow()` and is
477+
# stored in a custom format. Store it in standard ISO format with
478+
# explicit timezone annotation.
479+
timestamp = (
480+
datetime.strptime(self.timestamp, "%Y%m%d_%H%M%S")
481+
.replace(tzinfo=timezone.utc)
482+
.isoformat()
506483
)
507484

508-
def _gen_json_results_summary(self) -> str:
509-
"""Generate results summary in JSON format."""
510-
# The timestamp for this run has been taken with `utcnow()` and is
511-
# stored in a custom format. Store it in standard ISO format with
512-
# explicit timezone annotation.
513-
timestamp = (
514-
datetime.strptime(self.timestamp, "%Y%m%d_%H%M%S")
515-
.replace(tzinfo=timezone.utc)
516-
.isoformat()
517-
)
518-
519-
# Extract Git properties.
520-
m = re.search(
521-
r"https://github.com/.+?/tree/([0-9a-fA-F]+)",
522-
self.revision,
523-
)
524-
commit = m.group(1) if m else None
525-
526-
reports_dir = Path(self.scratch_base_path) / "reports"
527-
528-
return ResultsSummary(
529-
top=IPMeta(
530-
name=self.name,
531-
variant=self.variant,
532-
commit=str(commit),
533-
branch=self.branch,
534-
url=self.revision,
535-
),
536-
timestamp=timestamp,
537-
report_index={
538-
item.name: (item.results_dir / item.results_html_name).relative_to(reports_dir)
539-
for item in self.cfgs
540-
},
541-
report_path=(Path(self.results_dir) / self.results_html_name)
542-
.with_suffix(".json")
543-
.relative_to(reports_dir),
544-
).model_dump_json()
545-
546-
@abstractmethod
547-
def gen_results_summary(self) -> str:
548-
"""Public facing API to generate summary results for each IP/cfg file."""
549-
550-
def write_results(self, html_filename: str, text_md: str, json_str: str | None = None) -> None:
551-
"""Write results to files.
552-
553-
This function converts text_md to HTML and writes the result to a file
554-
in self.results_dir with the file name given by html_filename. If
555-
json_str is not None, this function additionally writes json_str to a
556-
file with the same path and base name as the HTML file but with '.json'
557-
as suffix.
558-
"""
559-
results_dir = Path(self.results_dir)
560-
mk_path(results_dir)
561-
562-
# Write results to the report area.
563-
(results_dir / html_filename).write_text(
564-
md_results_to_html(self.results_title, self.css_file, text_md)
565-
)
485+
# Extract Git properties.
486+
m = re.search(
487+
r"https://github.com/.+?/tree/([0-9a-fA-F]+)",
488+
self.revision,
489+
)
490+
commit = m.group(1) if m else None
491+
492+
results_summary = ResultsSummary(
493+
top=IPMeta(
494+
name=self.name,
495+
variant=self.variant,
496+
commit=str(commit),
497+
branch=self.branch,
498+
url=self.revision,
499+
),
500+
timestamp=timestamp,
501+
report_index={item.name: f"{item.name}.html" for item in self.cfgs},
502+
flow_results=all_flow_results,
503+
report_path=reports_dir,
504+
)
566505

567-
if json_str is not None:
568-
filename = Path(html_filename).with_suffix(".json")
569-
(results_dir / filename).write_text(json_str)
506+
# Write results to the report area.
507+
gen_summary_report(
508+
summary=results_summary,
509+
path=reports_dir,
510+
)
570511

571512
def _get_results_page_link(self, relative_to: str, link_text: str = "") -> str:
572513
"""Create a relative markdown link to the results page."""

0 commit comments

Comments
 (0)