|
21 | 21 | from dvsim.job.data import CompletedJobStatus |
22 | 22 | from dvsim.launcher.factory import get_launcher_cls |
23 | 23 | 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 |
25 | 26 | from dvsim.scheduler import Scheduler |
26 | 27 | from dvsim.utils import ( |
27 | 28 | find_and_substitute_wildcards, |
28 | | - md_results_to_html, |
29 | | - mk_path, |
30 | 29 | rm_path, |
31 | 30 | subst_wildcards, |
32 | 31 | ) |
@@ -90,7 +89,7 @@ def __init__(self, flow_cfg_file, hjson_data, args, mk_config) -> None: |
90 | 89 | self.overrides = [] |
91 | 90 |
|
92 | 91 | # List of cfgs if the parsed cfg is a primary cfg list |
93 | | - self.cfgs = [] |
| 92 | + self.cfgs: Sequence[FlowCfg] = [] |
94 | 93 |
|
95 | 94 | # Add a notion of "primary" cfg - this is indicated using |
96 | 95 | # a special key 'use_cfgs' within the hjson cfg. |
@@ -448,125 +447,67 @@ def deploy_objects(self) -> Sequence[CompletedJobStatus]: |
448 | 447 | interactive=self.interactive, |
449 | 448 | ).run() |
450 | 449 |
|
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 | | - |
469 | 450 | def gen_results(self, results: Sequence[CompletedJobStatus]) -> None: |
470 | 451 | """Generate flow results. |
471 | 452 |
|
472 | 453 | Args: |
473 | 454 | results: completed job status objects. |
474 | 455 |
|
475 | 456 | """ |
| 457 | + reports_dir = Path(self.scratch_base_path) / "reports" |
| 458 | + all_flow_results: Mapping[str, FlowResults] = {} |
| 459 | + |
476 | 460 | for item in self.cfgs: |
477 | 461 | project = item.name |
478 | 462 | item_results = [r for r in results if r.project == project] |
479 | 463 |
|
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 |
487 | 466 |
|
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, |
492 | 471 | ) |
493 | 472 |
|
494 | | - log.verbose("[report]: [%s] [%s/report.html]", project, item.results_dir) |
495 | | - |
496 | 473 | self.errors_seen |= item.errors_seen |
497 | 474 |
|
498 | 475 | 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() |
506 | 483 | ) |
507 | 484 |
|
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 | + ) |
566 | 505 |
|
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 | + ) |
570 | 511 |
|
571 | 512 | def _get_results_page_link(self, relative_to: str, link_text: str = "") -> str: |
572 | 513 | """Create a relative markdown link to the results page.""" |
|
0 commit comments