remove class mode
This commit is contained in:
128
src/beaky/cli.py
128
src/beaky/cli.py
@@ -28,17 +28,18 @@ def _verdict_str(verdict: TicketVerdict) -> str:
|
|||||||
|
|
||||||
|
|
||||||
_FC = 14 # field column visual width
|
_FC = 14 # field column visual width
|
||||||
_VC = 24 # value column visual width
|
_VC = 24 # value column visual width (dual)
|
||||||
_BET_W = 1 + (_FC + 2) + 1 + (_VC + 2) + 1 + (_VC + 2) + 1 # one bet table width
|
_SC = 38 # value column visual width (single classifier)
|
||||||
_GAP = " " # space between side-by-side tables
|
_BET_W = 1 + (_FC + 2) + 1 + (_VC + 2) + 1 + (_VC + 2) + 1 # dual table width
|
||||||
|
_BET_WS = 1 + (_FC + 2) + 1 + (_SC + 2) + 1 # single table width
|
||||||
|
_GAP = " "
|
||||||
|
|
||||||
_FIELD_LABELS: dict[str, str] = {
|
_FIELD_LABELS: dict[str, str] = {"team1Name": "team1", "team2Name": "team2"}
|
||||||
"team1Name": "team1",
|
|
||||||
"team2Name": "team2",
|
|
||||||
}
|
|
||||||
_FIELD_ORDER = ["type", "team1Name", "team2Name", "date", "league"]
|
_FIELD_ORDER = ["type", "team1Name", "team2Name", "date", "league"]
|
||||||
_SKIP_FIELDS = {"ticketType"}
|
_SKIP_FIELDS = {"ticketType"}
|
||||||
|
|
||||||
_BLANK_ROW = f"│{' ' * (_FC + 2)}│{' ' * (_VC + 2)}│{' ' * (_VC + 2)}│"
|
_BLANK_ROW = f"│{' ' * (_FC + 2)}│{' ' * (_VC + 2)}│{' ' * (_VC + 2)}│"
|
||||||
|
_BLANK_ROWS = f"│{' ' * (_FC + 2)}│{' ' * (_SC + 2)}│"
|
||||||
|
|
||||||
|
|
||||||
def _vlen(text: str) -> int:
|
def _vlen(text: str) -> int:
|
||||||
@@ -58,9 +59,10 @@ def _bet_fields(bet: Bet) -> dict[str, str]:
|
|||||||
return fields
|
return fields
|
||||||
|
|
||||||
|
|
||||||
|
# ── dual-column table (compare) ──────────────────────────────────────────────
|
||||||
|
|
||||||
def _tbl_row(field: str, lval: str, ival: str) -> str:
|
def _tbl_row(field: str, lval: str, ival: str) -> str:
|
||||||
label = _FIELD_LABELS.get(field, field)
|
return f"│ {_vpad(_FIELD_LABELS.get(field, field), _FC)} │ {_vpad(lval, _VC)} │ {_vpad(ival, _VC)} │"
|
||||||
return f"│ {_vpad(label, _FC)} │ {_vpad(lval, _VC)} │ {_vpad(ival, _VC)} │"
|
|
||||||
|
|
||||||
|
|
||||||
def _tbl_sep(left: str, mid: str, right: str) -> str:
|
def _tbl_sep(left: str, mid: str, right: str) -> str:
|
||||||
@@ -86,51 +88,76 @@ def _bet_to_lines(idx: int, link_bet: Bet | None, img_bet: Bet | None) -> list[s
|
|||||||
data_rows.append(_tbl_row(key, lval, ival))
|
data_rows.append(_tbl_row(key, lval, ival))
|
||||||
|
|
||||||
header = _vpad(_ansi.bold(_ansi.cyan(f" Bet {idx} ")), _BET_W)
|
header = _vpad(_ansi.bold(_ansi.cyan(f" Bet {idx} ")), _BET_W)
|
||||||
return [
|
return [header, _tbl_sep("┌", "┬", "┐"), _tbl_row("", _ansi.bold("link"), _ansi.bold("image")),
|
||||||
header,
|
_tbl_sep("├", "┼", "┤"), *data_rows, _tbl_sep("└", "┴", "┘")]
|
||||||
_tbl_sep("┌", "┬", "┐"),
|
|
||||||
_tbl_row("", _ansi.bold("link classifier"), _ansi.bold("image classifier")),
|
|
||||||
_tbl_sep("├", "┼", "┤"),
|
# ── single-column table (one classifier) ─────────────────────────────────────
|
||||||
*data_rows,
|
|
||||||
_tbl_sep("└", "┴", "┘"),
|
def _tbl_row_s(field: str, val: str) -> str:
|
||||||
|
return f"│ {_vpad(_FIELD_LABELS.get(field, field), _FC)} │ {_vpad(val, _SC)} │"
|
||||||
|
|
||||||
|
|
||||||
|
def _tbl_sep_s(left: str, mid: str, right: str) -> str:
|
||||||
|
return f"{left}{'─' * (_FC + 2)}{mid}{'─' * (_SC + 2)}{right}"
|
||||||
|
|
||||||
|
|
||||||
|
def _bet_to_lines_single(idx: int, bet: Bet, col_label: str) -> list[str]:
|
||||||
|
fields = _bet_fields(bet)
|
||||||
|
keys = [k for k in _FIELD_ORDER if k in fields] + [k for k in fields if k not in _FIELD_ORDER]
|
||||||
|
data_rows = [
|
||||||
|
_tbl_row_s(k, (v[:_SC - 1] + "…" if len(v) > _SC else v))
|
||||||
|
for k, v in ((k, fields[k]) for k in keys)
|
||||||
]
|
]
|
||||||
|
header = _vpad(_ansi.bold(_ansi.cyan(f" Bet {idx} ")), _BET_WS)
|
||||||
|
return [header, _tbl_sep_s("┌", "┬", "┐"), _tbl_row_s("", _ansi.bold(col_label)),
|
||||||
|
_tbl_sep_s("├", "┼", "┤"), *data_rows, _tbl_sep_s("└", "┴", "┘")]
|
||||||
|
|
||||||
|
|
||||||
def _pad_to(lines: list[str], target: int) -> list[str]:
|
# ── shared grid printer ───────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
def _pad_to(lines: list[str], target: int, blank: str) -> list[str]:
|
||||||
result = list(lines)
|
result = list(lines)
|
||||||
while len(result) < target:
|
while len(result) < target:
|
||||||
result.insert(-1, _BLANK_ROW)
|
result.insert(-1, blank)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def _print_compare(link_ticket: Ticket, img_ticket: Ticket) -> None:
|
def _print_bet_grid(ticket_header: str, all_lines: list[list[str]], blank: str, bet_w: int) -> None:
|
||||||
n_link = len(link_ticket.bets)
|
|
||||||
n_img = len(img_ticket.bets)
|
|
||||||
n_max = max(n_link, n_img)
|
|
||||||
|
|
||||||
term_w = shutil.get_terminal_size((120, 24)).columns
|
term_w = shutil.get_terminal_size((120, 24)).columns
|
||||||
n_cols = max(1, term_w // (_BET_W + len(_GAP)))
|
n_cols = max(1, term_w // (bet_w + len(_GAP)))
|
||||||
row_w = min(term_w, n_cols * (_BET_W + len(_GAP)) - len(_GAP) + 2)
|
row_w = min(term_w, n_cols * (bet_w + len(_GAP)) - len(_GAP) + 2)
|
||||||
|
|
||||||
header = f" Ticket {link_ticket.id} — link: {n_link} bet{'s' if n_link != 1 else ''} │ img: {n_img} bet{'s' if n_img != 1 else ''}"
|
|
||||||
print(f"\n{'═' * row_w}")
|
print(f"\n{'═' * row_w}")
|
||||||
print(_ansi.bold(header))
|
print(_ansi.bold(f" {ticket_header}"))
|
||||||
print(f"{'═' * row_w}")
|
print(f"{'═' * row_w}")
|
||||||
|
for start in range(0, len(all_lines), n_cols):
|
||||||
all_lines = [
|
|
||||||
_bet_to_lines(i + 1, link_ticket.bets[i] if i < n_link else None, img_ticket.bets[i] if i < n_img else None)
|
|
||||||
for i in range(n_max)
|
|
||||||
]
|
|
||||||
|
|
||||||
for start in range(0, n_max, n_cols):
|
|
||||||
chunk = all_lines[start:start + n_cols]
|
chunk = all_lines[start:start + n_cols]
|
||||||
max_h = max(len(b) for b in chunk)
|
max_h = max(len(b) for b in chunk)
|
||||||
padded = [_pad_to(b, max_h) for b in chunk]
|
padded = [_pad_to(b, max_h, blank) for b in chunk]
|
||||||
print()
|
print()
|
||||||
for row in zip(*padded):
|
for row in zip(*padded):
|
||||||
print(" " + _GAP.join(row))
|
print(" " + _GAP.join(row))
|
||||||
|
|
||||||
|
|
||||||
|
# ── public print functions ────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
def _print_compare(link_ticket: Ticket, img_ticket: Ticket) -> None:
|
||||||
|
n_link, n_img = len(link_ticket.bets), len(img_ticket.bets)
|
||||||
|
header = f"Ticket {link_ticket.id} — link: {n_link} bet{'s' if n_link != 1 else ''} │ img: {n_img} bet{'s' if n_img != 1 else ''}"
|
||||||
|
all_lines = [
|
||||||
|
_bet_to_lines(i + 1, link_ticket.bets[i] if i < n_link else None, img_ticket.bets[i] if i < n_img else None)
|
||||||
|
for i in range(max(n_link, n_img))
|
||||||
|
]
|
||||||
|
_print_bet_grid(header, all_lines, _BLANK_ROW, _BET_W)
|
||||||
|
|
||||||
|
|
||||||
|
def _print_single(ticket: Ticket, col_label: str) -> None:
|
||||||
|
n = len(ticket.bets)
|
||||||
|
header = f"Ticket {ticket.id} — {col_label} │ {n} bet{'s' if n != 1 else ''}"
|
||||||
|
all_lines = [_bet_to_lines_single(i + 1, ticket.bets[i], col_label) for i in range(n)]
|
||||||
|
_print_bet_grid(header, all_lines, _BLANK_ROWS, _BET_WS)
|
||||||
|
|
||||||
|
|
||||||
def load_config(path: str) -> Config | None:
|
def load_config(path: str) -> Config | None:
|
||||||
with open(path) as f:
|
with open(path) as f:
|
||||||
config_dict = yaml.safe_load(f)
|
config_dict = yaml.safe_load(f)
|
||||||
@@ -145,7 +172,9 @@ def main() -> None:
|
|||||||
parser = argparse.ArgumentParser(prog="beaky")
|
parser = argparse.ArgumentParser(prog="beaky")
|
||||||
parser.add_argument("--config", help="Path to config file.", default="config/application.yml")
|
parser.add_argument("--config", help="Path to config file.", default="config/application.yml")
|
||||||
parser.add_argument("--id", type=int, help="Select a single ticket by id.")
|
parser.add_argument("--id", type=int, help="Select a single ticket by id.")
|
||||||
parser.add_argument("mode", choices=["screen", "parse", "class", "resolve", "compare"], help="Mode of operation.")
|
parser.add_argument("mode", choices=["screen", "parse", "compare", "resolve"], help="Mode of operation.")
|
||||||
|
parser.add_argument("--classifier", choices=["link", "img", "both"], default="both",
|
||||||
|
help="Which classifier to use in compare mode (default: both).")
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
config = load_config(args.config)
|
config = load_config(args.config)
|
||||||
@@ -179,24 +208,19 @@ def main() -> None:
|
|||||||
for link in selected_links:
|
for link in selected_links:
|
||||||
print(link)
|
print(link)
|
||||||
|
|
||||||
if args.mode == "class":
|
|
||||||
classifier = LinkClassifier()
|
|
||||||
results = []
|
|
||||||
for link in selected_links:
|
|
||||||
results.append(classifier.classify(link))
|
|
||||||
ticket = results[-1]
|
|
||||||
print(f"\n=== Link {ticket.id} ({len(ticket.bets)} bets) ===")
|
|
||||||
for bet in ticket.bets:
|
|
||||||
print(f" [{type(bet).__name__}]")
|
|
||||||
for k, v in vars(bet).items():
|
|
||||||
print(f" {k}: {v}")
|
|
||||||
|
|
||||||
if args.mode == "compare":
|
if args.mode == "compare":
|
||||||
linkclassifier = LinkClassifier()
|
use_link = args.classifier in ("link", "both")
|
||||||
|
use_img = args.classifier in ("img", "both")
|
||||||
|
linkclassifier = LinkClassifier() if use_link else None
|
||||||
for link in selected_links:
|
for link in selected_links:
|
||||||
link_ticket = linkclassifier.classify(link)
|
link_ticket = linkclassifier.classify(link) if use_link else None
|
||||||
img_ticket = img_classify([f"./data/screenshots/{link.id}.png"], ticket_id=link.id)
|
img_ticket = img_classify([f"./data/screenshots/{link.id}.png"], ticket_id=link.id) if use_img else None
|
||||||
|
if args.classifier == "both" and link_ticket and img_ticket:
|
||||||
_print_compare(link_ticket, img_ticket)
|
_print_compare(link_ticket, img_ticket)
|
||||||
|
elif link_ticket:
|
||||||
|
_print_single(link_ticket, "link classifier")
|
||||||
|
elif img_ticket:
|
||||||
|
_print_single(img_ticket, "image classifier")
|
||||||
|
|
||||||
if args.mode == "resolve":
|
if args.mode == "resolve":
|
||||||
classifier = LinkClassifier()
|
classifier = LinkClassifier()
|
||||||
|
|||||||
Reference in New Issue
Block a user