|
1 | | -import random |
2 | | -import html2text |
3 | | -import markdown |
4 | | - |
5 | | -from typing import List, Dict, Any, Optional |
6 | | - |
7 | | -from utils.logger import log, LogLevel |
8 | | - |
9 | | -from handlers.SolutionHandler import SolutionHandler |
10 | | -from handlers.CacheHandler import cached_api |
11 | | - |
12 | | -difficulty_map = { |
13 | | - "easy": "Easy", |
14 | | - "medium": "Medium", |
15 | | - "hard": "Hard", |
16 | | -} |
17 | | - |
18 | | - |
19 | | -class PracticeMode: |
20 | | - def __init__(self): |
21 | | - pass |
22 | | - |
23 | | - def handle(self, args): |
24 | | - raise NotImplementedError("This method should be implemented by subclasses.") |
25 | | - |
26 | | - def log_problem_details(self, problem, difficulty_label, url): |
27 | | - log(f"🎯 Problem Selected: {problem.get('title', 'Unknown')}", LogLevel.INFO) |
28 | | - log(f"✨ Difficulty: {difficulty_label}", LogLevel.INFO) |
29 | | - log(f"🔗 URL: {url}", LogLevel.INFO) |
30 | | - |
31 | | - topic_tags = problem.get("topicTags", []) |
32 | | - if topic_tags: |
33 | | - tags = ", ".join(tag.get("name", "Unknown") for tag in topic_tags) |
34 | | - log(f"🏷️ Tags: {tags}", LogLevel.INFO) |
35 | | - |
36 | | - ac_rate = problem.get("acRate") |
37 | | - if ac_rate is not None: |
38 | | - log(f"📈 Acceptance Rate: {ac_rate:.2f}%", LogLevel.INFO) |
39 | | - |
40 | | - # Assuming `content` is the Markdown content |
41 | | - content = problem.get("content") |
42 | | - if content: |
43 | | - try: |
44 | | - html_content = markdown.markdown(content) |
45 | | - |
46 | | - text_maker = html2text.HTML2Text() |
47 | | - text_maker.ignore_links = True |
48 | | - plain_text = text_maker.handle(html_content) |
49 | | - |
50 | | - log(plain_text, LogLevel.INFO) |
51 | | - except Exception as e: |
52 | | - log(f"Failed to convert problem content: {str(e)}", LogLevel.ERROR) |
53 | | - |
54 | | - def open_in_browser(self, url, open_flag): |
55 | | - if open_flag: |
56 | | - import webbrowser |
57 | | - |
58 | | - webbrowser.open(url) |
59 | | - |
60 | | - def create_and_solve_handler( |
61 | | - self, problem_slug, code_snippets, difficulty_label, args |
62 | | - ): |
63 | | - # Determine the starter code based on the chosen language |
64 | | - |
65 | | - code = "" |
66 | | - for item in code_snippets: |
67 | | - if item.get("lang").lower() == args["language"].lower(): |
68 | | - code = item.get("code") |
69 | | - break |
70 | | - |
71 | | - if not code: |
72 | | - log( |
73 | | - f"Starter code not found for language: {args['language']}", |
74 | | - LogLevel.ERROR, |
75 | | - ) |
76 | | - return |
77 | | - |
78 | | - handler = SolutionHandler( |
79 | | - problem=problem_slug, |
80 | | - code=code, |
81 | | - difficulty=difficulty_label, |
82 | | - editor=args["editor"], |
83 | | - language=args["language"], |
84 | | - time_limit=args["time_limit"], |
85 | | - ) |
86 | | - handler.solve() |
87 | | - |
88 | | - def get_random_problem( |
89 | | - self, |
90 | | - difficulties: Optional[List[str]] = None, |
91 | | - ) -> Optional[Dict[str, Any]]: |
92 | | - """ |
93 | | - Get a random problem from LeetCode. |
94 | | - :param difficulties: Difficulty levels of the problems (e.g., "Easy", "Medium", "Hard"). |
95 | | - :return: A random problem dictionary or None if no problems are found. |
96 | | - """ |
97 | | - problems = cached_api.fetch_problems(limit=1000, difficulties=difficulties) |
98 | | - |
99 | | - if not problems: |
100 | | - return None |
101 | | - |
102 | | - random_index = random.randint(0, len(problems) - 1) |
103 | | - return problems[random_index] |
104 | | - |
105 | | - def get_study_plan_problems(self, slug: str) -> List[Dict[str, Any]]: |
106 | | - """ |
107 | | - Fetch the list of problems for a specific study plan. |
108 | | - :param slug: The slug of the study plan (e.g., "leetcode-75"). |
109 | | - :return: A list of dictionaries, each containing details of a problem. |
110 | | - """ |
111 | | - study_plan = cached_api.get_study_plan(slug) |
112 | | - |
113 | | - if not study_plan: |
114 | | - raise Exception(f"❌ Study plan not found for slug: {slug}") |
115 | | - |
116 | | - # Gather all questions from the study plan subgroups |
117 | | - problems = [] |
118 | | - for subgroup in study_plan.get("planSubGroups", []): |
119 | | - questions = subgroup.get("questions", []) |
120 | | - problems.extend(questions) # Add all questions to the list |
121 | | - |
122 | | - if not problems: |
123 | | - raise Exception(f"❌ No problems found for study plan: {slug}") |
124 | | - |
125 | | - return problems |
126 | | - |
127 | | - def get_random_study_plan_problem(self, slug: str) -> Optional[Dict[str, Any]]: |
128 | | - """ |
129 | | - Get a random problem from a specific study plan. |
130 | | - :param slug: The slug of the study plan (e.g., "leetcode-75"). |
131 | | - :return: A random problem dictionary or None if no problems are found. |
132 | | - """ |
133 | | - problems = self.get_study_plan_problems(slug) |
134 | | - |
135 | | - if not problems: |
136 | | - return None |
137 | | - |
138 | | - random_index = random.randint(0, len(problems) - 1) |
139 | | - problem = problems[random_index] |
140 | | - |
141 | | - # Get the problem in the right format |
142 | | - return cached_api.fetch_problem(problem["titleSlug"]) |
143 | | - |
144 | | - |
145 | | -class RandomProblemMode(PracticeMode): |
146 | | - def handle(self, args): |
147 | | - log("Selected 🎲 Random Problem Mode", LogLevel.INFO) |
148 | | - |
149 | | - try: |
150 | | - # Use the LeetCodeAPI's fetch_problems method |
151 | | - problem = self.get_random_problem(difficulties=args["difficulties"]) |
152 | | - |
153 | | - if not problem: |
154 | | - log("No problems found for the selected difficulties.", LogLevel.ERROR) |
155 | | - return |
156 | | - |
157 | | - difficulty_label = difficulty_map[problem["difficulty"].lower()] |
158 | | - url = f"https://leetcode.com/problems/{problem['titleSlug']}" |
159 | | - |
160 | | - self.log_problem_details(problem, difficulty_label, url) |
161 | | - self.open_in_browser(url, args["open_in_browser"]) |
162 | | - self.create_and_solve_handler( |
163 | | - problem["titleSlug"], problem["codeSnippets"], difficulty_label, args |
164 | | - ) |
165 | | - except Exception as e: |
166 | | - log(f"Failed to fetch random problem: {str(e)}", LogLevel.ERROR) |
167 | | - |
168 | | - |
169 | | -class DailyChallengeMode(PracticeMode): |
170 | | - def handle(self, args): |
171 | | - log("Selected 📅 Daily Challenge Mode", LogLevel.INFO) |
172 | | - |
173 | | - try: |
174 | | - # Use the LeetCodeAPI's fetch_daily_challenge method |
175 | | - daily_challenge = cached_api.fetch_daily_challenge() |
176 | | - difficulty_label = difficulty_map[ |
177 | | - daily_challenge["question"]["difficulty"].lower() |
178 | | - ] |
179 | | - url = f"https://leetcode.com{daily_challenge['link'].removesuffix('/')}" |
180 | | - |
181 | | - log("🎯 Daily Coding Challenge:", LogLevel.INFO) |
182 | | - log(f"📅 Date: {daily_challenge['date']}", LogLevel.INFO) |
183 | | - self.log_problem_details(daily_challenge["question"], difficulty_label, url) |
184 | | - self.open_in_browser(url, args["open_in_browser"]) |
185 | | - self.create_and_solve_handler( |
186 | | - daily_challenge["question"]["titleSlug"], |
187 | | - daily_challenge["codeSnippets"], |
188 | | - difficulty_label, |
189 | | - args, |
190 | | - ) |
191 | | - except Exception as e: |
192 | | - log(f"Failed to fetch daily challenge: {str(e)}", LogLevel.ERROR) |
193 | | - |
194 | | - |
195 | | -class CustomPracticeMode(PracticeMode): |
196 | | - def handle(self, args): |
197 | | - log("Selected 🧩 Custom Practice Mode", LogLevel.INFO) |
198 | | - |
199 | | - for slug in args["problems"]: |
200 | | - try: |
201 | | - problem = cached_api.fetch_problem(slug.strip()) |
202 | | - |
203 | | - if not problem: |
204 | | - log(f"Problem with slug '{slug}' not found.", LogLevel.ERROR) |
205 | | - continue |
206 | | - |
207 | | - difficulty_label = difficulty_map[problem["difficulty"].lower()] |
208 | | - url = f"https://leetcode.com/problems/{problem['titleSlug']}" |
209 | | - |
210 | | - self.log_problem_details(problem, difficulty_label, url) |
211 | | - self.open_in_browser(url, args["open_in_browser"]) |
212 | | - self.create_and_solve_handler( |
213 | | - slug, problem["codeSnippets"], difficulty_label, args |
214 | | - ) |
215 | | - except Exception as e: |
216 | | - log(f"Failed to process problem '{slug}': {str(e)}", LogLevel.ERROR) |
217 | | - |
218 | | - |
219 | | -class StudyPlanMode(PracticeMode): |
220 | | - def handle(self, args): |
221 | | - try: |
222 | | - log(f"Selected 🎯 Study Plan Mode: {args['plan_name']}", LogLevel.INFO) |
223 | | - |
224 | | - problem = self.get_random_study_plan_problem(args["plan_name"]) |
225 | | - |
226 | | - if not problem: |
227 | | - log("No problems found for the selected study plan.", LogLevel.ERROR) |
228 | | - return |
229 | | - |
230 | | - difficulty_label = difficulty_map[problem["difficulty"].lower()] |
231 | | - url = f"https://leetcode.com/problems/{problem['titleSlug']}" |
232 | | - |
233 | | - self.log_problem_details(problem, difficulty_label, url) |
234 | | - self.open_in_browser(url, args["open_in_browser"]) |
235 | | - self.create_and_solve_handler( |
236 | | - problem["titleSlug"], problem["codeSnippets"], difficulty_label, args |
237 | | - ) |
238 | | - except Exception as e: |
239 | | - log(f"Failed to fetch random problem: {str(e)}", LogLevel.ERROR) |
| 1 | +from modes.RandomProblemMode import RandomProblemMode |
| 2 | +from modes.DailyChallengeMode import DailyChallengeMode |
| 3 | +from modes.CustomPracticeMode import CustomPracticeMode |
| 4 | +from modes.StudyPlanMode import StudyPlanMode |
240 | 5 |
|
241 | 6 |
|
242 | 7 | class PracticeModeHandler: |
243 | 8 | @staticmethod |
244 | | - def get_mode(selection_mode: str) -> PracticeMode: |
| 9 | + def get_mode(selection_mode: str): |
245 | 10 | if selection_mode == "random": |
246 | 11 | return RandomProblemMode() |
247 | 12 | elif selection_mode == "daily": |
|
0 commit comments