1- from lib . fetch_problems import get_random_problem
2- from lib . fetch_daily_challenge import fetch_daily_challenge
3- from lib . fetch_problem import fetch_problem
1+ import random
2+ from typing import List , Dict , Any , Optional
3+
44from utils .logger import log , LogLevel
55from handlers .SolutionHandler import SolutionHandler
6+ from api .LeetCodeAPI import LeetCodeAPI
67
78difficulty_map = {
89 "easy" : "Easy" ,
1213
1314
1415class PracticeMode :
15- def handle (self , args ):
16- raise NotImplementedError ( "This method should be implemented by subclasses." )
16+ def __init__ (self ):
17+ self . leetcode_api = LeetCodeAPI ( )
1718
18-
19- class RandomProblemMode (PracticeMode ):
2019 def handle (self , args ):
21- problem = get_random_problem ( args [ "difficulties" ] )
20+ raise NotImplementedError ( "This method should be implemented by subclasses." )
2221
23- # Log problem details
22+ def log_problem_details ( self , problem , difficulty_label , url ):
2423 log (f"🎯 Problem Selected: { problem ['title' ]} " , LogLevel .INFO )
25- log (f"✨ Difficulty: { difficulty_map [ problem [ 'difficulty' ]] } " , LogLevel .INFO )
26- log (f"🔗 URL: https://leetcode.com/problems/ { problem [ 'slug' ] } " , LogLevel .INFO )
24+ log (f"✨ Difficulty: { difficulty_label } " , LogLevel .INFO )
25+ log (f"🔗 URL: { url } " , LogLevel .INFO )
2726
28- # Open in browser if flag is set
29- if args [ "open_in_browser" ] :
27+ def open_in_browser ( self , url , open_flag ):
28+ if open_flag :
3029 import webbrowser
3130
32- webbrowser .open (f"https://leetcode.com/problems/ { problem [ 'slug' ] } " )
31+ webbrowser .open (url )
3332
33+ def create_and_solve_handler (self , problem_slug , difficulty_label , args ):
3434 handler = SolutionHandler (
35- problem = problem [ "slug" ] ,
36- difficulty = difficulty_map [ problem [ "difficulty" ]] ,
35+ problem = problem_slug ,
36+ difficulty = difficulty_label ,
3737 editor = args ["editor" ],
3838 language = args ["language" ],
3939 time_limit = args ["time_limit" ],
4040 )
4141 handler .solve ()
4242
43+ def get_random_problem (
44+ self ,
45+ category_slug : Optional [str ] = None ,
46+ difficulties : Optional [List [str ]] = None ,
47+ ) -> Optional [Dict [str , Any ]]:
48+ """
49+ Get a random problem from LeetCode.
50+ :param category_slug: Slug of the category to fetch problems from.
51+ :param difficulties: Difficulty levels of the problems (e.g., "Easy", "Medium", "Hard").
52+ :return: A random problem dictionary or None if no problems are found.
53+ """
54+ problems = self .leetcode_api .fetch_problems (
55+ category_slug = category_slug , limit = 1000 , difficulties = difficulties
56+ )
4357
44- class DailyChallengeMode (PracticeMode ):
58+ if not problems :
59+ return None
60+
61+ random_index = random .randint (0 , len (problems ) - 1 )
62+ return problems [random_index ]
63+
64+ def get_study_plan_problems (self , slug : str ) -> List [Dict [str , Any ]]:
65+ """
66+ Fetch the list of problems for a specific study plan.
67+ :param slug: The slug of the study plan (e.g., "leetcode-75").
68+ :return: A list of dictionaries, each containing details of a problem.
69+ """
70+ study_plan = self .leetcode_api .get_study_plan (slug )
71+
72+ if not study_plan :
73+ raise Exception (f"❌ Study plan not found for slug: { slug } " )
74+
75+ # Gather all questions from the study plan subgroups
76+ problems = []
77+ for subgroup in study_plan .get ("planSubGroups" , []):
78+ questions = subgroup .get ("questions" , [])
79+ problems .extend (questions ) # Add all questions to the list
80+
81+ if not problems :
82+ raise Exception (f"❌ No problems found for study plan: { slug } " )
83+
84+ return problems
85+
86+ def get_random_study_plan_problem (self , slug : str ) -> Optional [Dict [str , Any ]]:
87+ """
88+ Get a random problem from a specific study plan.
89+ :param slug: The slug of the study plan (e.g., "leetcode-75").
90+ :return: A random problem dictionary or None if no problems are found.
91+ """
92+ problems = self .get_study_plan_problems (slug )
93+
94+ if not problems :
95+ return None
96+
97+ random_index = random .randint (0 , len (problems ) - 1 )
98+ return problems [random_index ]
99+
100+
101+ class RandomProblemMode (PracticeMode ):
45102 def handle (self , args ):
103+ log ("Selected 🎲 Random Problem Mode" , LogLevel .INFO )
104+
46105 try :
47- daily_challenge = fetch_daily_challenge ()
106+ # Use the LeetCodeAPI's fetch_problems method
107+ problem = self .get_random_problem (args ["difficulties" ])
108+
109+ if not problem :
110+ log ("No problems found for the selected difficulties." , LogLevel .ERROR )
111+ return
112+
113+ difficulty_label = difficulty_map [problem ["difficulty" ].lower ()]
114+ url = f"https://leetcode.com/problems/{ problem ['titleSlug' ]} "
48115
116+ self .log_problem_details (problem , difficulty_label , url )
117+ self .open_in_browser (url , args ["open_in_browser" ])
118+ self .create_and_solve_handler (problem ["titleSlug" ], difficulty_label , args )
119+ except Exception as e :
120+ log (f"Failed to fetch random problem: { str (e )} " , LogLevel .ERROR )
121+
122+
123+ class DailyChallengeMode (PracticeMode ):
124+ def handle (self , args ):
125+ log ("Selected 📅 Daily Challenge Mode" , LogLevel .INFO )
126+
127+ try :
128+ # Use the LeetCodeAPI's fetch_daily_challenge method
129+ daily_challenge = self .leetcode_api .fetch_daily_challenge ()
49130 difficulty_label = difficulty_map [
50131 daily_challenge ["question" ]["difficulty" ].lower ()
51132 ]
133+ url = f"https://leetcode.com{ daily_challenge ['link' ].removesuffix ('/' )} "
52134
53- # Log daily challenge details
54135 log ("🎯 Daily Coding Challenge:" , LogLevel .INFO )
55136 log (f"📅 Date: { daily_challenge ['date' ]} " , LogLevel .INFO )
56- log (f"📖 Title: { daily_challenge ['question' ]['title' ]} " , LogLevel .INFO )
57- log (f"✨ Difficulty: { difficulty_label } " , LogLevel .INFO )
58- log (
59- f"🔗 Link: https://leetcode.com{ daily_challenge ['link' ]} " , LogLevel .INFO
60- )
61-
62- if args ["open_in_browser" ]:
63- import webbrowser
64-
65- webbrowser .open (f"https://leetcode.com{ daily_challenge ['link' ]} " )
66-
67- handler = SolutionHandler (
68- problem = daily_challenge ["question" ]["titleSlug" ],
69- difficulty = difficulty_label ,
70- editor = args ["editor" ],
71- language = args ["language" ],
72- time_limit = args ["time_limit" ],
137+ self .log_problem_details (daily_challenge ["question" ], difficulty_label , url )
138+ self .open_in_browser (url , args ["open_in_browser" ])
139+ self .create_and_solve_handler (
140+ daily_challenge ["question" ]["titleSlug" ], difficulty_label , args
73141 )
74- handler .solve ()
75142 except Exception as e :
76143 log (f"Failed to fetch daily challenge: { str (e )} " , LogLevel .ERROR )
77144
78145
79146class CustomPracticeMode (PracticeMode ):
80147 def handle (self , args ):
81- problems = args [ "problems" ]
148+ log ( "Selected 🧩 Custom Practice Mode" , LogLevel . INFO )
82149
83- for slug in problems :
150+ for slug in args [ " problems" ] :
84151 log (f"🔍 Fetching details for problem: { slug } " , LogLevel .INFO )
85152 try :
86- problem = fetch_problem (slug .strip ())
153+ problem = self . leetcode_api . fetch_problem (slug .strip ())
87154
88155 if not problem :
89156 log (f"Problem with slug '{ slug } ' not found." , LogLevel .ERROR )
90157 continue
91158
92- log (f"🎯 Problem Selected: { problem ['title' ]} " , LogLevel .INFO )
93- log (f"✨ Difficulty: { problem ['difficulty' ]} " , LogLevel .INFO )
94- log (f"🔗 URL: https://leetcode.com/problems/{ slug } " , LogLevel .INFO )
159+ difficulty_label = difficulty_map [problem ["difficulty" ].lower ()]
160+ url = f"https://leetcode.com/problems/{ problem ['titleSlug' ]} "
95161
96- if args ["open_in_browser" ]:
97- import webbrowser
98-
99- webbrowser .open (f"https://leetcode.com/problems/{ problem ['slug' ]} " )
100-
101- handler = SolutionHandler (
102- problem = slug ,
103- difficulty = problem ["difficulty" ],
104- editor = args ["editor" ],
105- language = args ["language" ],
106- time_limit = args ["time_limit" ],
107- )
108- handler .solve ()
162+ self .log_problem_details (problem , difficulty_label , url )
163+ self .open_in_browser (url , args ["open_in_browser" ])
164+ self .create_and_solve_handler (slug , difficulty_label , args )
109165 except Exception as e :
110166 log (f"Failed to process problem '{ slug } ': { str (e )} " , LogLevel .ERROR )
111167
112168
169+ class StudyPlanMode (PracticeMode ):
170+ def handle (self , args ):
171+ try :
172+ log (f"Selected 🎯 Study Plan Mode: { args ['study_plan' ]} " , LogLevel .INFO )
173+
174+ # Use the LeetCodeAPI's fetch_problems method
175+ problem = self .get_random_study_plan_problem (args ["study_plan" ])
176+
177+ if not problem :
178+ log ("No problems found for the selected study plan." , LogLevel .ERROR )
179+ return
180+
181+ difficulty_label = difficulty_map [problem ["difficulty" ].lower ()]
182+ url = f"https://leetcode.com/problems/{ problem ['titleSlug' ]} "
183+
184+ self .log_problem_details (problem , difficulty_label , url )
185+ self .open_in_browser (url , args ["open_in_browser" ])
186+ self .create_and_solve_handler (problem ["titleSlug" ], difficulty_label , args )
187+ except Exception as e :
188+ log (f"Failed to fetch random problem: { str (e )} " , LogLevel .ERROR )
189+
190+
113191class PracticeModeFactory :
114192 @staticmethod
115193 def get_mode (selection_mode : str ) -> PracticeMode :
@@ -120,6 +198,6 @@ def get_mode(selection_mode: str) -> PracticeMode:
120198 elif selection_mode == "custom" :
121199 return CustomPracticeMode ()
122200 elif selection_mode == "study-plan" :
123- raise ValueError ( "Study Plan mode is not yet supported." )
201+ return StudyPlanMode ( )
124202 else :
125203 raise ValueError (f"Unsupported mode: { selection_mode } " )
0 commit comments