/ src / evidently / legacy / ui / storage / utils.py
utils.py
 1  import json
 2  from typing import Any
 3  from typing import Callable
 4  from typing import Iterator
 5  from typing import List
 6  from typing import Optional
 7  from typing import Tuple
 8  
 9  from evidently._pydantic_compat import BaseModel
10  from evidently.legacy.base_metric import MetricResult
11  from evidently.legacy.core import BaseResult
12  from evidently.legacy.utils import NumpyEncoder
13  
14  
15  def iterate_obj_fields(
16      obj: Any, paths: List[str], early_stop: Optional[Callable[[Any, List[str]], Optional[List[Tuple[str, Any]]]]] = None
17  ) -> Iterator[Tuple[str, Any]]:
18      if early_stop is not None:
19          es = early_stop(obj, paths)
20          if es is not None:
21              yield from es
22              return
23  
24      from evidently.ui.backport import ByLabelCountValueV1
25      from evidently.ui.backport import ByLabelValueV1
26  
27      if isinstance(obj, ByLabelValueV1):
28          yield from (
29              [(".".join(paths + ["values"]), obj.values)]
30              + [(".".join(paths + ["values", str(key)]), str(val)) for key, val in obj.values.items()]
31          )
32          return
33      if isinstance(obj, ByLabelCountValueV1):
34          yield from (
35              [(".".join(paths + ["counts"]), obj.counts)]
36              + [(".".join(paths + ["counts", str(key)]), str(val)) for key, val in obj.counts.items()]
37              + [(".".join(paths + ["shares"]), obj.shares)]
38              + [(".".join(paths + ["shares", str(key)]), str(val)) for key, val in obj.shares.items()]
39          )
40          return
41      if isinstance(obj, list):
42          return
43      if isinstance(obj, dict):
44          yield from (r for key, value in obj.items() for r in iterate_obj_fields(value, paths + [str(key)], early_stop))
45          return
46      if isinstance(obj, BaseResult) and obj.__config__.extract_as_obj:
47          yield ".".join(paths), obj
48          return
49      if isinstance(obj, BaseModel):
50          yield from (
51              r
52              for name, field in obj.__fields__.items()
53              for r in iterate_obj_fields(getattr(obj, name), paths + [name], early_stop)
54          )
55          return
56      yield ".".join(paths), obj
57  
58  
59  def iterate_obj_float_fields(obj: Any, paths: List[str]) -> Iterator[Tuple[str, str]]:
60      for path, value in iterate_obj_fields(obj, paths):
61          if isinstance(value, dict):
62              yield path, json.dumps(value, cls=NumpyEncoder)
63              continue
64          if isinstance(value, BaseResult) and value.__config__.extract_as_obj:
65              yield path, json.dumps(value.dict(), cls=NumpyEncoder)
66              continue
67          try:
68              value = str(float(value))
69              yield path, value
70          except (TypeError, ValueError):
71              continue
72  
73  
74  def iterate_metric_results_fields(metric_result: MetricResult) -> Iterator[Tuple[str, str]]:
75      yield from iterate_obj_float_fields(metric_result, [])