Harshal Vhatkar commited on
Commit
1586102
·
1 Parent(s): b572b15

add quiz in pre-class

Browse files
Files changed (7) hide show
  1. chatbot.py +16 -0
  2. course_creation.py +108 -0
  3. file_upload_vectorize.py +3 -1
  4. gen_mcqs.py +101 -32
  5. main.py +7 -4
  6. modify_schema.py +222 -0
  7. 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
- "uploaded_at": datetime.utcnow()
 
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 = eval(response.text) # Be careful with eval, consider using ast.literal_eval for production
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
- """Submit and score student's quiz answers"""
107
- quiz = quizzes_collection.find_one({"_id": quiz_id})
108
- if not quiz:
109
- return None
110
 
111
- # Calculate score
112
- correct_answers = 0
113
- total_questions = len(quiz['questions'])
114
 
115
- for q_idx, question in enumerate(quiz['questions']):
116
- if student_answers.get(str(q_idx)) == question['correct_option']:
117
- correct_answers += 1
118
 
119
- score = (correct_answers / total_questions) * 100
120
 
121
- # Store submission
122
- submission_data = {
123
- "student_id": student_id,
124
- "answers": student_answers,
125
- "score": score,
126
- "submitted_at": datetime.utcnow()
127
- }
128
 
129
- # Update quiz with submission
130
- quizzes_collection.update_one(
131
- {"_id": quiz_id},
132
- {
133
- "$push": {"submissions": submission_data}
134
- }
135
- )
136
 
137
- return score
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- "Select User Type", ["student", "faculty", "research_assistant", "analyst"]
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
- content = message.get("content", "") # Default to an empty string if "content" is not present
107
- role = message.get("role", "user") # Default to "user" if "role" is not present
108
- with st.chat_message(role):
109
- st.markdown(content)
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,