mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2025-12-31 11:01:19 +00:00
This CL adds the flag --save_stats_json which prints the MEAN of sweetberry readings into json format. Corresponding unit test has been added too. Printing just the MEAN in json format makes it easier to read it back in power_telemetry_logger, which uploads the stats to the dashboard. This CL is part of the effort to start sweetberry measurements while running power autotests. BRANCH=None BUG=b:68956240 TEST=./powerlog.py -b xxx.board -c xxx.scenario \ --save_stats_json [directory to store it] python -m unittest -v stats_manager_unittest Change-Id: I8274bcac21175f6c53184ced79b6ffe5e7d7a72a Signed-off-by: Mengqi Guo <mqg@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/807636 Reviewed-by: Nick Sanders <nsanders@chromium.org> Reviewed-by: Puthikorn Voravootivat <puthik@chromium.org> Reviewed-by: Todd Broch <tbroch@chromium.org>
157 lines
4.8 KiB
Python
157 lines
4.8 KiB
Python
# Copyright 2017 The Chromium OS Authors. All rights reserved.
|
|
# Use of this source code is governed by a BSD-style license that can be
|
|
# found in the LICENSE file.
|
|
|
|
"""Calculates statistics for lists of data and pretty print them."""
|
|
|
|
from __future__ import print_function
|
|
import collections
|
|
import json
|
|
import numpy
|
|
import os
|
|
|
|
STATS_PREFIX = '@@'
|
|
KEY_PREFIX = '__'
|
|
# This prefix is used for keys that should not be shown in the summary tab, such
|
|
# as timeline keys.
|
|
NOSHOW_PREFIX = '!!'
|
|
|
|
class StatsManager(object):
|
|
"""Calculates statistics for several lists of data(float)."""
|
|
|
|
def __init__(self):
|
|
"""Initialize infrastructure for data and their statistics."""
|
|
self._data = collections.defaultdict(list)
|
|
self._summary = {}
|
|
|
|
def AddValue(self, domain, value):
|
|
"""Add one value for a domain.
|
|
|
|
Args:
|
|
domain: the domain name for the value.
|
|
value: one time reading for domain, expect type float.
|
|
"""
|
|
if isinstance(value, int):
|
|
value = float(value)
|
|
if isinstance(value, float):
|
|
self._data[domain].append(value)
|
|
return
|
|
print('Warning: value %s for domain %s is not a number, thus ignored.' %
|
|
(value, domain))
|
|
|
|
def CalculateStats(self):
|
|
"""Calculate stats for all domain-data pairs.
|
|
|
|
First erases all previous stats, then calculate stats for all data.
|
|
"""
|
|
self._summary = {}
|
|
for domain, data in self._data.iteritems():
|
|
data_np = numpy.array(data)
|
|
self._summary[domain] = {
|
|
'mean' : data_np.mean(),
|
|
'min' : data_np.min(),
|
|
'max' : data_np.max(),
|
|
'stddev' : data_np.std(),
|
|
'count' : data_np.size,
|
|
}
|
|
|
|
def _SummaryToString(self, prefix=STATS_PREFIX):
|
|
"""Format summary into a string, ready for pretty print.
|
|
|
|
Args:
|
|
prefix: start every row in summary string with prefix, for easier reading.
|
|
"""
|
|
headers = ('NAME', 'COUNT', 'MEAN', 'STDDEV', 'MAX', 'MIN')
|
|
table = [headers]
|
|
for domain in sorted(self._summary.keys()):
|
|
if domain.startswith(NOSHOW_PREFIX):
|
|
continue
|
|
stats = self._summary[domain]
|
|
row = [domain.lstrip(KEY_PREFIX)]
|
|
row.append(str(stats['count']))
|
|
for entry in headers[2:]:
|
|
row.append('%.2f' % stats[entry.lower()])
|
|
table.append(row)
|
|
|
|
max_col_width = []
|
|
for col_idx in range(len(table[0])):
|
|
col_item_widths = [len(row[col_idx]) for row in table]
|
|
max_col_width.append(max(col_item_widths))
|
|
|
|
formatted_table = []
|
|
for row in table:
|
|
formatted_row = prefix + ' '
|
|
for i in range(len(row)):
|
|
formatted_row += row[i].rjust(max_col_width[i] + 2)
|
|
formatted_table.append(formatted_row)
|
|
return '\n'.join(formatted_table)
|
|
|
|
def PrintSummary(self, prefix=STATS_PREFIX):
|
|
"""Print the formatted summary.
|
|
|
|
Args:
|
|
prefix: start every row in summary string with prefix, for easier reading.
|
|
"""
|
|
summary_str = self._SummaryToString(prefix=prefix)
|
|
print(summary_str)
|
|
|
|
def GetSummary(self):
|
|
"""Getter for summary."""
|
|
return self._summary
|
|
|
|
def SaveSummary(self, directory, fname='summary.txt', prefix=STATS_PREFIX):
|
|
"""Save summary to file.
|
|
|
|
Args:
|
|
directory: directory to save the summary in.
|
|
fname: filename to save summary under.
|
|
prefix: start every row in summary string with prefix, for easier reading.
|
|
"""
|
|
summary_str = self._SummaryToString(prefix=prefix) + '\n'
|
|
|
|
if not os.path.exists(directory):
|
|
os.makedirs(directory)
|
|
fname = os.path.join(directory, fname)
|
|
with open(fname, 'w') as f:
|
|
f.write(summary_str)
|
|
|
|
def SaveSummaryJSON(self, directory, fname='summary.json'):
|
|
"""Save summary (only MEAN) into a JSON file.
|
|
|
|
Args:
|
|
directory: directory to save the JSON summary in.
|
|
fname: filename to save summary under.
|
|
"""
|
|
data = {
|
|
domain: self._summary[domain]['mean']
|
|
for domain in sorted(self._summary.keys())
|
|
if not domain.startswith(NOSHOW_PREFIX)
|
|
}
|
|
if not os.path.exists(directory):
|
|
os.makedirs(directory)
|
|
fname = os.path.join(directory, fname)
|
|
with open(fname, 'w') as f:
|
|
json.dump(data, f)
|
|
|
|
def GetRawData(self):
|
|
"""Getter for all raw_data."""
|
|
return self._data
|
|
|
|
def SaveRawData(self, directory, dirname='raw_data'):
|
|
"""Save raw data to file.
|
|
|
|
Args:
|
|
directory: directory to create the raw data folder in.
|
|
dirname: folder in which raw data live.
|
|
"""
|
|
if not os.path.exists(directory):
|
|
os.makedirs(directory)
|
|
dirname = os.path.join(directory, dirname)
|
|
if not os.path.exists(dirname):
|
|
os.makedirs(dirname)
|
|
for domain, data in self._data.iteritems():
|
|
fname = domain + '.txt'
|
|
fname = os.path.join(dirname, fname)
|
|
with open(fname, 'w') as f:
|
|
f.write('\n'.join('%.2f' % value for value in data) + '\n')
|