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"] def get_level_mapping(level): if level is None: raise gr.Error("Please select a level.") return { "Primary School": 0, "Middle School": 1, "High School": 2, "College": 3, "Academy": 4, }[level] 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 } 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, "question_type": question_type, "question_category": question_category } 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): """ 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. Returns ------- str The multiple-choice question. """ input_json = { "text": original_text, "type": True, "level": get_level_mapping(level), "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(): 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") distractors.change(update_distractors, [easy_distractors, distractors], [easy_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_multiplechoice, [input_field, level, easy_distractors, 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_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( ["Factual Knowledge", "Understanding of Concepts", "Application of Skills", "Analysys And Evaluation"], label="Question Category", value="Factual Knowledge", type="index") 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("