Skip to content

Commit 50151f2

Browse files
feat: add solution cmd
1 parent dc3100c commit 50151f2

File tree

6 files changed

+415
-64
lines changed

6 files changed

+415
-64
lines changed

src/commands/list_problems.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import typer
44
from rich.progress import Progress, SpinnerColumn, TextColumn
55

6-
from ..lib.ui import display_problem_list
6+
from ..lib.profile_ui import display_problem_list
77
from ..server.api import fetch_problem_list
88
from ..server.auth import Auth
99

src/commands/profile.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from rich.live import Live
22
from rich.spinner import Spinner
33

4-
from ..lib.ui import display_user_stats
4+
from ..lib.profile_ui import display_user_stats
55
from ..server.api import fetch_user_profile
66

77

src/commands/solution.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,13 @@
11
import typer
2+
from rich.progress import Progress, SpinnerColumn, TextColumn
3+
4+
from src.lib.solution_ui import SolutionUI
5+
6+
from ..server.auth import Auth
7+
from ..server.solution_manager import SolutionManager
8+
9+
auth = Auth()
10+
solution_manager = SolutionManager(auth.get_session())
211

312

413
def solutions(
@@ -10,4 +19,24 @@ def solutions(
1019
),
1120
):
1221
"""Fetch solution for a problem"""
13-
pass
22+
23+
try:
24+
with Progress(
25+
SpinnerColumn(),
26+
TextColumn("[progress.description]{task.description}"),
27+
transient=True,
28+
) as progress:
29+
progress.add_task("Fetching problem solutions...", total=1)
30+
fetched_solution = solution_manager.get_problem_solutions(problem, best)
31+
32+
if not fetched_solution:
33+
typer.echo("No solution found")
34+
else:
35+
solution_ui = SolutionUI(
36+
fetched_solution.get("data", {}).get("ugcArticleSolutionArticles")
37+
)
38+
solution_ui.show_solution()
39+
except Exception as e:
40+
typer.secho("Error fetching solution for problem ", fg="red", nl=False)
41+
typer.secho(f'"{problem}"', fg="bright_red", bold=True)
42+
typer.secho(f"\n{e}", fg="yellow", italic=True)
Lines changed: 111 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
1+
from datetime import datetime
2+
3+
from rich import box
14
from rich.console import Console
2-
from rich.table import Table
35
from rich.panel import Panel
4-
from rich import box
5-
from datetime import datetime
6+
from rich.table import Table
67

78
console = Console()
89

10+
911
def format_timestamp(timestamp):
1012
dt = datetime.fromtimestamp(timestamp)
1113
return dt.strftime("%Y-%m-%d %H:%M")
1214

15+
1316
def create_recent_activity(recent_submissions):
14-
if not recent_submissions or 'recentAcSubmissionList' not in recent_submissions:
17+
if not recent_submissions or "recentAcSubmissionList" not in recent_submissions:
1518
return None
1619

1720
table = Table(
@@ -20,112 +23,136 @@ def create_recent_activity(recent_submissions):
2023
border_style="cyan",
2124
pad_edge=False,
2225
show_edge=True,
23-
width=65
26+
width=65,
2427
)
2528
table.add_column("Time", style="dim", width=16)
2629
table.add_column("Problem", style="cyan")
2730

28-
for sub in recent_submissions['recentAcSubmissionList'][:5]:
31+
for sub in recent_submissions["recentAcSubmissionList"][:5]:
2932
table.add_row(
30-
format_timestamp(int(sub['timestamp'])),
31-
sub['title'],
33+
format_timestamp(int(sub["timestamp"])),
34+
sub["title"],
3235
)
3336
return table
3437

38+
3539
def create_progress_panel(data):
36-
if not data.get('progress'):
40+
if not data.get("progress"):
3741
return None
3842

39-
progress = data['progress']
40-
all_count = progress['allQuestionsCount']
41-
submit_stats = progress['matchedUser']['submitStats']
43+
progress = data["progress"]
44+
all_count = progress["allQuestionsCount"]
45+
submit_stats = progress["matchedUser"]["submitStats"]
4246
table = Table.grid(padding=(0, 1))
4347
table.add_column(justify="left")
4448

