Skip to content

Commit

Permalink
Counter panels for TestSuites (#843)
Browse files Browse the repository at this point in the history
  • Loading branch information
mike0sv authored Nov 15, 2023
1 parent 4258133 commit cf9d22f
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 1 deletion.
10 changes: 9 additions & 1 deletion src/evidently/pydantic_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,9 +138,17 @@ def __evidently_dependencies__(self):


class EnumValueMixin(BaseModel):
def _to_enum_value(self, key, value):
field = self.__fields__[key]
if not issubclass(field.type_, Enum):
return value
if isinstance(value, list):
return [v.value if isinstance(v, Enum) else v for v in value]
return value.value if isinstance(value, Enum) else value

def dict(self, *args, **kwargs) -> "DictStrAny":
res = super().dict(*args, **kwargs)
return {k: v.value if isinstance(v, Enum) else v for k, v in res.items()}
return {k: self._to_enum_value(k, v) for k, v in res.items()}


class ExcludeNoneMixin(BaseModel):
Expand Down
48 changes: 48 additions & 0 deletions src/evidently/ui/dashboards.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import abc
import datetime
import traceback
import typing
import uuid
from collections import Counter
from collections import defaultdict
Expand Down Expand Up @@ -424,6 +425,53 @@ def _to_period(self, timestamp: datetime.datetime) -> datetime.datetime:
return pd.Series([timestamp], name="dt").dt.to_period(self.time_agg)[0]


class DashboardPanelTestSuiteCounter(DashboardPanel):
agg: CounterAgg = CounterAgg.NONE
filter: ReportFilter = ReportFilter(metadata_values={}, tag_values=[], include_test_suites=True)
test_filters: List[TestFilter] = []
statuses: List[TestStatus] = [TestStatus.SUCCESS]

def _iter_statuses(self, reports: Iterable[ReportBase]):
for report in reports:
if not self.filter.filter(report):
continue
if not isinstance(report, TestSuite):
continue
if self.test_filters:
for test_filter in self.test_filters:
yield report.timestamp, test_filter.get(report).values()
else:
yield report.timestamp, TestFilter().get(report).values()

def build_widget(self, reports: Iterable[ReportBase]) -> BaseWidgetInfo:
if self.agg == CounterAgg.NONE:
statuses, postfix = self._build_none(reports)
elif self.agg == CounterAgg.LAST:
statuses, postfix = self._build_last(reports)
else:
raise ValueError(f"TestSuite Counter does not support agg {self.agg}")

total = sum(statuses.values())
value = sum(statuses[s] for s in self.statuses)
statuses_join = ", ".join(s.value for s in self.statuses)
return counter(counters=[CounterData(f"{value}/{total} {statuses_join}{postfix}", self.title)], size=self.size)

def _build_none(self, reports: Iterable[ReportBase]) -> Tuple[Counter, str]:
statuses: typing.Counter[TestStatus] = Counter()
for _, values in self._iter_statuses(reports):
statuses.update(values)
return statuses, ""

def _build_last(self, reports: Iterable[ReportBase]) -> Tuple[Counter, str]:
last_ts = None
statuses: typing.Counter[TestStatus] = Counter()
for ts, values in self._iter_statuses(reports):
if last_ts is None or ts > last_ts:
last_ts = ts
statuses = Counter(values)
return statuses, f" ({last_ts})"


class DashboardConfig(BaseModel):
name: str
panels: List[DashboardPanel]
Expand Down

0 comments on commit cf9d22f

Please sign in to comment.