CEC-Learning / app.py
Jeff Myers II
Replaced Gemma for OpenAI API and commented out visualize_quiz_answers (seems to be breaking the script).
54e639b
# %%
import random
import gradio as gr
from News import News
from Gemma import GemmaLLM
# import matplotlib.pyplot as plt
# %%
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("# Reading & Quiz Application")
######
###### State Variables and Components Initialization
######
init = ( ########################################### Initialize the Gradio interface with components and state variables
gr.Markdown("## Read 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=10, 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=10, 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="Select an Article", choices=headlines, value=headlines[0], interactive=True, visible=True)
description = gr.Textbox(label="Article 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)
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 ([], [], [])
quiz = [gr.Radio(label=f"{i + 1}: {questions[i]}", choices=options[i], visible=True) for i in range(len(mcqs))]
quiz += [gr.Radio(visible=False) for _ in range(10 - len(mcqs))]
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("## 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, response, evaluation = 0, list(quiz), ""
if not answers: return "Answers are empty."
elif not quiz: return "Quiz is empty."
elif not isinstance(answers, list): return f"Answers is a {type(answers)} but should be {type(list())}."
for i, (ans, resp) in enumerate(zip(answers, response)):
if ans == resp:
results += 1
evaluation += f"\n\t{i + 1}: Correct!"
else: evaluation += f"\n\t{i + 1}: Incorrect."
evaluation = f"You got {results} out of {len(answers)} correct.\n" + evaluation
results /= len(answers)
if 0.9 <= results <= 1.0: evaluation = f"Excellent! " + evaluation
elif 0.8 <= results < 0.9: evaluation = f"Great job! " + evaluation
elif 0.7 <= results < 0.8: evaluation = f"Good effort! " + evaluation
elif 0.6 <= results < 0.7: evaluation = f"Keep practicing! " + evaluation
elif 0.5 <= results < 0.6: evaluation = f"You can do better! " + evaluation
else: evaluation = f"Keep trying! " + evaluation
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()
# def visualize_quiz_answers(answers, *quiz_items):
# """
# Visualization of correct/incorrect answers from the quiz
# """
# if not answers:
# return None
# correct = 0
# for user_ans, question in zip(answers, quiz_items):
# if user_ans == question["correct_answer"]:
# correct += 1
# incorrect = len(answers) - correct
# fig, ax = plt.subplots()
# ax.bar(["Correct", "Incorrect"], [correct, incorrect])
# ax.set_ylabel("Questions")
# ax.set_title("Quiz Score Summary")
# return fig