45-
for diff in ['Easy', 'Medium', 'Hard']:
46-
total = next((q['count'] for q in all_count if q['difficulty'] == diff), 0)
47-
solved = next((s['count'] for s in submit_stats['acSubmissionNum'] if s['difficulty'] == diff), 0)
49+
for diff in ["Easy", "Medium", "Hard"]:
50+
total = next((q["count"] for q in all_count if q["difficulty"] == diff), 0)
51+
solved = next(
52+
(
53+
s["count"]
54+
for s in submit_stats["acSubmissionNum"]
55+
if s["difficulty"] == diff
56+
),
57+
0,
58+
)
4859
table.add_row(f"[dim]{diff}:[/dim] {solved}/{total}")
4960

5061
return Panel(table, title="[bold green]Progress[/bold green]", border_style="green")
5162

63+
5264
def create_social_links(user, websites=None):
5365
links = []
54-
if user['githubUrl']:
66+
if user["githubUrl"]:
5567
links.append(f"[blue]GitHub[/blue]: {user['githubUrl']}")
56-
if user['linkedinUrl']:
68+
if user["linkedinUrl"]:
5769
links.append(f"[blue]LinkedIn[/blue]: {user['linkedinUrl']}")
58-
if user['twitterUrl']:
70+
if user["twitterUrl"]:
5971
links.append(f"[blue]Twitter[/blue]: {user['twitterUrl']}")
6072

6173
if websites:
6274
for site in websites:
63-
url = f"https://{site}" if not site.startswith(('http://', 'https://')) else site
75+
url = (
76+
f"https://{site}"
77+
if not site.startswith(("http://", "https://"))
78+
else site
79+
)
6480
links.append(f"[blue]Website[/blue]: {url}")
6581

6682
return "\n".join(links) if links else "No social links"
6783

84+
6885
def create_language_stats(data):
69-
if not data or 'matchedUser' not in data:
86+
if not data or "matchedUser" not in data:
7087
return None
7188

72-
lang_stats = data['matchedUser']['languageProblemCount']
89+
lang_stats = data["matchedUser"]["languageProblemCount"]
7390
table = Table.grid(padding=(0, 1))
7491
table.add_column(justify="left")
7592
rows = []
76-
sorted_stats = sorted(lang_stats, key=lambda x: x['problemsSolved'], reverse=True)
93+
sorted_stats = sorted(lang_stats, key=lambda x: x["problemsSolved"], reverse=True)
7794

7895
for lang in sorted_stats[:5]:
7996
rows.append(f"[dim]{lang['languageName']}:[/dim] {lang['problemsSolved']}")
8097

8198
table.add_row("\n".join(rows))
82-
return Panel(table, title="[bold blue]Languages[/bold blue]", border_style="blue", width=35, padding=(0, 1))
99+
return Panel(
100+
table,
101+
title="[bold blue]Languages[/bold blue]",
102+
border_style="blue",
103+
width=35,
104+
padding=(0, 1),
105+
)
106+
83107

84108
def create_contest_stats(contest_info):
85109
stats = []
86-
ranking = contest_info.get('userContestRanking', {}) if contest_info else {}
110+
ranking = contest_info.get("userContestRanking", {}) if contest_info else {}
87111
if not ranking:
88112
return "No contest stats"
89113

90-
rating = ranking.get('rating', 0)
91-
attended = ranking.get('attendedContestsCount', 0)
114+
rating = ranking.get("rating", 0)
115+
attended = ranking.get("attendedContestsCount", 0)
92116
stats.append(f"[dim]Rating:[/dim] {rating:.1f}")
93117
stats.append(f"[dim]Contests:[/dim] {attended}")
94118
return "\n".join(stats)
95119

120+
96121
def create_skill_stats(data):
97-
if not data.get('skillStats'):
122+
if not data.get("skillStats"):
98123
return None
99124

