Skip to content

Commit a66f9ce

Browse files
Enhance functionality with improved problem handling and UI
Added problem tags, acceptance rate, and descriptions to logs for better user experience. Introduced default values for CLI arguments to streamline input handling. Minor logging improvements, upgraded dependencies, and refined problem fetching logic for robustness.
1 parent 6d87577 commit a66f9ce

File tree

8 files changed

+143
-28
lines changed

8 files changed

+143
-28
lines changed

PracticeFactory.py

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import random
2+
import html2text
3+
24
from typing import List, Dict, Any, Optional
35

46
from utils.logger import log, LogLevel
@@ -20,10 +22,30 @@ def handle(self, args):
2022
raise NotImplementedError("This method should be implemented by subclasses.")
2123

2224
def log_problem_details(self, problem, difficulty_label, url):
23-
log(f"🎯 Problem Selected: {problem['title']}", LogLevel.INFO)
25+
log(f"🎯 Problem Selected: {problem.get('title', 'Unknown')}", LogLevel.INFO)
2426
log(f"✨ Difficulty: {difficulty_label}", LogLevel.INFO)
2527
log(f"🔗 URL: {url}", LogLevel.INFO)
2628

29+
topic_tags = problem.get("topicTags", [])
30+
if topic_tags:
31+
tags = ", ".join(tag.get("name", "Unknown") for tag in topic_tags)
32+
log(f"🏷️ Tags: {tags}", LogLevel.INFO)
33+
34+
ac_rate = problem.get("acRate")
35+
if ac_rate is not None:
36+
log(f"📈 Acceptance Rate: {ac_rate:.2f}%", LogLevel.INFO)
37+
38+
content = problem.get("content")
39+
if content:
40+
try:
41+
text_maker = html2text.HTML2Text()
42+
text_maker.ignore_links = True
43+
plain_text = text_maker.handle(content)
44+
log(f"📝 Problem Description:", LogLevel.INFO)
45+
log(f"{plain_text}", LogLevel.INFO)
46+
except Exception as e:
47+
log(f"⚠️ Failed to process problem description: {e}", LogLevel.WARN)
48+
2749
def open_in_browser(self, url, open_flag):
2850
if open_flag:
2951
import webbrowser
@@ -42,17 +64,15 @@ def create_and_solve_handler(self, problem_slug, difficulty_label, args):
4264

4365
def get_random_problem(
4466
self,
45-
category_slug: Optional[str] = None,
4667
difficulties: Optional[List[str]] = None,
4768
) -> Optional[Dict[str, Any]]:
4869
"""
4970
Get a random problem from LeetCode.
50-
:param category_slug: Slug of the category to fetch problems from.
5171
:param difficulties: Difficulty levels of the problems (e.g., "Easy", "Medium", "Hard").
5272
:return: A random problem dictionary or None if no problems are found.
5373
"""
5474
problems = self.leetcode_api.fetch_problems(
55-
category_slug=category_slug, limit=1000, difficulties=difficulties
75+
limit=1000, difficulties=difficulties
5676
)
5777

5878
if not problems:
@@ -104,7 +124,7 @@ def handle(self, args):
104124

105125
try:
106126
# Use the LeetCodeAPI's fetch_problems method
107-
problem = self.get_random_problem(args["difficulties"])
127+
problem = self.get_random_problem(difficulties=args["difficulties"])
108128

109129
if not problem:
110130
log("No problems found for the selected difficulties.", LogLevel.ERROR)
@@ -148,7 +168,6 @@ def handle(self, args):
148168
log("Selected 🧩 Custom Practice Mode", LogLevel.INFO)
149169

150170
for slug in args["problems"]:
151-
log(f"🔍 Fetching details for problem: {slug}", LogLevel.INFO)
152171
try:
153172
problem = self.leetcode_api.fetch_problem(slug.strip())
154173

@@ -171,13 +190,15 @@ def handle(self, args):
171190
try:
172191
log(f"Selected 🎯 Study Plan Mode: {args['plan_name']}", LogLevel.INFO)
173192

174-
# Use the LeetCodeAPI's fetch_problems method
175193
problem = self.get_random_study_plan_problem(args["plan_name"])
176194

177195
if not problem:
178196
log("No problems found for the selected study plan.", LogLevel.ERROR)
179197
return
180198

199+
# Get the problem in the right format
200+
problem = self.leetcode_api.fetch_problem(problem["titleSlug"])
201+
181202
difficulty_label = difficulty_map[problem["difficulty"].lower()]
182203
url = f"https://leetcode.com/problems/{problem['titleSlug']}"
183204

README.md

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,24 @@ Optional arguments:
5959
- `--open-in-browser`: Opens the challenge in a browser window.
6060
- `--editor`: Open the code editor to write solutions. Supported editors include `default`, `vim`, `nano`, and others. Example usage: `--editor vim`
6161

