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]:
|