d
This commit is contained in:
105
cli_renderers.py
105
cli_renderers.py
@@ -6,7 +6,7 @@ specialized handling for planets plus generic list/dict/object renderers.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Dict, List, Optional, Sequence
|
||||
from typing import Any, Dict, List, Optional, Sequence, Tuple
|
||||
|
||||
from rich import box
|
||||
from rich.console import Console, Group
|
||||
@@ -15,6 +15,8 @@ from rich.table import Table
|
||||
|
||||
from tarot import Card
|
||||
from utils.attributes import Color, Planet
|
||||
from utils.dates import format_month_day
|
||||
from utils.object_formatting import format_value, get_object_attributes, is_nested_object
|
||||
|
||||
|
||||
class Renderer:
|
||||
@@ -69,27 +71,10 @@ class Renderer:
|
||||
self.console.print(table)
|
||||
|
||||
def print_cards_table(self, cards: List[Card]) -> None:
|
||||
table = Table(title="Cards", show_lines=True)
|
||||
table.add_column("#", justify="right")
|
||||
table.add_column("Name")
|
||||
table.add_column("Arcana")
|
||||
table.add_column("Suit")
|
||||
table.add_column("Pip/Court")
|
||||
|
||||
for card in cards:
|
||||
suit = getattr(getattr(card, "suit", None), "name", "")
|
||||
pip = str(getattr(card, "pip", "")) if getattr(card, "pip", None) else ""
|
||||
court = getattr(card, "court_rank", "")
|
||||
pip_display = court or pip
|
||||
table.add_row(
|
||||
str(getattr(card, "number", "")),
|
||||
getattr(card, "name", ""),
|
||||
getattr(card, "arcana", ""),
|
||||
suit,
|
||||
pip_display,
|
||||
)
|
||||
|
||||
self.console.print(table)
|
||||
for idx, card in enumerate(cards):
|
||||
self._render_card(card)
|
||||
if idx < len(cards) - 1:
|
||||
self.console.print()
|
||||
|
||||
# Internal renderers -------------------------------------------------
|
||||
def _render_card(self, card: Card) -> None:
|
||||
@@ -99,7 +84,6 @@ class Renderer:
|
||||
arcana = getattr(card, "arcana", "")
|
||||
suit_obj = getattr(card, "suit", None)
|
||||
suit = getattr(suit_obj, "name", "") if suit_obj is not None else ""
|
||||
pip = getattr(card, "pip", None)
|
||||
number = getattr(card, "number", "")
|
||||
keywords = getattr(card, "keywords", None) or []
|
||||
meaning = getattr(card, "meaning", None)
|
||||
@@ -112,18 +96,7 @@ class Renderer:
|
||||
top_right = f"#{number}" if number not in (None, "") else ""
|
||||
top.add_row(top_left, top_center, top_right)
|
||||
|
||||
body_lines = [f"Arcana: {arcana or '-'}"]
|
||||
if suit:
|
||||
body_lines.append(f"Suit: {suit}")
|
||||
if pip:
|
||||
body_lines.append(f"Pip: {pip}")
|
||||
if getattr(card, "court_rank", ""):
|
||||
body_lines.append(f"Court Rank: {card.court_rank}")
|
||||
if keywords:
|
||||
body_lines.append(f"Keywords: {', '.join(keywords)}")
|
||||
guidance = getattr(card, "guidance", None)
|
||||
if guidance:
|
||||
body_lines.append(f"Guidance: {guidance}")
|
||||
body_lines = self._card_field_lines(card)
|
||||
body = Panel("\n".join(body_lines), box=box.MINIMAL, padding=(1, 1))
|
||||
|
||||
bottom = Table(
|
||||
@@ -292,6 +265,68 @@ class Renderer:
|
||||
safe_val = value if value not in (None, "") else "—"
|
||||
return f"{label}:\n{safe_val}"
|
||||
|
||||
def _card_field_lines(self, card: Card) -> List[str]:
|
||||
exclude = {"name", "number", "meaning", "keywords"}
|
||||
lines: List[str] = []
|
||||
for attr_name, attr_value in get_object_attributes(card):
|
||||
if attr_name in exclude:
|
||||
continue
|
||||
if attr_value in (None, "", [], {}, ()): # Skip empty values
|
||||
continue
|
||||
|
||||
label = self._humanize_key(attr_name).title()
|
||||
if is_nested_object(attr_value) and not hasattr(attr_value, "name"):
|
||||
lines.append(f"{label}:")
|
||||
nested = format_value(attr_value, indent=2)
|
||||
lines.extend(nested.splitlines())
|
||||
continue
|
||||
|
||||
lines.append(f"{label}: {self._format_card_value(attr_value)}")
|
||||
|
||||
if not lines:
|
||||
lines.append("(no fields)")
|
||||
return lines
|
||||
|
||||
def _format_card_value(self, value: Any) -> str:
|
||||
if value is None:
|
||||
return "—"
|
||||
if isinstance(value, (str, int, float, bool)):
|
||||
return str(value)
|
||||
if isinstance(value, tuple):
|
||||
date_range = self._format_date_range(value)
|
||||
if date_range is not None:
|
||||
return date_range
|
||||
return ", ".join(self._format_card_value(v) for v in value)
|
||||
if isinstance(value, list):
|
||||
return ", ".join(self._format_card_value(v) for v in value)
|
||||
if isinstance(value, dict):
|
||||
return ", ".join(
|
||||
f"{self._humanize_key(str(k)).title()}: {self._format_card_value(v)}"
|
||||
for k, v in value.items()
|
||||
)
|
||||
if hasattr(value, "name"):
|
||||
return str(getattr(value, "name", value))
|
||||
if is_nested_object(value):
|
||||
return format_value(value, indent=2)
|
||||
return str(value)
|
||||
|
||||
@staticmethod
|
||||
def _format_date_range(value: Tuple[Any, ...]) -> Optional[str]:
|
||||
if len(value) != 2:
|
||||
return None
|
||||
start, end = value
|
||||
if not (
|
||||
isinstance(start, tuple)
|
||||
and isinstance(end, tuple)
|
||||
and len(start) == 2
|
||||
and len(end) == 2
|
||||
):
|
||||
return None
|
||||
try:
|
||||
return f"{format_month_day(start)} - {format_month_day(end)}"
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
class PlanetRenderer:
|
||||
"""Type-specific table rendering for planets."""
|
||||
|
||||
Reference in New Issue
Block a user