Spaces:
Running
Running
from __future__ import annotations | |
from pathlib import Path | |
import gradio as gr | |
from typing import Any, Callable | |
import contextvars | |
from uw_programmatic.uw_machine import UWMachine | |
from uw_programmatic.csv_to_markdown import convert_csv_to_markdown | |
import pandas as pd | |
from dotenv import load_dotenv | |
import os | |
# Used to ensure that EventBus works across Gradio and the StateMachine. | |
def run_with_context(func: Callable) -> Callable: | |
ctx = contextvars.copy_context() | |
def wrapper(*args, **kwargs) -> Any: | |
return ctx.run(func, *args, **kwargs) | |
return wrapper | |
# Runs the state machine - sends the parameters over and kicks off the whole process. | |
def generate_questions( | |
page_lower, page_higher, question_number, taxonomy | |
) -> tuple[str, gr.DownloadButton, gr.DownloadButton]: | |
if machine.value and machine.value.current_state_value == "start": | |
machine.value.start_machine() # Start the machine! | |
if not question_number or question_number <= 0: | |
msg = "Choose a valid question number." | |
raise gr.Error(msg) | |
if not page_lower or not page_higher or page_higher < page_lower: | |
msg = "Choose a valid page range." | |
raise gr.Error(msg) | |
if page_higher - page_lower <= 6: | |
msg = "Page range must be >6." | |
raise gr.Error(msg) | |
if not taxonomy or len(taxonomy) == 0: | |
msg = "Choose at least one taxonomy." | |
raise gr.Error(msg) | |
try: | |
machine.value.send( | |
"process_event", | |
event_={ | |
"type": "user_input", | |
"value": { | |
"page_range": (page_lower, page_higher), | |
"question_number": question_number, | |
"taxonomy": taxonomy, | |
}, | |
}, | |
) | |
except AttributeError: | |
create_statemachine() | |
machine.value.start_machine() | |
machine.value.send( | |
"process_event", | |
event_={ | |
"type": "user_input", | |
"value": { | |
"page_range": (page_lower, page_higher), | |
"question_number": question_number, | |
"taxonomy": taxonomy, | |
}, | |
}, | |
) | |
button = gr.DownloadButton( | |
label="Download Questions", | |
visible=True, | |
scale=1, | |
value=f"{Path.cwd().joinpath('outputs/professor_guide.csv')}", | |
) | |
button_2 = gr.DownloadButton( | |
visible=True, | |
value=f"{Path.cwd().joinpath('outputs/rejected_list.csv')}", | |
scale=1, | |
label="Download Rejected Questions", | |
) | |
markdown = "## Questions Ready for Download Below" | |
if machine.value.errored: | |
button = gr.update(visible=False) | |
button_2 = gr.update(visible=False) | |
markdown = "## Questions Not Ready for Download" | |
exception = "Bad Page Range Selected" | |
raise gr.Error(exception) | |
# After questions are generated, read the CSV file | |
csv_path = Path.cwd().joinpath("outputs/professor_guide.csv") | |
if csv_path.exists(): | |
checkFrame = pd.read_csv(csv_path, usecols=[0], header=None) | |
checkFrame.columns = ["Check"] | |
if checkFrame.iloc[0]["Check"] != "Failed to generate more questions." and checkFrame.iloc[0]["Check"] != "Too many rejected questions.": | |
# Generate markdown content | |
markdown_content = convert_csv_to_markdown(csv_path) | |
return ( | |
markdown, | |
button, | |
button_2, | |
gr.update(value=markdown_content, visible=True) | |
) | |
else: | |
return (markdown, button, button_2, gr.update(visible=False)) | |
else: | |
return (markdown, button, button_2, gr.update(visible=False)) | |
def create_statemachine() -> None: | |
# Creates UWMachine from the config.yaml in current directory | |
cwd_path = Path.cwd() / "uw_programmatic" | |
config_path = cwd_path.joinpath(Path("config.yaml")) | |
try: | |
machine.value = UWMachine.from_config_file(config_path) | |
except Exception as e: | |
raise gr.Error(str(e)) from e | |
def questions_downloaded() -> dict[str, Any]: | |
return gr.update(visible=False) | |
with gr.Blocks() as demo: | |
gr.Markdown("# UW Quiz Generator") | |
machine = gr.State(value=None) | |
with gr.Row(): | |
with gr.Column(scale=2): | |
taxonomy = gr.CheckboxGroup( | |
choices=["Knowledge", "Comprehension", "Application"], | |
label="Taxonomy", | |
value="Knowledge", | |
) | |
question_number = gr.Number( | |
minimum=1, maximum=25, label="Number of Questions", value=3 | |
) | |
gr.Markdown("For Textbook - Pages 1-348") | |
with gr.Row(): | |
page_lower = gr.Number( | |
label="First Page", minimum=1, value=1, maximum=348 | |
) | |
page_higher = gr.Number( | |
label="Last Page", minimum=1, value=348, maximum=348 | |
) | |
start_button = gr.Button(value="Generate Questions", scale=1) | |
with gr.Column(scale=1): | |
output = gr.Markdown("## Questions Not Ready for Download", visible=True) | |
download_professor = gr.DownloadButton( | |
label="Download Questions", visible=False, scale=1 | |
) | |
download_failures = gr.DownloadButton( | |
label="Download Failed Questions", visible=False, scale=1 | |
) | |
with gr.Row(): | |
#Markdown component to display generated questions as an ordered list | |
question_output = gr.Markdown(visible=False) | |
# State machine must be initialized in gr.Blocks for the context wrapper to work. | |
create_statemachine() | |
start_button.click( | |
fn=run_with_context(generate_questions), | |
inputs=[page_lower, page_higher, question_number, taxonomy], | |
outputs=[output, download_professor, download_failures, question_output], | |
) | |
download_professor.click( | |
fn=run_with_context(questions_downloaded), outputs=[download_professor] | |
) | |
download_failures.click( | |
fn=run_with_context(questions_downloaded), outputs=[download_failures] | |
) | |
load_dotenv() | |
demo.launch( | |
share=True, | |
ssr_mode=False, | |
auth=(os.environ.get("HF_USERNAME", ""), os.environ.get("HF_PASSWORD", "")), | |
) | |
#demo.launch() | |