File size: 12,440 Bytes
64fde65
 
 
1d0b89a
64fde65
ffa3445
64fde65
 
 
ffa3445
7908b9a
 
 
ffa3445
64fde65
 
 
 
 
 
 
 
7908b9a
ffa3445
64fde65
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ffa3445
64fde65
 
 
 
 
 
 
 
 
ffa3445
64fde65
ffa3445
7908b9a
64fde65
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7908b9a
64fde65
 
 
 
 
 
 
 
 
 
 
ffa3445
64fde65
ffa3445
7908b9a
64fde65
 
 
ffa3445
64fde65
 
7908b9a
64fde65
 
 
 
 
 
7908b9a
 
 
64fde65
 
 
 
7908b9a
64fde65
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7908b9a
64fde65
 
 
 
 
ffa3445
64fde65
 
7908b9a
64fde65
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7908b9a
64fde65
 
 
 
 
 
 
 
 
7908b9a
64fde65
 
 
 
 
 
 
 
7908b9a
64fde65
 
 
 
 
 
 
 
 
 
 
7908b9a
64fde65
 
 
 
 
 
 
7908b9a
 
64fde65
7908b9a
64fde65
7908b9a
64fde65
 
7908b9a
 
 
 
 
64fde65
7908b9a
64fde65
7908b9a
64fde65
 
 
 
 
7908b9a
64fde65
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7908b9a
64fde65
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7908b9a
64fde65
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
# %%
import random
import gradio as gr
from News import News
from Gemma import GemmaLLM

# %%
class Cooldown:
    ...

cooldown = Cooldown() ################################## News fetching cooldown in seconds
news = News() ########################################## Initialize the News object
model = GemmaLLM() ##################################### Initialize the Gemma model

