Skip to content
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions sr/comp/cli/command_line.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
schedule_league,
scorer,
shift_matches,
show_match_scores,
show_schedule,
summary,
top_match_points,
Expand Down Expand Up @@ -60,6 +61,7 @@ def argument_parser() -> argparse.ArgumentParser:
schedule_league.add_subparser(subparsers)
scorer.add_subparser(subparsers)
shift_matches.add_subparser(subparsers)
show_match_scores.add_subparser(subparsers)
show_schedule.add_subparser(subparsers)
summary.add_subparser(subparsers)
top_match_points.add_subparser(subparsers)
Expand Down
167 changes: 167 additions & 0 deletions sr/comp/cli/show_match_scores.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
__description__ = "Show the game and league points achieved for each match"

from typing import Dict, List, Union, NamedTuple
from sr.comp.types import TLA, GamePoints, ArenaName, MatchNumber
from sr.comp.scores import LeaguePosition


class MatchCorner(NamedTuple):
tla: TLA
ranking: Union[LeaguePosition, str] # to allow "???" to be used for unknown scores
game: Union[GamePoints, str]
league: Union[int, str]


class MatchResult(NamedTuple):
num: MatchNumber
arena: ArenaName
display_name: str
corners: Dict[int, MatchCorner]


def collect_match_info(comp, match) -> MatchResult:
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I expect mypy wants the arguments to be typed too.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added types in e0a900a but I have a type error with the degroup command.

sr/comp/cli/show_match_scores.py:47: error: Argument 1 to "degroup" has incompatible type "Mapping[RankedPosition, Set[TLA]]"; expected "Mapping[Optional[LeaguePosition], Iterable[TLA]]"  [arg-type]

from sr.comp.match_period import MatchType
from sr.comp.scores import degroup

if match.type == MatchType.knockout:
score_data = comp.scores.knockout
elif match.type == MatchType.tiebreaker:
score_data = comp.scores.tiebreaker
elif match.type == MatchType.league:
score_data = comp.scores.league

match_id = (match.arena, match.num)

if match_id in score_data.game_points:
league_points = score_data.ranked_points[match_id]
game_points = score_data.game_points[match_id]
ranking = degroup(score_data.game_positions[match_id])
else:
league_points = {}
game_points = {}
ranking = {}
for team in match.teams:
league_points[team] = "???"
game_points[team] = "???"
ranking[team] = "???"

corner_data: Dict[int, MatchCorner] = {}

for corner, team in enumerate(match.teams):
match_id = (match.arena, match.num)

if team: # corner occupied
corner_data[corner] = MatchCorner(
tla=team,
ranking=ranking[team],
game=game_points[team],
league=league_points[team],
)
else:
corner_data[corner] = MatchCorner(
tla='',
ranking='',
game='',
league='',
)

return MatchResult(
num=match.num,
arena=match.arena,
display_name=match.display_name,
corners=corner_data,
)


def print_col(text):
print(text, end='|')


def print_heading(num_corners, name_width, arena_name_width):
print_col("".center(name_width + 1 + arena_name_width))
for idx in range(num_corners):
print_col(f"Zone {idx}".center(22))
print()

print_col("Display Name".center(name_width))
print_col("Arena".center(arena_name_width))
for idx in range(num_corners):
print_col("TLA".center(5))
print_col("Rank")
print_col("Game")
print_col("League")
print()


def print_match(match: MatchResult, name_width, arena_name_width):
print_col(match.display_name.center(name_width))
print_col(match.arena.center(arena_name_width))

for corner in match.corners.values():
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feels like this is going to get pretty wide even just for a four zone match. Is this the best layout for a CLI tool?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the dummy comp the table was 120 wide, this increased to 146 when I moved to using tabulate so I've made it wrap each match at 2 zones in 52931ce which gives a width of 100.

At this point the only part that effects the width of the table is the match and arena names. The next version of tabulate looks to be adding and option to force wrap certain columns at a set width.

Alternatively if we can come up with a way to merge the game and league columns they are limited by the length of the heading. In fact omitting all the headers reduces the width to 78.

