77from rich .style import Style
88from rich .text import Text
99from rich .align import Align
10- from bs4 import BeautifulSoup
10+ from bs4 import BeautifulSoup , Tag
1111import json
1212from dataclasses import dataclass
1313from typing import List , Dict , Optional
@@ -36,7 +36,6 @@ def __init__(self, problem_data: dict):
3636 )
3737 self .code_snippets : List [Dict ] = problem_data ['codeSnippets' ]
3838
39- # Parse metadata
4039 self .function_metadata : Optional [FunctionMetadata ] = None
4140 try :
4241 metadata = json .loads (problem_data ['metaData' ])
@@ -48,6 +47,10 @@ def __init__(self, problem_data: dict):
4847 except Exception :
4948 pass
5049
50+ self .total_accepted : int = problem_data .get ('totalAccepted' , 0 )
51+ self .total_submissions : int = problem_data .get ('totalSubmissions' , 0 )
52+ self .acceptance_rate : float = problem_data .get ('acRate' , 0 )
53+
5154 self ._parse_content ()
5255 self .console_width = console .width
5356
@@ -82,16 +85,6 @@ def _parse_content(self):
8285 else :
8386 current_section .append (text )
8487
85- @property
86- def formatted_description (self ) -> str :
87- """Get the formatted problem description"""
88- sections = [
89- ' ' .join (self .description ),
90- * self .examples ,
91- * self .constraints
92- ]
93- return '\n ' .join (line for line in sections if line .strip ())
94-
9588 @property
9689 def available_languages (self ) -> List [str ]:
9790 """Get list of available programming languages"""
@@ -138,17 +131,16 @@ def _create_header(self):
138131 }
139132 difficulty_color = difficulty_colors .get (self .difficulty , 'white' )
140133
141- header = Text ()
142- header . append ( f" { self .question_id } . { self .title } " , style = "bold cyan" )
143- header . append ( " " , style = "dim" )
144- header . append ( self . difficulty , style = difficulty_color )
134+ header = (
135+ f"[bold cyan] { self .question_id } . { self .title } [/] "
136+ f"[ { difficulty_color } ] { self . difficulty } [/]"
137+ )
145138
146- return Align . center ( header )
139+ return header
147140
148141 def _format_description (self , html_content : str ) -> str :
149142 """Format the problem description, removing example test cases"""
150143 soup = BeautifulSoup (html_content , 'html.parser' )
151- typer .echo (soup .prettify ())
152144
153145 # Remove all pre tags and example sections
154146 for pre in soup .find_all ('pre' ):
@@ -163,9 +155,16 @@ def _format_description(self, html_content: str) -> str:
163155 for code in soup .find_all ('code' ):
164156 code .replace_with (BeautifulSoup (f'`{ code .text } `' , 'html.parser' ))
165157
166- # Format strong and em tags
167- for strong in soup .find_all ('strong' ):
168- strong .replace_with (BeautifulSoup (f'**{ strong .text } **' , 'html.parser' ))
158+ # Format HTML formatting tags
159+ for tag in soup .find_all (['strong' , 'em' , 'b' , 'i' ]):
160+ if not isinstance (tag , Tag ):
161+ continue
162+ text = tag .text .strip ()
163+ if tag .name in ['strong' , 'b' ]:
164+ replacement = f"**{ text } **"
165+ elif tag .name in ['em' , 'i' ]:
166+ replacement = f"_{ text } _"
167+ tag .replace_with (BeautifulSoup (replacement , 'html.parser' ))
169168
170169 for em in soup .find_all ('em' ):
171170 em .replace_with (BeautifulSoup (f'_{ em .text } _' , 'html.parser' ))
@@ -184,49 +183,87 @@ def _format_test_cases(self):
184183
185184 # Extract example explanations from HTML content
186185 soup = BeautifulSoup (self .content , 'html.parser' )
187- explanations = []
188- for strong in soup .find_all ('strong' ):
189- if 'Example' in strong .text :
190- parent = strong .find_parent (['p' , 'div' ])
191- if parent :
192- explanations .append (parent .get_text ())
193-
194- # Combine test cases with their explanations
195- for i , (test , explanation ) in enumerate (zip (examples , explanations )):
196- test_cases .append (
197- f"[bold blue]Example { i + 1 } :[/]\n "
198- f"[cyan]Input:[/] { test } \n "
199- f"{ explanation .replace ('Example ' + str (i + 1 ) + ':' , '[yellow]Explanation:[/]' )} "
186+
187+ examples_data = []
188+
189+ # Find all pre tags containing example data
190+ for pre in soup .find_all ('pre' ):
191+ # Extract input, output, and explanation
192+ if isinstance (pre , Tag ):
193+ input_tag = pre .find ('strong' , text = 'Input:' )
194+ output_tag = pre .find ('strong' , text = 'Output:' )
195+ explanation_tag = pre .find ('strong' , text = 'Explanation:' )
196+
197+ if input_tag and output_tag :
198+ input_text = str (input_tag .next_sibling ).strip () if input_tag and input_tag .next_sibling else ""
199+ output_text = str (output_tag .next_sibling ).strip () if output_tag and output_tag .next_sibling else ""
200+ explanation_text = str (explanation_tag .next_sibling ).strip () if explanation_tag and explanation_tag .next_sibling else ""
201+
202+ examples_data .append ({
203+ 'input' : input_text ,
204+ 'output' : output_text ,
205+ 'explanation' : explanation_text
206+ })
207+
208+ # Format the examples
209+ for i , example in enumerate (examples_data , 1 ):
210+ formatted_example = (
211+ f"[bold blue]Example { i } : [/]\n \n "
212+ f"[cyan]Input: [/]{ example ['input' ]} \n "
213+ f"[cyan]Output: [/]{ example ['output' ]} "
200214 )
215+ if example ['explanation' ]:
216+ formatted_example += f"\n [yellow]Explanation: [/]{ example ['explanation' ]} "
217+ test_cases .append (formatted_example )
201218
202219 return "\n \n " .join (test_cases )
203220
221+ def _format_stats (self ) -> str :
222+ """Format problem statistics"""
223+ acceptance_rate = f"{ self .acceptance_rate :.1f} %" if self .acceptance_rate else "N/A"
224+ total_accepted = f"{ self .total_accepted :,} " if self .total_accepted else "N/A"
225+ total_submissions = f"{ self .total_submissions :,} " if self .total_submissions else "N/A"
226+
227+ return (
228+ "[bold blue]Problem Stats[/]\n \n "
229+ f"[cyan]Acceptance Rate:[/] { acceptance_rate } \n "
230+ f"[cyan]Total Accepted:[/] { total_accepted } \n "
231+ f"[cyan]Total Submissions:[/] { total_submissions } "
232+ )
233+
204234 def display (self ):
205235 """Display the problem details"""
206- # Clear screen and show header
207236 console .clear ()
208- console .print (self ._create_header ())
209237 console .print ()
210238
239+ test_cases = self ._format_test_cases ()
240+ stats = self ._format_stats ()
241+
211242 # Create main layout
212243 layout = Layout ()
244+
245+ # Split into left and right sections
213246 layout .split_row (
214247 Layout (name = "description" , ratio = 2 ),
215- Layout (name = "examples" , ratio = 1 )
248+ Layout (name = "right_panel" , ratio = 1 )
249+ )
250+
251+ # Split right panel into examples and stats
252+ layout ["right_panel" ].split_column (
253+ Layout (name = "examples" ),
254+ Layout (name = "stats" , size = 10 )
216255 )
217256
218257 # Description panel
219- description = self ._format_description (self .content )
220258 layout ["description" ].update (Panel (
221- Markdown (description ),
259+ Markdown (self . _format_description ( self . content ) ),
222260 box = ROUNDED ,
223- title = "[bold blue]Description" ,
261+ title = str ( self . _create_header ()) ,
224262 border_style = "blue" ,
225263 padding = (1 , 2 )
226264 ))
227265
228266 # Example test cases panel
229- test_cases = self ._format_test_cases ()
230267 layout ["examples" ].update (Panel (
231268 test_cases ,
232269 box = ROUNDED ,
@@ -235,5 +272,13 @@ def display(self):
235272 padding = (1 , 2 )
236273 ))
237274
238- # Display the layout
275+ # Stats panel
276+ layout ["stats" ].update (Panel (
277+ stats ,
278+ box = ROUNDED ,
279+ title = "[bold blue]Statistics" ,
280+ border_style = "blue" ,
281+ padding = (1 , 2 )
282+ ))
283+
239284 console .print (layout )
0 commit comments