62+
```text
63+
Welcome to 🦑 SquidLeet!
64+
🔐 Using authenticated session
65+
Selected 📅 Daily Challenge Mode
66+
🎯 Daily Coding Challenge:
67+
📅 Date: 2025-01-19
68+
🎯 Problem Selected: Trapping Rain Water II
69+
✨ Difficulty: Hard
70+
🔗 URL: https://leetcode.com/problems/trapping-rain-water-ii
71+
🏷️ Tags: Array, Breadth-First Search, Heap (Priority Queue), Matrix
72+
📈 Acceptance Rate: 55.04%
73+
...
74+
```
75+
6276
### Random Problem Mode
6377

64-
Solve a randomly selected LeetCode problem based on difficulty.
65-
Random Practice Mode allows you to solve a randomly selected LeetCode problem. Enhanced difficulty classification includes options such as `easy`, `medium`, `hard`, or a combination (e.g., `--difficulty easy,medium`).
78+
Solve a randomly selected LeetCode problem based on difficulty. Random Practice Mode allows you to solve a randomly selected LeetCode problem. Enhanced difficulty classification includes options such as `easy`, `medium`, `hard`, or a combination (e.g., `--difficulty easy,medium`).
79+
6680
```bash
6781
python3 main.py --mode random --difficulty medium
6882
```
@@ -72,6 +86,18 @@ Optional arguments:
7286
- `--open-in-browser`: Opens the problem in a browser window.
7387
- `--editor`: Specify a code editor for writing solutions. Supported editors: `default`, `vim`, `nano`, etc.
7488

89+
```text
90+
Welcome to 🦑 SquidLeet!
91+
🔐 Using authenticated session
92+
Selected 🎲 Random Problem Mode
93+
🎯 Problem Selected: Shortest Distance in a Plane
94+
✨ Difficulty: Medium
95+
🔗 URL: https://leetcode.com/problems/shortest-distance-in-a-plane
96+
🏷️ Tags: Database
97+
📈 Acceptance Rate: 61.29%
98+
...
99+
```
100+
75101
### Custom Mode
76102

77103
Custom Modes enable solving specific problems or sets of problems by providing one or multiple problem slugs (e.g., `--problems two-sum,three-sum`).
@@ -84,6 +110,18 @@ Optional arguments:
84110
- `--open-in-browser`: Opens the problem in a browser window.
85111
- `--editor`: Specify the preferred code editor (e.g., `vim`, `nano`). Default is the system-configured default editor.
86112

113+
```text
114+
Welcome to 🦑 SquidLeet!
115+
🔐 Using authenticated session
116+
Selected 🧩 Custom Practice Mode
117+
🎯 Problem Selected: Two Sum
118+
✨ Difficulty: Easy
119+
🔗 URL: https://leetcode.com/problems/two-sum
120+
🏷️ Tags: Array, Hash Table
121+
📈 Acceptance Rate: 54.67%
122+
...
123+
```
124+
87125
### Study Plan Mode
88126

89127
Study Plan Mode allows you to fetch random problems based on a specific study plan. You can specify the study plan name to fetch problems from that plan.
@@ -96,6 +134,18 @@ Optional arguments:
96134
- `--open-in-browser`: Opens the problem in a browser window.
97135
- `--editor`: Specify the preferred code editor (e.g., `vim`, `nano`). Default is the system-configured default editor.
98136

137+
```text
138+
Welcome to 🦑 SquidLeet!
139+
🔐 Using authenticated session
140+
Selected 🎯 Study Plan Mode: top-interview-150
141+
🎯 Problem Selected: Game of Life
142+
✨ Difficulty: Medium
143+
🔗 URL: https://leetcode.com/problems/game-of-life
144+
🏷️ Tags: Array, Matrix, Simulation
145+
📈 Acceptance Rate: 70.65%
146+
...
147+
```
148+
99149
## Configurations
100150
Squidleet uses a `LEETCODE_SESSION` cookie for authentication. Setting the `LEETCODE_SESSION` environment variable is necessary for all operations, including fetching and submitting problems.
101151

api/LeetCodeAPI.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,12 @@ def __init__(self):
2323

