1bf215546Sopenharmony_cifrom __future__ import annotations
2bf215546Sopenharmony_ci
3bf215546Sopenharmony_ciimport re
4bf215546Sopenharmony_cifrom dataclasses import dataclass, field
5bf215546Sopenharmony_cifrom datetime import datetime, timedelta
6bf215546Sopenharmony_cifrom typing import TYPE_CHECKING, Optional
7bf215546Sopenharmony_ci
8bf215546Sopenharmony_cifrom lava.utils.console_format import CONSOLE_LOG
9bf215546Sopenharmony_ci
10bf215546Sopenharmony_ciif TYPE_CHECKING:
11bf215546Sopenharmony_ci    from lava.utils.log_section import LogSectionType
12bf215546Sopenharmony_ci
13bf215546Sopenharmony_ci
14bf215546Sopenharmony_ci@dataclass
15bf215546Sopenharmony_ciclass GitlabSection:
16bf215546Sopenharmony_ci    id: str
17bf215546Sopenharmony_ci    header: str
18bf215546Sopenharmony_ci    type: LogSectionType
19bf215546Sopenharmony_ci    start_collapsed: bool = False
20bf215546Sopenharmony_ci    escape: str = "\x1b[0K"
21bf215546Sopenharmony_ci    colour: str = f"{CONSOLE_LOG['BOLD']}{CONSOLE_LOG['FG_GREEN']}"
22bf215546Sopenharmony_ci    __start_time: Optional[datetime] = field(default=None, init=False)
23bf215546Sopenharmony_ci    __end_time: Optional[datetime] = field(default=None, init=False)
24bf215546Sopenharmony_ci
25bf215546Sopenharmony_ci    @classmethod
26bf215546Sopenharmony_ci    def section_id_filter(cls, value) -> str:
27bf215546Sopenharmony_ci        return str(re.sub(r"[^\w_-]+", "-", value))
28bf215546Sopenharmony_ci
29bf215546Sopenharmony_ci    def __post_init__(self):
30bf215546Sopenharmony_ci        self.id = self.section_id_filter(self.id)
31bf215546Sopenharmony_ci
32bf215546Sopenharmony_ci    @property
33bf215546Sopenharmony_ci    def has_started(self) -> bool:
34bf215546Sopenharmony_ci        return self.__start_time is not None
35bf215546Sopenharmony_ci
36bf215546Sopenharmony_ci    @property
37bf215546Sopenharmony_ci    def has_finished(self) -> bool:
38bf215546Sopenharmony_ci        return self.__end_time is not None
39bf215546Sopenharmony_ci
40bf215546Sopenharmony_ci    def get_timestamp(self, time: datetime) -> str:
41bf215546Sopenharmony_ci        unix_ts = datetime.timestamp(time)
42bf215546Sopenharmony_ci        return str(int(unix_ts))
43bf215546Sopenharmony_ci
44bf215546Sopenharmony_ci    def section(self, marker: str, header: str, time: datetime) -> str:
45bf215546Sopenharmony_ci        preamble = f"{self.escape}section_{marker}"
46bf215546Sopenharmony_ci        collapse = marker == "start" and self.start_collapsed
47bf215546Sopenharmony_ci        collapsed = "[collapsed=true]" if collapse else ""
48bf215546Sopenharmony_ci        section_id = f"{self.id}{collapsed}"
49bf215546Sopenharmony_ci
50bf215546Sopenharmony_ci        timestamp = self.get_timestamp(time)
51bf215546Sopenharmony_ci        before_header = ":".join([preamble, timestamp, section_id])
52bf215546Sopenharmony_ci        colored_header = f"{self.colour}{header}\x1b[0m" if header else ""
53bf215546Sopenharmony_ci        header_wrapper = "\r" + f"{self.escape}{colored_header}"
54bf215546Sopenharmony_ci
55bf215546Sopenharmony_ci        return f"{before_header}{header_wrapper}"
56bf215546Sopenharmony_ci
57bf215546Sopenharmony_ci    def __enter__(self):
58bf215546Sopenharmony_ci        print(self.start())
59bf215546Sopenharmony_ci        return self
60bf215546Sopenharmony_ci
61bf215546Sopenharmony_ci    def __exit__(self, exc_type, exc_val, exc_tb):
62bf215546Sopenharmony_ci        print(self.end())
63bf215546Sopenharmony_ci
64bf215546Sopenharmony_ci    def start(self) -> str:
65bf215546Sopenharmony_ci        assert not self.has_finished, "Starting an already finished section"
66bf215546Sopenharmony_ci        self.__start_time = datetime.now()
67bf215546Sopenharmony_ci        return self.section(marker="start", header=self.header, time=self.__start_time)
68bf215546Sopenharmony_ci
69bf215546Sopenharmony_ci    def end(self) -> str:
70bf215546Sopenharmony_ci        assert self.has_started, "Ending an uninitialized section"
71bf215546Sopenharmony_ci        self.__end_time = datetime.now()
72bf215546Sopenharmony_ci        assert (
73bf215546Sopenharmony_ci            self.__end_time >= self.__start_time
74bf215546Sopenharmony_ci        ), "Section execution time will be negative"
75bf215546Sopenharmony_ci        return self.section(marker="end", header="", time=self.__end_time)
76bf215546Sopenharmony_ci
77bf215546Sopenharmony_ci    def delta_time(self) -> Optional[timedelta]:
78bf215546Sopenharmony_ci        if self.__start_time and self.__end_time:
79bf215546Sopenharmony_ci            return self.__end_time - self.__start_time
80bf215546Sopenharmony_ci
81bf215546Sopenharmony_ci        if self.has_started:
82bf215546Sopenharmony_ci            return datetime.now() - self.__start_time
83bf215546Sopenharmony_ci
84bf215546Sopenharmony_ci        return None
85