feat(REQ-001): add portfolio comment history API, CLI, repository, migration, and tests

Agent-Logs-Url: https://github.com/genichin/portfolio-hub/sessions/64fc2cec-78f5-41d1-99a4-45f868781edd

Co-authored-by: genichin <4282227+genichin@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-04-10 13:54:18 +00:00
committed by GitHub
co-authored by genichin
parent 4684597353
commit e8f782bec6
14 changed files with 1173 additions and 10 deletions
+43 -1
View File
@@ -13,7 +13,7 @@ from rich.panel import Panel
from rich.table import Table
from app.core.errors import FeatureNotReadyError
from app.schemas.portfolio import PortfolioDetailResponse, PortfolioListItem
from app.schemas.portfolio import CommentResponse, PortfolioDetailResponse, PortfolioListItem
from app.schemas.user import UserResponse
T = TypeVar("T")
@@ -162,6 +162,41 @@ def print_user_detail(console: Console, user: UserResponse) -> None:
)
def print_comment_table(console: Console, comments: list[CommentResponse]) -> None:
table = Table(title="Portfolio Comments", expand=True)
table.add_column("ID", style="cyan", no_wrap=True)
table.add_column("Portfolio ID", no_wrap=True)
table.add_column("Content")
table.add_column("Created At", no_wrap=True)
table.add_column("Updated At", no_wrap=True)
for comment in comments:
table.add_row(
comment.id,
comment.portfolio_id,
comment.content,
comment.created_at.isoformat(timespec="seconds") if comment.created_at else "N/A",
comment.updated_at.isoformat(timespec="seconds") if comment.updated_at else "N/A",
)
console.print(table)
def print_comment_detail(console: Console, comment: CommentResponse) -> None:
console.print(
Panel.fit(
"\n".join(
[
f"id: {comment.id}",
f"portfolio_id: {comment.portfolio_id}",
f"content: {comment.content}",
f"created_at: {comment.created_at.isoformat(timespec='seconds') if comment.created_at else 'N/A'}",
f"updated_at: {comment.updated_at.isoformat(timespec='seconds') if comment.updated_at else 'N/A'}",
]
),
title="Comment",
)
)
def print_json(console: Console, payload: Any) -> None:
console.print_json(data=to_plain_data(payload))
@@ -181,6 +216,9 @@ def render_output(console: Console, output_format: str, payload: Any, *, empty_m
if all(isinstance(item, UserResponse) for item in payload):
print_user_table(console, payload)
return
if all(isinstance(item, CommentResponse) for item in payload):
print_comment_table(console, payload)
return
return
if isinstance(payload, PortfolioDetailResponse):
@@ -191,6 +229,10 @@ def render_output(console: Console, output_format: str, payload: Any, *, empty_m
print_user_detail(console, payload)
return
if isinstance(payload, CommentResponse):
print_comment_detail(console, payload)
return
console.print(payload)