100-
skills = data['skillStats']['matchedUser']['tagProblemCounts']
125+
skills = data["skillStats"]["matchedUser"]["tagProblemCounts"]
101126
table = Table(
102127
title="[bold magenta]Skills[/bold magenta]",
103128
box=box.ROUNDED,
104129
border_style="magenta",
105130
width=65,
106-
pad_edge=False
131+
pad_edge=False,
107132
)
108133
table.add_column("Level", style="magenta")
109134
table.add_column("Tag", style="cyan")
110135
table.add_column("Solved", justify="right")
111136

112-
for level in ['advanced', 'intermediate', 'fundamental']:
137+
for level in ["advanced", "intermediate", "fundamental"]:
113138
for skill in skills[level][:2]:
114-
table.add_row(level.capitalize(), skill['tagName'], str(skill['problemsSolved']))
139+
table.add_row(
140+
level.capitalize(), skill["tagName"], str(skill["problemsSolved"])
141+
)
115142

116143
return table
117144

145+
118146
def display_user_stats(data):
119147
console.clear()
120-
width = min(console.width - 4, 180)
121148
profile_width, stats_width, progress_width = 65, 35, 30
122149

123-
if not data.get('userProfile'):
150+
if not data.get("userProfile"):
124151
console.print(Panel("Could not fetch user profile", border_style="red"))
125152
return
126153

127-
user = data['userProfile']['matchedUser']
128-
profile = user['profile']
154+
user = data["userProfile"]["matchedUser"]
155+
profile = user["profile"]
129156

130157
profile_table = Table.grid(padding=(0, 1))
131158
profile_table.add_column(justify="left")
@@ -135,21 +162,30 @@ def display_user_stats(data):
135162
f"[dim]Ranking:[/dim] {profile.get('ranking', 'N/A')}",
136163
]
137164

138-
for label, field in [('Company', 'company'), ('Job Title', 'jobTitle'),
139-
('School', 'school'), ('Location', 'countryName')]:
165+
for label, field in [
166+
("Company", "company"),
167+
("Job Title", "jobTitle"),
168+
("School", "school"),
169+
("Location", "countryName"),
170+
]:
140171
if field in profile and profile[field]:
141172
profile_info.append(f"[dim]{label}:[/dim] {profile[field]}")
142173

143-
if profile.get('skillTags'):
174+
if profile.get("skillTags"):
144175
profile_info.append(f"[dim]Skills:[/dim] {', '.join(profile['skillTags'])}")
145176

146-
social_links = create_social_links(user, profile.get('websites', []))
177+
social_links = create_social_links(user, profile.get("websites", []))
147178
if social_links != "No social links":
148179
profile_info.append("\n" + social_links)
149180

150181
profile_table.add_row("\n".join(profile_info))
151-
profile_panel = Panel(profile_table, title="[bold cyan]Profile Info[/bold cyan]",
152-
border_style="cyan", width=profile_width, padding=(0, 1))
182+
profile_panel = Panel(
183+
profile_table,
184+
title="[bold cyan]Profile Info[/bold cyan]",
185+
border_style="cyan",
186+
width=profile_width,
187+
padding=(0, 1),
188+
)
153189

154190
stats_table = Table.grid(padding=(0, 1))
155191
stats_table.add_column(justify="left")
@@ -158,30 +194,41 @@ def display_user_stats(data):
158194
f"[dim]Reputation:[/dim] {profile.get('reputation', 0)} ({profile.get('reputationDiff', 0):+d})",
159195
f"[dim]Views:[/dim] {profile.get('postViewCount', 0)} ({profile.get('postViewCountDiff', 0):+d})",
160196
]
161-
if 'categoryDiscussCount' in profile:
162-
stats.append(f"[dim]Discussions:[/dim] {profile['categoryDiscussCount']} ({profile.get('categoryDiscussCountDiff', 0):+d})")
197+
if "categoryDiscussCount" in profile:
198+
stats.append(
199+
f"[dim]Discussions:[/dim] {profile['categoryDiscussCount']} ({profile.get('categoryDiscussCountDiff', 0):+d})"
200+
)
163201
stats_table.add_row("\n".join(stats))
164202

