Commit 3fbe1d9d authored by Tomas Krizek's avatar Tomas Krizek

diffsum: refactor printing global statistics

parent 1e88e873
from argparse import ArgumentParser, Namespace from argparse import ArgumentParser, Namespace
import logging import logging
import math
import os import os
import sys import sys
from typing import Mapping from typing import Dict, Mapping, Optional, Tuple, Union # noqa
from tabulate import tabulate from tabulate import tabulate
import cfg import cfg
from dataformat import DataMismatch, FieldCounter, FieldLabel from dataformat import DataMismatch, DiffReport, FieldCounter, FieldLabel
Number = Union[int, float]
LOGGING_LEVEL = logging.DEBUG LOGGING_LEVEL = logging.DEBUG
CONFIG_FILENAME = 'respdiff.cfg' CONFIG_FILENAME = 'respdiff.cfg'
REPORT_FILENAME = 'report.json' REPORT_FILENAME = 'report.json'
...@@ -48,6 +51,61 @@ def get_datafile(args: Namespace, check_exists: bool = True, key: str = 'datafil ...@@ -48,6 +51,61 @@ def get_datafile(args: Namespace, check_exists: bool = True, key: str = 'datafil
return datafile return datafile
def format_stats_line(
description: str,
number: int,
pct: float = None,
diff: int = None,
diff_pct: float = None,
additional: str = None
) -> str:
s = {} # type: Dict[str, str]
s['description'] = '{:21s}'.format(description)
s['number'] = '{:8d}'.format(number)
s['pct'] = '{:6.2f} %'.format(pct) if pct is not None else ' ' * 8
s['additional'] = '{:30s}'.format(additional) if additional is not None else ' ' * 30
s['diff'] = '{:+6d}'.format(diff) if diff is not None else ' ' * 6
s['diff_pct'] = '{:+7.2f}'.format(diff_pct) if diff_pct is not None else ' ' * 7
return '{description} {number} {pct} {additional} {diff} {diff_pct}'.format(**s)
def get_stats_data(
n: int,
total: int = None,
ref_n: int = None,
) -> Tuple[int, float, Optional[int], Optional[float]]:
"""
Return absolute and relative data statistics
Optionally, the data is compared with a reference.
"""
def percentage(
dividend: Union[int, float],
divisor: Union[int, float]
) -> Optional[float]:
"""Return dividend/divisor value in %"""
if divisor is None:
return None
if divisor == 0:
if dividend > 0:
return float('+inf')
if dividend < 0:
return float('-inf')
return float('nan')
return dividend * 100.0 / divisor
pct = percentage(n, total)
diff = None
diff_pct = None
if ref_n is not None:
diff = n - ref_n
diff_pct = percentage(diff, ref_n)
return n, pct, diff, diff_pct
def print_fields_overview( def print_fields_overview(
field_counters: Mapping[FieldLabel, FieldCounter] field_counters: Mapping[FieldLabel, FieldCounter]
) -> None: ) -> None:
...@@ -83,3 +141,44 @@ def print_field_mismatch_stats( ...@@ -83,3 +141,44 @@ def print_field_mismatch_stats(
tablefmt='simple', tablefmt='simple',
floatfmt='.2f')) floatfmt='.2f'))
print('') print('')
def print_global_stats(report: DiffReport, reference: DiffReport = None) -> None:
ref_duration = getattr(reference, 'duration', None)
ref_total_answers = getattr(reference, 'total_answers', None)
ref_total_queries = getattr(reference, 'total_queries', None)
print('== Global statistics')
print(format_stats_line('duration', *get_stats_data(
report.duration, ref_n=ref_duration),
additional='seconds'))
print(format_stats_line('queries', *get_stats_data(
report.total_queries, ref_n=ref_total_queries)))
print(format_stats_line('answers', *get_stats_data(
report.total_answers, report.total_queries,
ref_total_answers, ref_total_queries)))
print('')
def print_differences_stats(report: DiffReport, reference: DiffReport = None) -> None:
ref_upstream_unstable = getattr(reference, 'upstream_unstable', None)
ref_total_answers = getattr(reference, 'total_answers', None)
ref_not_reproducible = getattr(reference, 'not_reproducible', None)
ref_summary = getattr(reference, 'summary', None)
ref_target_disagrees = len(ref_summary) if ref_summary is not None else None
ref_usable_answers = getattr(reference, 'usable_answers', None)
print('== Differences statistics')
print(format_stats_line('upstream unstable', *get_stats_data(
report.summary.upstream_unstable, report.total_answers,
ref_upstream_unstable, ref_total_answers),
additional='of answers (ignoring)'))
print(format_stats_line('not 100% reproducible', *get_stats_data(
report.summary.not_reproducible, report.total_answers,
ref_not_reproducible, ref_total_answers),
additional='of answers (ignoring)'))
print(format_stats_line('target disagrees', *get_stats_data(
len(report.summary), report.summary.usable_answers,
ref_target_disagrees, ref_usable_answers),
additional='of not ignored answers'))
print('')
...@@ -123,8 +123,8 @@ def main(): ...@@ -123,8 +123,8 @@ def main():
report = DiffReport.from_json(datafile) report = DiffReport.from_json(datafile)
report.summary = Summary.from_report(report, field_weights) report.summary = Summary.from_report(report, field_weights)
print_global_stats(report) cli.print_global_stats(report)
print_differences_stats(report.summary, report.total_answers) cli.print_differences_stats(report)
if report.summary: if report.summary:
field_counters = report.summary.get_field_counters() field_counters = report.summary.get_field_counters()
......
import math
import pytest
from pytest import approx
from cli import get_stats_data
@pytest.mark.parametrize('n, total, ref_n, expected', [
(9, None, None, (9, None, None, None)),
(9, 10, None, (9, 90, None, None)),
(11, None, 10, (11, None, 1, 10)),
(9, None, 10, (9, None, -1, -10)),
(10, None, 10, (10, None, 0, 0)),
(10, None, 0, (10, None, +10, float('inf'))),
(0, None, 0, (0, None, 0, float('nan'))),
(9, 10, 10, (9, 90, -1, -10)),
(9, 10, 90, (9, 90, -81, -81*100.0/90)),
(90, 100, 9, (90, 90, 81, 81*100.0/9)),
])
def test_get_stats_data(n, total, ref_n, expected):
got_n, got_pct, got_diff, got_diff_pct = get_stats_data(n, total, ref_n)
assert got_n == expected[0]
assert got_diff == expected[2]
def compare_float(got, exp):
if got is None:
return exp is None
if math.isnan(got):
return math.isnan(exp)
return got == approx(exp)
assert compare_float(got_pct, expected[1])
assert compare_float(got_diff_pct, expected[3])
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment