def test_pretty_table_printer(): # Functional equivalence to # https://pypi.org/project/pretty-table-printer # https://github.com/cfmeyers/pretty_table_printer # (tests/test_pretty_table_printer.py # test_it_knows_how_to_print_itself) # # delta: # * KVITable doesn't have explicit field widths/truncation (implicit in pretty_table_printer?) # * KVITable default alignment is right instead of left # * KVITable doesn't show a bottom border kvit = KVITable(valuecol_name='name') kvit.add('Sam', id=1) kvit.add('Layla', id=2) kvit.add('Jack Gabriel', id=3) rows = kvit.get_rows() assert [ [ 1, 'Sam' ], [ 2, 'Layla' ], [ 3, 'Jack Gabriel' ], ] == rows show = kvit.render() assert '\n'.join([ '| id | name |', '+----+--------------+', '| 1 | Sam |', '| 2 | Layla |', '| 3 | Jack Gabriel |', ]) == show
def test_ptable(): # Functional equivalence to https://pypi.org/project/PTable # # delta: # * KVITable default alignment is right instead of left kvit = KVITable(valuecol_name='Annual Rainfall') kvit.add(600.5, ('City name', 'Adelaide'), Area=1295, Population=1158259) kvit.add(1146.4, ('City name', 'Brisbane'), Area=5905, Population=1857594) kvit.add(1714.7, ('City name', 'Darwin'), Area=112, Population=120900) kvit.add(619.5, ('City name', 'Hobart'), Area=1357, Population=205556) kvit.add(646.9, ('City name', 'Melbourne'), Area=1566, Population=3806092) kvit.add(869.4, ('City name', 'Perth'), Area=5386, Population=1554769) kvit.add(1214.8, ('City name', 'Sydney'), Area=2058, Population=4336374) show = kvit.render() print(show) assert '\n'.join([ '| City name | Area | Population | Annual Rainfall |', '+-----------+------+------------+-----------------+', '| Adelaide | 1295 | 1158259 | 600.5 |', '| Brisbane | 5905 | 1857594 | 1146.4 |', '| Darwin | 112 | 120900 | 1714.7 |', '| Hobart | 1357 | 205556 | 619.5 |', '| Melbourne | 1566 | 3806092 | 646.9 |', '| Perth | 5386 | 1554769 | 869.4 |', '| Sydney | 2058 | 4336374 | 1214.8 |', ]) == show
def test_empty_kvitable_with_labels_render_text(): kvit = KVITable({'foo':[], 'dog':[]}) show = kvit.render(as_format='ascii') assert '\n'.join([ '| foo | dog | Value |', '+-----+-----+-------+', ]) == show
def test_empty_kvitable_render_text(): kvit = KVITable() show = kvit.render(as_format='ascii') assert '\n'.join([ '| Value |', '+-------+', '| |', ]) == show
def test_non_frozen_kvitable_add_key(): kvit = KVITable({'foo':['bar','baz']}, kv_frozen=False) kvit.add("hi", foo='bar') kvit.add("yo", foo='baz', dog='woof') rows = kvit.get_rows() assert [ [ 'bar', '', 'hi' ], [ 'baz', 'woof', 'yo'], ] == rows show = kvit.render() assert '\n'.join([ '| foo | dog | Value |', '+-----+------+-------+', '| bar | | hi |', '| baz | woof | yo |', ]) == show
def test_table_printer(): # Functional equivalence to https://pypi.org/project/table_printer # delta: # * KVITable does not support left/right/center alignment # * KVITable does not support explicit field widths kvit = KVITable(valuecol_name='Description') kvit.add('World', Title='Hello') kvit.add('1 title', Title='TOTAL') show = kvit.render(row_group=['Title']) assert '\n'.join([ '| Title | Description |', '+-------+-------------+', '| Hello | World |', '+-------+-------------+', '| TOTAL | 1 title |', '+-------+-------------+', ]) == show
def test_zoo_no_subtype_colstack_render(zoo_table): kv = zoo_table.keyvals() subtype_idx = list(kv.keys()).index('Subtype') del kv['Subtype'] zt2 = KVITable(kv, valuecol_name='Count', default_factory=int, kv_frozen=False) for row in zoo_table.get_rows(): del row[subtype_idx] zt2.add(lambda v: v + row[-1], *tuple(zip(kv, row[:-1]))) show = zt2.render(row_repeat=False, sort_vals=True, row_group=['Location', 'Biome', 'Category'], colstack_at='Name') assert '\n'.join([ '| Location | Biome | Category | Diet | Bear | Giraffe | Hippo | Lion | Penguin | Rhino | <- Name', '+-----------+----------+----------+-----------+------+---------+-------+------+---------+-------+', '| LA | Jungle | Animal | Herbivore | | | 1 | | | |', '| +----------+----------+-----------+------+---------+-------+------+---------+-------+', '| | Savannah | Animal | Carnivore | | | | 4 | | |', '| | | | Herbivore | | 2 | | | | 3 |', '+-----------+----------+----------+-----------+------+---------+-------+------+---------+-------+', '| Miami | Polar | Bird | Carnivore | | | | | 20 | |', '| +----------+----------+-----------+------+---------+-------+------+---------+-------+', '| | Savannah | Animal | Carnivore | | | | 2 | | |', '| | | | Herbivore | | 3 | | | | |', '+-----------+----------+----------+-----------+------+---------+-------+------+---------+-------+', '| New York | Savannah | Animal | Carnivore | | | | 3 | | |', '+-----------+----------+----------+-----------+------+---------+-------+------+---------+-------+', '| San Diego | Jungle | Animal | Omnivore | 1 | | | | | |', '| +----------+----------+-----------+------+---------+-------+------+---------+-------+', '| | Plains | Animal | Omnivore | 2 | | | | | |', '| +----------+----------+-----------+------+---------+-------+------+---------+-------+', '| | Polar | Animal | Omnivore | 1 | | | | | |', '| | +----------+-----------+------+---------+-------+------+---------+-------+', '| | | Bird | Carnivore | | | | | 10 | |', '| +----------+----------+-----------+------+---------+-------+------+---------+-------+', '| | Savannah | Animal | Carnivore | | | | 9 | | |', '+-----------+----------+----------+-----------+------+---------+-------+------+---------+-------+', ]) == show
def test_non_frozen_kvitable_add_deep_key(): kvit = KVITable({'foo':['bar','baz'], 'moon':['beam', 'pie'], }, kv_frozen=False, valuecol_name='says', keyval_factory=lambda key: '?') kvit.add("hi", foo='bar', moon='pie') kvit.add("yo", foo='baz', moon='beam', dog='woof') kvit.add("Excellent!", foo='Bill', moon='Ted', dog='arf arf') rows = kvit.get_rows() assert [ [ 'Bill', 'Ted', 'arf arf', 'Excellent!'], [ 'bar', 'pie', '?', 'hi' ], [ 'baz', 'beam', 'woof', 'yo'], ] == rows show = kvit.render(sort_vals=True) assert '\n'.join([ '| foo | moon | dog | says |', '+------+------+---------+------------+', '| Bill | Ted | arf arf | Excellent! |', '| bar | pie | ? | hi |', '| baz | beam | woof | yo |', ]) == show
def test_empty_kvitable_with_labels_render_html(): kvit = KVITable({'foo':[], 'dog':[]}) show = kvit.render(as_format='html') assert 'Value' in show
def test_empty_kvitable_render_html(): kvit = KVITable() show = kvit.render(as_format='html') assert 'Value' in show
def html_summary(repdata, base_builder_url=None): section_hdrfun = lambda msg: '<br/><hr class="section_line"/><br/><h2>' + msg + '</h2><br/>' subsection_hdrfun = lambda msg: '<br/><h3>' + msg + '</h3>' entshow_fun = tcell_entshow(base_builder_url) projects = set( [sr.project for sr in repdata if isinstance(sr, StatusReport)]) summary = KVITable(default_factory=int, valuecol_name='Total') summary.add(len(projects), Element='Projects') summary.add(len( set([ sr.branch for sr in repdata if isinstance(sr, StatusReport) and sr.branchtype == 'pullreq' ])), Element='Pull Requests') projtable = KVITable( { 'Project': sorted( list( set([ sr.project for sr in repdata if isinstance(sr, StatusReport) ]))), 'Status': ['TOTAL', 'ok', 'FAIL', 'pending'], }, valuecol_name='Number', kv_frozen=False, default_factory=int) fulltable = KVITable( { 'Branch': [], 'system': ['x86-64_linux'], 'Strategy': ['regular', 'submodules', 'HEADs'], 'Project': [], }, valuecol_name='Build Status', default_factory=lambda: None, keyval_factory=lambda key: 'x86_64-linux' if key == 'system' else 'n/a', kv_frozen=False) mkDetailTable = lambda: KVITable( { 'system': ['x86-64_linux'], 'Branch': [], 'Strategy': ['regular', 'submodules', 'HEADs'], }, valuecol_name='Build Status', default_factory=lambda: None, keyval_factory=lambda key: 'x86_64-linux' if key == 'system' else '', kv_frozen=False) detailtables = defaultdict(mkDetailTable) projtable_sts = lambda s: { 'initial_success': 'ok', 'succeeded': 'ok', # 'pending': 'pending', }.get(s, 'FAIL') for sr in repdata: if isinstance(sr, Notify): summary.add(_inc, Element='Notifications') elif isinstance(sr, PendingStatus): prev = [ r for r in repdata if isinstance(r, StatusReport) and r.project == sr.project and r.buildname == sr.buildname ] if not prev: summary.add(_inc, Element='Builds') projtable.add(_inc, Project=sr.project, Status="TOTAL") else: projtable.add(_dec, Project=sr.project, Status=projtable_sts(prev[0].status)) projtable.add(_inc, Project=sr.project, Status="pending") vars = tuple([(v.varname, v.varvalue) for v in sr.bldvars]) fulltable.add(TCell_PendingBld(sr.project, sr.buildname), *vars, Project=sr.project, Branch=tbl_branch(sr), Strategy=sr.strategy) detailtables[sr.project].add(TCell_PendingBld( sr.project, sr.buildname), *vars, Branch=tbl_branch(sr), Strategy=sr.strategy) elif isinstance(sr, NewPending): summary.add(_inc, Element='Builds') projectname = sr.bldcfg.projectname buildname = buildcfg_name(sr.bldcfg) tbl_brname = tbl_branch_(buildname, sr.bldcfg.branchname) projtable.add(_inc, Project=projectname, Status="TOTAL") projtable.add(_inc, Project=projectname, Status="pending") vars = tuple([(v.varname, v.varvalue) for v in sr.bldcfg.bldvars]) fulltable.add(TCell_PendingBld(projectname, buildname), *vars, Project=projectname, Branch=tbl_brname, Strategy=sr.bldcfg.strategy) detailtables[sr.bldcfg.projectname].add( TCell_PendingBld(projectname, buildname), *vars, Branch=tbl_brname, Strategy=sr.bldcfg.strategy) elif isinstance(sr, StatusReport): summary.add(_inc, Element='Builds') projtable.add(_inc, Project=sr.project, Status=projtable_sts(sr.status)) projtable.add(_inc, Project=sr.project, Status='TOTAL') bldres = { 'initial_success': TCell_GoodBld, 'succeeded': TCell_GoodBld, 'fixed': TCell_GoodBld, 'bad_config': TCell_BadCfgBld, }.get(sr.status, lambda proj, name: TCell_FailBld(proj, name, sr.status))( sr.project, sr.buildname) fulltable.add(bldres, *tuple([(v.varname, v.varvalue) for v in sr.bldvars]), Project=sr.project, Branch=tbl_branch(sr), Strategy=sr.strategy) detailtables[sr.project].add(bldres, *tuple([(v.varname, v.varvalue) for v in sr.bldvars]), Branch=tbl_branch(sr), Strategy=sr.strategy) return '\n\n'.join([ summary.render(as_format='html', sort_vals=True), section_hdrfun('Per-project Build Status Summary ::'), projtable.render(row_group=['Project'], row_repeat=False, sort_vals=False, as_format='html', caption='Per-project Build Status Summary', colstack_at='Status'), section_hdrfun('Combined Details ::'), fulltable.render( row_group=['system', 'Branch', 'Strategy'], row_repeat=False, sort_vals=True, entrystr=entshow_fun, as_format='html', caption='Combined Details', colstack_at=(list(fulltable.keyvals().keys()) + [None])[4], ), section_hdrfun('Individual Project Summaries ::'), '\n\n'.join([ subsection_hdrfun('Project %s:\n' % p) + detailtables[p].render( row_repeat=False, as_format='html', caption='Project %s' % p, sort_vals=True, colstack_at=(list(detailtables[p].keyvals().keys()) + [None])[3], row_group=['system', 'Branch'], entrystr=entshow_fun, ) for p in sorted(projects) ]) ])
def text_summary(repdata): sepline = '=' * 60 hashline = '#' * 60 banner = '\n\n%(sepline)s\n%(hashline)s\n%(sepline)s\n\n' % locals() section_hdrfun = lambda msg: banner + msg subsection_hdrfun = lambda msg: msg + '\n' entshow_fun = _show_with_fail projects = set( [sr.project for sr in repdata if isinstance(sr, StatusReport)]) summary = KVITable(default_factory=int, valuecol_name='Total') summary.add(len(projects), Element='Projects') summary.add(len( set([ sr.branch for sr in repdata if isinstance(sr, StatusReport) and sr.branchtype == 'pullreq' ])), Element='Pull Requests') projtable = KVITable( { 'Project': sorted( list( set([ sr.project for sr in repdata if isinstance(sr, StatusReport) ]))), 'Status': ['TOTAL', 'ok', 'FAIL', 'pending'], }, valuecol_name='Number', kv_frozen=False, default_factory=int) fulltable = KVITable( { 'Branch': [], 'system': ['x86-64_linux'], 'Strategy': ['regular', 'submodules', 'HEADs'], 'Project': [], }, valuecol_name='Build Status', default_factory=FailCount, keyval_factory=lambda key: 'x86_64-linux' if key == 'system' else 'n/a', kv_frozen=False) mkDetailTable = lambda: KVITable( { 'system': ['x86-64_linux'], 'Branch': [], 'Strategy': ['regular', 'submodules', 'HEADs'], }, valuecol_name='Build Status', default_factory=FailCount, keyval_factory=lambda key: 'x86_64-linux' if key == 'system' else '', kv_frozen=False) detailtables = defaultdict(mkDetailTable) projtable_sts = lambda s: { 'initial_success': 'ok', 'succeeded': 'ok', # 'pending': 'pending', }.get(s, 'FAIL') for sr in repdata: if isinstance(sr, Notify): summary.add(_inc, Element='Notifications') elif isinstance(sr, PendingStatus): prev = [ r for r in repdata if isinstance(r, StatusReport) and r.project == sr.project and r.buildname == sr.buildname ] if not prev: summary.add(_inc, Element='Builds') projtable.add(_inc, Project=sr.project, Status="TOTAL") else: projtable.add(_dec, Project=sr.project, Status=projtable_sts(prev[0].status)) projtable.add(_inc, Project=sr.project, Status="pending") vars = tuple([(v.varname, v.varvalue) for v in sr.bldvars]) fulltable.add(PendingBld, *vars, Project=sr.project, Branch=tbl_branch(sr), Strategy=sr.strategy) detailtables[sr.project].add(PendingBld, *vars, Branch=tbl_branch(sr), Strategy=sr.strategy) elif isinstance(sr, NewPending): summary.add(_inc, Element='Builds') projtable.add(_inc, Project=sr.bldcfg.projectname, Status="TOTAL") projtable.add(_inc, Project=sr.bldcfg.projectname, Status="pending") vars = tuple([(v.varname, v.varvalue) for v in sr.bldcfg.bldvars]) buildname = buildcfg_name(sr.bldcfg) tbl_brname = tbl_branch_(buildname, sr.bldcfg.branchname) fulltable.add(PendingBld, *vars, Project=sr.bldcfg.projectname, Branch=tbl_brname, Strategy=sr.bldcfg.strategy) detailtables[sr.bldcfg.projectname].add( PendingBld, *vars, Branch=tbl_brname, Strategy=sr.bldcfg.strategy) elif isinstance(sr, StatusReport): summary.add(_inc, Element='Builds') projtable.add(_inc, Project=sr.project, Status=projtable_sts(sr.status)) projtable.add(_inc, Project=sr.project, Status='TOTAL') bldres = _add_if_int({ 'initial_success': '+', 'succeeded': '+', 'fixed': '+', 'bad_config': '-CFG', }.get(sr.status, sr.status)) vars = tuple([(v.varname, v.varvalue) for v in sr.bldvars]) fulltable.add(bldres, *vars, Project=sr.project, Branch=tbl_branch(sr), Strategy=sr.strategy) detailtables[sr.project].add(bldres, *vars, Branch=tbl_branch(sr), Strategy=sr.strategy) keytable = KVITable({'Symbol': []}, valuecol_name='Meaning', kv_frozen=False) keytable.add('Success', Symbol='+') keytable.add("'n' build components failed", Symbol='FAIL*n') keytable.add('Build configuration error', Symbol='-CFG') keytable.add('Pending, no previous builds', Symbol='??') keytable.add('Pending, previously suceeding', Symbol='(+)?') keytable.add('Pending, previous config error', Symbol='(-CFG)?') keytable.add("Pending, previously 'n' components failed", Symbol='(-n)?') return '\n\n'.join([ summary.render( as_format='ascii', sort_vals=True, ), section_hdrfun('Per-project Build Status Summary ::'), projtable.render(row_group=['Project'], row_repeat=False, sort_vals=False, as_format='ascii', colstack_at='Status'), section_hdrfun('Combined Details ::'), fulltable.render( row_group=['system', 'Branch', 'Strategy'], row_repeat=False, sort_vals=True, entrystr=entshow_fun, as_format='ascii', colstack_at=(list(fulltable.keyvals().keys()) + [None])[4], ), section_hdrfun('Individual Project Summaries ::'), '\n\n'.join([ subsection_hdrfun('Project %s:\n' % p) + detailtables[p].render( row_repeat=False, sort_vals=True, as_format='ascii', colstack_at=(list(detailtables[p].keyvals().keys()) + [None])[3], row_group=['system', 'Branch'], entrystr=entshow_fun, ) for p in sorted(projects) ]), section_hdrfun('KEY ::'), keytable.render(as_format='ascii'), ])