165203
progress = create_progress_panel(data)
166204
if progress:
167205
progress.width = progress_width
168206

169207
middle_column = Table.grid(padding=(0, 1))
170-
if data.get('languageStats'):
171-
middle_column.add_row(create_language_stats(data['languageStats']))
208+
if data.get("languageStats"):
209+
middle_column.add_row(create_language_stats(data["languageStats"]))
172210
middle_column.add_row("")
173-
if "contestInfo" in data and data.get('contestInfo'):
174-
contest_panel = Panel(create_contest_stats(data['contestInfo']),
175-
title="[bold yellow]Contest Stats[/bold yellow]",
176-
border_style="yellow", width=stats_width, padding=(0, 1))
211+
if "contestInfo" in data and data.get("contestInfo"):
212+
contest_panel = Panel(
213+
create_contest_stats(data["contestInfo"]),
214+
title="[bold yellow]Contest Stats[/bold yellow]",
215+
border_style="yellow",
216+
width=stats_width,
217+
padding=(0, 1),
218+
)
177219
middle_column.add_row(contest_panel)
178220

179221
right_column = Table.grid()
180222
if progress:
181223
right_column.add_row(progress)
182224
right_column.add_row("")
183-
stats_panel = Panel(stats_table, title="[bold blue]Activity Stats[/bold blue]",
184-
border_style="blue", width=progress_width, padding=(0, 1))
225+
stats_panel = Panel(
226+
stats_table,
227+
title="[bold blue]Activity Stats[/bold blue]",
228+
border_style="blue",
229+
width=progress_width,
230+
padding=(0, 1),
231+
)
185232
right_column.add_row(stats_panel)
186233

187234
top_grid = Table.grid(padding=(0, 2))
@@ -191,8 +238,10 @@ def display_user_stats(data):
191238
bottom_grid.add_column(width=65)
192239
bottom_grid.add_column(width=65)
193240
bottom_grid.add_row(
194-
create_skill_stats(data) if data.get('skillStats') else "",
195-
create_recent_activity(data['recentAcSubmissions']) if data.get('recentAcSubmissions') else ""
241+
create_skill_stats(data) if data.get("skillStats") else "",
242+
create_recent_activity(data["recentAcSubmissions"])
243+
if data.get("recentAcSubmissions")
244+
else "",
196245
)
197246

198247
console.print("\n")
@@ -201,6 +250,7 @@ def display_user_stats(data):
201250
console.print(bottom_grid)
202251
console.print("\n")
203252

253+
204254
def display_problem_list(data):
205255
console.clear()
206256
console.print("\n")
@@ -210,7 +260,7 @@ def display_problem_list(data):
210260
box=box.ROUNDED,
211261
border_style="cyan",
212262
pad_edge=False,
213-
show_edge=True
263+
show_edge=True,
214264
)
215265

216266
table.add_column("ID", style="dim", width=6)
@@ -219,16 +269,16 @@ def display_problem_list(data):
219269
table.add_column("Status", justify="center", width=8)
220270
table.add_column("AC Rate", justify="right", width=8)
221271

222-
for problem in data['problemsetQuestionList']['questions']:
223-
status_icon = "[green]✓" if problem['status'] == "ac" else "[red]✗"
272+
for problem in data["problemsetQuestionList"]["questions"]:
273+
status_icon = "[green]✓" if problem["status"] == "ac" else "[red]✗"
224274
ac_rate = f"{problem['acRate']:.1f}%"
225275
table.add_row(
226-
problem['frontendQuestionId'],
227-
problem['title'],
228-
problem['difficulty'],
276+
problem["frontendQuestionId"],
277+
problem["title"],
278+
problem["difficulty"],
229279
status_icon,
230-
ac_rate
280+
ac_rate,
231281
)
232282

233283
console.print(table)
234-
console.print("\n")
284+
console.print("\n")

0 commit comments

Comments
 (0)