# %%
with gr.Blocks() as demo:
    gr.Markdown("# News Quiz Application")
    
    ######
    ###### State Variables and Components Initialization
    ######
    
    init = ( ########################################### Initialize the Gradio interface with components and state variables

        gr.Markdown("## News Articles"),
    
        ## State variables for news articles and quiz,
        
        gr.State({}), gr.State([]), gr.State({}),
        
        gr.Slider(label="Number of Articles", minimum=1, maximum=10, value=3, step=1),
        gr.Radio(label="Category (optional)", choices=news.__CATEGORIES__),
        gr.Button("Get Articles"),
        
        gr.Radio(visible=False),
        gr.Textbox(visible=False),
        gr.Button(visible=False),
        
        gr.Checkbox(visible=False),
        gr.Textbox(visible=False),
        gr.Button(visible=False),

        ## State variables for quiz
        
        gr.State([]), gr.State([]), gr.State(),
        
        [gr.Radio(visible=False) for _ in range(10)],
        gr.Button(visible=False),
        
        gr.Textbox(visible=False),
        gr.Button(visible=False),

    )

    ( ################################################## State variables and components for news articles and quiz
        
        heading,
        
        ## Components for news articles
        
        article, articles, descriptions,
        num_headlines, category, get,
        headline, description, show,
        summarize, content, ready,
        
        ## Components for quiz
        
        answers, response, results,
        quiz, submit,
        evaluation, read,
        
    ) = init
    
    def hide_news(): ################################### Hide news-related components
        num_headlines = gr.Slider(visible=False)
        category = gr.Radio(visible=False)
        get = gr.Button(visible=False)
        
        headline = gr.Radio(visible=False)
        description = gr.Textbox(visible=False)
        show = gr.Button(visible=False)
        
        summarize = gr.Checkbox(visible=False)
        content = gr.Textbox(visible=False)
        ready = gr.Button(visible=False)

        return num_headlines, category, get, headline, description, show, summarize, content, ready

    def show_news(): ################################### Show news-related components    
        num_headlines = gr.Slider(label="Number of Articles", minimum=1, maximum=10, value=3, step=1, visible=True)
        category = gr.Radio(label="Category (optional)", choices=news.__CATEGORIES__, visible=True)
        get = gr.Button("Get Articles", visible=True)

        return num_headlines, category, get
    
    def show_headline(headlines, descriptions): ######## Show news headlines and descriptions
        headline = gr.Radio(label="News Headlines", choices=headlines, value=headlines[0], interactive=True, visible=True)
        description = gr.Textbox(label="Headline Description", value=descriptions[0], visible=True)
        show = gr.Button("Show Content", visible=True)
        
        return headline, description, show
    
    def show_content(content): ######################### Show article content and summary
        summarize = gr.Checkbox(label="Show Summary?", value=False, interactive=True, visible=True)
        content = gr.Textbox(label="Content", value=content, visible=True)
        ready = gr.Button("Begin Quiz", visible=True)
        
        return summarize, content, ready
    
    def format_mcq(mcq): ############################### Format multiple choice question for quiz
        if not mcq or not isinstance(mcq, dict):
            print(f"Multiple choice question object is a {type(mcq)} but should be {type(dict())}.")
            return "Invalid multiple choice question.", None
        
        question = mcq.get('question', 'No question provided.')
        answer = mcq.get('correct_answer', 'No correct answer provided.')
        false_answers = mcq.get('false_answers', [])
        
        if not isinstance(false_answers, list):
            print(f"False answers is a {type(false_answers)} but should be {type(list())}.")
            return "Invalid false answers format.", None
        
        options = random.shuffle([answer] + false_answers)
        
        print("Question:", question)
        print(f"Formatted options: {options}")
        
        return question, options, answer
    
    def hide_quiz(): ################################### Hide quiz-related components
        quiz = [gr.Radio(visible=False) for _ in range(10)]
        submit = gr.Button(visible=False)
        
        evaluation = gr.Textbox(visible=False)
        read = gr.Button(visible=False)

        return read, evaluation, submit, *quiz

    def show_quiz(mcqs): ############################### Show quiz-related components 
        quiz = [(mcq["question"], mcq["false_answers"], mcq["correct_answer"]) for mcq in mcqs]
        quiz = [(question, random.sample(distractors + [answer], 4), answer) for question, distractors, answer in quiz]
        questions, options, answers = zip(*quiz) if quiz else ([], [], [])
        
        print("options", len(options))
        
        quiz = [gr.Radio(label=f"{i + 1}: {questions[i]}", choices=options[i], visible=True) for i in range(len(mcqs))]\
             + [gr.Radio(visible=False) for _ in range(10 - len(mcqs))]
        
        print("quiz", len(quiz))
        
        submit = gr.Button("Submit Answers", interactive=bool(answers), visible=True)
        
        return submit, list(answers), *quiz
    
    def show_eval(eva): ################################ Show evaluation of user's response to the quiz
        evaluation = gr.Textbox(label="Evaluation", value=eva, visible=True)
        read = gr.Button("Read Articles", visible=True)
        
        return evaluation, read
    
    ######
    ###### Get and Display News Articles
    ######
    
    def get_headline(category, num_headlines): ######### Get news headlines based on selected category and number
        articles = news.get_top_headlines(category=category, num_headlines=num_headlines)
        headlines, descriptions = zip(*[(article['title'], article.get('description', 'No description available.')) for article in articles])
        show = show_headline(headlines, descriptions)
        descriptions = {h: d for h, d in zip(headlines, descriptions)}
        return articles, descriptions, *show
    
    get.click(get_headline, inputs=[category, num_headlines], outputs=[articles, descriptions, headline, description, show])
    
    def get_description(descriptions, headline): ####### Get description for the selected headline
        description = "No description available."
        
        if   not descriptions:                   print("Descriptions are empty.")
        elif not headline:                       print("Headline is empty.")
        elif not isinstance(descriptions, dict): print(f"Descriptions is a {type(descriptions)} but should be {type(dict())}.")
        else:                                    description = descriptions.get(headline, description)
        
        return description
    
    headline.change(get_description, inputs=[descriptions, headline], outputs=[description])
    
    def get_article(articles, headline): ############### Get article for the selected headline
        headlines = [a['title'] for a in articles]
        
        if headline not in headlines: return {}
        return articles[headlines.index(headline)]
    
    show.click(get_article, inputs=[articles, headline], outputs=[article])
    
    def get_content(article): ########################## Get content for the selected article
        return article, *show_content(article.get("content", "Content not available."))
    
    article.change(get_content, inputs=[article], outputs=[article, summarize, content, ready])
    
    def toggle_summary(articles, article, summarize): ## Toggle between showing summary and full content
        if not article:                     print("Selected article is empty.")
        elif not isinstance(article, dict): print(f"Selected article is a {type(article)} but should be {type(dict())}.")
        elif not summarize:                 return article.get("content", "Content not available.")
        elif "summary" not in article:
                                            idx = articles.index(article)
                                            articles[idx]["summary"] = model.get_summary(article, 1)
                                            article = articles[idx]
        
        return  article.get("summary", "Summary not available.")
    
    summarize.change(toggle_summary, inputs=[articles, article, summarize], outputs=[content])
    
    ######
    ###### Quiz Generation and Evaluation
    ######
    
    def get_quiz(content): ############################# Generate quiz questions from the article content
        multichoicequests = []
        
        if not content:                     mcqs = multichoicequests
        else:                               mcqs = model.get_questions(content, 3, "Moderate")
        
        if not isinstance(mcqs, list):      print(f"Multiple choice questions object is a {type(mcqs)} but should be {type(list())}.")
        elif len(mcqs) == 0:                print("Content is empty or no multiple choice questions generated.")
        
        for mcq in mcqs:
            missing = set()
            
            if not isinstance(mcq, dict):   print(f"Multiple choice question object is {type(mcq)} but should be {type(dict())}.")
            else:                           missing = set(['question', 'correct_answer', 'false_answers']) - set(mcq.keys())
            
            if missing:                     print(f"Multiple choice question object is missing keys: {missing}.")
            else:                           multichoicequests.append(mcq)
        
        return gr.Markdown("## News Quiz"), *hide_news(), *show_quiz(multichoicequests)
    
    ready.click(get_quiz, inputs=[content], outputs=[
        heading, num_headlines, category, get, headline, description, show, summarize, content, ready, submit, answers, *quiz])
    
    def get_evaluation(answers, *quiz): ################ Evaluate the user's responses to the quiz
        results = -1
        
        if not answers:                     print("Answers are empty.")
        elif not quiz:                      print("Quiz is empty.")
        elif not isinstance(answers, list): print(f"Answers is a {type(answers)} but should be {type(list())}.")
        else:                               
                                            results = sum(1 for ans, resp in zip(answers, list(quiz)) if ans == resp) / len(answers)
                                            results = round(results, 4)
                                            
        if 0.9 <= results <= 1.0:           evaluation = f"Excellent! You scored {results * 100}%."
        elif 0.8 <= results < 0.9:          evaluation = f"Great job! You scored {results * 100}%."
        elif 0.7 <= results < 0.8:          evaluation = f"Good effort! You scored {results * 100}%."
        elif 0.6 <= results < 0.7:          evaluation = f"You scored {results * 100}%. Keep practicing!"
        elif 0.5 <= results < 0.6:          evaluation = f"You scored {results * 100}%. You can do better!"
        elif results < 0:                   evaluation = f"Unable to evaluate. Please try again."
        else:                               evaluation = f"You scored {results * 100}%. Keep trying!"
        
        return show_eval(evaluation)
    
    submit.click(get_evaluation, inputs=[answers, *quiz], outputs=[evaluation, read])
    
    def read_articles(): ############################### Reset the interface to read articles again
        return gr.Markdown("## News Articles"), *show_news(), *hide_quiz()
    
    read.click(read_articles, outputs=[heading, num_headlines, category, get, read, evaluation, submit, *quiz])
    
demo.launch()