Spaces:
Running
Running
mah-diaa
commited on
Commit
Β·
ec833a4
1
Parent(s):
165d9b8
Remove right sidebar, add quiz popup modal, fix quiz generation
Browse files
app.py
CHANGED
|
@@ -15,7 +15,9 @@ user_state = {
|
|
| 15 |
"key_points": [],
|
| 16 |
"document_parsed": False,
|
| 17 |
"learning_style": None,
|
| 18 |
-
"parsed_data": None
|
|
|
|
|
|
|
| 19 |
}
|
| 20 |
|
| 21 |
|
|
@@ -124,29 +126,24 @@ For example: "I want to learn about binary search" or "Teach me recursion" """
|
|
| 124 |
try:
|
| 125 |
questions = generate_quiz(
|
| 126 |
concepts=user_state["concepts"],
|
| 127 |
-
topics=user_state
|
| 128 |
num_questions=10,
|
| 129 |
question_types=["mcq"],
|
| 130 |
document_context=user_state.get("parsed_data", {}).get("text_preview", "")
|
| 131 |
)
|
| 132 |
|
| 133 |
if questions:
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
if q.get('type') == 'mcq' and 'options' in q:
|
| 138 |
-
for j, opt in enumerate(q['options']):
|
| 139 |
-
quiz_text += f" {chr(65+j)}. {opt}\n"
|
| 140 |
-
quiz_text += f"\n*Answer: {q.get('explanation', '')[:100]}...*\n\n---\n\n"
|
| 141 |
|
| 142 |
-
|
| 143 |
-
quiz_text += f"\n*... and {len(questions) - 5} more questions!*\n\n"
|
| 144 |
-
|
| 145 |
-
response = quiz_text + "\n\nWould you like to:\n- See all questions?\n- Take the quiz interactively?\n- Generate more questions?"
|
| 146 |
else:
|
| 147 |
response = "β οΈ I had trouble generating questions. Could you try again or provide more specific concepts?"
|
| 148 |
except Exception as e:
|
| 149 |
-
|
|
|
|
|
|
|
| 150 |
|
| 151 |
elif any(word in message_lower for word in ["curriculum", "course", "path", "learn step", "progressive"]):
|
| 152 |
if not user_state["concepts"]:
|
|
@@ -488,8 +485,8 @@ with gr.Blocks(title="Gnosis Tutor") as app:
|
|
| 488 |
**Powered by MCP Server Integration**
|
| 489 |
""")
|
| 490 |
|
| 491 |
-
# Center - Main Chat Area
|
| 492 |
-
with gr.Column(scale=
|
| 493 |
gr.Markdown("""
|
| 494 |
<div class="main-chat-area">
|
| 495 |
<h2 style="margin-top: 0; color: #667eea; text-align: center;">π¬ Chat with Gnosis</h2>
|
|
@@ -512,47 +509,36 @@ with gr.Blocks(title="Gnosis Tutor") as app:
|
|
| 512 |
textbox=gr.MultimodalTextbox(file_types=[".pdf"])
|
| 513 |
)
|
| 514 |
|
| 515 |
-
#
|
| 516 |
-
|
| 517 |
-
|
| 518 |
-
|
| 519 |
-
|
| 520 |
-
|
| 521 |
-
|
| 522 |
-
|
| 523 |
-
)
|
| 524 |
-
|
| 525 |
-
resource_output = gr.Markdown(label="Resources")
|
| 526 |
|
| 527 |
-
|
| 528 |
-
gr.Markdown("**Create Learning Path:**")
|
| 529 |
-
curriculum_concept = gr.Textbox(
|
| 530 |
-
label="Concept:",
|
| 531 |
-
placeholder="e.g., recursion"
|
| 532 |
-
)
|
| 533 |
-
curriculum_lang = gr.Dropdown(
|
| 534 |
-
choices=["python", "javascript", "typescript"],
|
| 535 |
-
value="python",
|
| 536 |
-
label="Language"
|
| 537 |
-
)
|
| 538 |
-
curriculum_btn = gr.Button("π Generate", variant="primary")
|
| 539 |
-
curriculum_output = gr.Markdown(label="Curriculum")
|
| 540 |
|
| 541 |
-
with gr.
|
| 542 |
-
gr.Markdown(""
|
| 543 |
-
|
| 544 |
-
|
| 545 |
-
|
| 546 |
-
|
| 547 |
-
|
| 548 |
-
|
| 549 |
-
|
| 550 |
-
|
| 551 |
-
|
| 552 |
-
|
| 553 |
-
|
| 554 |
-
|
| 555 |
-
|
|
|
|
|
|
|
| 556 |
|
| 557 |
# Handle file upload separately to update sidebars
|
| 558 |
def handle_file_upload(file):
|
|
@@ -670,6 +656,120 @@ with gr.Blocks(title="Gnosis Tutor") as app:
|
|
| 670 |
except Exception as e:
|
| 671 |
return f"β Error: {str(e)}"
|
| 672 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 673 |
# Connect file upload to update sidebars
|
| 674 |
file_upload.change(
|
| 675 |
handle_file_upload,
|
|
@@ -677,12 +777,45 @@ with gr.Blocks(title="Gnosis Tutor") as app:
|
|
| 677 |
outputs=[concepts_display, topics_display, key_points_display]
|
| 678 |
)
|
| 679 |
|
|
|
|
| 680 |
quick_quiz_btn.click(quick_generate_quiz, None, [quick_output])
|
| 681 |
quick_curriculum_btn.click(quick_generate_curriculum, None, [quick_output])
|
| 682 |
quick_resources_btn.click(quick_find_resources, None, [quick_output])
|
| 683 |
|
| 684 |
-
|
| 685 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 686 |
|
| 687 |
|
| 688 |
if __name__ == "__main__":
|
|
|
|
| 15 |
"key_points": [],
|
| 16 |
"document_parsed": False,
|
| 17 |
"learning_style": None,
|
| 18 |
+
"parsed_data": None,
|
| 19 |
+
"current_quiz": None, # Store generated quiz
|
| 20 |
+
"show_quiz_modal": False # Control quiz popup visibility
|
| 21 |
}
|
| 22 |
|
| 23 |
|
|
|
|
| 126 |
try:
|
| 127 |
questions = generate_quiz(
|
| 128 |
concepts=user_state["concepts"],
|
| 129 |
+
topics=user_state.get("topics", []),
|
| 130 |
num_questions=10,
|
| 131 |
question_types=["mcq"],
|
| 132 |
document_context=user_state.get("parsed_data", {}).get("text_preview", "")
|
| 133 |
)
|
| 134 |
|
| 135 |
if questions:
|
| 136 |
+
# Store quiz in state
|
| 137 |
+
user_state["current_quiz"] = questions
|
| 138 |
+
user_state["show_quiz_modal"] = True
|
|
|
|
|
|
|
|
|
|
|
|
|
| 139 |
|
| 140 |
+
response = f"β
**Quiz Generated!** ({len(questions)} questions)\n\nI've created a quiz based on your concepts. Check the quiz panel on the right! π\n\nYou can:\n- Take the quiz interactively\n- Review all questions\n- Generate more questions"
|
|
|
|
|
|
|
|
|
|
| 141 |
else:
|
| 142 |
response = "β οΈ I had trouble generating questions. Could you try again or provide more specific concepts?"
|
| 143 |
except Exception as e:
|
| 144 |
+
import traceback
|
| 145 |
+
error_details = traceback.format_exc()
|
| 146 |
+
response = f"β Error generating quiz: {str(e)}\n\nDebug info: {error_details[:200]}"
|
| 147 |
|
| 148 |
elif any(word in message_lower for word in ["curriculum", "course", "path", "learn step", "progressive"]):
|
| 149 |
if not user_state["concepts"]:
|
|
|
|
| 485 |
**Powered by MCP Server Integration**
|
| 486 |
""")
|
| 487 |
|
| 488 |
+
# Center - Main Chat Area (full width now)
|
| 489 |
+
with gr.Column(scale=4):
|
| 490 |
gr.Markdown("""
|
| 491 |
<div class="main-chat-area">
|
| 492 |
<h2 style="margin-top: 0; color: #667eea; text-align: center;">π¬ Chat with Gnosis</h2>
|
|
|
|
| 509 |
textbox=gr.MultimodalTextbox(file_types=[".pdf"])
|
| 510 |
)
|
| 511 |
|
| 512 |
+
# Quiz Popup Modal (shown when quiz is generated) - positioned as floating panel
|
| 513 |
+
quiz_data_state = gr.State(value=None)
|
| 514 |
+
current_question_idx = gr.State(value=0)
|
| 515 |
+
quiz_score_state = gr.State(value={"correct": 0, "total": 0})
|
| 516 |
+
|
| 517 |
+
with gr.Column(visible=True) as quiz_modal:
|
| 518 |
+
with gr.Box(elem_classes="quiz-modal", elem_id="quiz-modal-box"):
|
| 519 |
+
with gr.Row():
|
| 520 |
+
gr.Markdown("## π Interactive Quiz", elem_id="quiz-title")
|
| 521 |
+
quiz_close_btn = gr.Button("β Close", variant="stop", size="sm", scale=0)
|
|
|
|
| 522 |
|
| 523 |
+
quiz_status = gr.Markdown("**No quiz available yet. Ask me to generate a quiz!**")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 524 |
|
| 525 |
+
with gr.Row():
|
| 526 |
+
quiz_question_num = gr.Markdown("**Question 1 of 10**")
|
| 527 |
+
quiz_score_display = gr.Markdown("**Score: 0/0**")
|
| 528 |
+
|
| 529 |
+
quiz_question_text = gr.Markdown("", label="")
|
| 530 |
+
quiz_options = gr.Radio(label="Select your answer", choices=[], interactive=True, visible=False)
|
| 531 |
+
quiz_submit_btn = gr.Button("Submit Answer", variant="primary", visible=False)
|
| 532 |
+
|
| 533 |
+
quiz_feedback = gr.Markdown(label="", visible=False)
|
| 534 |
+
quiz_explanation = gr.Markdown(label="", visible=False)
|
| 535 |
+
|
| 536 |
+
with gr.Row():
|
| 537 |
+
quiz_prev_btn = gr.Button("β Previous", variant="secondary", visible=False)
|
| 538 |
+
quiz_next_btn = gr.Button("Next β", variant="secondary", visible=False)
|
| 539 |
+
|
| 540 |
+
quiz_complete = gr.Markdown(label="", visible=False)
|
| 541 |
+
quiz_restart_btn = gr.Button("π Start Over", variant="primary", visible=False)
|
| 542 |
|
| 543 |
# Handle file upload separately to update sidebars
|
| 544 |
def handle_file_upload(file):
|
|
|
|
| 656 |
except Exception as e:
|
| 657 |
return f"β Error: {str(e)}"
|
| 658 |
|
| 659 |
+
# Quiz modal functions
|
| 660 |
+
def load_quiz_to_modal():
|
| 661 |
+
"""Load quiz from state into modal."""
|
| 662 |
+
quiz = user_state.get("current_quiz")
|
| 663 |
+
if not quiz:
|
| 664 |
+
return (
|
| 665 |
+
gr.update(visible=True, value="**No quiz available yet. Ask me to generate a quiz!**"),
|
| 666 |
+
gr.update(visible=False),
|
| 667 |
+
gr.update(visible=False),
|
| 668 |
+
gr.update(visible=False),
|
| 669 |
+
gr.update(visible=False),
|
| 670 |
+
gr.update(visible=False),
|
| 671 |
+
gr.update(visible=False),
|
| 672 |
+
gr.update(visible=False),
|
| 673 |
+
gr.update(visible=False),
|
| 674 |
+
gr.update(visible=False),
|
| 675 |
+
gr.update(value=0),
|
| 676 |
+
gr.update(value={"correct": 0, "total": 0}),
|
| 677 |
+
gr.update(value=quiz)
|
| 678 |
+
)
|
| 679 |
+
|
| 680 |
+
# Show first question
|
| 681 |
+
return display_question(quiz, 0, {"correct": 0, "total": 0}, quiz)
|
| 682 |
+
|
| 683 |
+
def display_question(quiz_data, idx, score, _):
|
| 684 |
+
"""Display a question in the quiz modal."""
|
| 685 |
+
if not quiz_data or idx >= len(quiz_data):
|
| 686 |
+
return (
|
| 687 |
+
gr.update(visible=True, value="**Quiz complete!**"),
|
| 688 |
+
gr.update(visible=False),
|
| 689 |
+
gr.update(visible=False),
|
| 690 |
+
gr.update(visible=False),
|
| 691 |
+
gr.update(visible=False),
|
| 692 |
+
gr.update(visible=False),
|
| 693 |
+
gr.update(visible=False),
|
| 694 |
+
gr.update(visible=False),
|
| 695 |
+
gr.update(visible=False),
|
| 696 |
+
gr.update(visible=False),
|
| 697 |
+
gr.update(value=idx),
|
| 698 |
+
gr.update(value=score),
|
| 699 |
+
gr.update(value=quiz_data)
|
| 700 |
+
)
|
| 701 |
+
|
| 702 |
+
q = quiz_data[idx]
|
| 703 |
+
question_text = f"**{q.get('question', '')}**"
|
| 704 |
+
question_num = f"**Question {idx + 1} of {len(quiz_data)}**"
|
| 705 |
+
score_text = f"**Score: {score['correct']}/{score['total']}**"
|
| 706 |
+
|
| 707 |
+
options = []
|
| 708 |
+
if q.get('type') == 'mcq' and 'options' in q:
|
| 709 |
+
options = q['options']
|
| 710 |
+
|
| 711 |
+
return (
|
| 712 |
+
gr.update(visible=True, value=question_text),
|
| 713 |
+
gr.update(visible=True, value=question_num),
|
| 714 |
+
gr.update(visible=True, value=score_text),
|
| 715 |
+
gr.update(visible=True, value=question_text),
|
| 716 |
+
gr.update(visible=len(options) > 0, choices=options, value=None),
|
| 717 |
+
gr.update(visible=len(options) > 0),
|
| 718 |
+
gr.update(visible=False),
|
| 719 |
+
gr.update(visible=False),
|
| 720 |
+
gr.update(visible=idx > 0),
|
| 721 |
+
gr.update(visible=idx < len(quiz_data) - 1),
|
| 722 |
+
gr.update(visible=False),
|
| 723 |
+
gr.update(visible=False),
|
| 724 |
+
gr.update(value=idx),
|
| 725 |
+
gr.update(value=score),
|
| 726 |
+
gr.update(value=quiz_data)
|
| 727 |
+
)
|
| 728 |
+
|
| 729 |
+
def submit_answer(selected, quiz_data, idx, score):
|
| 730 |
+
"""Handle answer submission."""
|
| 731 |
+
if not quiz_data or idx >= len(quiz_data):
|
| 732 |
+
return display_question(quiz_data, idx, score, quiz_data)
|
| 733 |
+
|
| 734 |
+
q = quiz_data[idx]
|
| 735 |
+
correct_idx = q.get('correct_answer', 0)
|
| 736 |
+
is_correct = False
|
| 737 |
+
|
| 738 |
+
if q.get('type') == 'mcq':
|
| 739 |
+
is_correct = (selected == correct_idx) if selected is not None else False
|
| 740 |
+
elif q.get('type') == 'true_false':
|
| 741 |
+
is_correct = (selected == correct_idx)
|
| 742 |
+
|
| 743 |
+
new_score = {
|
| 744 |
+
"correct": score["correct"] + (1 if is_correct else 0),
|
| 745 |
+
"total": score["total"] + 1
|
| 746 |
+
}
|
| 747 |
+
|
| 748 |
+
feedback = "β
**Correct!**" if is_correct else "β **Incorrect**"
|
| 749 |
+
explanation = f"**Explanation:** {q.get('explanation', 'No explanation available.')}"
|
| 750 |
+
|
| 751 |
+
return (
|
| 752 |
+
gr.update(visible=True, value=feedback),
|
| 753 |
+
gr.update(visible=True, value=explanation),
|
| 754 |
+
gr.update(visible=False),
|
| 755 |
+
gr.update(visible=idx < len(quiz_data) - 1),
|
| 756 |
+
gr.update(value=idx),
|
| 757 |
+
gr.update(value=new_score),
|
| 758 |
+
gr.update(value=quiz_data)
|
| 759 |
+
)
|
| 760 |
+
|
| 761 |
+
def next_question(quiz_data, idx, score):
|
| 762 |
+
"""Move to next question."""
|
| 763 |
+
return display_question(quiz_data, idx + 1, score, quiz_data)
|
| 764 |
+
|
| 765 |
+
def prev_question(quiz_data, idx, score):
|
| 766 |
+
"""Move to previous question."""
|
| 767 |
+
return display_question(quiz_data, idx - 1, score, quiz_data)
|
| 768 |
+
|
| 769 |
+
def close_quiz_modal():
|
| 770 |
+
"""Close quiz modal."""
|
| 771 |
+
return gr.update(visible=False)
|
| 772 |
+
|
| 773 |
# Connect file upload to update sidebars
|
| 774 |
file_upload.change(
|
| 775 |
handle_file_upload,
|
|
|
|
| 777 |
outputs=[concepts_display, topics_display, key_points_display]
|
| 778 |
)
|
| 779 |
|
| 780 |
+
# Connect quick action buttons
|
| 781 |
quick_quiz_btn.click(quick_generate_quiz, None, [quick_output])
|
| 782 |
quick_curriculum_btn.click(quick_generate_curriculum, None, [quick_output])
|
| 783 |
quick_resources_btn.click(quick_find_resources, None, [quick_output])
|
| 784 |
|
| 785 |
+
# Quiz modal interactions
|
| 786 |
+
chatbot_interface.chatbot.load(load_quiz_to_modal, None, [
|
| 787 |
+
quiz_status, quiz_question_num, quiz_score_display, quiz_question_text,
|
| 788 |
+
quiz_options, quiz_submit_btn, quiz_feedback, quiz_explanation,
|
| 789 |
+
quiz_prev_btn, quiz_next_btn, quiz_complete, quiz_restart_btn,
|
| 790 |
+
current_question_idx, quiz_score_state, quiz_data_state
|
| 791 |
+
])
|
| 792 |
+
|
| 793 |
+
quiz_submit_btn.click(
|
| 794 |
+
submit_answer,
|
| 795 |
+
inputs=[quiz_options, quiz_data_state, current_question_idx, quiz_score_state],
|
| 796 |
+
outputs=[quiz_feedback, quiz_explanation, quiz_submit_btn, quiz_next_btn,
|
| 797 |
+
current_question_idx, quiz_score_state, quiz_data_state]
|
| 798 |
+
)
|
| 799 |
+
|
| 800 |
+
quiz_next_btn.click(
|
| 801 |
+
next_question,
|
| 802 |
+
inputs=[quiz_data_state, current_question_idx, quiz_score_state],
|
| 803 |
+
outputs=[quiz_status, quiz_question_num, quiz_score_display, quiz_question_text,
|
| 804 |
+
quiz_options, quiz_submit_btn, quiz_feedback, quiz_explanation,
|
| 805 |
+
quiz_prev_btn, quiz_next_btn, quiz_complete, quiz_restart_btn,
|
| 806 |
+
current_question_idx, quiz_score_state, quiz_data_state]
|
| 807 |
+
)
|
| 808 |
+
|
| 809 |
+
quiz_prev_btn.click(
|
| 810 |
+
prev_question,
|
| 811 |
+
inputs=[quiz_data_state, current_question_idx, quiz_score_state],
|
| 812 |
+
outputs=[quiz_status, quiz_question_num, quiz_score_display, quiz_question_text,
|
| 813 |
+
quiz_options, quiz_submit_btn, quiz_feedback, quiz_explanation,
|
| 814 |
+
quiz_prev_btn, quiz_next_btn, quiz_complete, quiz_restart_btn,
|
| 815 |
+
current_question_idx, quiz_score_state, quiz_data_state]
|
| 816 |
+
)
|
| 817 |
+
|
| 818 |
+
quiz_close_btn.click(close_quiz_modal, None, [quiz_modal])
|
| 819 |
|
| 820 |
|
| 821 |
if __name__ == "__main__":
|