2424
def fetch_problems(
2525
self,
26-
category_slug: str = "all-code-essentials",
2726
limit: int = 50,
2827
skip: int = 0,
2928
difficulties: Optional[List[str]] = None,
3029
) -> List[Dict[str, Any]]:
3130
"""
3231
Fetch a list of problems from LeetCode.
33-
:param category_slug: The category of problems to fetch.
3432
:param limit: Number of problems to fetch.
3533
:param skip: Offset for pagination.
3634
:param difficulties: List of difficulty levels (e.g., ["Easy", "Medium", "Hard"]).
@@ -74,7 +72,7 @@ def fetch_for_difficulty(difficulty: str) -> List[Dict[str, Any]]:
7472

7573
filters = {"difficulty": difficulty_enum.get(difficulty.lower())}
7674
variables = {
77-
"categorySlug": category_slug,
75+
"categorySlug": "all-code-essentials",
7876
"limit": limit,
7977
"skip": skip,
8078
"filters": filters,
@@ -126,6 +124,7 @@ def fetch_daily_challenge(self) -> Dict[str, Any]:
126124
titleSlug
127125
title
128126
translatedTitle
127+
content
129128
acRate
130129
difficulty
131130
freqBar

main.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ def main():
77
# Parse CLI options
88
cli_options = CommandParser.parse()
99

10+
log("Welcome to 🦑 SquidLeet!", LogLevel.INFO)
11+
1012
# Validate and set the LeetCode session token
1113
SessionManager.initialize(cli_options)
1214

13-
log("Welcome to 🦑 SquidLeet!", LogLevel.INFO)
14-
1515
# Collect & validate inputs and detect practice mode
1616
inputs = InputsCollector.collect(cli_options)
1717

requirements.txt

Lines changed: 47 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,53 @@
11
black==24.10.0
2-
certifi==2024.12.14
3-
charset-normalizer==3.4.1
2+
build==1.2.1
3+
CacheControl==0.14.0
4+
certifi==2024.7.4
5+
cffi==1.16.0
6+
cfgv==3.4.0
7+
charset-normalizer==3.3.2
8+
cleo==2.1.0
49
click==8.1.8
5-
idna==3.10
10+
crashtest==0.4.1
11+
distlib==0.3.8
12+
dulwich==0.21.7
13+
fastjsonschema==2.20.0
14+
filelock==3.15.4
15+
identify==2.6.5
16+
idna==3.7
17+
importlib_metadata==8.2.0
18+
installer==0.7.0
19+
jaraco.classes==3.4.0
20+
keyring==24.3.1
21+
more-itertools==10.3.0
22+
msgpack==1.0.8
623
mypy-extensions==1.0.0
7-
packaging==24.2
24+
nodeenv==1.9.1
25+
packaging==24.1
826
pathspec==0.12.1
9-
platformdirs==4.3.6
10-
python-dotenv==1.0.1
27+
pexpect==4.9.0
28+
pkginfo==1.11.1
29+
platformdirs==4.2.2
30+
poetry==1.8.3
31+
poetry-core==1.9.0
32+
poetry-plugin-export==1.8.0
33+
pre_commit==4.0.1
34+
ptyprocess==0.7.0
35+
pycparser==2.22
36+
pyproject_hooks==1.1.0
37+
PyYAML==6.0.2
38+
rapidfuzz==3.9.4
1139
requests==2.32.3
12-
tomli==2.2.1
40+
requests-toolbelt==1.0.0
41+
shellingham==1.5.4
42+
six==1.17.0
43+
tomli==2.0.1
44+
tomlkit==0.13.0
45+
trove-classifiers==2024.7.2
1346
typing_extensions==4.12.2
14-
urllib3==2.3.0
47+
urllib3==2.2.2
48+
virtualenv==20.26.3
49+
xattr==1.1.0
50+
zipp==3.19.2
51+
52+
python-dotenv~=1.0.1
53+
html2text~=2024.2.26

services/CommandParser.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@ def parse():
1515
"--difficulties",
1616
type=str,
1717
help="Comma-separated difficulty levels (easy, medium, hard)",
18+
default="easy,medium,hard",
1819
)
1920
parser.add_argument(
2021
"--problems",
2122
type=str,
2223
help="Comma-separated problem slugs (e.g., 'two-sum,fizz-buzz')",
24+
default="two-sum",
2325
)
2426
parser.add_argument(
2527
"--plan-name",

services/InputsCollector.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,15 @@ def collect(cli_options):
77
problems = None
88

99
if practice_mode == "random":
10-
difficulties = cli_options.get(
11-
"difficulties", ["easy", "medium", "hard"]
12-
).split(",")
10+
difficulties = cli_options.get("difficulties", "easy,medium,hard")
11+
12+
if difficulties:
13+
difficulties = difficulties.split(",")
1314
elif practice_mode == "custom":
14-
problems = cli_options.get("problems", "").split(",")
15+
problems = cli_options.get("problems", "")
16+
17+
if problems:
18+
problems = problems.split(",")
1519

1620
log_level = cli_options.get("log_level", "INFO")
1721
plan_name = cli_options.get("study_plan", "top-interview-150")

services/SessionManager.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ def initialize(cli_options: dict):
1212
"LEETCODE_SESSION"
1313
)
1414
if not leetcode_session:
15-
log("Using unauthenticated session", LogLevel.WARN)
15+
log("🔓 Using unauthenticated session", LogLevel.WARN)
1616
return
1717

1818
os.environ["LEETCODE_SESSION"] = leetcode_session
19-
log("Using authenticated session", LogLevel.DEBUG)
19+
log("🔐 Using authenticated session", LogLevel.INFO)

0 commit comments

Comments
 (0)