77# Required for Questions Panel
88import os
99import time
10+ import locale
1011from collections import defaultdict
1112from simple_term_menu import TerminalMenu
1213import webbrowser
3334
3435console = Console ()
3536
37+ class Playbook ():
38+ def __init__ (self ):
39+ self .linux_path = "/home/{}/Documents/dynamic" .format (os .getenv ('USER' ))
40+ self .mac_path = "/Users/{}/Documents/dynamic" .format (os .getenv ('USER' ))
41+ self .file_name = 'dynamic_playbook.json'
42+ self .key = 'DYNAMIC'
43+
44+ @property
45+ def playbook_path (self ):
46+ # Create an environment variable 'DYNAMIC' containing the path of dynamic_playbook.json and returns it
47+ if not os .getenv (self .key ):
48+ if (sys .platform == 'linux' ):
49+ os .environ [self .key ] = os .path .join (self .linux_path , self .file_name )
50+ elif (sys .platform == 'darwin' ):
51+ os .environ [self .key ] = os .path .join (self .mac_path , self .file_name )
52+ return os .getenv (self .key )
53+
54+ @property
55+ def playbook_template (self ):
56+ # Basic template and fields of playbook
57+ return {"time_of_update" : time .time (),"items_stackoverflow" :[]}
58+
59+ @property
60+ def playbook_content (self ):
61+ # Reads playbook data from local storage and returns it
62+ try :
63+ with open (self .playbook_path , 'r' ) as playbook :
64+ return json .load (playbook )
65+ except FileNotFoundError :
66+ os .makedirs (os .path .dirname (self .playbook_path ), exist_ok = True )
67+ with open (self .playbook_path , 'w' ) as playbook :
68+ json .dump (self .playbook_template , playbook , ensure_ascii = False )
69+ return self .playbook_content
70+
71+ @playbook_content .setter
72+ def playbook_content (self , value ):
73+ if isinstance (value , dict ):
74+ with open (self .playbook_path , 'w' ) as playbook :
75+ json .dump (value , playbook , ensure_ascii = False )
76+ else :
77+ raise ValueError ("value should be of type dict" )
78+
79+ def is_question_in_playbook (self , question_id ):
80+ content = self .playbook_content
81+ for entry in content ['items_stackoverflow' ]:
82+ if int (entry ['question_id' ]) == int (question_id ):
83+ return True
84+ return False
85+
86+ def add_to_playbook (self , stackoverflow_object , question_id ):
87+ """
88+ Receives a QuestionsPanelStackoverflow object and
89+ saves data of a particular question into playbook
90+ Saves playbook in the following format
91+ {
92+ time_of_update: unix,
93+ items_stackoverflow:
94+ [
95+ {
96+ time: unix timestamp
97+ question_id: 123456,
98+ question_title: 'question_title',
99+ question_link: 'link',
100+ answer_body: 'body of the answer'
101+ },
102+ ...
103+ ]
104+ """
105+ if self .is_question_in_playbook (question_id ):
106+ console .print ("[red] Question is already in the playbook, No need to add" )
107+ return
108+ for question in stackoverflow_object .questions_data :
109+ if (int (question [1 ])== int (question_id )):
110+ content = self .playbook_content
111+ now = time .time ()
112+ content ['time_of_update' ] = now
113+ content ['items_stackoverflow' ].append ({
114+ 'time_of_creation' : now ,
115+ 'question_id' : int (question_id ),
116+ 'question_title' : question [0 ],
117+ 'question_link' : question [2 ],
118+ 'answer_body' : stackoverflow_object .answer_data [int (question_id )]
119+ })
120+ self .playbook_content = content
121+ console .print ("[green] Question added to the playbook" )
122+
123+ def delete_from_playbook (self , stackoverflow_object , question_id ):
124+ content = self .playbook_content
125+ for i in range (len (content ["items_stackoverflow" ])):
126+ if content ["items_stackoverflow" ][i ]["question_id" ] == question_id :
127+ del content ["items_stackoverflow" ][i ]
128+ break
129+ self .playbook_content = content
130+ os .system ('cls' if os .name == 'nt' else 'clear' )
131+ self = Playbook ()
132+ self .display_panel ()
133+
134+ def display_panel (self ):
135+ playbook_data = self .playbook_content
136+ if (len (playbook_data ['items_stackoverflow' ]) == 0 ):
137+ SearchError ("You have no entries in the playbook" , "Browse and save entries in playbook with 'p' key" )
138+ sys .exit ()
139+ # Creates QuestionPanelStackoverflow object, populates its question_data and answer_data and displays it
140+ question_panel = QuestionsPanelStackoverflow ()
141+ for item in playbook_data ['items_stackoverflow' ]:
142+ question_panel .questions_data .append ( [item ['question_title' ], item ['question_id' ], item ['question_link' ]] )
143+ question_panel .answer_data [item ['question_id' ]] = item ['answer_body' ]
144+ question_panel .display_panel ([], playbook = True )
145+
36146class QuestionsPanelStackoverflow ():
37147 def __init__ (self ):
38148 self .questions_data = [] # list( list( question_title, question_id, question_link )... )
39149 self .answer_data = defaultdict (lambda : False ) # dict( question_id:list( body, link )) corresponding to self.questions_data
40150 self .line_color = "bold red"
41151 self .heading_color = "bold blue"
42152 self .utility = Utility ()
153+ self .playbook = Playbook ()
43154
44155 def populate_question_data (self , questions_list ):
45156 """
@@ -84,7 +195,7 @@ def populate_answer_data(self, questions_list):
84195 self .populate_answer_data (failed_ques_id )
85196
86197 def return_formatted_ans (self , ques_id ):
87- # This function uses pygments lexers ad formatters to format the content in the preview screen
198+ # This function uses uses Rich Markdown to format answers body.
88199 body_markdown = self .answer_data [int (ques_id )]
89200 body_markdown = str (body_markdown )
90201 xml_markup_replacement = [("&" , "&" ), ("<" , "<" ), (">" , ">" ), (""" , "\" " ), ("'" , "\' " ), ("'" , "\' " )]
@@ -96,32 +207,40 @@ def return_formatted_ans(self, ques_id):
96207 with console .capture () as capture :
97208 console .print (markdown )
98209 highlighted = capture .get ()
99- box_replacement = [("─" , "-" ), ("═" ,"=" ), ("║" ,"|" ), ("│" , "|" ), ('┌' , '+' ), ("└" , "+" ), ("┐" , "+" ), ("┘" , "+" ), ("╔" , "+" ), ("╚" , "+" ), ("╗" ,"+" ), ("╝" , "+" ), ("•" ,"*" )]
100- for convert_from , convert_to in box_replacement :
101- highlighted = highlighted .replace (convert_from , convert_to )
210+ if locale .getlocale ()[1 ] != 'UTF-8' :
211+ box_replacement = [("─" , "-" ), ("═" ,"=" ), ("║" ,"|" ), ("│" , "|" ), ('┌' , '+' ), ("└" , "+" ), ("┐" , "+" ), ("┘" , "+" ), ("╔" , "+" ), ("╚" , "+" ), ("╗" ,"+" ), ("╝" , "+" ), ("•" ,"*" )]
212+ for convert_from , convert_to in box_replacement :
213+ highlighted = highlighted .replace (convert_from , convert_to )
102214 return highlighted
103215
104- def navigate_questions_panel (self ):
216+ def navigate_questions_panel (self , playbook = False ):
105217 # Code for navigating through the question panel
106- console .rule ('[bold blue] Relevant Questions' , style = "bold red" )
107- console .print ("[yellow] Use arrow keys to navigate. 'q' or 'Esc' to quit. 'Enter' to open in a browser" )
218+ (message , instructions , keys ) = ('Playbook Questions' , ". Press 'd' to delete from playbook" , ('enter' , 'd' )) if (playbook ) else ('Relevant Questions' , ". Press 'p' to save in playbook" , ('p' , 'enter' ))
219+ console .rule ('[bold blue] {}' .format (message ), style = "bold red" )
220+ console .print ("[yellow] Use arrow keys to navigate. 'q' or 'Esc' to quit. 'Enter' to open in a browser" + instructions )
108221 console .print ()
109222 options = ["|" .join (map (str , question )) for question in self .questions_data ]
110- question_menu = TerminalMenu (options , preview_command = self .return_formatted_ans , preview_size = 0.75 , )
223+ question_menu = TerminalMenu (options , preview_command = self .return_formatted_ans , preview_size = 0.75 , accept_keys = keys )
111224 quitting = False
112225 while not (quitting ):
113226 options_index = question_menu .show ()
114227 try :
115228 question_link = self .questions_data [options_index ][2 ]
116229 except Exception :
117- return
230+ return sys . exit () if playbook else None
118231 else :
119- webbrowser .open (question_link )
232+ if (question_menu .chosen_accept_key == 'enter' ):
233+ webbrowser .open (question_link )
234+ elif (question_menu .chosen_accept_key == 'p' ):
235+ self .playbook .add_to_playbook (self , self .questions_data [options_index ][1 ])
236+ elif (question_menu .chosen_accept_key == 'd' and playbook ):
237+ self .playbook .delete_from_playbook (self , self .questions_data [options_index ][1 ])
120238
121- def display_panel (self , questions_list ):
122- self .populate_question_data (questions_list )
123- self .populate_answer_data (questions_list )
124- self .navigate_questions_panel ()
239+ def display_panel (self , questions_list , playbook = False ):
240+ if not playbook :
241+ self .populate_question_data (questions_list )
242+ self .populate_answer_data (questions_list )
243+ self .navigate_questions_panel (playbook = playbook )
125244
126245class Utility ():
127246 def __init__ (self ):
0 commit comments