koder aka kdanilov | 7f59d56 | 2016-12-26 01:34:23 +0200 | [diff] [blame] | 1 | import abc |
koder aka kdanilov | 23e6bdf | 2016-12-24 02:18:54 +0200 | [diff] [blame] | 2 | import array |
koder aka kdanilov | ffaf48d | 2016-12-27 02:25:29 +0200 | [diff] [blame^] | 3 | from typing import Dict, List, Any, Optional |
| 4 | |
| 5 | import numpy |
| 6 | from scipy import stats |
| 7 | |
| 8 | |
| 9 | from .utils import IStorable, Number, round_digits |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 10 | |
| 11 | |
koder aka kdanilov | 23e6bdf | 2016-12-24 02:18:54 +0200 | [diff] [blame] | 12 | class TimeSerie: |
| 13 | name = None # type: str |
| 14 | start_at = None # type: int |
| 15 | step = None # type: int |
| 16 | data = None # type: List[int] |
| 17 | second_axis_size = None # type: int |
| 18 | raw = None # type: Optional[bytes] |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 19 | |
koder aka kdanilov | 23e6bdf | 2016-12-24 02:18:54 +0200 | [diff] [blame] | 20 | def __init__(self, name: str, raw: Optional[bytes], second_axis_size: int, |
| 21 | start_at: int, step: int, data: array.array) -> None: |
| 22 | self.name = name |
| 23 | self.start_at = start_at |
| 24 | self.step = step |
| 25 | self.second_axis_size = second_axis_size |
| 26 | self.data = data # type: ignore |
| 27 | self.raw = raw |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 28 | |
koder aka kdanilov | 23e6bdf | 2016-12-24 02:18:54 +0200 | [diff] [blame] | 29 | def meta(self) -> Dict[str, Any]: |
| 30 | return { |
| 31 | "start_at": self.start_at, |
| 32 | "step": self.step, |
| 33 | "second_axis_size": self.second_axis_size |
| 34 | } |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 35 | |
| 36 | |
| 37 | class SensorInfo: |
| 38 | """Holds information from a single sensor from a single node""" |
| 39 | node_id = None # type: str |
| 40 | source_id = None # type: str |
| 41 | sensor_name = None # type: str |
| 42 | begin_time = None # type: int |
| 43 | end_time = None # type: int |
koder aka kdanilov | 23e6bdf | 2016-12-24 02:18:54 +0200 | [diff] [blame] | 44 | data = None # type: List[int] |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 45 | |
| 46 | def __init__(self, node_id: str, source_id: str, sensor_name: str) -> None: |
| 47 | self.node_id = node_id |
| 48 | self.source_id = source_id |
| 49 | self.sensor_name = sensor_name |
| 50 | |
| 51 | |
| 52 | class TestInfo: |
| 53 | """Contains done test information""" |
| 54 | name = None # type: str |
| 55 | iteration_name = None # type: str |
| 56 | nodes = None # type: List[str] |
| 57 | start_time = None # type: int |
| 58 | stop_time = None # type: int |
| 59 | params = None # type: Dict[str, Any] |
| 60 | config = None # type: str |
| 61 | node_ids = None # type: List[str] |
| 62 | |
| 63 | |
koder aka kdanilov | 23e6bdf | 2016-12-24 02:18:54 +0200 | [diff] [blame] | 64 | class NodeTestResults: |
| 65 | name = None # type: str |
| 66 | node_id = None # type: str |
| 67 | summary = None # type: str |
| 68 | |
| 69 | load_start_at = None # type: int |
| 70 | load_stop_at = None # type: int |
| 71 | |
| 72 | series = None # type: Dict[str, TimeSerie] |
| 73 | |
| 74 | def __init__(self, name: str, node_id: str, summary: str) -> None: |
| 75 | self.name = name |
| 76 | self.node_id = node_id |
| 77 | self.summary = summary |
| 78 | self.series = {} |
| 79 | self.extra_logs = {} # type: Dict[str, bytes] |
| 80 | |
| 81 | |
koder aka kdanilov | ffaf48d | 2016-12-27 02:25:29 +0200 | [diff] [blame^] | 82 | class NormStatProps(IStorable): |
| 83 | "Statistic properties for timeserie" |
| 84 | def __init__(self, data: List[Number]) -> None: |
| 85 | self.average = None # type: float |
| 86 | self.deviation = None # type: float |
| 87 | self.confidence = None # type: float |
| 88 | self.confidence_level = None # type: float |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 89 | |
koder aka kdanilov | ffaf48d | 2016-12-27 02:25:29 +0200 | [diff] [blame^] | 90 | self.perc_99 = None # type: float |
| 91 | self.perc_95 = None # type: float |
| 92 | self.perc_90 = None # type: float |
| 93 | self.perc_50 = None # type: float |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 94 | |
koder aka kdanilov | ffaf48d | 2016-12-27 02:25:29 +0200 | [diff] [blame^] | 95 | self.min = None # type: Number |
| 96 | self.max = None # type: Number |
koder aka kdanilov | 7f59d56 | 2016-12-26 01:34:23 +0200 | [diff] [blame] | 97 | |
koder aka kdanilov | ffaf48d | 2016-12-27 02:25:29 +0200 | [diff] [blame^] | 98 | # bin_center: bin_count |
| 99 | self.bins_populations = None # type: List[int] |
| 100 | self.bins_edges = None # type: List[float] |
| 101 | self.data = data |
koder aka kdanilov | 7f59d56 | 2016-12-26 01:34:23 +0200 | [diff] [blame] | 102 | |
koder aka kdanilov | ffaf48d | 2016-12-27 02:25:29 +0200 | [diff] [blame^] | 103 | self.normtest = None # type: Any |
koder aka kdanilov | 7f59d56 | 2016-12-26 01:34:23 +0200 | [diff] [blame] | 104 | |
koder aka kdanilov | ffaf48d | 2016-12-27 02:25:29 +0200 | [diff] [blame^] | 105 | def __str__(self) -> str: |
| 106 | res = ["StatProps(size = {}):".format(len(self.data)), |
| 107 | " distr = {} ~ {}".format(round_digits(self.average), round_digits(self.deviation)), |
| 108 | " confidence({0.confidence_level}) = {1}".format(self, round_digits(self.confidence)), |
| 109 | " perc_50 = {}".format(round_digits(self.perc_50)), |
| 110 | " perc_90 = {}".format(round_digits(self.perc_90)), |
| 111 | " perc_95 = {}".format(round_digits(self.perc_95)), |
| 112 | " perc_99 = {}".format(round_digits(self.perc_99)), |
| 113 | " range {} {}".format(round_digits(self.min), round_digits(self.max)), |
| 114 | " normtest = {0.normtest}".format(self)] |
| 115 | return "\n".join(res) |
| 116 | |
| 117 | def __repr__(self) -> str: |
| 118 | return str(self) |
| 119 | |
koder aka kdanilov | 7f59d56 | 2016-12-26 01:34:23 +0200 | [diff] [blame] | 120 | def raw(self) -> Dict[str, Any]: |
koder aka kdanilov | ffaf48d | 2016-12-27 02:25:29 +0200 | [diff] [blame^] | 121 | data = self.__dict__.copy() |
| 122 | data['nortest'] = (data['nortest'].statistic, data['nortest'].pvalue) |
| 123 | data['bins_edges'] = list(self.bins_edges) |
| 124 | return data |
koder aka kdanilov | 7f59d56 | 2016-12-26 01:34:23 +0200 | [diff] [blame] | 125 | |
koder aka kdanilov | ffaf48d | 2016-12-27 02:25:29 +0200 | [diff] [blame^] | 126 | @classmethod |
| 127 | def fromraw(cls, data: Dict[str, Any]) -> 'NormStatProps': |
| 128 | data['nortest'] = stats.mstats.NormaltestResult(data['nortest'].statistic, data['nortest'].pvalue) |
| 129 | data['bins_edges'] = numpy.array(data['bins_edges']) |
| 130 | res = cls.__new__(cls) |
| 131 | res.__dict__.update(data) |
| 132 | return res |
koder aka kdanilov | 7f59d56 | 2016-12-26 01:34:23 +0200 | [diff] [blame] | 133 | |
| 134 | |
koder aka kdanilov | ffaf48d | 2016-12-27 02:25:29 +0200 | [diff] [blame^] | 135 | class ProcessedTestResults: |
| 136 | def __init__(self, info: Dict[str, Any], |
| 137 | metrics: Dict[str, NormStatProps]) -> None: |
| 138 | self.test = info['test'] |
| 139 | self.profile = info['profile'] |
| 140 | self.suite = info['suite'] |
| 141 | self.name = "{0.suite}.{0.test}.{0.profile}".format(self) |
| 142 | self.info = info |
| 143 | self.metrics = metrics # mapping {metrics_name: StatProps} |
| 144 | |
| 145 | |
| 146 | # class FullTestResult: |
| 147 | # test_info = None # type: TestInfo |
| 148 | # |
| 149 | # # TODO(koder): array.array or numpy.array? |
| 150 | # # {(node_id, perf_metrics_name): values} |
| 151 | # performance_data = None # type: Dict[Tuple[str, str], List[int]] |
| 152 | # |
| 153 | # # {(node_id, perf_metrics_name): values} |
| 154 | # sensors_data = None # type: Dict[Tuple[str, str, str], SensorInfo] |
| 155 | |
koder aka kdanilov | 7f59d56 | 2016-12-26 01:34:23 +0200 | [diff] [blame] | 156 | |