Skip to content

Commit 18b26c0

Browse files
committed
Create navigation style question panel
1 parent 94f584e commit 18b26c0

File tree

4 files changed

+101
-114
lines changed

4 files changed

+101
-114
lines changed

requirements.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,6 @@ colorama==0.4.4
1111
configparser==5.0.2
1212
crayons==0.4.0
1313
selenium==3.141.0
14-
webdriver-manager==3.3.0
14+
webdriver-manager==3.3.0
15+
simple-term-menu==1.0.1
16+
keyboard==0.13.5

src/arguments/markdown.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,3 @@ def __len__(self):
3333

3434
def __str__(self):
3535
return str(self.render)
36-
37-
def __repr__(self):
38-
return str(self.render)

src/arguments/search.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ def search_for_results(self, save=False):
5454
queries = ["What do you want to search", "Tags"]
5555
query_solutions = []
5656

57-
# ask quesiton
57+
# ask question
5858
for each_query in queries:
5959
# Be careful if there are
6060
# KeyBoard Interrupts or EOErrors

src/arguments/utility.py

Lines changed: 97 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,16 @@
22
import requests
33
from rich.console import Console
44
import sys as sys
5+
6+
# Required for Questions Panel
57
import os
68
import time
79
from collections import defaultdict
10+
from simple_term_menu import TerminalMenu
11+
import webbrowser
12+
from pygments import highlight
13+
from pygments.lexers.markup import MarkdownLexer
14+
from pygments.formatters import Terminal256Formatter
815

916
from .error import SearchError
1017
from .save import SaveSearchResults
@@ -28,6 +35,86 @@
2835

2936
console = Console()
3037

38+
class Questions_Panel_Stackoverflow():
39+
def __init__(self):
40+
self.questions_data = [] # list( list( question_title, question_link, question_id )... )
41+
self.answer_data = defaultdict(lambda: False) # dict( question_id:list( body, link )) corresponding to self.questions_data
42+
self.batch_ques_id = ""
43+
self.line_color = "bold red"
44+
self.heading_color = "bold blue"
45+
self.search_content_url = "https://api.stackexchange.com/"
46+
47+
def populate_question_data(self, questions_list):
48+
""" Function to populate question data property
49+
Creates batch_id request to stackexchange API and
50+
Stores the returned data data in the following format:
51+
list( list( question_title, question_link, question_id ) )
52+
"""
53+
for question_id in questions_list:
54+
self.batch_ques_id += str(question_id) + ";"
55+
try:
56+
resp = requests.get(
57+
f"{self.search_content_url}/2.2/questions/{self.batch_ques_id[:-1]}?order=desc&sort=votes&site=stackoverflow&filter=!--1nZwsgqvRX"
58+
)
59+
except:
60+
SearchError("Search Failed", "Try connecting to the internet")
61+
sys.exit()
62+
json_ques_data = resp.json()
63+
self.questions_data = [[item['title'], item['question_id'], item['link']] for item in json_ques_data["items"]]
64+
return
65+
66+
def populate_answer_data(self):
67+
""" Function to populate answer data property
68+
Creates batch_id request to stackexchange API and
69+
Stores the returned data data in the following format:
70+
list( list( question_title, question_link, question_id ) )
71+
"""
72+
try:
73+
resp = requests.get(
74+
f"{self.search_content_url}/2.2/questions/{self.batch_ques_id[:-1]}/answers?order=desc&sort=votes&site=stackoverflow&filter=!--1nZwsgqvRX"
75+
)
76+
except:
77+
SearchError("Search Failed", "Try connecting to the internet")
78+
sys.exit()
79+
json_ans_data = resp.json()
80+
for item in json_ans_data["items"]:
81+
self.answer_data[item['question_id']] = item['body_markdown']
82+
83+
def return_formatted_ans(self, id):
84+
# This function uses pygments lexers ad formatters to format the content in the preview screen
85+
body_markdown = self.answer_data[int(id)]
86+
if(body_markdown):
87+
body_markdown = str(body_markdown)
88+
body_markdown = body_markdown.replace("&", "&")
89+
body_markdown = body_markdown.replace("&lt;", "<")
90+
body_markdown = body_markdown.replace("&gt;", ">")
91+
body_markdown = body_markdown.replace("&quot;", "\"")
92+
body_markdown = body_markdown.replace("&apos;", "\'")
93+
body_markdown = body_markdown.replace("&#39;", "\'")
94+
lexer = MarkdownLexer()
95+
formatter = Terminal256Formatter(bg="light")
96+
highlighted = highlight(body_markdown, lexer, formatter)
97+
else:
98+
highlighted = "Answer not viewable. Press enter to open in a browser"
99+
return highlighted
100+
101+
def navigate_questions_panel(self):
102+
# Code for navigating through the question panel
103+
console.rule('[bold blue] Relevant Questions', style="bold red")
104+
console.print("[yellow] Use arrow keys to navigate. 'q' or 'Esc' to quit. 'Enter' to open in a browser")
105+
console.print()
106+
options = ["|".join(map(str, question)) for question in self.questions_data]
107+
question_menu = TerminalMenu(options, preview_command=self.return_formatted_ans)
108+
quitting = False
109+
while not(quitting):
110+
options_index = question_menu.show()
111+
try:
112+
options_choice = options[options_index]
113+
except:
114+
return
115+
else:
116+
question_link = self.questions_data[options_index][2]
117+
webbrowser.open(question_link)
31118

