Spaces:
Build error
Build error
Harshal Vhatkar
commited on
Commit
·
1586102
1
Parent(s):
b572b15
add quiz in pre-class
Browse files- chatbot.py +16 -0
- course_creation.py +108 -0
- file_upload_vectorize.py +3 -1
- gen_mcqs.py +101 -32
- main.py +7 -4
- modify_schema.py +222 -0
- session_page.py +313 -8
chatbot.py
CHANGED
@@ -7,6 +7,7 @@ import os
|
|
7 |
from datetime import datetime
|
8 |
from bson import ObjectId
|
9 |
from file_upload_vectorize import model
|
|
|
10 |
|
11 |
load_dotenv()
|
12 |
MONGO_URI = os.getenv('MONGO_URI')
|
@@ -49,3 +50,18 @@ def give_chat_response(user_id, session_id, question, title, description, contex
|
|
49 |
insert_chat_message(user_id, session_id, "assistant", assistant_response)
|
50 |
|
51 |
return assistant_response
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
7 |
from datetime import datetime
|
8 |
from bson import ObjectId
|
9 |
from file_upload_vectorize import model
|
10 |
+
from gen_mcqs import generate_mcqs, quizzes_collection
|
11 |
|
12 |
load_dotenv()
|
13 |
MONGO_URI = os.getenv('MONGO_URI')
|
|
|
50 |
insert_chat_message(user_id, session_id, "assistant", assistant_response)
|
51 |
|
52 |
return assistant_response
|
53 |
+
|
54 |
+
def create_quiz_by_context(user_id, session_id, context, length, session_title, session_description):
|
55 |
+
"""Create a quiz based on the context provided"""
|
56 |
+
quiz = generate_mcqs(context, length, session_title, session_description)
|
57 |
+
if not quiz:
|
58 |
+
return "No quiz generated";
|
59 |
+
|
60 |
+
# Save the quiz
|
61 |
+
quizzes_collection.insert_one({
|
62 |
+
"user_id": ObjectId(user_id),
|
63 |
+
"session_id": ObjectId(session_id),
|
64 |
+
"questions": quiz,
|
65 |
+
"timestamp": datetime.utcnow()
|
66 |
+
})
|
67 |
+
return "Quiz created successfully"
|
course_creation.py
ADDED
@@ -0,0 +1,108 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import pandas as pd
|
2 |
+
import json
|
3 |
+
import os
|
4 |
+
from datetime import datetime
|
5 |
+
import openai
|
6 |
+
from openai import OpenAI
|
7 |
+
import streamlit as st
|
8 |
+
from llama_index.core import VectorStoreIndex, Document
|
9 |
+
from llama_index.core.vector_stores import SimpleVectorStore
|
10 |
+
from llama_index.core.storage.storage_context import StorageContext
|
11 |
+
from llama_index.embeddings.openai import OpenAIEmbedding
|
12 |
+
from pymongo import MongoClient
|
13 |
+
import numpy as np
|
14 |
+
from numpy.linalg import norm
|
15 |
+
import PyPDF2
|
16 |
+
from io import BytesIO
|
17 |
+
import base64
|
18 |
+
|
19 |
+
MONGO_URI = os.getenv('MONGO_URI')
|
20 |
+
PERPLEXITY_KEY = os.getenv('PERPLEXITY_KEY')
|
21 |
+
|
22 |
+
# Connect to MongoDB
|
23 |
+
client = MongoClient(MONGO_URI)
|
24 |
+
db = client['novascholar_db']
|
25 |
+
|
26 |
+
# Function to fetch data from Perplexity API
|
27 |
+
def fetch_perplexity_data(api_key, topic):
|
28 |
+
headers = {
|
29 |
+
"accept": "application/json",
|
30 |
+
"content-type": "application/json",
|
31 |
+
"authorization": f"Bearer {api_key}"
|
32 |
+
}
|
33 |
+
messages = [
|
34 |
+
{
|
35 |
+
"role": "system",
|
36 |
+
"content": (
|
37 |
+
"You are a expert providing official information about the given topic. Provide only verified information with atleast 3 working reference links for citations."
|
38 |
+
),
|
39 |
+
},
|
40 |
+
{
|
41 |
+
"role": "user",
|
42 |
+
"content": topic
|
43 |
+
},
|
44 |
+
]
|
45 |
+
|
46 |
+
try:
|
47 |
+
client = OpenAI(api_key=api_key, base_url="https://api.perplexity.ai")
|
48 |
+
response = client.chat.completions.create(
|
49 |
+
model="llama-3.1-sonar-small-128k-online",
|
50 |
+
messages=messages,
|
51 |
+
)
|
52 |
+
content = response.choices[0].message.content
|
53 |
+
return content
|
54 |
+
except Exception as e:
|
55 |
+
st.error(f"Failed to fetch data from Perplexity API: {e}")
|
56 |
+
return ""
|
57 |
+
|
58 |
+
def structure_data(api_key, generated_text, columns):
|
59 |
+
prompt = f"You are given a large amount of data that can be structured into a table with many rows. Structure the following data into a JSON format with columns: {columns}. Data: {generated_text}. Ensure that you only output the data in JSON format without any other text at all, not even backtics `` and the word JSON. Do not include any other information in the output."
|
60 |
+
messages = [
|
61 |
+
{
|
62 |
+
"role": "system",
|
63 |
+
"content": "You are an AI that structures data into JSON format for converting unstructured text data into tables. Ensure that you have atlest as many rows in the output as much mentioned in the input text. Return the data in such a way that it is a list of dictionaried that can be converted to a pandas dataframe directly."
|
64 |
+
},
|
65 |
+
{
|
66 |
+
"role": "user",
|
67 |
+
"content": prompt
|
68 |
+
},
|
69 |
+
]
|
70 |
+
|
71 |
+
try:
|
72 |
+
client = OpenAI(api_key=api_key)
|
73 |
+
response = client.chat.completions.create(
|
74 |
+
model="gpt-4o",
|
75 |
+
messages=messages,
|
76 |
+
temperature=0.1,
|
77 |
+
)
|
78 |
+
json_content = response.choices[0].message.content
|
79 |
+
return json.loads(json_content)
|
80 |
+
except Exception as e:
|
81 |
+
st.error(f"Failed to structure data using GPT-4o Mini: {e}")
|
82 |
+
return []
|
83 |
+
|
84 |
+
def generate_theme_title(api_key, text):
|
85 |
+
prompt = f"Provide a 2-3 word title that captures the main theme of the following text: {text} Return only the 2 3 word string and nothing else. Do not include any other information in the output."
|
86 |
+
messages = [
|
87 |
+
{
|
88 |
+
"role": "system",
|
89 |
+
"content": "You are an AI that generates concise titles for text content."
|
90 |
+
},
|
91 |
+
{
|
92 |
+
"role": "user",
|
93 |
+
"content": prompt
|
94 |
+
},
|
95 |
+
]
|
96 |
+
|
97 |
+
try:
|
98 |
+
client = OpenAI(api_key=api_key)
|
99 |
+
response = client.chat.completions.create(
|
100 |
+
model="gpt-4o-mini",
|
101 |
+
messages=messages,
|
102 |
+
temperature=0.1,
|
103 |
+
)
|
104 |
+
theme_title = response.choices[0].message.content.strip()
|
105 |
+
return theme_title
|
106 |
+
except Exception as e:
|
107 |
+
st.error(f"Failed to generate theme title using GPT-4o Mini: {e}")
|
108 |
+
return "Unnamed Theme"
|
file_upload_vectorize.py
CHANGED
@@ -70,12 +70,14 @@ def assignment_submit(student_id, course_id, session_id, assignment_id, file_na
|
|
70 |
"student_id": student_id,
|
71 |
"course_id": course_id,
|
72 |
"session_id": session_id,
|
|
|
73 |
"file_name": file_name,
|
74 |
"file_type": file_content.type,
|
75 |
"file_content": original_file_content, # Store the original file content
|
76 |
"text_content": text_content,
|
77 |
"material_type": material_type,
|
78 |
-
"
|
|
|
79 |
}
|
80 |
try:
|
81 |
courses_collection2.update_one(
|
|
|
70 |
"student_id": student_id,
|
71 |
"course_id": course_id,
|
72 |
"session_id": session_id,
|
73 |
+
"assignment_id": assignment_id,
|
74 |
"file_name": file_name,
|
75 |
"file_type": file_content.type,
|
76 |
"file_content": original_file_content, # Store the original file content
|
77 |
"text_content": text_content,
|
78 |
"material_type": material_type,
|
79 |
+
"submitted_at": datetime.utcnow(),
|
80 |
+
"file_url": "sample_url"
|
81 |
}
|
82 |
try:
|
83 |
courses_collection2.update_one(
|
gen_mcqs.py
CHANGED
@@ -1,3 +1,4 @@
|
|
|
|
1 |
from pymongo import MongoClient
|
2 |
from datetime import datetime
|
3 |
import openai
|
@@ -23,6 +24,17 @@ client = MongoClient(MONGO_URI)
|
|
23 |
db = client['novascholar_db']
|
24 |
quizzes_collection = db["quizzes"]
|
25 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
26 |
# New function to generate MCQs using Gemini
|
27 |
def generate_mcqs(context, num_questions, session_title, session_description):
|
28 |
"""Generate MCQs either from context or session details"""
|
@@ -58,29 +70,46 @@ def generate_mcqs(context, num_questions, session_title, session_description):
|
|
58 |
}}
|
59 |
|
60 |
Generate challenging but clear questions.
|
61 |
-
Return only the Python list of dictionaries
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
62 |
"""
|
63 |
|
64 |
response = model.generate_content(prompt)
|
|
|
|
|
|
|
|
|
65 |
# Extract and parse the response to get the list of MCQs
|
66 |
-
mcqs =
|
67 |
print(mcqs)
|
|
|
|
|
68 |
return mcqs
|
69 |
except Exception as e:
|
70 |
-
print(f"Error generating MCQs: {e}")
|
71 |
return None
|
72 |
|
73 |
# New function to save quiz to database
|
74 |
-
def save_quiz(course_id, session_id, title, questions):
|
75 |
"""Save quiz to database"""
|
76 |
try:
|
77 |
quiz_data = {
|
|
|
78 |
"course_id": course_id,
|
79 |
"session_id": session_id,
|
80 |
"title": title,
|
81 |
"questions": questions,
|
82 |
"created_at": datetime.utcnow(),
|
83 |
-
"status": "active"
|
|
|
84 |
}
|
85 |
result = quizzes_collection.insert_one(quiz_data)
|
86 |
return result.inserted_id
|
@@ -102,36 +131,76 @@ def get_student_quiz_score(quiz_id, student_id):
|
|
102 |
return quiz['submissions'][0].get('score')
|
103 |
return None
|
104 |
|
105 |
-
def submit_quiz_answers(quiz_id, student_id, student_answers):
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
|
119 |
-
|
120 |
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
|
137 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import ast
|
2 |
from pymongo import MongoClient
|
3 |
from datetime import datetime
|
4 |
import openai
|
|
|
24 |
db = client['novascholar_db']
|
25 |
quizzes_collection = db["quizzes"]
|
26 |
|
27 |
+
def strip_code_markers(response_text):
|
28 |
+
"""Strip off the markers ``` and python from a LLM model's response"""
|
29 |
+
if response_text.startswith("```python"):
|
30 |
+
response_text = response_text[len("```python"):].strip()
|
31 |
+
if response_text.startswith("```"):
|
32 |
+
response_text = response_text[len("```"):].strip()
|
33 |
+
if response_text.endswith("```"):
|
34 |
+
response_text = response_text[:-len("```")].strip()
|
35 |
+
return response_text
|
36 |
+
|
37 |
+
|
38 |
# New function to generate MCQs using Gemini
|
39 |
def generate_mcqs(context, num_questions, session_title, session_description):
|
40 |
"""Generate MCQs either from context or session details"""
|
|
|
70 |
}}
|
71 |
|
72 |
Generate challenging but clear questions.
|
73 |
+
Return only the Python list of dictionaries without any additional formatting or markers
|
74 |
+
Do not write any other text, do not start the response with (```python), do not end the response with backticks(```)
|
75 |
+
A Sample response should look like this: Response Text: [
|
76 |
+
{
|
77 |
+
"question": "Which of the following is NOT a valid data type in C++?",
|
78 |
+
"options": ["int", "double", "boolean", "char"],
|
79 |
+
"correct_option": "C"
|
80 |
+
}
|
81 |
+
] (Notice that there are no backticks(```) around the response and no (```python))
|
82 |
+
.
|
83 |
"""
|
84 |
|
85 |
response = model.generate_content(prompt)
|
86 |
+
response_text = response.text.strip()
|
87 |
+
print("Response Text:", response_text)
|
88 |
+
modified_response_text = strip_code_markers(response_text)
|
89 |
+
print("Response Text Modified to:", modified_response_text)
|
90 |
# Extract and parse the response to get the list of MCQs
|
91 |
+
mcqs = ast.literal_eval(modified_response_text) # Be careful with eval, consider using ast.literal_eval for production
|
92 |
print(mcqs)
|
93 |
+
if not mcqs:
|
94 |
+
raise ValueError("No questions generated")
|
95 |
return mcqs
|
96 |
except Exception as e:
|
97 |
+
print(f"Error generating MCQs: , error: {e}")
|
98 |
return None
|
99 |
|
100 |
# New function to save quiz to database
|
101 |
+
def save_quiz(course_id, session_id, title, questions, user_id):
|
102 |
"""Save quiz to database"""
|
103 |
try:
|
104 |
quiz_data = {
|
105 |
+
"user_id": user_id,
|
106 |
"course_id": course_id,
|
107 |
"session_id": session_id,
|
108 |
"title": title,
|
109 |
"questions": questions,
|
110 |
"created_at": datetime.utcnow(),
|
111 |
+
"status": "active",
|
112 |
+
"submissions": []
|
113 |
}
|
114 |
result = quizzes_collection.insert_one(quiz_data)
|
115 |
return result.inserted_id
|
|
|
131 |
return quiz['submissions'][0].get('score')
|
132 |
return None
|
133 |
|
134 |
+
# def submit_quiz_answers(quiz_id, student_id, student_answers):
|
135 |
+
# """Submit and score student's quiz answers"""
|
136 |
+
# quiz = quizzes_collection.find_one({"_id": quiz_id})
|
137 |
+
# if not quiz:
|
138 |
+
# return None
|
139 |
|
140 |
+
# # Calculate score
|
141 |
+
# correct_answers = 0
|
142 |
+
# total_questions = len(quiz['questions'])
|
143 |
|
144 |
+
# for q_idx, question in enumerate(quiz['questions']):
|
145 |
+
# if student_answers.get(str(q_idx)) == question['correct_option']:
|
146 |
+
# correct_answers += 1
|
147 |
|
148 |
+
# score = (correct_answers / total_questions) * 100
|
149 |
|
150 |
+
# # Store submission
|
151 |
+
# submission_data = {
|
152 |
+
# "student_id": student_id,
|
153 |
+
# "answers": student_answers,
|
154 |
+
# "score": score,
|
155 |
+
# "submitted_at": datetime.utcnow()
|
156 |
+
# }
|
157 |
|
158 |
+
# # Update quiz with submission
|
159 |
+
# quizzes_collection.update_one(
|
160 |
+
# {"_id": quiz_id},
|
161 |
+
# {
|
162 |
+
# "$push": {"submissions": submission_data}
|
163 |
+
# }
|
164 |
+
# )
|
165 |
|
166 |
+
# return score
|
167 |
+
def submit_quiz_answers(quiz_id, student_id, student_answers):
|
168 |
+
"""Submit and score student's quiz answers"""
|
169 |
+
try:
|
170 |
+
quiz = quizzes_collection.find_one({"_id": quiz_id})
|
171 |
+
if not quiz:
|
172 |
+
return None
|
173 |
+
|
174 |
+
# Calculate score
|
175 |
+
correct_answers = 0
|
176 |
+
total_questions = len(quiz['questions'])
|
177 |
+
|
178 |
+
for q_idx, question in enumerate(quiz['questions']):
|
179 |
+
student_answer = student_answers.get(str(q_idx))
|
180 |
+
if student_answer: # Only check if answer was provided
|
181 |
+
# Extract the option letter (A, B, C, D) from the full answer string
|
182 |
+
answer_letter = student_answer.split(')')[0].strip()
|
183 |
+
if answer_letter == question['correct_option']:
|
184 |
+
correct_answers += 1
|
185 |
+
|
186 |
+
score = (correct_answers / total_questions) * 100
|
187 |
+
|
188 |
+
# Store submission
|
189 |
+
submission_data = {
|
190 |
+
"student_id": student_id,
|
191 |
+
"answers": student_answers,
|
192 |
+
"score": score,
|
193 |
+
"submitted_at": datetime.utcnow()
|
194 |
+
}
|
195 |
+
|
196 |
+
# Update quiz with submission
|
197 |
+
result = quizzes_collection.update_one(
|
198 |
+
{"_id": quiz_id},
|
199 |
+
{"$push": {"submissions": submission_data}}
|
200 |
+
)
|
201 |
+
|
202 |
+
return score if result.modified_count > 0 else None
|
203 |
+
|
204 |
+
except Exception as e:
|
205 |
+
print(f"Error submitting quiz: {e}")
|
206 |
+
return None
|
main.py
CHANGED
@@ -101,8 +101,9 @@ def login_form():
|
|
101 |
st.title("Welcome to NOVAScholar")
|
102 |
|
103 |
with st.form("login_form"):
|
|
|
104 |
user_type = st.selectbox(
|
105 |
-
"
|
106 |
)
|
107 |
username = st.text_input("Username")
|
108 |
password = st.text_input("Password", type="password")
|
@@ -184,6 +185,11 @@ def create_session_form(course_id):
|
|
184 |
"""Display form to create a new session and perform the creation operation"""
|
185 |
st.title("Create New Session")
|
186 |
|
|
|
|
|
|
|
|
|
|
|
187 |
with st.form("create_session_form"):
|
188 |
session_title = st.text_input("Session Title")
|
189 |
session_date = st.date_input("Session Date", date.today(), key="session_date")
|
@@ -191,9 +197,6 @@ def create_session_form(course_id):
|
|
191 |
"Session Time", st.session_state.session_time, key="session_time"
|
192 |
)
|
193 |
|
194 |
-
if "show_create_session_form" not in st.session_state:
|
195 |
-
st.session_state.show_create_session_form = False
|
196 |
-
|
197 |
new_session_id = None
|
198 |
# Generate new session ID
|
199 |
course = courses_collection2.find_one({"course_id": course_id})
|
|
|
101 |
st.title("Welcome to NOVAScholar")
|
102 |
|
103 |
with st.form("login_form"):
|
104 |
+
|
105 |
user_type = st.selectbox(
|
106 |
+
"Please select your Role", ["student", "faculty", "research_assistant", "analyst"]
|
107 |
)
|
108 |
username = st.text_input("Username")
|
109 |
password = st.text_input("Password", type="password")
|
|
|
185 |
"""Display form to create a new session and perform the creation operation"""
|
186 |
st.title("Create New Session")
|
187 |
|
188 |
+
if 'session_time' not in st.session_state:
|
189 |
+
st.session_state.session_time = datetime.now().time()
|
190 |
+
if 'show_create_session_form' not in st.session_state:
|
191 |
+
st.session_state.show_create_session_form = False
|
192 |
+
|
193 |
with st.form("create_session_form"):
|
194 |
session_title = st.text_input("Session Title")
|
195 |
session_date = st.date_input("Session Date", date.today(), key="session_date")
|
|
|
197 |
"Session Time", st.session_state.session_time, key="session_time"
|
198 |
)
|
199 |
|
|
|
|
|
|
|
200 |
new_session_id = None
|
201 |
# Generate new session ID
|
202 |
course = courses_collection2.find_one({"course_id": course_id})
|
modify_schema.py
ADDED
@@ -0,0 +1,222 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from db import courses_collection2
|
2 |
+
from dotenv import load_dotenv
|
3 |
+
import os
|
4 |
+
from pymongo import MongoClient
|
5 |
+
from datetime import datetime
|
6 |
+
|
7 |
+
|
8 |
+
|
9 |
+
load_dotenv()
|
10 |
+
MONGO_URI = os.getenv("MONGO_URI")
|
11 |
+
|
12 |
+
client = MongoClient(MONGO_URI)
|
13 |
+
db = client["novascholar_db"]
|
14 |
+
|
15 |
+
# Define the updated course schema
|
16 |
+
updated_course_schema = {
|
17 |
+
"bsonType": "object",
|
18 |
+
"required": [
|
19 |
+
"course_id",
|
20 |
+
"title",
|
21 |
+
"description",
|
22 |
+
"faculty",
|
23 |
+
"faculty_id",
|
24 |
+
"duration",
|
25 |
+
"created_at",
|
26 |
+
],
|
27 |
+
"properties": {
|
28 |
+
"course_id": {
|
29 |
+
"bsonType": "string",
|
30 |
+
"description": "Unique identifier for the course",
|
31 |
+
},
|
32 |
+
"title": {"bsonType": "string", "description": "Title of the course"},
|
33 |
+
"description": {
|
34 |
+
"bsonType": "string",
|
35 |
+
"description": "Description of the course",
|
36 |
+
},
|
37 |
+
"faculty": {"bsonType": "string", "description": "Name of the faculty"},
|
38 |
+
"duration": {"bsonType": "string", "description": "Duration of the course"},
|
39 |
+
"created_at": {
|
40 |
+
"bsonType": "date",
|
41 |
+
"description": "Date when the course was created",
|
42 |
+
},
|
43 |
+
"sessions": {
|
44 |
+
"bsonType": "array",
|
45 |
+
"description": "List of sessions associated with the course",
|
46 |
+
"items": {
|
47 |
+
"bsonType": "object",
|
48 |
+
"required": ["session_id", "title", "date"],
|
49 |
+
"properties": {
|
50 |
+
"session_id": {
|
51 |
+
"bsonType": "string",
|
52 |
+
"description": "Unique identifier for the session",
|
53 |
+
},
|
54 |
+
"title": {
|
55 |
+
"bsonType": "string",
|
56 |
+
"description": "Title of the session",
|
57 |
+
},
|
58 |
+
"date": {"bsonType": "date", "description": "Date of the session"},
|
59 |
+
"status": {
|
60 |
+
"bsonType": "string",
|
61 |
+
"description": "Status of the session (e.g., completed, upcoming)",
|
62 |
+
},
|
63 |
+
"created_at": {
|
64 |
+
"bsonType": "date",
|
65 |
+
"description": "Date when the session was created",
|
66 |
+
},
|
67 |
+
"pre_class": {
|
68 |
+
"bsonType": "object",
|
69 |
+
"description": "Pre-class segment data",
|
70 |
+
"properties": {
|
71 |
+
"resources": {
|
72 |
+
"bsonType": "array",
|
73 |
+
"description": "List of pre-class resources",
|
74 |
+
"items": {
|
75 |
+
"bsonType": "object",
|
76 |
+
"required": ["type", "title", "url"],
|
77 |
+
"properties": {
|
78 |
+
"type": {
|
79 |
+
"bsonType": "string",
|
80 |
+
"description": "Type of resource (e.g., pdf, video)",
|
81 |
+
},
|
82 |
+
"title": {
|
83 |
+
"bsonType": "string",
|
84 |
+
"description": "Title of the resource",
|
85 |
+
},
|
86 |
+
"url": {
|
87 |
+
"bsonType": "string",
|
88 |
+
"description": "URL of the resource",
|
89 |
+
},
|
90 |
+
"vector": {
|
91 |
+
"bsonType": "array",
|
92 |
+
"description": "Vector representation of the resource",
|
93 |
+
"items": {"bsonType": "double"},
|
94 |
+
},
|
95 |
+
},
|
96 |
+
},
|
97 |
+
},
|
98 |
+
"completion_required": {
|
99 |
+
"bsonType": "bool",
|
100 |
+
"description": "Indicates if completion of pre-class resources is required",
|
101 |
+
},
|
102 |
+
},
|
103 |
+
},
|
104 |
+
"in_class": {
|
105 |
+
"bsonType": "object",
|
106 |
+
"description": "In-class segment data",
|
107 |
+
"properties": {
|
108 |
+
"topics": {
|
109 |
+
"bsonType": "array",
|
110 |
+
"description": "List of topics covered in the session",
|
111 |
+
"items": {"bsonType": "string"},
|
112 |
+
},
|
113 |
+
"quiz": {
|
114 |
+
"bsonType": "object",
|
115 |
+
"description": "Quiz data",
|
116 |
+
"properties": {
|
117 |
+
"title": {
|
118 |
+
"bsonType": "string",
|
119 |
+
"description": "Title of the quiz",
|
120 |
+
},
|
121 |
+
"questions": {
|
122 |
+
"bsonType": "int",
|
123 |
+
"description": "Number of questions in the quiz",
|
124 |
+
},
|
125 |
+
"duration": {
|
126 |
+
"bsonType": "int",
|
127 |
+
"description": "Duration of the quiz in minutes",
|
128 |
+
},
|
129 |
+
},
|
130 |
+
},
|
131 |
+
"polls": {
|
132 |
+
"bsonType": "array",
|
133 |
+
"description": "List of polls conducted during the session",
|
134 |
+
"items": {
|
135 |
+
"bsonType": "object",
|
136 |
+
"required": ["question", "options"],
|
137 |
+
"properties": {
|
138 |
+
"question": {
|
139 |
+
"bsonType": "string",
|
140 |
+
"description": "Poll question",
|
141 |
+
},
|
142 |
+
"options": {
|
143 |
+
"bsonType": "array",
|
144 |
+
"description": "List of poll options",
|
145 |
+
"items": {"bsonType": "string"},
|
146 |
+
},
|
147 |
+
"responses": {
|
148 |
+
"bsonType": "object",
|
149 |
+
"description": "Responses to the poll",
|
150 |
+
"additionalProperties": {"bsonType": "int"},
|
151 |
+
},
|
152 |
+
},
|
153 |
+
},
|
154 |
+
},
|
155 |
+
},
|
156 |
+
},
|
157 |
+
"post_class": {
|
158 |
+
"bsonType": "object",
|
159 |
+
"description": "Post-class segment data",
|
160 |
+
"properties": {
|
161 |
+
"assignments": {
|
162 |
+
"bsonType": "array",
|
163 |
+
"description": "List of assignments",
|
164 |
+
"items": {
|
165 |
+
"bsonType": "object",
|
166 |
+
"required": ["id", "title", "due_date", "status"],
|
167 |
+
"properties": {
|
168 |
+
"id": {
|
169 |
+
"bsonType": ["objectId", "int"],
|
170 |
+
"description": "Assignment ID",
|
171 |
+
},
|
172 |
+
"title": {
|
173 |
+
"bsonType": "string",
|
174 |
+
"description": "Title of the assignment",
|
175 |
+
},
|
176 |
+
"due_date": {
|
177 |
+
"bsonType": "date",
|
178 |
+
"description": "Due date of the assignment",
|
179 |
+
},
|
180 |
+
"status": {
|
181 |
+
"bsonType": "string",
|
182 |
+
"description": "Status of the assignment (e.g., pending, completed)",
|
183 |
+
},
|
184 |
+
"submissions": {
|
185 |
+
"bsonType": "array",
|
186 |
+
"description": "List of submissions",
|
187 |
+
"items": {
|
188 |
+
"bsonType": "object",
|
189 |
+
"properties": {
|
190 |
+
"student_id": {
|
191 |
+
"bsonType": "objectId",
|
192 |
+
"description": "ID of the student who submitted the assignment",
|
193 |
+
},
|
194 |
+
"file_url": {
|
195 |
+
"bsonType": "string",
|
196 |
+
"description": "URL of the submitted file",
|
197 |
+
},
|
198 |
+
"submitted_at": {
|
199 |
+
"bsonType": "date",
|
200 |
+
"description": "Date when the assignment was submitted",
|
201 |
+
},
|
202 |
+
},
|
203 |
+
},
|
204 |
+
},
|
205 |
+
},
|
206 |
+
},
|
207 |
+
}
|
208 |
+
},
|
209 |
+
},
|
210 |
+
},
|
211 |
+
},
|
212 |
+
},
|
213 |
+
},
|
214 |
+
}
|
215 |
+
|
216 |
+
# Update the schema using the collMod command
|
217 |
+
db.command({
|
218 |
+
"collMod": "courses_collection2",
|
219 |
+
"validator": {"$jsonSchema": updated_course_schema}
|
220 |
+
})
|
221 |
+
|
222 |
+
print("Schema updated successfully!")
|
session_page.py
CHANGED
@@ -1,3 +1,4 @@
|
|
|
|
1 |
import streamlit as st
|
2 |
from datetime import datetime
|
3 |
from utils.helpers import display_progress_bar, create_notification, format_datetime
|
@@ -24,7 +25,7 @@ def get_current_user():
|
|
24 |
return None
|
25 |
return students_collection.find_one({"_id": st.session_state.user_id})
|
26 |
|
27 |
-
def display_preclass_content(session, student_id, course_id):
|
28 |
"""Display pre-class materials for a session"""
|
29 |
|
30 |
# Initialize 'messages' in session_state if it doesn't exist
|
@@ -101,13 +102,308 @@ def display_preclass_content(session, student_id, course_id):
|
|
101 |
with st.chat_message("assistant"):
|
102 |
st.markdown(response)
|
103 |
|
104 |
-
st.subheader("Your Chat History")
|
105 |
-
for message in st.session_state.messages:
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
user = get_current_user()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
111 |
|
112 |
def display_in_class_content(session, user_type):
|
113 |
# """Display in-class activities and interactions"""
|
@@ -123,6 +419,10 @@ def display_in_class_content(session, user_type):
|
|
123 |
else:
|
124 |
live_polls.display_student_interface(session['session_id'])
|
125 |
|
|
|
|
|
|
|
|
|
126 |
def display_post_class_content(session, student_id, course_id):
|
127 |
"""Display post-class assignments and submissions"""
|
128 |
st.header("Post-class Work")
|
@@ -178,17 +478,22 @@ def display_post_class_content(session, student_id, course_id):
|
|
178 |
st.success("Quiz saved successfully!")
|
179 |
else:
|
180 |
st.error("Error saving quiz.")
|
|
|
|
|
181 |
|
182 |
st.subheader("Add Assignments")
|
183 |
# Add assignment form
|
184 |
with st.form("add_assignment_form"):
|
185 |
title = st.text_input("Assignment Title")
|
186 |
due_date = st.date_input("Due Date")
|
|
|
187 |
submit = st.form_submit_button("Add Assignment")
|
188 |
|
189 |
if submit:
|
190 |
due_date = datetime.combine(due_date, datetime.min.time())
|
191 |
# Save the assignment to the database
|
|
|
|
|
192 |
assignment = {
|
193 |
"id": ObjectId(),
|
194 |
"title": title,
|
|
|
1 |
+
import random
|
2 |
import streamlit as st
|
3 |
from datetime import datetime
|
4 |
from utils.helpers import display_progress_bar, create_notification, format_datetime
|
|
|
25 |
return None
|
26 |
return students_collection.find_one({"_id": st.session_state.user_id})
|
27 |
|
28 |
+
# def display_preclass_content(session, student_id, course_id):
|
29 |
"""Display pre-class materials for a session"""
|
30 |
|
31 |
# Initialize 'messages' in session_state if it doesn't exist
|
|
|
102 |
with st.chat_message("assistant"):
|
103 |
st.markdown(response)
|
104 |
|
105 |
+
# st.subheader("Your Chat History")
|
106 |
+
# for message in st.session_state.messages:
|
107 |
+
# content = message.get("content", "") # Default to an empty string if "content" is not present
|
108 |
+
# role = message.get("role", "user") # Default to "user" if "role" is not present
|
109 |
+
# with st.chat_message(role):
|
110 |
+
# st.markdown(content)
|
111 |
+
# user = get_current_user()
|
112 |
+
|
113 |
+
def display_preclass_content(session, student_id, course_id):
|
114 |
+
"""Display pre-class materials for a session"""
|
115 |
+
st.subheader("Pre-class Materials")
|
116 |
+
|
117 |
+
# Display pre-class materials
|
118 |
+
materials = resources_collection.find({"session_id": session['session_id']})
|
119 |
+
for material in materials:
|
120 |
+
with st.expander(f"{material['file_name']} ({material['material_type'].upper()})"):
|
121 |
+
file_type = material.get('file_type', 'unknown')
|
122 |
+
if file_type == 'application/pdf':
|
123 |
+
st.markdown(f"📑 [Open PDF Document]({material['file_name']})")
|
124 |
+
if st.button("View PDF", key=f"view_pdf_{material['file_name']}"):
|
125 |
+
st.text_area("PDF Content", material['text_content'], height=300)
|
126 |
+
if st.button("Download PDF", key=f"download_pdf_{material['file_name']}"):
|
127 |
+
st.download_button(
|
128 |
+
label="Download PDF",
|
129 |
+
data=material['file_content'],
|
130 |
+
file_name=material['file_name'],
|
131 |
+
mime='application/pdf'
|
132 |
+
)
|
133 |
+
if st.button("Mark PDF as Read", key=f"pdf_{material['file_name']}"):
|
134 |
+
create_notification("PDF marked as read!", "success")
|
135 |
+
|
136 |
+
# Chat input
|
137 |
+
# Add a check, if materials are available, only then show the chat input
|
138 |
+
if(st.session_state.user_type == "student"):
|
139 |
+
if materials:
|
140 |
+
if prompt := st.chat_input("Ask a question about Pre-class Materials"):
|
141 |
+
if len(st.session_state.messages) >= 20:
|
142 |
+
st.warning("Message limit (20) reached for this session.")
|
143 |
+
return
|
144 |
+
|
145 |
+
st.session_state.messages.append({"role": "user", "content": prompt})
|
146 |
+
|
147 |
+
# Display User Message
|
148 |
+
with st.chat_message("user"):
|
149 |
+
st.markdown(prompt)
|
150 |
+
|
151 |
+
# Get document context
|
152 |
+
context = ""
|
153 |
+
materials = resources_collection.find({"session_id": session['session_id']})
|
154 |
+
context = ""
|
155 |
+
vector_data = None
|
156 |
+
|
157 |
+
context = ""
|
158 |
+
for material in materials:
|
159 |
+
resource_id = material['_id']
|
160 |
+
vector_data = vectors_collection.find_one({"resource_id": resource_id})
|
161 |
+
if vector_data and 'text' in vector_data:
|
162 |
+
context += vector_data['text'] + "\n"
|
163 |
+
|
164 |
+
if not vector_data:
|
165 |
+
st.error("No Pre-class materials found for this session.")
|
166 |
+
return
|
167 |
+
|
168 |
+
try:
|
169 |
+
# Generate response using Gemini
|
170 |
+
context_prompt = f"""
|
171 |
+
Based on the following context, answer the user's question:
|
172 |
+
|
173 |
+
Context:
|
174 |
+
{context}
|
175 |
+
|
176 |
+
Question: {prompt}
|
177 |
+
|
178 |
+
Please provide a clear and concise answer based only on the information provided in the context.
|
179 |
+
"""
|
180 |
+
|
181 |
+
response = model.generate_content(context_prompt)
|
182 |
+
if not response or not response.text:
|
183 |
+
st.error("No response received from the model")
|
184 |
+
return
|
185 |
+
|
186 |
+
assistant_response = response.text
|
187 |
+
# Display Assistant Response
|
188 |
+
with st.chat_message("assistant"):
|
189 |
+
st.markdown(assistant_response)
|
190 |
+
|
191 |
+
# Build the message
|
192 |
+
new_message = {
|
193 |
+
"prompt": prompt,
|
194 |
+
"response": assistant_response,
|
195 |
+
"timestamp": datetime.utcnow()
|
196 |
+
}
|
197 |
+
st.session_state.messages.append(new_message)
|
198 |
+
|
199 |
+
# Update database
|
200 |
+
try:
|
201 |
+
chat_history_collection.update_one(
|
202 |
+
{
|
203 |
+
"user_id": student_id,
|
204 |
+
"session_id": session['session_id']
|
205 |
+
},
|
206 |
+
{
|
207 |
+
"$push": {"messages": new_message},
|
208 |
+
"$setOnInsert": {
|
209 |
+
"user_id": student_id,
|
210 |
+
"session_id": session['session_id'],
|
211 |
+
"timestamp": datetime.utcnow()
|
212 |
+
}
|
213 |
+
},
|
214 |
+
upsert=True
|
215 |
+
)
|
216 |
+
except Exception as db_error:
|
217 |
+
st.error(f"Error saving chat history: {str(db_error)}")
|
218 |
+
except Exception as e:
|
219 |
+
st.error(f"Error generating response: {str(e)}")
|
220 |
+
|
221 |
+
else:
|
222 |
+
st.subheader("Upload Pre-class Material")
|
223 |
+
# File upload section for students
|
224 |
+
uploaded_file = st.file_uploader("Upload Material", type=['txt', 'pdf', 'docx'])
|
225 |
+
if uploaded_file is not None:
|
226 |
+
with st.spinner("Processing document..."):
|
227 |
+
file_name = uploaded_file.name
|
228 |
+
file_content = extract_text_from_file(uploaded_file)
|
229 |
+
if file_content:
|
230 |
+
material_type = st.selectbox("Select Material Type", ["pdf", "docx", "txt"])
|
231 |
+
if st.button("Upload Material"):
|
232 |
+
upload_resource(course_id, session['session_id'], file_name, uploaded_file, material_type)
|
233 |
+
|
234 |
+
# Search for the newly uploaded resource's _id in resources_collection
|
235 |
+
resource_id = resources_collection.find_one({"file_name": file_name})["_id"]
|
236 |
+
create_vector_store(file_content, resource_id)
|
237 |
+
st.success("Material uploaded successfully!")
|
238 |
+
|
239 |
+
# st.subheader("Your Chat History")
|
240 |
+
if st.button("View Chat History"):
|
241 |
+
# Initialize chat messages from database
|
242 |
+
if 'messages' not in st.session_state or not st.session_state.messages:
|
243 |
+
existing_chat = chat_history_collection.find_one({
|
244 |
+
"user_id": student_id,
|
245 |
+
"session_id": session['session_id']
|
246 |
+
})
|
247 |
+
if existing_chat and 'messages' in existing_chat:
|
248 |
+
st.session_state.messages = existing_chat['messages']
|
249 |
+
else:
|
250 |
+
st.session_state.messages = []
|
251 |
+
|
252 |
+
# Display existing chat history
|
253 |
+
try:
|
254 |
+
for message in st.session_state.messages:
|
255 |
+
if 'prompt' in message and 'response' in message:
|
256 |
+
with st.chat_message("user"):
|
257 |
+
st.markdown(message["prompt"])
|
258 |
+
with st.chat_message("assistant"):
|
259 |
+
st.markdown(message["response"])
|
260 |
+
except Exception as e:
|
261 |
+
st.error(f"Error displaying chat history: {str(e)}")
|
262 |
+
st.session_state.messages = []
|
263 |
+
|
264 |
+
if st.session_state.user_type == 'student':
|
265 |
+
st.subheader("Create a Practice Quiz")
|
266 |
+
questions = []
|
267 |
+
quiz_id = ""
|
268 |
+
with st.form("create_quiz_form"):
|
269 |
+
num_questions = st.number_input("Number of Questions", min_value=1, max_value=20, value=2)
|
270 |
+
submit_quiz = st.form_submit_button("Generate Quiz")
|
271 |
+
if submit_quiz:
|
272 |
+
# Get pre-class materials from resources_collection
|
273 |
+
materials = resources_collection.find({"session_id": session['session_id']})
|
274 |
+
context = ""
|
275 |
+
for material in materials:
|
276 |
+
if 'text_content' in material:
|
277 |
+
context += material['text_content'] + "\n"
|
278 |
+
|
279 |
+
if not context:
|
280 |
+
st.error("No pre-class materials found for this session.")
|
281 |
+
return
|
282 |
+
|
283 |
+
# Generate MCQs from context
|
284 |
+
questions = generate_mcqs(context, num_questions, session['title'], session.get('description', ''))
|
285 |
+
if questions:
|
286 |
+
quiz_id = save_quiz(course_id, session['session_id'], "Practice Quiz", questions, student_id)
|
287 |
+
if quiz_id:
|
288 |
+
st.success("Quiz saved successfully!")
|
289 |
+
st.session_state.show_quizzes = True
|
290 |
+
else:
|
291 |
+
st.error("Error saving quiz.")
|
292 |
+
else:
|
293 |
+
st.error("Error generating questions.")
|
294 |
+
|
295 |
+
# if st.button("Attempt Practice Quizzes "):
|
296 |
+
# quizzes = list(quizzes_collection.find({"course_id": course_id, "session_id": session['session_id'], "user_id": student_id}))
|
297 |
+
|
298 |
+
|
299 |
+
if getattr(st.session_state, 'show_quizzes', False):
|
300 |
+
# quiz = quizzes_collection.find_one({"course_id": course_id, "session_id": session['session_id'], "user_id": student_id})
|
301 |
+
quiz = quizzes_collection.find_one(
|
302 |
+
{"course_id": course_id, "session_id": session['session_id'], "user_id": student_id},
|
303 |
+
sort=[("created_at", -1)]
|
304 |
+
)
|
305 |
+
if not quiz:
|
306 |
+
st.info("No practice quizzes created.")
|
307 |
+
else:
|
308 |
+
with st.expander(f"📝 Practice Quiz", expanded=False):
|
309 |
+
# Check if student has already taken this quiz
|
310 |
+
existing_score = get_student_quiz_score(quiz['_id'], student_id)
|
311 |
+
|
312 |
+
if existing_score is not None:
|
313 |
+
st.success(f"Quiz completed! Your score: {existing_score:.1f}%")
|
314 |
+
|
315 |
+
# Display correct answers after submission
|
316 |
+
st.subheader("Quiz Review")
|
317 |
+
for i, question in enumerate(quiz['questions']):
|
318 |
+
st.markdown(f"**Question {i+1}:** {question['question']}")
|
319 |
+
for opt in question['options']:
|
320 |
+
if opt.startswith(question['correct_option']):
|
321 |
+
st.markdown(f"✅ {opt}")
|
322 |
+
else:
|
323 |
+
st.markdown(f"- {opt}")
|
324 |
+
|
325 |
+
else:
|
326 |
+
# Initialize quiz state for this specific quiz
|
327 |
+
quiz_key = f"quiz_{quiz['_id']}_student_{student_id}"
|
328 |
+
if quiz_key not in st.session_state:
|
329 |
+
st.session_state[quiz_key] = {
|
330 |
+
'submitted': False,
|
331 |
+
'score': None,
|
332 |
+
'answers': {}
|
333 |
+
}
|
334 |
+
|
335 |
+
# If quiz was just submitted, show the results
|
336 |
+
if st.session_state[quiz_key]['submitted']:
|
337 |
+
st.success(f"Quiz submitted successfully! Your score: {st.session_state[quiz_key]['score']:.1f}%")
|
338 |
+
# Reset the quiz state
|
339 |
+
st.session_state[quiz_key]['submitted'] = False
|
340 |
+
|
341 |
+
|
342 |
+
# Display quiz questions
|
343 |
+
st.write("Please select your answers:")
|
344 |
+
|
345 |
+
# Create a form for quiz submission
|
346 |
+
form_key = f"quiz_form_{quiz['_id']}_student_{student_id}"
|
347 |
+
with st.form(key=form_key):
|
348 |
+
student_answers = {}
|
349 |
+
|
350 |
+
for i, question in enumerate(quiz['questions']):
|
351 |
+
st.markdown(f"**Question {i+1}:** {question['question']}")
|
352 |
+
options = [opt for opt in question['options']]
|
353 |
+
# student_answers[str(i)] = st.radio(
|
354 |
+
# f"Select answer for question {i+1}:",
|
355 |
+
# options=options,
|
356 |
+
# key=f"q_{i}",
|
357 |
+
# index=None
|
358 |
+
# )
|
359 |
+
answer = st.radio(
|
360 |
+
f"Select answer for question {i+1}:",
|
361 |
+
options=options,
|
362 |
+
key=f"{quiz['_id']}_{i}", # Simplify the radio button key
|
363 |
+
index=None
|
364 |
+
)
|
365 |
+
if answer: # Only add to answers if a selection was made
|
366 |
+
student_answers[str(i)] = answer
|
367 |
+
|
368 |
+
# Submit button
|
369 |
+
# submitted = st.form_submit_button("Submit Quiz")
|
370 |
+
print("Before the submit button")
|
371 |
+
submit_button = st.form_submit_button("Submit Quiz")
|
372 |
+
print("After the submit button")
|
373 |
+
if submit_button and student_answers:
|
374 |
+
print("Clicked the button")
|
375 |
+
print(student_answers)
|
376 |
+
correct_answers = 0
|
377 |
+
for i, question in enumerate(quiz['questions']):
|
378 |
+
if student_answers[str(i)] == question['correct_option']:
|
379 |
+
correct_answers += 1
|
380 |
+
score = (correct_answers / len(quiz['questions'])) * 100
|
381 |
+
|
382 |
+
if score is not None:
|
383 |
+
st.success(f"Quiz submitted successfully! Your score: {score:.1f}%")
|
384 |
+
st.session_state[quiz_key]['submitted'] = True
|
385 |
+
st.session_state[quiz_key]['score'] = score
|
386 |
+
st.session_state[quiz_key]['answers'] = student_answers
|
387 |
+
# This will trigger a rerun, but now we'll handle it properly
|
388 |
+
st.rerun()
|
389 |
+
|
390 |
+
else:
|
391 |
+
st.error("Error submitting quiz. Please try again.")
|
392 |
+
# correct_answers = 0
|
393 |
+
# for i, question in enumerate(quiz['questions']):
|
394 |
+
# if student_answers[str(i)] == question['correct_option']:
|
395 |
+
# correct_answers += 1
|
396 |
+
# score = (correct_answers / len(quiz['questions'])) * 100
|
397 |
+
# print(score)
|
398 |
+
# try:
|
399 |
+
# quizzes_collection.update_one(
|
400 |
+
# {"_id": quiz['_id']},
|
401 |
+
# {"$push": {"submissions": {"student_id": student_id, "score": score}}}
|
402 |
+
# )
|
403 |
+
# st.success(f"Quiz submitted successfully! Your score: {score:.1f}%")
|
404 |
+
# except Exception as db_error:
|
405 |
+
# st.error(f"Error saving submission: {str(db_error)}")
|
406 |
+
|
407 |
|
408 |
def display_in_class_content(session, user_type):
|
409 |
# """Display in-class activities and interactions"""
|
|
|
419 |
else:
|
420 |
live_polls.display_student_interface(session['session_id'])
|
421 |
|
422 |
+
def generate_random_assignment_id():
|
423 |
+
"""Generate a random integer ID for assignments"""
|
424 |
+
return random.randint(100000, 999999)
|
425 |
+
|
426 |
def display_post_class_content(session, student_id, course_id):
|
427 |
"""Display post-class assignments and submissions"""
|
428 |
st.header("Post-class Work")
|
|
|
478 |
st.success("Quiz saved successfully!")
|
479 |
else:
|
480 |
st.error("Error saving quiz.")
|
481 |
+
else:
|
482 |
+
st.error("Error generating questions.")
|
483 |
|
484 |
st.subheader("Add Assignments")
|
485 |
# Add assignment form
|
486 |
with st.form("add_assignment_form"):
|
487 |
title = st.text_input("Assignment Title")
|
488 |
due_date = st.date_input("Due Date")
|
489 |
+
due_date = datetime.strptime(due_date.strftime('%Y-%m-%d'), '%Y-%m-%d')
|
490 |
submit = st.form_submit_button("Add Assignment")
|
491 |
|
492 |
if submit:
|
493 |
due_date = datetime.combine(due_date, datetime.min.time())
|
494 |
# Save the assignment to the database
|
495 |
+
assignment_id = ObjectId()
|
496 |
+
print("Assignment ID: ", assignment_id)
|
497 |
assignment = {
|
498 |
"id": ObjectId(),
|
499 |
"title": title,
|