+-----------------------+------+------+------+------+--------+------+------+------+------+--------+
|         Match         | Zone | TLA  | Rank | Game | League | Zone | TLA  | Rank | Game | League |
+-----------------------+------+------+------+------+--------+------+------+------+------+--------+
|     Match 0 in A      |    0 |      |      |      |        |    1 | CLY  |    1 |    9 |      8 |
|                       |    2 | TTN  |    2 |    6 |      6 |    3 |      |      |      |        |
|     Match 0 in B      |    0 | GRS  |    1 |    5 |      8 |    1 | QMC  |    2 |    3 |      6 |
|                       |    2 |      |      |      |        |    3 |      |      |      |        |
|     Match 1 in A      |    0 | WYC  |    4 |    3 |      2 |    1 | QMS  |    2 |    7 |      6 |
|                       |    2 | LSS  |    1 |   10 |      8 |    3 | EMM  |    3 |    5 |      4 |
|     Match 1 in B      |    0 | BPV  |    2 |    6 |      6 |    1 | BDF  |    4 |    2 |      2 |
|                       |    2 | NHS  |    1 |    7 |      8 |    3 | MEA  |    3 |    4 |      4 |
| Quarter 1 (#123) in A |    0 | ???  |   ?? |   ?? |     ?? |    1 | ???  |   ?? |   ?? |     ?? |
|                       |    2 | ???  |   ?? |   ?? |     ?? |    3 | ???  |   ?? |   ?? |     ?? |
| Quarter 2 (#124) in A |    0 | ???  |   ?? |   ?? |     ?? |    1 | ???  |   ?? |   ?? |     ?? |
|                       |    2 | ???  |   ?? |   ?? |     ?? |    3 | ???  |   ?? |   ?? |     ?? |
| Quarter 3 (#125) in A |    0 | ???  |   ?? |   ?? |     ?? |    1 | ???  |   ?? |   ?? |     ?? |
|                       |    2 | ???  |   ?? |   ?? |     ?? |    3 | ???  |   ?? |   ?? |     ?? |
| Quarter 4 (#126) in A |    0 | ???  |   ?? |   ?? |     ?? |    1 | ???  |   ?? |   ?? |     ?? |
|                       |    2 | ???  |   ?? |   ?? |     ?? |    3 | ???  |   ?? |   ?? |     ?? |

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hrm. I definitely think we shouldn't mix wrapping with putting things next to each other -- so if we're having a line per zone we should always have that. Both in the sense that the output shouldn't mix the two styles, but also that we shouldn't try to guess which style to use. I can maybe see an argument for letting the user choose, however in that case I think we should ignore the width that that ends up being (since that's then the user's problem -- they got what they asked for).

I think for a first pass here we should go with a simple approach which would always work. This probably means roughly the style you've suggested here, but only one zone wide. (I think this would also simplify the code a bit?)

I think I'd also push the arena name back into its own column? Not sure how that would look in the two-arena case though.

print_col(f" {corner.tla:<4}")
print_col(f"{corner.ranking:>3} ")
print_col(f"{corner.game:>3} ")
print_col(f"{corner.league:>5} ")
print()


def command(settings):
import os.path

from sr.comp.comp import SRComp

comp = SRComp(os.path.realpath(settings.compstate))

match_results: List[MatchResult] = []

filter_tla = settings.tla
skip_filter = not filter_tla

for slots in comp.schedule.matches:
match_results.extend(
collect_match_info(comp, match)
for match in slots.values()
if filter_tla in match.teams or skip_filter
)

if len(match_results) == 0:
print("Not matches found, TLA may be invalid")
return

# Calculate "Display Name" and "Arena" column widths
display_name_width = 12 # start with width of label
arena_name_width = 5 # start with width of label
for match in match_results:
display_name_width = max(display_name_width, len(match.display_name))
arena_name_width = max(arena_name_width, len(match.arena))

# Add some padding
display_name_width += 2
arena_name_width += 2

# TODO hide arena column w/ single arena?

num_teams_per_arena = getattr(comp, 'num_teams_per_arena', len(comp.corners))

print_heading(num_teams_per_arena, display_name_width, arena_name_width)

for match in match_results:
print_match(match, display_name_width, arena_name_width)


def add_subparser(subparsers):
parser = subparsers.add_parser(
'show-match-scores',
help=__description__,
description=__description__,
)
parser.add_argument(
'compstate',
help="competition state repo",
)
parser.add_argument(
'tla',
nargs='?',
help="filter to matches containing this TLA",
)
parser.set_defaults(func=command)