32119
class Utility():
33120
def __init__(self):
@@ -52,12 +139,12 @@ def make_request(self, que, tag: str):
52139
:return: Json response from the api call.
53140
:rtype: Json format data
54141
"""
55-
print("\U0001F50E Searching for the answer")
56-
try:
57-
resp = requests.get(self.__get_search_url(que, tag))
58-
except:
59-
SearchError("\U0001F613 Search Failed", "\U0001F4BB Try connecting to the internet")
60-
sys.exit()
142+
with console.status("Searching..."):
143+
try:
144+
resp = requests.get(self.__get_search_url(que, tag))
145+
except:
146+
SearchError("\U0001F613 Search Failed", "\U0001F4BB Try connecting to the internet")
147+
sys.exit()
61148
return resp.json()
62149

63150
def get_que(self, json_data):
@@ -73,109 +160,10 @@ def get_que(self, json_data):
73160
return que_id
74161

75162
def get_ans(self, questions_list):
76-
"""
77-
This function prints the answer to the queries
78-
(question and tags) provided by the user. It does so
79-
in the following manner :
80-
1) Gets the details of all the relevant question and stores their title, link and id in "question_data" list.
81-
2) I have introduced the concept of active question, i.e. , the question whose answer is currently being displayed.
82-
3) The index of the active question in "question_data" array is stored in "question_posx"
83-
2) By Default, shows the answer to the first question. Creates an breakable infinite loop asking the user answer to which question he wants to see.
84-
4) The value of "question_posx" changes according to user input, displaying answer to different questions in "questions_data" list.
85-
3) The answers to the questions requested by the user are stored in cache for faster access time during subsequent calls.
86-
"""
87-
# Create batch request to get details of all the questions
88-
batch_ques_id = ""
89-
for question_id in questions_list:
90-
batch_ques_id += str(question_id) + ";"
91-
try:
92-
resp = requests.get(
93-
f"{self.search_content_url}/2.2/questions/{batch_ques_id[:-1]}?order=desc&sort=votes&site=stackoverflow&filter=!--1nZwsgqvRX"
94-
)
95-
except:
96-
SearchError("Search Failed", "Try connecting to the internet")
97-
sys.exit()
98-
json_ques_data = resp.json()
99-
"""
100-
Store the received questions data into the following data format:
101-
list( list( question_title, question_link, question_id ) )
102-
"""
103-
questions_data = [[item['title'], item['link'], item['question_id']] for item in json_ques_data["items"] ]
104-
# Clear terminal
105-
os.system('cls' if os.name == 'nt' else 'clear')
106-
107-
# cache array to store the requested answers. Format of storage { question_id:[answer_body, answer_link] }
108-
downloaded_questions_cache = defaultdict(lambda: False)
109-
110-
# Stores the currently showing question index in questions_data
111-
question_posx = 0
112-
113-
while True:
114-
os.system('cls' if os.name == 'nt' else 'clear')
115-
# Printing all the questions. The active question is printed GREEN.
116-
console.rule('[bold blue] Relevant Questions', style="bold red")
117-
for idx, question_data in enumerate(questions_data):
118-
if question_posx == idx:
119-
console.print("[green]{}. {} | {}".format(idx+1, question_data[0], question_data[1]))
120-
else:
121-
console.print("{}. {} | {}".format(idx+1, question_data[0], question_data[1]))
122-
console.rule("[bold blue] Answer of question {}".format(question_posx+1), style="bold red")
123-
124-
# Gets the question_id of active question
125-
current_question_id = questions_data[question_posx][2]
126-
127-
# Searches for the id in cache. If present then prints it
128-
if(downloaded_questions_cache[current_question_id]):
129-
output_content = downloaded_questions_cache[current_question_id]
130-
for output_index, output_text in enumerate(output_content):
131-
"""
132-
Loop through the output_text and print the element
133-
if it is the last one, the text[0] is printed
134-
along with text[-1]
135-
136-
if text is markdown , render the markdown
137-
"""
138-
if output_index == len(output_content) - 1:
139-
console.print("Link to the answer: " + output_text)
140-
break
141-
142-
if output_index == len(output_content) - 2:
143-
MarkdownRenderer(output_text)
144-
continue
145-
146-
console.print(output_text)
147-
# If the cache has no entry for the said question id, then downloads the answer
148-
# and makes an for it entry in the cache array in the said format and restarts the loop.
149-
else:
150-
try:
151-
resp = requests.get(
152-
f"{self.search_content_url}/2.2/questions/{current_question_id}/answers?order=desc&sort=votes&site=stackoverflow&filter=!--1nZwsgqvRX"
153-
)
154-
except:
155-
SearchError("Search Failed", "Try connecting to the internet")
156-
sys.exit()
157-
json_ans_data = resp.json()
158-
print(json_ans_data["items"])
159-
most_upvoted = json_ans_data["items"][0]
160-
downloaded_questions_cache[current_question_id] = [most_upvoted["body_markdown"], most_upvoted['link']]
161-
del most_upvoted
162-
continue
163-
164-
console.rule("[bold blue]", style="bold red", align="right")
165-
# Asks the user for next question number. Makes it the active question and the loop restarts
166-
while True:
167-
try:
168-
posx = int(input("Enter the question number you want to view (Press 0 to quit): ")) - 1
169-
except ValueError:
170-
SearchError("You didn't enter a question number", "Enter a question number from the relevant questions list")
171-
if (posx == -1):
172-
return
173-
elif (0<=posx<len(questions_data)):
174-
question_posx = posx
175-
break
176-
else:
177-
console.print("Please enter a valid question number")
178-
continue
163+
stackoverflow_panel = Questions_Panel_Stackoverflow()
164+
stackoverflow_panel.populate_question_data(questions_list)
165+
stackoverflow_panel.populate_answer_data()
166+
stackoverflow_panel.navigate_questions_panel()
179167

180168
# Get an access token and extract to a JSON file "access_token.json"
181169
@classmethod

0 commit comments

Comments
 (0)