tommymarto's picture
Update demo.py
7fcb63d
raw
history blame
19.3 kB
import datetime
import os
import json
import gradio as gr
import requests
import firebase_admin
from firebase_admin import db, credentials
def clamp(x, minimum, maximum):
return max(minimum, min(x, maximum))
#################################################################################################################################################
# API calls
#################################################################################################################################################
# read secret api key
API_KEY = os.environ['ApiKey']
FIREBASE_API_KEY = os.environ['FirebaseSecret']
FIREBASE_URL = os.environ['FirebaseURL']
creds = credentials.Certificate(json.loads(FIREBASE_API_KEY))
firebase_app = firebase_admin.initialize_app(creds, {'databaseURL': FIREBASE_URL})
firebase_data_ref = db.reference("data")
firebase_current_ref = None
base_url = "https://skapi.polyglot-edu.com/"
levels = ["Primary School", "Middle School", "High School", "College", "Academy"]
languages = ["English", "Italian", "French", "German", "Spanish"]
categories = ["Factual Knowledge", "Understanding of Concepts", "Application of Skills", "Analysys And Evaluation"]
def get_level_mapping(level):
if level is None:
raise gr.Error("Please select a level.")
return levels.index(level)
def get_category_mapping(category):
if category is None:
raise gr.Error("Please select a level.")
return categories.index(category)
def generate_fill_gaps(original_text, level, number_of_words, number_of_gaps, number_of_distractors, temperature, language):
"""
Generate a fill-gaps question from a given text.
Parameters
----------
original_text : str
The original text from which to generate the fill-gaps question.
number_of_words : int
The number of words of the generated text.
number_of_gaps : int
The number of gaps to generate.
number_of_distractors : int
The number of distractors to generate for each gap.
temperature : float
The temperature for the generation.
language : str
The language of the generated text.
Returns
-------
str
The fill-gaps question.
"""
match number_of_words:
case "β‰ˆ 150 Words":
number_of_words = 150
case "β‰ˆ 250 Words":
number_of_words = 250
case "β‰ˆ 350 Words":
number_of_words = 350
input_json = {
"text": original_text,
"level": get_level_mapping(level),
"n_o_w": int(number_of_words),
"n_o_g": int(number_of_gaps),
"n_o_d": int(number_of_distractors),
"temperature": int(temperature) * 0.2,
"language": language
}
print(input_json)
output = ""
try:
response = requests.post(
base_url + "FillTheGaps/generateexercise",
headers={"ApiKey": API_KEY},
json=input_json,
timeout=20
)
if response.status_code != 200:
output = f"API call failed with status code {response.status_code} and message '{response.text}'"
raise Exception(f"API call failed with status code {response.status_code} and message '{response.text}'")
output = response.text
return output
finally:
global firebase_current_ref
firebase_current_ref = firebase_data_ref.push({
"type": "fill_the_gaps",
**input_json,
"output": output,
"datetime": str(datetime.datetime.now()),
"like": 0,
"comment_text": "",
"flagged": False,
})
def generate_open_question(original_text, level, temperature, language, question_type, question_category):
"""
Generate an open question from a given text.
Parameters
----------
original_text : str
The original text from which to generate the open question.
temperature : float
The temperature for the generation.
language : str
The language of the generated text.
question_type : str
The type of the question.
question_category : str
The category of the question.
Returns
-------
str
The open question.
"""
input_json = {
"text": original_text,
"level": get_level_mapping(level),
"temperature": int(temperature) * 0.2,
"language": language,
"type": question_type,
"category": get_category_mapping(question_category),
}
print(input_json)
output = ""
try:
response = requests.post(
base_url + "QuestionExercise/generateexercise",
headers={"ApiKey": API_KEY},
json=input_json,
timeout=20
)
if response.status_code != 200:
output = f"API call failed with status code {response.status_code} and message '{response.text}'"
raise Exception(f"API call failed with status code {response.status_code} and message '{response.text}'")
output = response.text
return output
finally:
global firebase_current_ref
firebase_current_ref = firebase_data_ref.push({
"type": "open_question",
**input_json,
"output": output,
"datetime": str(datetime.datetime.now()),
"like": 0,
"comment_text": "",
"flagged": False,
})
def generate_multiplechoice(original_text, level, number_of_easy_distractors, number_of_distractors, temperature, language, question_category, correct_answers, exercise_type):
"""
Generate a multiple-choice question from a given text.
Parameters
----------
original_text : str
The original text from which to generate the multiple-choice question.
number_of_easy_distractors : int
The number of easy distractors to generate for each option.
number_of_distractors : int
The number of distractors to generate for each option.
temperature : float
The temperature for the generation.
language : str
The language of the generated text.
question_category : str
The category of the question.
correct_answers : int
The number of correct answers.
exercise_type : bool
The type of the exercise (theory or practice).
Returns
-------
str
The multiple-choice question.
"""
input_json = {
"text": original_text,
"type": bool(exercise_type),
"level": get_level_mapping(level),
"category": get_category_mapping(question_category),
"n_o_ca": int(correct_answers),
"n_o_d": int(number_of_distractors),
"nedd": int(number_of_easy_distractors),
"temperature": int(temperature) * 0.2,
"language": language
}
print(input_json)
output = ""
try:
response = requests.post(
base_url + "QuizExercise/generateexercise",
headers={"ApiKey": API_KEY},
json=input_json,
timeout=20
)
if response.status_code != 200:
output = f"API call failed with status code {response.status_code} and message '{response.text}'"
raise Exception(f"API call failed with status code {response.status_code} and message '{response.text}'")
output = response.text
return output
finally:
global firebase_current_ref
firebase_current_ref = firebase_data_ref.push({
"type": "open_question",
**input_json,
"output": output,
"datetime": str(datetime.datetime.now()),
"like": 0,
"comment_text": "",
"flagged": False,
})
def like():
global firebase_current_ref
if firebase_current_ref is not None:
firebase_current_ref.update({"like": 1})
gr.Info("Generated text liked.")
else:
gr.Warning("No generated text to vote.")
def neutral_like():
global firebase_current_ref
if firebase_current_ref is not None:
firebase_current_ref.update({"like": 0})
gr.Info("Generated text preference removed.")
else:
gr.Warning("No generated text to vote.")
def dislike():
global firebase_current_ref
if firebase_current_ref is not None:
firebase_current_ref.update({"like": -1})
gr.Info("Generated text disliked.")
else:
gr.Warning("No generated text to vote.")
def comment(comment_text):
global firebase_current_ref
if firebase_current_ref is not None:
firebase_current_ref.update({"comment": comment_text})
gr.Info("Comment added.")
else:
raise gr.Error("No generated text to comment.")
def flag(flag_btn):
global firebase_current_ref
if firebase_current_ref is not None:
firebase_current_ref.update({"flagged": not firebase_current_ref.get()["flagged"]})
if firebase_current_ref.get()["flagged"]:
gr.Info("Generated text flagged.")
value = "⚠️ Unflag"
else:
gr.Info("Generated text unflagged.")
value = "⚠️ Flag"
return gr.Button(value=value)
else:
gr.Warning("No generated text to flag.")
return flag_btn
#################################################################################################################################################
# Interface building
#################################################################################################################################################
def build_fill_gaps_interface():
"""
Build the fill-gaps interface.
"""
with gr.Blocks(title="Fill-gaps") as demo:
with gr.Row():
with gr.Column(scale=5):
input_field = gr.TextArea(lines=10, max_lines=10, label="Input Educational resource text or URL",
info="Note: If the input resource is too long, the API will return an error. If the input is an URL, it must be a valid URL to webpage or a file.")
submit_btn = gr.Button(value="Submit")
with gr.Column(scale=4):
level = gr.Radio(levels, label="Level")
language = gr.Dropdown(languages, label="Language", value="English")
output_text_length = gr.Radio(["β‰ˆ 150 Words", "β‰ˆ 250 Words", "β‰ˆ 350 Words"], label="Output text length", value="β‰ˆ 150 Words")
with gr.Row():
blanks = gr.Number(value=5, minimum=4, maximum=8, step=1, label="Number of blanks")
distractors = gr.Number(value=2, minimum=0, maximum=5, step=1, label="Number of distractors")
temperature = gr.Checkbox(value=False, label="Increase creativity (decreases preciseness)", visible=False)
def update_numeric(output_text_length, blanks, distractors):
if output_text_length == "β‰ˆ 150 Words":
min_, max_ = 4, 8
elif output_text_length == "β‰ˆ 250 Words":
min_, max_ = 6, 10
elif output_text_length == "β‰ˆ 350 Words":
min_, max_ = 8, 12
return (
gr.Number(value=clamp(blanks, min_, max_), minimum=min_, maximum=max_, label="Number of blanks"),
gr.Number(value=clamp(distractors, 0, blanks), minimum=0, maximum=blanks, label="Number of distractors")
)
def update_blanks(blanks, distractors):
return gr.Number(value=clamp(distractors, 0, blanks), minimum=0, maximum=blanks, label="Number of distractors")
blanks.change(update_blanks, [blanks, distractors], [distractors])
output_text_length.change(update_numeric, [output_text_length, blanks, distractors], [blanks, distractors])
with gr.Row():
output = gr.TextArea(placeholder="Generated text", label="Output")
with gr.Row() as button_row:
upvote_btn = gr.Button(value="πŸ‘ Upvote")
remove_preference_btn = gr.Button(value="Remove Preference")
downvote_btn = gr.Button(value="πŸ‘Ž Downvote")
flag_btn = gr.Button(value="⚠️ Flag")
submit_btn.click(generate_fill_gaps, [input_field, level, output_text_length, blanks, distractors, temperature, language], [output])
upvote_btn.click(like)
remove_preference_btn.click(neutral_like)
downvote_btn.click(dislike)
flag_btn.click(flag, [flag_btn], [flag_btn])
return demo
def build_multiplechoice_interface():
"""
Build the open question interface.
"""
with gr.Blocks(title="Open Question") as demo:
with gr.Row():
with gr.Column(scale=5):
input_field = gr.TextArea(lines=10, max_lines=10, label="Input Educational resource text or URL",
info="Note: If the input resource is too long, the API will return an error. If the input is an URL, it must be a valid URL to webpage or a file.")
submit_btn = gr.Button(value="Submit")
with gr.Column(scale=4):
level = gr.Radio(levels, label="Level")
language = gr.Dropdown(languages, label="Language", value="English")
with gr.Row():
exercise_type = gr.Radio(["Theoretical", "Practical"], label="Exercise type", value="Theoretical", type="index")
question_category = gr.Dropdown(categories, label="Question Category", value="Factual Knowledge")
with gr.Row():
correct_answers = gr.Number(value=1, minimum=1, maximum=3, step=1, label="Number of correct answers")
easy_distractors = gr.Number(value=1, minimum=0, maximum=8, step=1, label="Number of easy distractors")
distractors = gr.Number(value=1, minimum=0, maximum=8, step=1, label="Number of distractors")
temperature = gr.Checkbox(value=False, label="Increase creativity (decreases preciseness)", visible=False)
def update_distractors(easy_distractors, distractors):
easy_distractors = clamp(easy_distractors, 0, distractors)
return gr.Number(value=easy_distractors, minimum=0, maximum=distractors, label="Number of easy distractors")
def update_exercise_type(exercise_type, question_category, number_of_correct_answers):
if exercise_type == 0:
return (
gr.Dropdown(categories, label="Question Category", value="Analysys And Evaluation"),
gr.Number(value=number_of_correct_answers, minimum=1, maximum=3, step=1, label="Number of correct answers")
)
elif exercise_type == 1:
return (
gr.Dropdown(["Analysys And Evaluation"], label="Question Category", value="Analysys And Evaluation"),
gr.Number(value=1, minimum=1, maximum=1, step=1, label="Number of correct answers")
)
distractors.change(update_distractors, [easy_distractors, distractors], [easy_distractors])
exercise_type.change(update_exercise_type, [exercise_type, question_category, correct_answers], [question_category, correct_answers])
with gr.Row():
output = gr.TextArea(placeholder="Generated text", label="Output")
with gr.Row() as button_row:
upvote_btn = gr.Button(value="πŸ‘ Upvote")
remove_preference_btn = gr.Button(value="Remove Preference")
downvote_btn = gr.Button(value="πŸ‘Ž Downvote")
flag_btn = gr.Button(value="⚠️ Flag")
submit_btn.click(generate_multiplechoice, [input_field, level, easy_distractors, distractors, temperature, language, question_category, correct_answers, exercise_type], [output])
upvote_btn.click(like)
remove_preference_btn.click(neutral_like)
downvote_btn.click(dislike)
flag_btn.click(flag, [flag_btn], [flag_btn])
return demo
def build_open_question_interface():
"""
Build the multiple-choice interface.
"""
with gr.Blocks(title="Multiple choice") as demo:
with gr.Row():
with gr.Column(scale=5):
input_field = gr.TextArea(lines=10, max_lines=10, label="Input Educational resource text or URL",
info="Note: If the input resource is too long, the API will return an error. If the input is an URL, it must be a valid URL to webpage or a file.")
submit_btn = gr.Button(value="Submit")
with gr.Column(scale=4):
level = gr.Radio(levels, label="Level")
language = gr.Dropdown(languages, label="Language", value="English")
with gr.Row():
question_type = gr.Dropdown(["Open", "ShortAnswer", "TrueFalse"], label="Question Type", value="Open", type="index")
question_category = gr.Dropdown(categories, label="Question Category", value="Factual Knowledge")
temperature = gr.Checkbox(value=False, label="Increase creativity (decreases preciseness)", visible=False)
with gr.Row():
output = gr.TextArea(placeholder="Generated text", label="Output")
with gr.Row() as button_row:
upvote_btn = gr.Button(value="πŸ‘ Upvote")
remove_preference_btn = gr.Button(value="Remove Preference")
downvote_btn = gr.Button(value="πŸ‘Ž Downvote")
flag_btn = gr.Button(value="⚠️ Flag")
submit_btn.click(generate_open_question, [input_field, level, temperature, language, question_type, question_category], [output])
upvote_btn.click(like)
remove_preference_btn.click(neutral_like)
downvote_btn.click(dislike)
flag_btn.click(flag, [flag_btn], [flag_btn])
return demo
def build_demo():
with gr.Blocks(title="Educational AI") as demo:
gr.Markdown("<h1 style='text-align: center; margin-bottom: 1rem'>Educational AI</h1>")
with gr.Row():
gr.Markdown("<h4>Click on the button on the right to fill up our questionnaire!</h4>")
gr.Button(value="πŸ“ Questionnaire", link="https://forms.gle/T8CS5CiQgPbKUdeM9", scale=0.5, interactive=True)
with gr.Tab("Fill-gaps"):
build_fill_gaps_interface()
with gr.Tab("Open question"):
build_open_question_interface()
with gr.Tab("Multiple-choice"):
build_multiplechoice_interface()
with gr.Blocks():
with gr.Row():
comment_text = gr.TextArea(placeholder="Comment", label="Comment", scale=6)
comment_btn = gr.Button(value="πŸ’¬ Comment", scale=2)
comment_btn.click(comment, [comment_text])
return demo
if __name__ == "__main__":
build_demo().launch(share=False)