Harshal Vhatkar
commited on
Commit
·
cf5bc29
1
Parent(s):
2008141
add question_bank in pre_class_quiz
Browse files- gen_mcqs.py +108 -1
- session_page.py +394 -98
gen_mcqs.py
CHANGED
@@ -225,7 +225,7 @@ GEMINI_KEY = os.getenv('GEMINI_KEY')
|
|
225 |
# Configure APIs
|
226 |
openai.api_key = OPENAI_KEY
|
227 |
genai.configure(api_key=GEMINI_KEY)
|
228 |
-
model = genai.GenerativeModel('gemini-
|
229 |
|
230 |
# Connect to MongoDB
|
231 |
client = MongoClient(MONGO_URI)
|
@@ -306,6 +306,113 @@ def generate_mcqs(context, num_questions, session_title, session_description):
|
|
306 |
print(f"Error generating MCQs: , error: {e}")
|
307 |
return None
|
308 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
309 |
def save_pre_class_quiz(course_id: str, session_id: str, title: str, questions: List[Dict], user_id: str, duration: int):
|
310 |
"""Save pre-class quiz to database"""
|
311 |
try:
|
|
|
225 |
# Configure APIs
|
226 |
openai.api_key = OPENAI_KEY
|
227 |
genai.configure(api_key=GEMINI_KEY)
|
228 |
+
model = genai.GenerativeModel('gemini-1.5-flash')
|
229 |
|
230 |
# Connect to MongoDB
|
231 |
client = MongoClient(MONGO_URI)
|
|
|
306 |
print(f"Error generating MCQs: , error: {e}")
|
307 |
return None
|
308 |
|
309 |
+
def generate_pre_class_question_bank(context: str, session_title: str, session_description: str) -> List[Dict]:
|
310 |
+
"""Generate a bank of 40 MCQ questions for pre-class quiz"""
|
311 |
+
try:
|
312 |
+
prompt = f"""
|
313 |
+
Based on the following content, generate 50 multiple choice questions for a question bank.
|
314 |
+
Format each question as a Python dictionary with this exact structure:
|
315 |
+
{{
|
316 |
+
"question": "Question text here",
|
317 |
+
"options": ["A) option1", "B) option2", "C) option3", "D) option4"],
|
318 |
+
"correct_option": "A) option1" or "B) option2" or "C) option3" or "D) option4",
|
319 |
+
"difficulty": "easy|medium|hard",
|
320 |
+
"topic": "specific topic from content"
|
321 |
+
}}
|
322 |
+
|
323 |
+
Content:
|
324 |
+
{context}
|
325 |
+
|
326 |
+
Session Title: {session_title}
|
327 |
+
Description: {session_description}
|
328 |
+
|
329 |
+
Requirements:
|
330 |
+
1. Questions should cover all important concepts
|
331 |
+
2. Mix of easy (20%), medium (50%), and hard (30%) questions
|
332 |
+
3. Clear and unambiguous questions
|
333 |
+
4. Options should be plausible but only one correct answer
|
334 |
+
5. Include topic tags for categorization
|
335 |
+
6. **NUMBER OF QUESTIONS SHOULD BE GREATER THAN OR EQUAL TO 60**
|
336 |
+
|
337 |
+
Return ONLY the JSON array of questions.
|
338 |
+
"""
|
339 |
+
|
340 |
+
response = model.generate_content(
|
341 |
+
prompt,
|
342 |
+
generation_config=genai.GenerationConfig(
|
343 |
+
# temperature=0.7,
|
344 |
+
response_mime_type="application/json"
|
345 |
+
)
|
346 |
+
)
|
347 |
+
response_text = response.text.strip()
|
348 |
+
modified_response_text = strip_code_markers(response_text)
|
349 |
+
question_bank = ast.literal_eval(modified_response_text)
|
350 |
+
print(question_bank)
|
351 |
+
# Validate question bank
|
352 |
+
if not isinstance(question_bank, list):
|
353 |
+
raise ValueError("Invalid question bank format or count")
|
354 |
+
|
355 |
+
return question_bank
|
356 |
+
|
357 |
+
except Exception as e:
|
358 |
+
print(f"Error generating question bank: {e}")
|
359 |
+
return None
|
360 |
+
|
361 |
+
|
362 |
+
def save_pre_class_quiz_with_bank(
|
363 |
+
course_id: str,
|
364 |
+
session_id: str,
|
365 |
+
title: str,
|
366 |
+
question_bank: List[Dict],
|
367 |
+
num_questions: int,
|
368 |
+
duration: int,
|
369 |
+
user_id: str
|
370 |
+
) -> str:
|
371 |
+
"""Save pre-class quiz with question bank to database"""
|
372 |
+
try:
|
373 |
+
quiz_data = {
|
374 |
+
"user_id": user_id,
|
375 |
+
"course_id": course_id,
|
376 |
+
"session_id": session_id,
|
377 |
+
"title": title,
|
378 |
+
"question_bank": question_bank,
|
379 |
+
"num_questions": num_questions,
|
380 |
+
"duration_minutes": duration,
|
381 |
+
"created_at": datetime.now(),
|
382 |
+
"status": "active",
|
383 |
+
"submissions": [],
|
384 |
+
"quiz_type": "pre_class"
|
385 |
+
}
|
386 |
+
result = quizzes_collection.insert_one(quiz_data)
|
387 |
+
return result.inserted_id
|
388 |
+
except Exception as e:
|
389 |
+
print(f"Error saving quiz with question bank: {e}")
|
390 |
+
return None
|
391 |
+
|
392 |
+
def get_randomized_questions(quiz_id) -> List[Dict]:
|
393 |
+
"""Get randomly selected questions from question bank based on quiz settings"""
|
394 |
+
try:
|
395 |
+
quiz = quizzes_collection.find_one({"_id": quiz_id})
|
396 |
+
if not quiz:
|
397 |
+
return None
|
398 |
+
|
399 |
+
num_questions = quiz.get("num_questions", 0)
|
400 |
+
question_bank = quiz.get("question_bank", [])
|
401 |
+
|
402 |
+
if not question_bank or num_questions <= 0:
|
403 |
+
return None
|
404 |
+
|
405 |
+
# Randomly select questions
|
406 |
+
import random
|
407 |
+
selected_questions = random.sample(question_bank, num_questions)
|
408 |
+
|
409 |
+
return selected_questions
|
410 |
+
|
411 |
+
except Exception as e:
|
412 |
+
print(f"Error getting randomized questions: {e}")
|
413 |
+
return None
|
414 |
+
|
415 |
+
|
416 |
def save_pre_class_quiz(course_id: str, session_id: str, title: str, questions: List[Dict], user_id: str, duration: int):
|
417 |
"""Save pre-class quiz to database"""
|
418 |
try:
|
session_page.py
CHANGED
@@ -17,7 +17,7 @@ import plotly.express as px
|
|
17 |
from dotenv import load_dotenv
|
18 |
import os
|
19 |
from pymongo import MongoClient
|
20 |
-
from gen_mcqs import generate_mcqs, save_pre_class_quiz, save_quiz, quizzes_collection, get_student_quiz_score, submit_quiz_answers
|
21 |
from create_course import courses_collection
|
22 |
# from pre_class_analytics import NovaScholarAnalytics
|
23 |
from pre_class_analytics2 import NovaScholarAnalytics
|
@@ -831,56 +831,225 @@ def display_pre_class_quiz_tab(student_id, course_id, session_id):
|
|
831 |
st.success(f"Quiz completed! Score: {existing_score:.1f}%")
|
832 |
|
833 |
# Show review
|
834 |
-
st.
|
835 |
-
|
836 |
-
|
837 |
-
|
838 |
-
|
839 |
-
|
840 |
-
|
841 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
842 |
|
843 |
else:
|
844 |
-
# Check time remaining
|
845 |
-
start_time = quiz.get('start_time')
|
846 |
-
if not start_time:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
847 |
if st.button("Start Quiz", key=f"start_{quiz['_id']}"):
|
848 |
-
|
849 |
-
|
850 |
-
|
851 |
-
|
852 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
853 |
else:
|
854 |
-
|
855 |
-
|
856 |
-
|
857 |
-
|
|
|
|
|
|
|
|
|
|
|
858 |
|
859 |
-
|
860 |
-
|
861 |
-
|
862 |
-
|
863 |
-
|
864 |
-
|
865 |
-
student_answers
|
866 |
-
|
867 |
-
|
868 |
-
|
869 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
870 |
|
871 |
-
|
872 |
-
|
873 |
-
|
874 |
-
|
875 |
-
|
876 |
-
|
877 |
-
|
878 |
-
|
879 |
-
|
880 |
-
|
881 |
-
|
882 |
-
|
883 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
884 |
|
885 |
|
886 |
def get_chat_context(session_id):
|
@@ -1389,13 +1558,76 @@ def upload_preclass_materials(session_id, course_id, student_id):
|
|
1389 |
with pre_class_quiz:
|
1390 |
if st.session_state.user_type == "faculty":
|
1391 |
st.subheader("Create Pre-class Quiz")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1392 |
with st.form("create_pre_class_quiz_form"):
|
1393 |
quiz_title = st.text_input("Quiz Title")
|
1394 |
num_questions = st.number_input(
|
1395 |
-
"Number of Questions",
|
1396 |
-
min_value=
|
1397 |
-
max_value=
|
1398 |
-
value=
|
1399 |
)
|
1400 |
duration = st.number_input(
|
1401 |
"Quiz Duration (minutes)",
|
@@ -1404,54 +1636,111 @@ def upload_preclass_materials(session_id, course_id, student_id):
|
|
1404 |
value=15
|
1405 |
)
|
1406 |
|
1407 |
-
|
1408 |
-
|
1409 |
-
|
1410 |
-
|
1411 |
-
|
1412 |
-
|
1413 |
-
# Get pre-class materials
|
1414 |
-
materials = resources_collection.find({"session_id": session_id})
|
1415 |
-
context = ""
|
1416 |
-
for material in materials:
|
1417 |
-
if 'text_content' in material:
|
1418 |
-
context += material['text_content'] + "\n"
|
1419 |
-
|
1420 |
-
if not context:
|
1421 |
-
st.error("No pre-class materials found")
|
1422 |
-
return
|
1423 |
|
1424 |
-
|
1425 |
-
|
1426 |
-
|
1427 |
-
|
1428 |
-
|
1429 |
-
|
1430 |
-
)
|
1431 |
|
1432 |
-
|
1433 |
-
|
1434 |
-
|
1435 |
-
for i, q in enumerate(questions, 1):
|
1436 |
-
st.markdown(f"**Q{i}:** {q['question']}")
|
1437 |
-
for opt in q['options']:
|
1438 |
-
st.markdown(f"- {opt}")
|
1439 |
-
st.markdown(f"*Correct: {q['correct_option']}*")
|
1440 |
-
|
1441 |
-
# Save quiz
|
1442 |
-
quiz_id = save_pre_class_quiz(
|
1443 |
-
course_id=course_id,
|
1444 |
-
session_id=session_id,
|
1445 |
-
title=quiz_title,
|
1446 |
-
questions=questions,
|
1447 |
-
user_id=st.session_state.user_id,
|
1448 |
-
duration=duration
|
1449 |
-
)
|
1450 |
|
1451 |
-
|
1452 |
-
|
1453 |
-
|
1454 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1455 |
|
1456 |
def extract_external_content(url, content_type):
|
1457 |
"""Extract content from external resources based on their type"""
|
@@ -3440,13 +3729,19 @@ def display_session_analytics(session, course_id):
|
|
3440 |
def display_quiz_tab(student_id, course_id, session_id):
|
3441 |
"""Display quizzes for students"""
|
3442 |
st.header("Course Quizzes")
|
3443 |
-
|
3444 |
-
# Get available quizzes for this session
|
3445 |
quizzes = quizzes_collection.find({
|
3446 |
"course_id": course_id,
|
3447 |
"session_id": session_id,
|
3448 |
-
"status": "active"
|
|
|
3449 |
})
|
|
|
|
|
|
|
|
|
|
|
|
|
3450 |
|
3451 |
quizzes = list(quizzes)
|
3452 |
if not quizzes:
|
@@ -3570,7 +3865,8 @@ def display_session_content(student_id, course_id, session, username, user_type)
|
|
3570 |
with tabs[2]:
|
3571 |
display_post_class_content(session, student_id, course_id)
|
3572 |
with tabs[3]:
|
3573 |
-
|
|
|
3574 |
with tabs[4]:
|
3575 |
display_inclass_analytics(session, course_id)
|
3576 |
with tabs[5]:
|
|
|
17 |
from dotenv import load_dotenv
|
18 |
import os
|
19 |
from pymongo import MongoClient
|
20 |
+
from gen_mcqs import generate_mcqs, generate_pre_class_question_bank, get_randomized_questions, save_pre_class_quiz, save_pre_class_quiz_with_bank, save_quiz, quizzes_collection, get_student_quiz_score, submit_quiz_answers
|
21 |
from create_course import courses_collection
|
22 |
# from pre_class_analytics import NovaScholarAnalytics
|
23 |
from pre_class_analytics2 import NovaScholarAnalytics
|
|
|
831 |
st.success(f"Quiz completed! Score: {existing_score:.1f}%")
|
832 |
|
833 |
# Show review
|
834 |
+
st.markdown("#### Quiz Review")
|
835 |
+
submission = next(
|
836 |
+
(sub for sub in quiz["submissions"] if sub["student_id"] == student_id),
|
837 |
+
None
|
838 |
+
)
|
839 |
+
# for i, question in enumerate(quiz["questions"]):
|
840 |
+
# st.markdown(f"**Q{i+1}:** {question['question']}")
|
841 |
+
# for opt in question["options"]:
|
842 |
+
# if opt.startswith(question["correct_option"]):
|
843 |
+
# st.markdown(f"✅ {opt}")
|
844 |
+
# else:
|
845 |
+
# st.markdown(f"- {opt}")
|
846 |
+
if submission:
|
847 |
+
questions = submission.get("allocated_questions", [])
|
848 |
+
answers = submission.get("answers", {})
|
849 |
+
for i, question in enumerate(questions):
|
850 |
+
st.markdown(f"**Q{i+1}:** {question['question']}")
|
851 |
+
for opt in question["options"]:
|
852 |
+
if opt == question["correct_option"]:
|
853 |
+
st.markdown(f"✅ {opt}")
|
854 |
+
elif opt == answers.get(str(i)):
|
855 |
+
st.markdown(f"❌ {opt}")
|
856 |
+
else:
|
857 |
+
st.markdown(f"- {opt}")
|
858 |
|
859 |
else:
|
860 |
+
# # Check time remaining
|
861 |
+
# start_time = quiz.get('start_time')
|
862 |
+
# if not start_time:
|
863 |
+
# if st.button("Start Quiz", key=f"start_{quiz['_id']}"):
|
864 |
+
# quizzes_collection.update_one(
|
865 |
+
# {"_id": quiz["_id"]},
|
866 |
+
# {"$set": {"start_time": datetime.now()}}
|
867 |
+
# )
|
868 |
+
# st.rerun()
|
869 |
+
# Start quiz flow
|
870 |
+
quiz_state = quizzes_collection.find_one({
|
871 |
+
"_id": quiz["_id"],
|
872 |
+
"student_states.student_id": student_id
|
873 |
+
})
|
874 |
+
if not quiz_state or "student_states" not in quiz_state:
|
875 |
+
# Not started yet
|
876 |
if st.button("Start Quiz", key=f"start_{quiz['_id']}"):
|
877 |
+
# Get random questions
|
878 |
+
questions = get_randomized_questions(quiz["_id"])
|
879 |
+
if questions:
|
880 |
+
# Save allocated questions and start time
|
881 |
+
result = quizzes_collection.update_one(
|
882 |
+
{"_id": quiz["_id"]},
|
883 |
+
{
|
884 |
+
"$push": {
|
885 |
+
"student_states": {
|
886 |
+
"student_id": student_id,
|
887 |
+
"allocated_questions": questions,
|
888 |
+
"start_time": datetime.now(),
|
889 |
+
"completed": False
|
890 |
+
}
|
891 |
+
}
|
892 |
+
}
|
893 |
+
)
|
894 |
+
if result.modified_count > 0:
|
895 |
+
st.rerun()
|
896 |
+
else:
|
897 |
+
st.error("Error getting quiz questions")
|
898 |
else:
|
899 |
+
# Quiz already started
|
900 |
+
student_state = next(
|
901 |
+
(state for state in quiz_state["student_states"]
|
902 |
+
if state["student_id"] == student_id),
|
903 |
+
None
|
904 |
+
)
|
905 |
+
if student_state:
|
906 |
+
start_time = student_state["start_time"]
|
907 |
+
time_remaining = (start_time + timedelta(minutes=quiz['duration_minutes'])) - datetime.now()
|
908 |
|
909 |
+
if time_remaining.total_seconds() > 0:
|
910 |
+
st.info(f"Time remaining: {int(time_remaining.total_seconds() // 60)}:{int(time_remaining.total_seconds() % 60):02d}")
|
911 |
+
|
912 |
+
# Display questions
|
913 |
+
questions = student_state["allocated_questions"]
|
914 |
+
with st.form(f"pre_class_quiz_{quiz['_id']}"):
|
915 |
+
student_answers = {}
|
916 |
+
|
917 |
+
for i, question in enumerate(questions):
|
918 |
+
st.markdown(f"**Q{i+1}:** {question['question']}")
|
919 |
+
options = question["options"]
|
920 |
+
answer = st.radio(
|
921 |
+
"Select answer:",
|
922 |
+
options=options,
|
923 |
+
key=f"q_{quiz['_id']}_{i}"
|
924 |
+
)
|
925 |
+
if answer:
|
926 |
+
student_answers[str(i)] = answer
|
927 |
|
928 |
+
if st.form_submit_button("Submit Quiz"):
|
929 |
+
if len(student_answers) != len(questions):
|
930 |
+
st.error("Please answer all questions before submitting.")
|
931 |
+
else:
|
932 |
+
# Calculate score
|
933 |
+
correct_answers = 0
|
934 |
+
for i, question in enumerate(questions):
|
935 |
+
if student_answers.get(str(i)) == question["correct_option"]:
|
936 |
+
correct_answers += 1
|
937 |
+
|
938 |
+
score = (correct_answers / len(questions)) * 100
|
939 |
+
|
940 |
+
# Save submission
|
941 |
+
result = quizzes_collection.update_one(
|
942 |
+
{"_id": quiz["_id"]},
|
943 |
+
{
|
944 |
+
"$push": {
|
945 |
+
"submissions": {
|
946 |
+
"student_id": student_id,
|
947 |
+
"allocated_questions": questions,
|
948 |
+
"answers": student_answers,
|
949 |
+
"score": score,
|
950 |
+
"submitted_at": datetime.now()
|
951 |
+
}
|
952 |
+
},
|
953 |
+
"$set": {
|
954 |
+
"student_states.$[state].completed": True
|
955 |
+
}
|
956 |
+
},
|
957 |
+
array_filters=[
|
958 |
+
{"state.student_id": student_id}
|
959 |
+
]
|
960 |
+
)
|
961 |
+
if result.modified_count > 0:
|
962 |
+
st.success(f"Quiz submitted! Score: {score:.1f}%")
|
963 |
+
st.rerun()
|
964 |
+
else:
|
965 |
+
st.error("Error submitting quiz")
|
966 |
+
else:
|
967 |
+
st.error("Quiz time expired")
|
968 |
+
# start_time = quiz.get("start_time")
|
969 |
+
# if not start_time:
|
970 |
+
# if st.button("Start Quiz", key=f"start_{quiz['_id']}"):
|
971 |
+
# # Get random questions when starting
|
972 |
+
# questions = get_randomized_questions(quiz["_id"])
|
973 |
+
# if questions:
|
974 |
+
# # Save selected questions and start time
|
975 |
+
# result = quizzes_collection.update_one(
|
976 |
+
# {"_id": quiz["_id"]},
|
977 |
+
# {
|
978 |
+
# "$set": {
|
979 |
+
# "start_time": datetime.now(),
|
980 |
+
# f"student_questions.{student_id}": questions
|
981 |
+
# }
|
982 |
+
# }
|
983 |
+
# )
|
984 |
+
# if result.modified_count > 0:
|
985 |
+
# st.rerun()
|
986 |
+
# else:
|
987 |
+
# st.error("Error getting quiz questions")
|
988 |
+
# else:
|
989 |
+
# time_remaining = (start_time + timedelta(minutes=quiz['duration_minutes'])) - datetime.now()
|
990 |
+
|
991 |
+
# # if time_remaining.total_seconds() > 0:
|
992 |
+
# # st.info(f"Time remaining: {int(time_remaining.total_seconds() // 60)}:{int(time_remaining.total_seconds() % 60):02d}")
|
993 |
+
|
994 |
+
# # # Display quiz form
|
995 |
+
# # with st.form(f"pre_class_quiz_{quiz['_id']}"):
|
996 |
+
# # student_answers = {}
|
997 |
+
# # for i, question in enumerate(quiz["questions"]):
|
998 |
+
# # st.markdown(f"**Q{i+1}:** {question['question']}")
|
999 |
+
# # options = [opt for opt in question["options"]]
|
1000 |
+
# # student_answers[str(i)] = st.radio(
|
1001 |
+
# # f"Select answer:",
|
1002 |
+
# # options=options,
|
1003 |
+
# # key=f"q_{quiz['_id']}_{i}"
|
1004 |
+
# # )
|
1005 |
+
|
1006 |
+
# # if st.form_submit_button("Submit Quiz"):
|
1007 |
+
# # score = submit_quiz_answers(
|
1008 |
+
# # quiz["_id"],
|
1009 |
+
# # student_id,
|
1010 |
+
# # student_answers
|
1011 |
+
# # )
|
1012 |
+
# # if score is not None:
|
1013 |
+
# # st.success(f"Quiz submitted! Score: {score:.1f}%")
|
1014 |
+
# # st.rerun()
|
1015 |
+
# # else:
|
1016 |
+
# # st.error("Error submitting quiz")
|
1017 |
+
# # else:
|
1018 |
+
# # st.error("Quiz time expired")
|
1019 |
+
# if time_remaining.total_seconds() > 0:
|
1020 |
+
# st.info(f"Time remaining: {int(time_remaining.total_seconds() // 60)}:{int(time_remaining.total_seconds() % 60):02d}")
|
1021 |
+
|
1022 |
+
# # Get student's randomized questions
|
1023 |
+
# student_questions = quiz.get("student_questions", {}).get(student_id)
|
1024 |
+
|
1025 |
+
# if student_questions:
|
1026 |
+
# with st.form(f"pre_class_quiz_{quiz['_id']}"):
|
1027 |
+
# student_answers = {}
|
1028 |
+
|
1029 |
+
# for i, question in enumerate(student_questions):
|
1030 |
+
# st.markdown(f"**Q{i+1}:** {question['question']}")
|
1031 |
+
# options = question["options"]
|
1032 |
+
# student_answers[str(i)] = st.radio(
|
1033 |
+
# "Select answer:",
|
1034 |
+
# options=options,
|
1035 |
+
# key=f"q_{quiz['_id']}_{i}"
|
1036 |
+
# )
|
1037 |
+
|
1038 |
+
# if st.form_submit_button("Submit Quiz"):
|
1039 |
+
# score = submit_quiz_answers(
|
1040 |
+
# quiz["_id"],
|
1041 |
+
# student_id,
|
1042 |
+
# student_answers,
|
1043 |
+
# student_questions
|
1044 |
+
# )
|
1045 |
+
|
1046 |
+
# if score is not None:
|
1047 |
+
# st.success(f"Quiz submitted! Score: {score:.1f}%")
|
1048 |
+
# st.rerun()
|
1049 |
+
# else:
|
1050 |
+
# st.error("Error submitting quiz")
|
1051 |
+
# else:
|
1052 |
+
# st.error("Quiz time expired")
|
1053 |
|
1054 |
|
1055 |
def get_chat_context(session_id):
|
|
|
1558 |
with pre_class_quiz:
|
1559 |
if st.session_state.user_type == "faculty":
|
1560 |
st.subheader("Create Pre-class Quiz")
|
1561 |
+
# with st.form("create_pre_class_quiz_form"):
|
1562 |
+
# quiz_title = st.text_input("Quiz Title")
|
1563 |
+
# num_questions = st.number_input(
|
1564 |
+
# "Number of Questions",
|
1565 |
+
# min_value=1,
|
1566 |
+
# max_value=20,
|
1567 |
+
# value=5
|
1568 |
+
# )
|
1569 |
+
# duration = st.number_input(
|
1570 |
+
# "Quiz Duration (minutes)",
|
1571 |
+
# min_value=5,
|
1572 |
+
# max_value=60,
|
1573 |
+
# value=15
|
1574 |
+
# )
|
1575 |
+
|
1576 |
+
# # Generate quiz button
|
1577 |
+
# if st.form_submit_button("Generate Pre-class Quiz"):
|
1578 |
+
# if not quiz_title:
|
1579 |
+
# st.error("Please enter a quiz title")
|
1580 |
+
# return
|
1581 |
+
|
1582 |
+
# # Get pre-class materials
|
1583 |
+
# materials = resources_collection.find({"session_id": session_id})
|
1584 |
+
# context = ""
|
1585 |
+
# for material in materials:
|
1586 |
+
# if 'text_content' in material:
|
1587 |
+
# context += material['text_content'] + "\n"
|
1588 |
+
|
1589 |
+
# if not context:
|
1590 |
+
# st.error("No pre-class materials found")
|
1591 |
+
# return
|
1592 |
+
|
1593 |
+
# # Generate questions
|
1594 |
+
# questions = generate_mcqs(
|
1595 |
+
# context=context,
|
1596 |
+
# num_questions=num_questions,
|
1597 |
+
# session_title=session['title'],
|
1598 |
+
# session_description=session.get('description', '')
|
1599 |
+
# )
|
1600 |
+
|
1601 |
+
# if questions:
|
1602 |
+
# # Preview questions
|
1603 |
+
# st.subheader("Preview Questions")
|
1604 |
+
# for i, q in enumerate(questions, 1):
|
1605 |
+
# st.markdown(f"**Q{i}:** {q['question']}")
|
1606 |
+
# for opt in q['options']:
|
1607 |
+
# st.markdown(f"- {opt}")
|
1608 |
+
# st.markdown(f"*Correct: {q['correct_option']}*")
|
1609 |
+
|
1610 |
+
# # Save quiz
|
1611 |
+
# quiz_id = save_pre_class_quiz(
|
1612 |
+
# course_id=course_id,
|
1613 |
+
# session_id=session_id,
|
1614 |
+
# title=quiz_title,
|
1615 |
+
# questions=questions,
|
1616 |
+
# user_id=st.session_state.user_id,
|
1617 |
+
# duration=duration
|
1618 |
+
# )
|
1619 |
+
|
1620 |
+
# if quiz_id:
|
1621 |
+
# st.success("Pre-class quiz created successfully!")
|
1622 |
+
# else:
|
1623 |
+
# st.error("Error saving quiz")
|
1624 |
with st.form("create_pre_class_quiz_form"):
|
1625 |
quiz_title = st.text_input("Quiz Title")
|
1626 |
num_questions = st.number_input(
|
1627 |
+
"Number of Questions per Student",
|
1628 |
+
min_value=5,
|
1629 |
+
max_value=30,
|
1630 |
+
value=10
|
1631 |
)
|
1632 |
duration = st.number_input(
|
1633 |
"Quiz Duration (minutes)",
|
|
|
1636 |
value=15
|
1637 |
)
|
1638 |
|
1639 |
+
if st.form_submit_button("Generate Question Bank"):
|
1640 |
+
with st.spinner("Generating question bank..."):
|
1641 |
+
try:
|
1642 |
+
if not quiz_title:
|
1643 |
+
st.error("Please enter a quiz title")
|
1644 |
+
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1645 |
|
1646 |
+
# Get pre-class materials
|
1647 |
+
materials = resources_collection.find({"session_id": session_id})
|
1648 |
+
context = ""
|
1649 |
+
for material in materials:
|
1650 |
+
if 'text_content' in material:
|
1651 |
+
context += material['text_content'] + "\n"
|
|
|
1652 |
|
1653 |
+
if not context:
|
1654 |
+
st.error("No pre-class materials found")
|
1655 |
+
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1656 |
|
1657 |
+
# Generate question bank
|
1658 |
+
question_bank = generate_pre_class_question_bank(
|
1659 |
+
context=context,
|
1660 |
+
session_title=session['title'],
|
1661 |
+
session_description=session.get('description', '')
|
1662 |
+
)
|
1663 |
+
if question_bank:
|
1664 |
+
print(question_bank)
|
1665 |
+
# Preview question bank
|
1666 |
+
st.subheader("Question Bank Preview")
|
1667 |
+
|
1668 |
+
# Show statistics
|
1669 |
+
difficulties = [q['difficulty'] for q in question_bank]
|
1670 |
+
st.write("Question Distribution:")
|
1671 |
+
st.write(f"- Easy: {difficulties.count('easy')}")
|
1672 |
+
st.write(f"- Medium: {difficulties.count('medium')}")
|
1673 |
+
st.write(f"- Hard: {difficulties.count('hard')}")
|
1674 |
+
|
1675 |
+
# Show questions
|
1676 |
+
for i, q in enumerate(question_bank, 1):
|
1677 |
+
with st.expander(f"Q{i}: {q['topic']} ({q['difficulty']})"):
|
1678 |
+
st.markdown(f"**Question:** {q['question']}")
|
1679 |
+
st.markdown("**Options:**")
|
1680 |
+
for opt in q['options']:
|
1681 |
+
if opt == q['correct_option']:
|
1682 |
+
st.markdown(f"✅ {opt}")
|
1683 |
+
else:
|
1684 |
+
st.markdown(f"- {opt}")
|
1685 |
+
# Save quiz with question bank
|
1686 |
+
quiz_id = save_pre_class_quiz_with_bank(
|
1687 |
+
course_id=course_id,
|
1688 |
+
session_id=session_id,
|
1689 |
+
title=quiz_title,
|
1690 |
+
question_bank=question_bank,
|
1691 |
+
num_questions=num_questions,
|
1692 |
+
duration=duration,
|
1693 |
+
user_id=st.session_state.user_id
|
1694 |
+
)
|
1695 |
+
if quiz_id:
|
1696 |
+
st.success("Pre-class quiz created successfully!")
|
1697 |
+
else:
|
1698 |
+
st.error("Error generating question bank")
|
1699 |
+
except Exception as e:
|
1700 |
+
st.error("Error saving quiz", e)
|
1701 |
+
# else:
|
1702 |
+
# st.error("Error generating question bank")
|
1703 |
+
# question_banks = quizzes_collection.find({
|
1704 |
+
# "course_id": course_id,
|
1705 |
+
# "session_id": session_id,
|
1706 |
+
# "quiz_type": "pre_class"
|
1707 |
+
# })
|
1708 |
+
question_banks = list(quizzes_collection.find({
|
1709 |
+
"course_id": course_id,
|
1710 |
+
"session_id": session_id,
|
1711 |
+
"quiz_type": "pre_class"
|
1712 |
+
}))
|
1713 |
+
if question_banks and len(question_banks) > 0:
|
1714 |
+
st.markdown("#### Existing Question Banks")
|
1715 |
+
for bank in question_banks:
|
1716 |
+
try:
|
1717 |
+
faculty_member = faculty_collection.find_one({"_id": ObjectId(bank['user_id'])})
|
1718 |
+
faculty_name = faculty_member['full_name'] if faculty_member else "Unknown Faculty"
|
1719 |
+
|
1720 |
+
with st.expander(f"📚 {bank.get('title', 'Untitled Quiz')}"):
|
1721 |
+
# Check if question bank exists
|
1722 |
+
if 'question_bank' in bank and bank['question_bank']:
|
1723 |
+
st.markdown(f"**Number of Questions:** {len(bank['question_bank'])}")
|
1724 |
+
st.markdown(f"**Created By:** {faculty_name}")
|
1725 |
+
st.markdown(f"**Created At:** {bank['created_at'].strftime('%Y-%m-%d %H:%M:%S')}")
|
1726 |
+
|
1727 |
+
# Display questions
|
1728 |
+
for i, question in enumerate(bank['question_bank'], 1):
|
1729 |
+
st.markdown(f"**Question {i}:** {question['question']}")
|
1730 |
+
st.markdown("**Options:**")
|
1731 |
+
for opt in question['options']:
|
1732 |
+
if opt == question['correct_option']:
|
1733 |
+
st.markdown(f"✅ {opt}")
|
1734 |
+
else:
|
1735 |
+
st.markdown(f"- {opt}")
|
1736 |
+
st.markdown(f"**Difficulty:** {question['difficulty']}")
|
1737 |
+
st.markdown(f"**Topic:** {question['topic']}")
|
1738 |
+
else:
|
1739 |
+
st.warning("This question bank appears to be empty")
|
1740 |
+
except Exception as e:
|
1741 |
+
st.error(f"Error displaying question bank: {str(e)}")
|
1742 |
+
else:
|
1743 |
+
st.info("No question banks yet for this session")
|
1744 |
|
1745 |
def extract_external_content(url, content_type):
|
1746 |
"""Extract content from external resources based on their type"""
|
|
|
3729 |
def display_quiz_tab(student_id, course_id, session_id):
|
3730 |
"""Display quizzes for students"""
|
3731 |
st.header("Course Quizzes")
|
3732 |
+
# Get available quizzes for this session, excluding pre-class quizzes
|
|
|
3733 |
quizzes = quizzes_collection.find({
|
3734 |
"course_id": course_id,
|
3735 |
"session_id": session_id,
|
3736 |
+
"status": "active",
|
3737 |
+
"quiz_type": {"$ne": "pre_class"}
|
3738 |
})
|
3739 |
+
# Get available quizzes for this session
|
3740 |
+
# quizzes = quizzes_collection.find({
|
3741 |
+
# "course_id": course_id,
|
3742 |
+
# "session_id": session_id,
|
3743 |
+
# "status": "active"
|
3744 |
+
# })
|
3745 |
|
3746 |
quizzes = list(quizzes)
|
3747 |
if not quizzes:
|
|
|
3865 |
with tabs[2]:
|
3866 |
display_post_class_content(session, student_id, course_id)
|
3867 |
with tabs[3]:
|
3868 |
+
if st.button("Generate Pre-class Analytics") and not st.session_state.analytics_data:
|
3869 |
+
display_preclass_analytics2(session, course_id)
|
3870 |
with tabs[4]:
|
3871 |
display_inclass_analytics(session, course_id)
|
3872 |
with tabs[5]:
|