Spaces:
Build error
Build error
Harshal V
commited on
Commit
·
9630b25
0
Parent(s):
Initial Commit
Browse files- .gitignore +3 -0
- db.py +535 -0
- file_upload_vectorize.py +105 -0
- main.py +134 -0
- session_page.py +270 -0
- utils/helpers.py +83 -0
- utils/sample_data.py +226 -0
.gitignore
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
# Ignore .env file
|
2 |
+
.env
|
3 |
+
__pycache__/
|
db.py
ADDED
@@ -0,0 +1,535 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Setup for MongoDB
|
2 |
+
from pymongo import MongoClient
|
3 |
+
from datetime import datetime
|
4 |
+
from werkzeug.security import generate_password_hash
|
5 |
+
import os
|
6 |
+
from dotenv import load_dotenv
|
7 |
+
|
8 |
+
load_dotenv()
|
9 |
+
MONGO_URI = os.getenv('MONGO_URI')
|
10 |
+
|
11 |
+
client = MongoClient(MONGO_URI)
|
12 |
+
try:
|
13 |
+
client.admin.command('ping')
|
14 |
+
print("MongoDB connection successful")
|
15 |
+
except Exception as e:
|
16 |
+
print(f"MongoDB connection failed: {e}")
|
17 |
+
|
18 |
+
db = client['novascholar_db']
|
19 |
+
|
20 |
+
# Define the course schema
|
21 |
+
course_schema = {
|
22 |
+
"bsonType": "object",
|
23 |
+
"required": ["course_id", "title", "description", "faculty", "faculty_id", "duration", "created_at"],
|
24 |
+
"properties": {
|
25 |
+
"course_id": {
|
26 |
+
"bsonType": "string",
|
27 |
+
"description": "Unique identifier for the course"
|
28 |
+
},
|
29 |
+
"title": {
|
30 |
+
"bsonType": "string",
|
31 |
+
"description": "Title of the course"
|
32 |
+
},
|
33 |
+
"description": {
|
34 |
+
"bsonType": "string",
|
35 |
+
"description": "Description of the course"
|
36 |
+
},
|
37 |
+
"faculty": {
|
38 |
+
"bsonType": "string",
|
39 |
+
"description": "Name of the faculty"
|
40 |
+
},
|
41 |
+
"duration": {
|
42 |
+
"bsonType": "string",
|
43 |
+
"description": "Duration of the course"
|
44 |
+
},
|
45 |
+
"created_at": {
|
46 |
+
"bsonType": "date",
|
47 |
+
"description": "Date when the course was created"
|
48 |
+
},
|
49 |
+
"sessions": {
|
50 |
+
"bsonType": "array",
|
51 |
+
"description": "List of sessions associated with the course",
|
52 |
+
"items": {
|
53 |
+
"bsonType": "object",
|
54 |
+
"required": ["session_id", "title", "date", "status", "created_at"],
|
55 |
+
"properties": {
|
56 |
+
"session_id": {
|
57 |
+
"bsonType": "string",
|
58 |
+
"description": "Unique identifier for the session"
|
59 |
+
},
|
60 |
+
"title": {
|
61 |
+
"bsonType": "string",
|
62 |
+
"description": "Title of the session"
|
63 |
+
},
|
64 |
+
"date": {
|
65 |
+
"bsonType": "date",
|
66 |
+
"description": "Date of the session"
|
67 |
+
},
|
68 |
+
"status": {
|
69 |
+
"bsonType": "string",
|
70 |
+
"description": "Status of the session (e.g., completed, upcoming)"
|
71 |
+
},
|
72 |
+
"created_at": {
|
73 |
+
"bsonType": "date",
|
74 |
+
"description": "Date when the session was created"
|
75 |
+
},
|
76 |
+
"pre_class": {
|
77 |
+
"bsonType": "object",
|
78 |
+
"description": "Pre-class segment data",
|
79 |
+
"properties": {
|
80 |
+
"resources": {
|
81 |
+
"bsonType": "array",
|
82 |
+
"description": "List of pre-class resources",
|
83 |
+
"items": {
|
84 |
+
"bsonType": "object",
|
85 |
+
"required": ["type", "title", "url"],
|
86 |
+
"properties": {
|
87 |
+
"type": {
|
88 |
+
"bsonType": "string",
|
89 |
+
"description": "Type of resource (e.g., pdf, video)"
|
90 |
+
},
|
91 |
+
"title": {
|
92 |
+
"bsonType": "string",
|
93 |
+
"description": "Title of the resource"
|
94 |
+
},
|
95 |
+
"url": {
|
96 |
+
"bsonType": "string",
|
97 |
+
"description": "URL of the resource"
|
98 |
+
},
|
99 |
+
"vector": {
|
100 |
+
"bsonType": "array",
|
101 |
+
"description": "Vector representation of the resource",
|
102 |
+
"items": {
|
103 |
+
"bsonType": "double"
|
104 |
+
}
|
105 |
+
}
|
106 |
+
}
|
107 |
+
}
|
108 |
+
},
|
109 |
+
"completion_required": {
|
110 |
+
"bsonType": "bool",
|
111 |
+
"description": "Indicates if completion of pre-class resources is required"
|
112 |
+
}
|
113 |
+
}
|
114 |
+
},
|
115 |
+
"in_class": {
|
116 |
+
"bsonType": "object",
|
117 |
+
"description": "In-class segment data",
|
118 |
+
"properties": {
|
119 |
+
"topics": {
|
120 |
+
"bsonType": "array",
|
121 |
+
"description": "List of topics covered in the session",
|
122 |
+
"items": {
|
123 |
+
"bsonType": "string"
|
124 |
+
}
|
125 |
+
},
|
126 |
+
"quiz": {
|
127 |
+
"bsonType": "object",
|
128 |
+
"description": "Quiz data",
|
129 |
+
"properties": {
|
130 |
+
"title": {
|
131 |
+
"bsonType": "string",
|
132 |
+
"description": "Title of the quiz"
|
133 |
+
},
|
134 |
+
"questions": {
|
135 |
+
"bsonType": "int",
|
136 |
+
"description": "Number of questions in the quiz"
|
137 |
+
},
|
138 |
+
"duration": {
|
139 |
+
"bsonType": "int",
|
140 |
+
"description": "Duration of the quiz in minutes"
|
141 |
+
}
|
142 |
+
}
|
143 |
+
},
|
144 |
+
"polls": {
|
145 |
+
"bsonType": "array",
|
146 |
+
"description": "List of polls conducted during the session",
|
147 |
+
"items": {
|
148 |
+
"bsonType": "object",
|
149 |
+
"required": ["question", "options"],
|
150 |
+
"properties": {
|
151 |
+
"question": {
|
152 |
+
"bsonType": "string",
|
153 |
+
"description": "Poll question"
|
154 |
+
},
|
155 |
+
"options": {
|
156 |
+
"bsonType": "array",
|
157 |
+
"description": "List of poll options",
|
158 |
+
"items": {
|
159 |
+
"bsonType": "string"
|
160 |
+
}
|
161 |
+
},
|
162 |
+
"responses": {
|
163 |
+
"bsonType": "object",
|
164 |
+
"description": "Responses to the poll",
|
165 |
+
"additionalProperties": {
|
166 |
+
"bsonType": "int"
|
167 |
+
}
|
168 |
+
}
|
169 |
+
}
|
170 |
+
}
|
171 |
+
}
|
172 |
+
}
|
173 |
+
},
|
174 |
+
"post_class": {
|
175 |
+
"bsonType": "object",
|
176 |
+
"description": "Post-class segment data",
|
177 |
+
"properties": {
|
178 |
+
"assignments": {
|
179 |
+
"bsonType": "array",
|
180 |
+
"description": "List of assignments",
|
181 |
+
"items": {
|
182 |
+
"bsonType": "object",
|
183 |
+
"required": ["id", "title", "due_date", "status"],
|
184 |
+
"properties": {
|
185 |
+
"id": {
|
186 |
+
"bsonType": "int",
|
187 |
+
"description": "Assignment ID"
|
188 |
+
},
|
189 |
+
"title": {
|
190 |
+
"bsonType": "string",
|
191 |
+
"description": "Title of the assignment"
|
192 |
+
},
|
193 |
+
"due_date": {
|
194 |
+
"bsonType": "date",
|
195 |
+
"description": "Due date of the assignment"
|
196 |
+
},
|
197 |
+
"status": {
|
198 |
+
"bsonType": "string",
|
199 |
+
"description": "Status of the assignment (e.g., pending, completed)"
|
200 |
+
},
|
201 |
+
"submissions": {
|
202 |
+
"bsonType": "array",
|
203 |
+
"description": "List of submissions",
|
204 |
+
"items": {
|
205 |
+
"bsonType": "object",
|
206 |
+
"required": ["student_id", "file_url", "submitted_at"],
|
207 |
+
"properties": {
|
208 |
+
"student_id": {
|
209 |
+
"bsonType": "string",
|
210 |
+
"description": "ID of the student who submitted the assignment"
|
211 |
+
},
|
212 |
+
"file_url": {
|
213 |
+
"bsonType": "string",
|
214 |
+
"description": "URL of the submitted file"
|
215 |
+
},
|
216 |
+
"submitted_at": {
|
217 |
+
"bsonType": "date",
|
218 |
+
"description": "Date when the assignment was submitted"
|
219 |
+
}
|
220 |
+
}
|
221 |
+
}
|
222 |
+
}
|
223 |
+
}
|
224 |
+
}
|
225 |
+
}
|
226 |
+
}
|
227 |
+
}
|
228 |
+
}
|
229 |
+
}
|
230 |
+
}
|
231 |
+
}
|
232 |
+
}
|
233 |
+
|
234 |
+
# Create the collection with the schema
|
235 |
+
# db.create_collection("courses_collection2", validator={"$jsonSchema": course_schema})
|
236 |
+
|
237 |
+
# sample_course = {
|
238 |
+
# "course_id": "CS101",
|
239 |
+
# "title": "Introduction to Computer Science",
|
240 |
+
# "description": "This course covers the basics of computer science and programming.",
|
241 |
+
# "faculty": "Dr. John Doe",
|
242 |
+
# "faculty_id": "F101",
|
243 |
+
# "duration": "10 weeks",
|
244 |
+
# "created_at": datetime.utcnow(),
|
245 |
+
# "sessions": [
|
246 |
+
# {
|
247 |
+
# "session_id": "S101",
|
248 |
+
# "title": "Introduction to Programming Fundamentals",
|
249 |
+
# "date": datetime.utcnow() - timedelta(days=7),
|
250 |
+
# "status": "completed",
|
251 |
+
# "created_at": datetime.utcnow() - timedelta(days=7),
|
252 |
+
# "pre_class": {
|
253 |
+
# "resources": [
|
254 |
+
# {
|
255 |
+
# "type": "pdf",
|
256 |
+
# "title": "Introduction to Python Basics",
|
257 |
+
# "url": "/assets/python_basics.pdf",
|
258 |
+
# "vector": [0.1, 0.2, 0.3] # Example vector
|
259 |
+
# }
|
260 |
+
# ],
|
261 |
+
# "completion_required": True
|
262 |
+
# },
|
263 |
+
# "in_class": {
|
264 |
+
# "topics": ["Variables", "Data Types", "Basic Operations"],
|
265 |
+
# "quiz": {
|
266 |
+
# "title": "Python Basics Quiz",
|
267 |
+
# "questions": 5,
|
268 |
+
# "duration": 15
|
269 |
+
# },
|
270 |
+
# "polls": [
|
271 |
+
# {
|
272 |
+
# "question": "How comfortable are you with Python syntax?",
|
273 |
+
# "options": ["Very", "Somewhat", "Not at all"],
|
274 |
+
# "responses": {"Very": 10, "Somewhat": 5, "Not at all": 2}
|
275 |
+
# }
|
276 |
+
# ]
|
277 |
+
# },
|
278 |
+
# "post_class": {
|
279 |
+
# "assignments": [
|
280 |
+
# {
|
281 |
+
# "id": 1,
|
282 |
+
# "title": "Basic Python Programs",
|
283 |
+
# "due_date": datetime.utcnow() + timedelta(days=2),
|
284 |
+
# "status": "pending",
|
285 |
+
# "submissions": []
|
286 |
+
# }
|
287 |
+
# ]
|
288 |
+
# }
|
289 |
+
# },
|
290 |
+
# {
|
291 |
+
# "session_id": "S102",
|
292 |
+
# "title": "Control Flow and Functions",
|
293 |
+
# "date": datetime.utcnow() - timedelta(days=3),
|
294 |
+
# "status": "completed",
|
295 |
+
# "created_at": datetime.utcnow() - timedelta(days=3),
|
296 |
+
# "pre_class": {
|
297 |
+
# "resources": [
|
298 |
+
# {
|
299 |
+
# "type": "pdf",
|
300 |
+
# "title": "Control Flow in Python",
|
301 |
+
# "url": "/assets/control_flow.pdf",
|
302 |
+
# "vector": [0.4, 0.5, 0.6] # Example vector
|
303 |
+
# }
|
304 |
+
# ],
|
305 |
+
# "completion_required": True
|
306 |
+
# },
|
307 |
+
# "in_class": {
|
308 |
+
# "topics": ["If-else statements", "Loops", "Function definitions"],
|
309 |
+
# "quiz": {
|
310 |
+
# "title": "Control Flow Quiz",
|
311 |
+
# "questions": 8,
|
312 |
+
# "duration": 20
|
313 |
+
# },
|
314 |
+
# "polls": [
|
315 |
+
# {
|
316 |
+
# "question": "Which loop type do you find more intuitive?",
|
317 |
+
# "options": ["For loops", "While loops", "Both"],
|
318 |
+
# "responses": {"For loops": 12, "While loops": 8, "Both": 10}
|
319 |
+
# }
|
320 |
+
# ]
|
321 |
+
# },
|
322 |
+
# "post_class": {
|
323 |
+
# "assignments": [
|
324 |
+
# {
|
325 |
+
# "id": 2,
|
326 |
+
# "title": "Function Implementation Exercise",
|
327 |
+
# "due_date": datetime.utcnow() + timedelta(days=4),
|
328 |
+
# "status": "pending",
|
329 |
+
# "submissions": []
|
330 |
+
# }
|
331 |
+
# ]
|
332 |
+
# }
|
333 |
+
# }
|
334 |
+
# ]
|
335 |
+
# }
|
336 |
+
courses_collection2 = db['courses_collection2']
|
337 |
+
|
338 |
+
# courses_collection2.insert_one(sample_course)
|
339 |
+
# print("Sample course inserted successfully!")
|
340 |
+
|
341 |
+
|
342 |
+
# sessions_collection.insert_one(session_data)
|
343 |
+
# sessions_collection.delete_one({"session_id": "S101"})
|
344 |
+
|
345 |
+
# course_id = "C101"
|
346 |
+
# sessions = sessions_collection.find({"course_id": course_id})
|
347 |
+
# for session in sessions:
|
348 |
+
# print(session)
|
349 |
+
|
350 |
+
|
351 |
+
# Define the users schema
|
352 |
+
users_schema = {
|
353 |
+
"bsonType": "object",
|
354 |
+
"required": ["user_id", "username", "password", "role", "created_at"],
|
355 |
+
"properties": {
|
356 |
+
"user_id": {
|
357 |
+
"bsonType": "string",
|
358 |
+
"description": "Unique identifier for the user"
|
359 |
+
},
|
360 |
+
"username": {
|
361 |
+
"bsonType": "string",
|
362 |
+
"description": "Name of the User"
|
363 |
+
},
|
364 |
+
"password": {
|
365 |
+
"bsonType": "string",
|
366 |
+
"description": "Password of the user"
|
367 |
+
},
|
368 |
+
"role": {
|
369 |
+
"bsonType": "string",
|
370 |
+
"description": "Type of user (e.g., student, faculty)"
|
371 |
+
},
|
372 |
+
"created_at": {
|
373 |
+
"bsonType": "date",
|
374 |
+
"description": "Date when the user was created"
|
375 |
+
}
|
376 |
+
}
|
377 |
+
}
|
378 |
+
# Create the collection with the schema
|
379 |
+
# db.create_collection("users", validator={"$jsonSchema": users_schema})
|
380 |
+
users_collection = db['users']
|
381 |
+
|
382 |
+
# sample_user = {
|
383 |
+
# "user_id": "U103",
|
384 |
+
# "username": "Yash Desai",
|
385 |
+
# "password": generate_password_hash("yash"),
|
386 |
+
# "role": "Faculty",
|
387 |
+
# "created_at": datetime.utcnow()
|
388 |
+
# }
|
389 |
+
# users_collection.insert_one(sample_user)
|
390 |
+
# print("Sample user inserted successfully!")
|
391 |
+
|
392 |
+
# Defining the Student Collection
|
393 |
+
student_schema = {
|
394 |
+
"bsonType": "object",
|
395 |
+
"required": ["SID", "full_name", "password", "enrolled_courses", "created_at"],
|
396 |
+
"properties": {
|
397 |
+
"SID": {
|
398 |
+
"bsonType": "string",
|
399 |
+
"description": "Unique identifier for the student"
|
400 |
+
},
|
401 |
+
"full_name": {
|
402 |
+
"bsonType": "string",
|
403 |
+
"description": "Full name of the student"
|
404 |
+
},
|
405 |
+
"password": {
|
406 |
+
"bsonType": "string",
|
407 |
+
"description": "Hashed password of the student"
|
408 |
+
},
|
409 |
+
"enrolled_courses": {
|
410 |
+
"bsonType": "array",
|
411 |
+
"description": "List of courses the student is enrolled in",
|
412 |
+
"items": {
|
413 |
+
"bsonType": "object",
|
414 |
+
"required": ["course_id", "title"],
|
415 |
+
"properties": {
|
416 |
+
"course_id": {
|
417 |
+
"bsonType": "string",
|
418 |
+
"description": "Unique identifier for the course"
|
419 |
+
},
|
420 |
+
"title": {
|
421 |
+
"bsonType": "string",
|
422 |
+
"description": "Title of the course"
|
423 |
+
}
|
424 |
+
}
|
425 |
+
}
|
426 |
+
},
|
427 |
+
"created_at": {
|
428 |
+
"bsonType": "date",
|
429 |
+
"description": "Date when the student was created"
|
430 |
+
}
|
431 |
+
}
|
432 |
+
}
|
433 |
+
# Defining the Faculty Collection
|
434 |
+
faculty_schema = {
|
435 |
+
"bsonType": "object",
|
436 |
+
"required": ["TID", "full_name", "password", "courses_taught", "created_at"],
|
437 |
+
"properties": {
|
438 |
+
"TID": {
|
439 |
+
"bsonType": "string",
|
440 |
+
"description": "Unique identifier for the faculty"
|
441 |
+
},
|
442 |
+
"full_name": {
|
443 |
+
"bsonType": "string",
|
444 |
+
"description": "Full name of the faculty"
|
445 |
+
},
|
446 |
+
"password": {
|
447 |
+
"bsonType": "string",
|
448 |
+
"description": "Hashed password of the faculty"
|
449 |
+
},
|
450 |
+
"courses_taught": {
|
451 |
+
"bsonType": "array",
|
452 |
+
"description": "List of courses the faculty is teaching",
|
453 |
+
"items": {
|
454 |
+
"bsonType": "object",
|
455 |
+
"required": ["course_id", "title"],
|
456 |
+
"properties": {
|
457 |
+
"course_id": {
|
458 |
+
"bsonType": "string",
|
459 |
+
"description": "Unique identifier for the course"
|
460 |
+
},
|
461 |
+
"title": {
|
462 |
+
"bsonType": "string",
|
463 |
+
"description": "Title of the course"
|
464 |
+
}
|
465 |
+
}
|
466 |
+
}
|
467 |
+
},
|
468 |
+
"created_at": {
|
469 |
+
"bsonType": "date",
|
470 |
+
"description": "Date when the faculty was created"
|
471 |
+
}
|
472 |
+
}
|
473 |
+
}
|
474 |
+
# Creating the Collections
|
475 |
+
# db.create_collection("students", validator={"$jsonSchema": student_schema})
|
476 |
+
# db.create_collection("faculty", validator={"$jsonSchema": faculty_schema})
|
477 |
+
|
478 |
+
students_collection = db['students']
|
479 |
+
faculty_collection = db['faculty']
|
480 |
+
|
481 |
+
# Inserting Sample Student Data
|
482 |
+
# sample_student = {
|
483 |
+
# "SID": "S102",
|
484 |
+
# "full_name": "Omkar Surve",
|
485 |
+
# "password": generate_password_hash("omkar"),
|
486 |
+
# "enrolled_courses": [
|
487 |
+
# {"course_id": "CS101", "title": "Introduction to Computer Science"}
|
488 |
+
# ],
|
489 |
+
# "created_at": datetime.utcnow()
|
490 |
+
# }
|
491 |
+
# # students_collection.insert_one(sample_student)
|
492 |
+
# print("Sample student inserted successfully!")
|
493 |
+
|
494 |
+
# Inserting Sample Faculty Data
|
495 |
+
# sample_faculty = {
|
496 |
+
# "TID": "F101",
|
497 |
+
# "full_name": "Dr. John Doe",
|
498 |
+
# "password": generate_password_hash("john"),
|
499 |
+
# "courses_taught": [
|
500 |
+
# {"course_id": "CS101", "title": "Introduction to Computer Science"}
|
501 |
+
# ],
|
502 |
+
# "created_at": datetime.utcnow()
|
503 |
+
# }
|
504 |
+
# faculty_collection.insert_one(sample_faculty)
|
505 |
+
# print("Sample faculty inserted successfully!")
|
506 |
+
|
507 |
+
# Defining the Vector Collection Schema
|
508 |
+
vector_schema = {
|
509 |
+
"bsonType": "object",
|
510 |
+
"required": ["resource_id", "vector"],
|
511 |
+
"properties": {
|
512 |
+
"resource_id": {
|
513 |
+
"bsonType": "objectId",
|
514 |
+
"description": "Unique identifier for the resource"
|
515 |
+
},
|
516 |
+
"vector": {
|
517 |
+
"bsonType": "array",
|
518 |
+
"description": "Vector representation of the resource",
|
519 |
+
"items": {
|
520 |
+
"bsonType": "double"
|
521 |
+
}
|
522 |
+
},
|
523 |
+
"text": {
|
524 |
+
"bsonType": "string",
|
525 |
+
"description": "Text content of the resource"
|
526 |
+
},
|
527 |
+
"created_at": {
|
528 |
+
"bsonType": "date",
|
529 |
+
"description": "Date when the vector was created"
|
530 |
+
}
|
531 |
+
}
|
532 |
+
}
|
533 |
+
# Creating the Vector Collection
|
534 |
+
# db.create_collection("vectors", validator={"$jsonSchema": vector_schema})
|
535 |
+
vectors_collection = db['vectors']
|
file_upload_vectorize.py
ADDED
@@ -0,0 +1,105 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from pymongo import MongoClient
|
2 |
+
from datetime import datetime
|
3 |
+
import openai
|
4 |
+
import google.generativeai as genai
|
5 |
+
import streamlit as st
|
6 |
+
from db import courses_collection2, faculty_collection, students_collection, vectors_collection
|
7 |
+
from PIL import Image
|
8 |
+
import PyPDF2, docx, io
|
9 |
+
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader, Document
|
10 |
+
from bson import ObjectId
|
11 |
+
from dotenv import load_dotenv
|
12 |
+
import os
|
13 |
+
|
14 |
+
load_dotenv()
|
15 |
+
MONGO_URI = os.getenv('MONGO_URI')
|
16 |
+
OPENAI_KEY = os.getenv('OPENAI_KEY')
|
17 |
+
GEMINI_KEY = os.getenv('GEMINI_KEY')
|
18 |
+
|
19 |
+
|
20 |
+
client = MongoClient(MONGO_URI)
|
21 |
+
db = client['novascholar_db']
|
22 |
+
resources_collection = db['resources']
|
23 |
+
|
24 |
+
# Configure APIs
|
25 |
+
openai.api_key = OPENAI_KEY
|
26 |
+
genai.configure(api_key=GEMINI_KEY)
|
27 |
+
model = genai.GenerativeModel('gemini-pro')
|
28 |
+
|
29 |
+
def upload_resource(course_id, session_id, file_name, file_content, material_type):
|
30 |
+
# material_data = {
|
31 |
+
# "session_id": session_id,
|
32 |
+
# "course_id": course_id,
|
33 |
+
# "file_name": file_name,
|
34 |
+
# "file_content": file_content,
|
35 |
+
# "material_type": material_type,
|
36 |
+
# "uploaded_at": datetime.utcnow()
|
37 |
+
# }
|
38 |
+
# return resources_collection.insert_one(material_data)
|
39 |
+
resource_id = ObjectId()
|
40 |
+
|
41 |
+
# Extract text content from the file
|
42 |
+
text_content = extract_text_from_file(file_content)
|
43 |
+
|
44 |
+
# Read the file content
|
45 |
+
file_content.seek(0) # Reset the file pointer to the beginning
|
46 |
+
original_file_content = file_content.read()
|
47 |
+
|
48 |
+
|
49 |
+
resource_data = {
|
50 |
+
"resource_id": resource_id,
|
51 |
+
"course_id": course_id,
|
52 |
+
"session_id": session_id,
|
53 |
+
"file_name": file_name,
|
54 |
+
"file_type": file_content.type,
|
55 |
+
"text_content": text_content,
|
56 |
+
"file_content": original_file_content, # Store the original file content
|
57 |
+
"material_type": material_type,
|
58 |
+
"uploaded_at": datetime.utcnow()
|
59 |
+
}
|
60 |
+
|
61 |
+
resources_collection.insert_one(resource_data)
|
62 |
+
return resource_id
|
63 |
+
|
64 |
+
def extract_text_from_file(uploaded_file):
|
65 |
+
text = ""
|
66 |
+
file_type = uploaded_file.type
|
67 |
+
|
68 |
+
try:
|
69 |
+
if file_type == "text/plain":
|
70 |
+
text = uploaded_file.getvalue().decode("utf-8")
|
71 |
+
elif file_type == "application/pdf":
|
72 |
+
pdf_reader = PyPDF2.PdfReader(io.BytesIO(uploaded_file.getvalue()))
|
73 |
+
for page in pdf_reader.pages:
|
74 |
+
text += page.extract_text() + "\n"
|
75 |
+
elif file_type == "application/vnd.openxmlformats-officedocument.wordprocessingml.document":
|
76 |
+
doc = docx.Document(io.BytesIO(uploaded_file.getvalue()))
|
77 |
+
for para in doc.paragraphs:
|
78 |
+
text += para.text + "\n"
|
79 |
+
return text
|
80 |
+
except Exception as e:
|
81 |
+
st.error(f"Error processing file: {str(e)}")
|
82 |
+
return None
|
83 |
+
|
84 |
+
def get_embedding(text):
|
85 |
+
response = openai.embeddings.create(
|
86 |
+
model="text-embedding-ada-002",
|
87 |
+
input=text
|
88 |
+
)
|
89 |
+
return response.data[0].embedding
|
90 |
+
|
91 |
+
def create_vector_store(text, resource_id):
|
92 |
+
resource_object_id = ObjectId(resource_id)
|
93 |
+
document = Document(text=text)
|
94 |
+
embedding = get_embedding(text)
|
95 |
+
|
96 |
+
vector_data = {
|
97 |
+
"resource_id": resource_object_id,
|
98 |
+
"vector": embedding,
|
99 |
+
"text": text,
|
100 |
+
"created_at": datetime.utcnow()
|
101 |
+
}
|
102 |
+
|
103 |
+
vectors_collection.insert_one(vector_data)
|
104 |
+
|
105 |
+
return VectorStoreIndex.from_documents([document])
|
main.py
ADDED
@@ -0,0 +1,134 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
import datetime
|
3 |
+
from pathlib import Path
|
4 |
+
from utils.sample_data import SAMPLE_COURSES, SAMPLE_SESSIONS
|
5 |
+
from session_page import display_session_content
|
6 |
+
from db import courses_collection2, faculty_collection, students_collection
|
7 |
+
from werkzeug.security import generate_password_hash, check_password_hash
|
8 |
+
import hashlib
|
9 |
+
|
10 |
+
def init_session_state():
|
11 |
+
"""Initialize session state variables"""
|
12 |
+
if 'authenticated' not in st.session_state:
|
13 |
+
st.session_state.authenticated = False
|
14 |
+
if 'user_type' not in st.session_state:
|
15 |
+
st.session_state.user_type = None
|
16 |
+
if 'username' not in st.session_state:
|
17 |
+
st.session_state.username = None
|
18 |
+
if 'selected_course' not in st.session_state:
|
19 |
+
st.session_state.selected_course = None
|
20 |
+
|
21 |
+
def login_user(username, password, user_type):
|
22 |
+
"""Login user based on credentials"""
|
23 |
+
if user_type == "student":
|
24 |
+
user = students_collection.find_one({"full_name": username})
|
25 |
+
else:
|
26 |
+
user = faculty_collection.find_one({"full_name": username})
|
27 |
+
|
28 |
+
if user and check_password_hash(user['password'], password):
|
29 |
+
st.session_state.authenticated = True
|
30 |
+
st.session_state.user_type = user_type
|
31 |
+
st.session_state.username = username
|
32 |
+
return True
|
33 |
+
return False
|
34 |
+
|
35 |
+
|
36 |
+
def login_form():
|
37 |
+
"""Display login form"""
|
38 |
+
st.title("Welcome to NOVAScholar")
|
39 |
+
|
40 |
+
with st.form("login_form"):
|
41 |
+
user_type = st.selectbox("Select User Type", ["student", "faculty"])
|
42 |
+
username = st.text_input("Username")
|
43 |
+
password = st.text_input("Password", type="password")
|
44 |
+
submit = st.form_submit_button("Login")
|
45 |
+
|
46 |
+
if submit:
|
47 |
+
if login_user(username, password, user_type):
|
48 |
+
st.success("Login successful!")
|
49 |
+
st.rerun()
|
50 |
+
else:
|
51 |
+
st.error("Invalid credentials!")
|
52 |
+
|
53 |
+
def get_courses(username, user_type):
|
54 |
+
if user_type == "student":
|
55 |
+
student = students_collection.find_one({"full_name": username})
|
56 |
+
if student:
|
57 |
+
enrolled_course_ids = [course['course_id'] for course in student.get('enrolled_courses', [])]
|
58 |
+
courses = courses_collection2.find({"course_id": {"$in": enrolled_course_ids}})
|
59 |
+
# course_titles = [course['title'] for course in courses]
|
60 |
+
print(courses)
|
61 |
+
return list(courses)
|
62 |
+
elif user_type == "faculty":
|
63 |
+
faculty = faculty_collection.find_one({"full_name": username})
|
64 |
+
if faculty:
|
65 |
+
course_ids = [course['course_id'] for course in faculty.get('courses_taught', [])]
|
66 |
+
courses = courses_collection2.find({"course_id": {"$in": course_ids}})
|
67 |
+
return list(courses)
|
68 |
+
else:
|
69 |
+
return []
|
70 |
+
|
71 |
+
def get_course_ids():
|
72 |
+
"""Get course IDs for sample courses"""
|
73 |
+
return [course['course_id'] for course in SAMPLE_COURSES]
|
74 |
+
|
75 |
+
def get_sessions(course_id):
|
76 |
+
"""Get sessions for a given course ID"""
|
77 |
+
course = courses_collection2.find_one({"course_id": course_id})
|
78 |
+
if course:
|
79 |
+
return course.get('sessions', [])
|
80 |
+
return []
|
81 |
+
|
82 |
+
def main_dashboard():
|
83 |
+
# st.title(f"Welcome, {st.session_state.username}")
|
84 |
+
selected_course_id = None
|
85 |
+
with st.sidebar:
|
86 |
+
st.title("Courses")
|
87 |
+
|
88 |
+
# Course selection
|
89 |
+
enrolled_courses = get_courses(st.session_state.username, st.session_state.user_type)
|
90 |
+
course_titles = [course['title'] for course in enrolled_courses]
|
91 |
+
course_ids = [course['course_id'] for course in enrolled_courses]
|
92 |
+
|
93 |
+
selected_course = st.selectbox("Select Course", course_titles)
|
94 |
+
selected_course_id = course_ids[course_titles.index(selected_course)]
|
95 |
+
print(selected_course_id)
|
96 |
+
|
97 |
+
st.session_state.selected_course = selected_course
|
98 |
+
st.session_state.selected_course_id = selected_course_id
|
99 |
+
|
100 |
+
# Display course sessions
|
101 |
+
sessions = get_sessions(selected_course_id)
|
102 |
+
print(sessions)
|
103 |
+
|
104 |
+
st.title("Sessions")
|
105 |
+
for i, session in enumerate(sessions, start=1):
|
106 |
+
if st.button(f"Session {i}", key=f"session_{i}", use_container_width=True):
|
107 |
+
st.session_state.selected_session = session
|
108 |
+
|
109 |
+
if st.button("Logout", use_container_width=True):
|
110 |
+
for key in st.session_state.keys():
|
111 |
+
del st.session_state[key]
|
112 |
+
st.rerun()
|
113 |
+
|
114 |
+
# Main content
|
115 |
+
if 'selected_session' in st.session_state:
|
116 |
+
display_session_content(selected_course_id, st.session_state.selected_session, st.session_state.username)
|
117 |
+
|
118 |
+
|
119 |
+
def main():
|
120 |
+
st.set_page_config(
|
121 |
+
page_title="NOVAScholar",
|
122 |
+
page_icon="📚",
|
123 |
+
layout="wide"
|
124 |
+
)
|
125 |
+
init_session_state()
|
126 |
+
|
127 |
+
if not st.session_state.authenticated:
|
128 |
+
login_form()
|
129 |
+
else:
|
130 |
+
main_dashboard()
|
131 |
+
|
132 |
+
if __name__ == "__main__":
|
133 |
+
main()
|
134 |
+
|
session_page.py
ADDED
@@ -0,0 +1,270 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
from datetime import datetime
|
3 |
+
from utils.sample_data import SAMPLE_CHAT_HISTORY, SAMPLE_STUDENT_PROGRESS
|
4 |
+
from utils.helpers import display_progress_bar, create_notification, format_datetime
|
5 |
+
from utils.sample_data import SAMPLE_SESSIONS, SAMPLE_COURSES
|
6 |
+
from file_upload_vectorize import upload_resource, extract_text_from_file, create_vector_store, resources_collection
|
7 |
+
from db import courses_collection2
|
8 |
+
def display_preclass_content(session, username):
|
9 |
+
"""Display pre-class materials for a session"""
|
10 |
+
st.subheader("Pre-class Materials")
|
11 |
+
|
12 |
+
# Display progress bar
|
13 |
+
# progress = SAMPLE_STUDENT_PROGRESS.get(st.session_state.username, {}).get(session['session_id'], {}).get('pre_class', 0)
|
14 |
+
# display_progress_bar(progress, 100, "Pre-class completion")
|
15 |
+
|
16 |
+
# for resource in session['pre_class']['resources']:
|
17 |
+
# with st.expander(f"{resource['title']} ({resource['type'].upper()})"):
|
18 |
+
# if resource['type'] == 'pdf':
|
19 |
+
# st.markdown(f"📑 [Open PDF Document]({resource['url']})")
|
20 |
+
# if st.button("Mark PDF as Read", key=f"pdf_{resource['title']}"):
|
21 |
+
# create_notification("PDF marked as read!", "success")
|
22 |
+
|
23 |
+
# elif resource['type'] == 'video':
|
24 |
+
# st.markdown(f"🎥 Video Duration: {resource['duration']}")
|
25 |
+
# col1, col2 = st.columns([3, 1])
|
26 |
+
# with col1:
|
27 |
+
# st.video("https://example.com/placeholder.mp4")
|
28 |
+
# with col2:
|
29 |
+
# if st.button("Mark Video Complete", key=f"video_{resource['title']}"):
|
30 |
+
# create_notification("Video marked as complete!", "success")
|
31 |
+
|
32 |
+
# elif resource['type'] == 'reading':
|
33 |
+
# st.markdown(f"📖 Reading Assignment: Pages {resource['pages']}")
|
34 |
+
# if st.button("Mark Reading Complete", key=f"reading_{resource['title']}"):
|
35 |
+
# create_notification("Reading marked as complete!", "success")
|
36 |
+
|
37 |
+
# st.markdown("---")
|
38 |
+
# st.markdown("**Notes:**")
|
39 |
+
# notes = st.text_area("Add your notes here", key=f"notes_{resource['title']}")
|
40 |
+
# if st.button("Save Notes", key=f"save_notes_{resource['title']}"):
|
41 |
+
# create_notification("Notes saved successfully!", "success")
|
42 |
+
|
43 |
+
# Display pre-class materials
|
44 |
+
materials = resources_collection.find({"session_id": session['session_id']})
|
45 |
+
for material in materials:
|
46 |
+
# with st.expander(f"{material['file_name']} ({material['material_type'].upper()})"):
|
47 |
+
# if material['material_type'] == 'pdf':
|
48 |
+
# # st.markdown(f"📑 [Open PDF Document]({material['url']})")
|
49 |
+
# if st.button("Mark PDF as Read", key=f"pdf_{material['file_name']}"):
|
50 |
+
# create_notification("PDF marked as read!", "success")
|
51 |
+
with st.expander(f"{material['file_name']} ({material['material_type'].upper()})"):
|
52 |
+
file_type = material.get('file_type', 'unknown')
|
53 |
+
if file_type == 'application/pdf':
|
54 |
+
st.markdown(f"📑 [Open PDF Document]({material['file_name']})")
|
55 |
+
if st.button("View PDF", key=f"view_pdf_{material['file_name']}"):
|
56 |
+
st.text_area("PDF Content", material['text_content'], height=300)
|
57 |
+
if st.button("Download PDF", key=f"download_pdf_{material['file_name']}"):
|
58 |
+
st.download_button(
|
59 |
+
label="Download PDF",
|
60 |
+
data=material['file_content'],
|
61 |
+
file_name=material['file_name'],
|
62 |
+
mime='application/pdf'
|
63 |
+
)
|
64 |
+
if st.button("Mark PDF as Read", key=f"pdf_{material['file_name']}"):
|
65 |
+
create_notification("PDF marked as read!", "success")
|
66 |
+
|
67 |
+
def display_in_class_content(session):
|
68 |
+
"""Display in-class activities and interactions"""
|
69 |
+
st.header("In-class Activities")
|
70 |
+
|
71 |
+
# Topics covered
|
72 |
+
with st.expander("Topics Covered", expanded=True):
|
73 |
+
for topic in session['in_class']['topics']:
|
74 |
+
st.markdown(f"- {topic}")
|
75 |
+
|
76 |
+
# Live Quiz section
|
77 |
+
st.subheader("Session Quiz")
|
78 |
+
quiz = session['in_class']['quiz']
|
79 |
+
with st.expander(f"Quiz: {quiz['title']}"):
|
80 |
+
st.markdown(f"- Number of questions: {quiz['questions']}")
|
81 |
+
st.markdown(f"- Time allowed: {quiz['duration']} minutes")
|
82 |
+
if session['status'] == 'in_progress':
|
83 |
+
if st.button("Start Quiz"):
|
84 |
+
create_notification("Quiz will begin shortly!", "info")
|
85 |
+
else:
|
86 |
+
st.info("Quiz not available at this time")
|
87 |
+
|
88 |
+
# Live Polls
|
89 |
+
st.subheader("Interactive Polls")
|
90 |
+
for idx, poll in enumerate(session['in_class']['polls']):
|
91 |
+
with st.expander(f"Poll {idx + 1}: {poll['question']}"):
|
92 |
+
selected_option = st.radio(
|
93 |
+
"Your response:",
|
94 |
+
options=poll['options'],
|
95 |
+
key=f"poll_{session['session_id']}_{idx}"
|
96 |
+
)
|
97 |
+
if st.button("Submit Response", key=f"submit_poll_{idx}"):
|
98 |
+
create_notification("Poll response recorded!", "success")
|
99 |
+
|
100 |
+
# Chat Interface
|
101 |
+
st.subheader("Class Discussion")
|
102 |
+
chat_container = st.container()
|
103 |
+
with chat_container:
|
104 |
+
# Display existing messages
|
105 |
+
messages = SAMPLE_CHAT_HISTORY.get(session['session_id'], [])
|
106 |
+
for msg in messages:
|
107 |
+
with st.chat_message(msg['user']):
|
108 |
+
st.write(msg['message'])
|
109 |
+
|
110 |
+
# New message input
|
111 |
+
if session['status'] == 'in_progress':
|
112 |
+
if prompt := st.chat_input("Ask a question..."):
|
113 |
+
if len(messages) < 20:
|
114 |
+
with st.chat_message("user"):
|
115 |
+
st.write(prompt)
|
116 |
+
with st.chat_message("assistant"):
|
117 |
+
st.write("This is a sample response to your question.")
|
118 |
+
else:
|
119 |
+
create_notification("Message limit (20) reached for this session.", "warning")
|
120 |
+
|
121 |
+
def display_post_class_content(session):
|
122 |
+
"""Display post-class assignments and submissions"""
|
123 |
+
st.header("Post-class Work")
|
124 |
+
|
125 |
+
# Display assignments
|
126 |
+
for assignment in session['post_class']['assignments']:
|
127 |
+
with st.expander(f"Assignment: {assignment['title']}", expanded=True):
|
128 |
+
st.markdown(f"**Due Date:** {format_datetime(assignment['due_date'])}")
|
129 |
+
st.markdown(f"**Status:** {assignment['status'].replace('_', ' ').title()}")
|
130 |
+
|
131 |
+
# Assignment details
|
132 |
+
st.markdown("### Instructions")
|
133 |
+
st.markdown("Complete the assignment according to the provided guidelines.")
|
134 |
+
|
135 |
+
# File submission
|
136 |
+
st.markdown("### Submission")
|
137 |
+
uploaded_file = st.file_uploader(
|
138 |
+
"Upload your work",
|
139 |
+
type=['pdf', 'py', 'ipynb'],
|
140 |
+
key=f"upload_{assignment['id']}"
|
141 |
+
)
|
142 |
+
|
143 |
+
if uploaded_file is not None:
|
144 |
+
st.success("File uploaded successfully!")
|
145 |
+
if st.button("Submit Assignment", key=f"submit_{assignment['id']}"):
|
146 |
+
create_notification("Assignment submitted successfully!", "success")
|
147 |
+
|
148 |
+
# Feedback section (if assignment is completed)
|
149 |
+
if assignment['status'] == 'completed':
|
150 |
+
st.markdown("### Feedback")
|
151 |
+
st.info("Feedback will be provided here once the assignment is graded.")
|
152 |
+
|
153 |
+
def display_preclass_analytics(session):
|
154 |
+
"""Display pre-class analytics for faculty"""
|
155 |
+
st.subheader("Pre-class Analytics")
|
156 |
+
|
157 |
+
# Display pre-class resource completion rates
|
158 |
+
for resource in session['pre_class']['resources']:
|
159 |
+
progress = SAMPLE_STUDENT_PROGRESS.get(resource['title'], 0)
|
160 |
+
display_progress_bar(progress, 100, resource['title'])
|
161 |
+
|
162 |
+
def display_inclass_analytics(session):
|
163 |
+
"""Display in-class analytics for faculty"""
|
164 |
+
st.subheader("In-class Analytics")
|
165 |
+
|
166 |
+
# Display chat usage metrics
|
167 |
+
chat_messages = SAMPLE_CHAT_HISTORY.get(session['session_id'], [])
|
168 |
+
st.metric("Total Chat Messages", len(chat_messages))
|
169 |
+
|
170 |
+
# Display live quiz/poll results
|
171 |
+
# for poll in session['in_class']['polls']:
|
172 |
+
# st.subheader(poll['question'])
|
173 |
+
# for option, count in poll['responses'].items():
|
174 |
+
# st.metric(option, count)
|
175 |
+
for poll in session.get('in_class', {}).get('polls', []):
|
176 |
+
st.text(poll.get('question', 'No question available'))
|
177 |
+
responses = poll.get('responses', {})
|
178 |
+
if responses:
|
179 |
+
for option, count in responses.items():
|
180 |
+
st.metric(option, count)
|
181 |
+
else:
|
182 |
+
st.warning("No responses available for this poll")
|
183 |
+
|
184 |
+
def display_postclass_analytics(session):
|
185 |
+
"""Display post-class analytics for faculty"""
|
186 |
+
st.subheader("Post-class Analytics")
|
187 |
+
|
188 |
+
# Display assignment completion rates
|
189 |
+
for assignment in session['post_class']['assignments']:
|
190 |
+
progress = SAMPLE_STUDENT_PROGRESS.get(assignment['id'], 0)
|
191 |
+
display_progress_bar(progress, 100, assignment['title'])
|
192 |
+
|
193 |
+
|
194 |
+
def upload_preclass_materials(session_id, course_id):
|
195 |
+
"""Upload pre-class materials for a session"""
|
196 |
+
st.subheader("Upload Pre-class Materials")
|
197 |
+
|
198 |
+
# File upload section
|
199 |
+
uploaded_file = st.file_uploader("Upload Material", type=['txt', 'pdf', 'docx'])
|
200 |
+
if uploaded_file is not None:
|
201 |
+
with st.spinner("Processing document..."):
|
202 |
+
file_name = uploaded_file.name
|
203 |
+
file_content = extract_text_from_file(uploaded_file)
|
204 |
+
if file_content:
|
205 |
+
material_type = st.selectbox("Select Material Type", ["pdf", "docx", "txt"])
|
206 |
+
if st.button("Upload Material"):
|
207 |
+
upload_resource(course_id, session_id, file_name, uploaded_file, material_type)
|
208 |
+
|
209 |
+
# Search for the newly uploaded resource's _id in resources_collection
|
210 |
+
resource_id = resources_collection.find_one({"file_name": file_name})["_id"]
|
211 |
+
create_vector_store(file_content, resource_id)
|
212 |
+
st.success("Material uploaded successfully!")
|
213 |
+
|
214 |
+
# Display existing materials
|
215 |
+
materials = resources_collection.find({"course_id": course_id, "session_id": session_id})
|
216 |
+
for material in materials:
|
217 |
+
st.markdown(f"""
|
218 |
+
* **{material['file_name']}** ({material['material_type']})
|
219 |
+
Uploaded on: {material['uploaded_at'].strftime('%Y-%m-%d %H:%M')}
|
220 |
+
""")
|
221 |
+
|
222 |
+
|
223 |
+
|
224 |
+
|
225 |
+
def display_session_content(course_id, session, username):
|
226 |
+
st.title(f"Session {session['session_id']}: {session['title']}")
|
227 |
+
st.markdown(f"**Date:** {format_datetime(session['date'])}")
|
228 |
+
st.markdown(f"**Status:** {session['status'].replace('_', ' ').title()}")
|
229 |
+
|
230 |
+
# Find the course_id of the session in
|
231 |
+
|
232 |
+
if st.session_state.user_type == 'student':
|
233 |
+
tabs = (["Pre-class Work", "In-class Work", "Post-class Work"])
|
234 |
+
else:
|
235 |
+
tabs = (["Pre-class Analytics", "In-class Analytics", "Post-class Analytics"])
|
236 |
+
|
237 |
+
# Create tabs for different sections
|
238 |
+
# pre_class_tab, in_class_tab, post_class_tab, faculty_tab = st.tabs([
|
239 |
+
# "Pre-class Work",
|
240 |
+
# "In-class Work",
|
241 |
+
# "Post-class Work",
|
242 |
+
# "Faculty Analytics"
|
243 |
+
# ])
|
244 |
+
|
245 |
+
if st.session_state.user_type == 'student':
|
246 |
+
pre_class_tab, in_class_tab, post_class_tab = st.tabs(["Pre-class Work", "In-class Work", "Post-class Work"])
|
247 |
+
else:
|
248 |
+
pre_class_work, in_class_work, post_class_work, preclass_analytics, inclass_analytics, postclass_analytics = st.tabs(["Pre-class Work", "In-class Work", "Post-class Work", "Pre-class Analytics", "In-class Analytics", "Post-class Analytics"])
|
249 |
+
|
250 |
+
# Display pre-class materials
|
251 |
+
if st.session_state.user_type == 'student':
|
252 |
+
with pre_class_tab:
|
253 |
+
display_preclass_content(session, username)
|
254 |
+
|
255 |
+
with in_class_tab:
|
256 |
+
display_in_class_content(session)
|
257 |
+
|
258 |
+
# Post-class Content
|
259 |
+
with post_class_tab:
|
260 |
+
display_post_class_content(session)
|
261 |
+
|
262 |
+
if st.session_state.user_type == 'faculty':
|
263 |
+
with pre_class_work:
|
264 |
+
upload_preclass_materials(session['session_id'], course_id)
|
265 |
+
with preclass_analytics:
|
266 |
+
display_preclass_analytics(session)
|
267 |
+
with inclass_analytics:
|
268 |
+
display_inclass_analytics(session)
|
269 |
+
with postclass_analytics:
|
270 |
+
display_postclass_analytics(session)
|
utils/helpers.py
ADDED
@@ -0,0 +1,83 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from datetime import datetime, timedelta
|
2 |
+
import streamlit as st
|
3 |
+
|
4 |
+
def format_datetime(dt):
|
5 |
+
"""Format datetime for display"""
|
6 |
+
return dt.strftime("%Y-%m-%d %H:%M")
|
7 |
+
|
8 |
+
def get_session_progress(username, course_id, session_id):
|
9 |
+
"""
|
10 |
+
Get user's progress for a specific session
|
11 |
+
Returns dict with pre_class, in_class, and post_class completion status
|
12 |
+
"""
|
13 |
+
# Demo implementation - replace with actual database queries
|
14 |
+
return {
|
15 |
+
'pre_class': {
|
16 |
+
'completed': True,
|
17 |
+
'last_access': datetime.now() - timedelta(days=1),
|
18 |
+
'resources_viewed': 3,
|
19 |
+
'total_resources': 3
|
20 |
+
},
|
21 |
+
'in_class': {
|
22 |
+
'completed': False,
|
23 |
+
'attendance': True,
|
24 |
+
'quiz_completed': False,
|
25 |
+
'questions_asked': 5
|
26 |
+
},
|
27 |
+
'post_class': {
|
28 |
+
'completed': False,
|
29 |
+
'assignments_submitted': 1,
|
30 |
+
'total_assignments': 2,
|
31 |
+
'grade': None
|
32 |
+
}
|
33 |
+
}
|
34 |
+
|
35 |
+
def get_course_sessions(course_id):
|
36 |
+
"""Get all sessions for a course"""
|
37 |
+
# Demo implementation - replace with database query
|
38 |
+
return [
|
39 |
+
{
|
40 |
+
'id': 1,
|
41 |
+
'title': 'Introduction to Programming Concepts',
|
42 |
+
'date': datetime.now() + timedelta(days=i),
|
43 |
+
'status': 'completed' if i < 0 else 'upcoming'
|
44 |
+
}
|
45 |
+
for i in range(-2, 5)
|
46 |
+
]
|
47 |
+
|
48 |
+
def display_progress_bar(completed, total, text=""):
|
49 |
+
"""Display a progress bar with text"""
|
50 |
+
progress = completed / total if total > 0 else 0
|
51 |
+
st.progress(progress)
|
52 |
+
st.text(f"{text}: {completed}/{total} ({progress*100:.1f}%)")
|
53 |
+
|
54 |
+
def create_notification(message, type="info"):
|
55 |
+
"""Create a notification message"""
|
56 |
+
if type == "success":
|
57 |
+
st.success(message)
|
58 |
+
elif type == "error":
|
59 |
+
st.error(message)
|
60 |
+
elif type == "warning":
|
61 |
+
st.warning(message)
|
62 |
+
else:
|
63 |
+
st.info(message)
|
64 |
+
|
65 |
+
class SessionManager:
|
66 |
+
"""Manage session state and navigation"""
|
67 |
+
@staticmethod
|
68 |
+
def get_current_session():
|
69 |
+
"""Get current session information"""
|
70 |
+
if 'current_session' not in st.session_state:
|
71 |
+
st.session_state.current_session = 1
|
72 |
+
return st.session_state.current_session
|
73 |
+
|
74 |
+
@staticmethod
|
75 |
+
def set_current_session(session_id):
|
76 |
+
"""Set current session"""
|
77 |
+
st.session_state.current_session = session_id
|
78 |
+
|
79 |
+
@staticmethod
|
80 |
+
def clear_session():
|
81 |
+
"""Clear session state"""
|
82 |
+
for key in list(st.session_state.keys()):
|
83 |
+
del st.session_state[key]
|
utils/sample_data.py
ADDED
@@ -0,0 +1,226 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from datetime import datetime, timedelta
|
2 |
+
|
3 |
+
SAMPLE_COURSES = [
|
4 |
+
{
|
5 |
+
'course_id': 'CS101',
|
6 |
+
'title': 'Introduction to Computer Science',
|
7 |
+
'description': 'This course covers the basics of computer science and programming.',
|
8 |
+
'instructor': 'Dr. John Doe',
|
9 |
+
'duration': '10 weeks'
|
10 |
+
},
|
11 |
+
{
|
12 |
+
'course_id': 'CS102',
|
13 |
+
'title': 'Data Structures and Algorithms',
|
14 |
+
'description': 'This course introduces data structures and algorithms for efficient data processing.',
|
15 |
+
'instructor': 'Dr. Jane Smith',
|
16 |
+
'duration': '12 weeks'
|
17 |
+
},
|
18 |
+
{
|
19 |
+
'course_id': 'CS103',
|
20 |
+
'title': 'Advanced Python Programming',
|
21 |
+
'description': 'This course covers advanced topics in Python programming, including file handling and exception management.',
|
22 |
+
'instructor': 'Dr. Emily Johnson',
|
23 |
+
'duration': '8 weeks'
|
24 |
+
}
|
25 |
+
]
|
26 |
+
|
27 |
+
SAMPLE_SESSIONS = [
|
28 |
+
{
|
29 |
+
'id': 1,
|
30 |
+
'course_id': 'CS101',
|
31 |
+
'title': 'Introduction to Programming Fundamentals',
|
32 |
+
'date': datetime.now() - timedelta(days=7),
|
33 |
+
'status': 'completed',
|
34 |
+
'pre_class': {
|
35 |
+
'resources': [
|
36 |
+
{'type': 'pdf', 'title': 'Introduction to Python Basics', 'url': '/assets/python_basics.pdf'},
|
37 |
+
{'type': 'video', 'title': 'Programming Fundamentals', 'duration': '15:00'},
|
38 |
+
{'type': 'reading', 'title': 'Chapter 1: Getting Started', 'pages': '1-15'}
|
39 |
+
],
|
40 |
+
'completion_required': True
|
41 |
+
},
|
42 |
+
'in_class': {
|
43 |
+
'topics': ['Variables', 'Data Types', 'Basic Operations'],
|
44 |
+
'quiz': {
|
45 |
+
'title': 'Python Basics Quiz',
|
46 |
+
'questions': 5,
|
47 |
+
'duration': 15
|
48 |
+
},
|
49 |
+
'polls': [
|
50 |
+
{'question': 'How comfortable are you with Python syntax?', 'options': ['Very', 'Somewhat', 'Not at all']}
|
51 |
+
]
|
52 |
+
},
|
53 |
+
'post_class': {
|
54 |
+
'assignments': [
|
55 |
+
{
|
56 |
+
'id': 1,
|
57 |
+
'title': 'Basic Python Programs',
|
58 |
+
'due_date': datetime.now() + timedelta(days=2),
|
59 |
+
'status': 'pending'
|
60 |
+
}
|
61 |
+
]
|
62 |
+
}
|
63 |
+
},
|
64 |
+
{
|
65 |
+
'id': 2,
|
66 |
+
'course_id': 'CS101',
|
67 |
+
'title': 'Control Flow and Functions',
|
68 |
+
'date': datetime.now() - timedelta(days=3),
|
69 |
+
'status': 'completed',
|
70 |
+
'pre_class': {
|
71 |
+
'resources': [
|
72 |
+
{'type': 'pdf', 'title': 'Control Flow in Python', 'url': '/assets/control_flow.pdf'},
|
73 |
+
{'type': 'video', 'title': 'Functions and Methods', 'duration': '20:00'}
|
74 |
+
],
|
75 |
+
'completion_required': True
|
76 |
+
},
|
77 |
+
'in_class': {
|
78 |
+
'topics': ['If-else statements', 'Loops', 'Function definitions'],
|
79 |
+
'quiz': {
|
80 |
+
'title': 'Control Flow Quiz',
|
81 |
+
'questions': 8,
|
82 |
+
'duration': 20
|
83 |
+
},
|
84 |
+
'polls': [
|
85 |
+
{'question': 'Which loop type do you find more intuitive?', 'options': ['For loops', 'While loops', 'Both']}
|
86 |
+
]
|
87 |
+
},
|
88 |
+
'post_class': {
|
89 |
+
'assignments': [
|
90 |
+
{
|
91 |
+
'id': 2,
|
92 |
+
'title': 'Function Implementation Exercise',
|
93 |
+
'due_date': datetime.now() + timedelta(days=4),
|
94 |
+
'status': 'pending'
|
95 |
+
}
|
96 |
+
]
|
97 |
+
}
|
98 |
+
},
|
99 |
+
{
|
100 |
+
'id': 3,
|
101 |
+
'course_id': 'CS102',
|
102 |
+
'title': 'Data Structures',
|
103 |
+
'date': datetime.now(),
|
104 |
+
'status': 'in_progress',
|
105 |
+
'pre_class': {
|
106 |
+
'resources': [
|
107 |
+
{'type': 'pdf', 'title': 'Python Data Structures', 'url': '/assets/data_structures.pdf'},
|
108 |
+
{'type': 'video', 'title': 'Lists and Dictionaries', 'duration': '25:00'}
|
109 |
+
],
|
110 |
+
'completion_required': True
|
111 |
+
},
|
112 |
+
'in_class': {
|
113 |
+
'topics': ['Lists', 'Tuples', 'Dictionaries', 'Sets'],
|
114 |
+
'quiz': {
|
115 |
+
'title': 'Data Structures Quiz',
|
116 |
+
'questions': 10,
|
117 |
+
'duration': 25
|
118 |
+
},
|
119 |
+
'polls': [
|
120 |
+
{'question': 'Which data structure do you use most often?', 'options': ['Lists', 'Dictionaries', 'Sets', 'Tuples']}
|
121 |
+
]
|
122 |
+
},
|
123 |
+
'post_class': {
|
124 |
+
'assignments': [
|
125 |
+
{
|
126 |
+
'id': 3,
|
127 |
+
'title': 'Data Structure Implementation',
|
128 |
+
'due_date': datetime.now() + timedelta(days=7),
|
129 |
+
'status': 'not_started'
|
130 |
+
}
|
131 |
+
]
|
132 |
+
}
|
133 |
+
},
|
134 |
+
{
|
135 |
+
'id': 4,
|
136 |
+
'course_id': 'CS101',
|
137 |
+
'title': 'Object-Oriented Programming',
|
138 |
+
'date': datetime.now() + timedelta(days=4),
|
139 |
+
'status': 'upcoming',
|
140 |
+
'pre_class': {
|
141 |
+
'resources': [
|
142 |
+
{'type': 'pdf', 'title': 'OOP Concepts', 'url': '/assets/oop_concepts.pdf'},
|
143 |
+
{'type': 'video', 'title': 'Classes and Objects', 'duration': '30:00'}
|
144 |
+
],
|
145 |
+
'completion_required': True
|
146 |
+
},
|
147 |
+
'in_class': {
|
148 |
+
'topics': ['Classes', 'Objects', 'Inheritance', 'Polymorphism'],
|
149 |
+
'quiz': {
|
150 |
+
'title': 'OOP Concepts Quiz',
|
151 |
+
'questions': 12,
|
152 |
+
'duration': 30
|
153 |
+
},
|
154 |
+
'polls': [
|
155 |
+
{'question': 'Have you used OOP before?', 'options': ['Yes', 'No', 'Not sure'], 'responses': {'For loops': 12, 'While loops': 8, 'Both': 10}}
|
156 |
+
]
|
157 |
+
},
|
158 |
+
'post_class': {
|
159 |
+
'assignments': [
|
160 |
+
{
|
161 |
+
'id': 4,
|
162 |
+
'title': 'Class Implementation Project',
|
163 |
+
'due_date': datetime.now() + timedelta(days=11),
|
164 |
+
'status': 'not_started'
|
165 |
+
}
|
166 |
+
]
|
167 |
+
}
|
168 |
+
},
|
169 |
+
{
|
170 |
+
'id': 5,
|
171 |
+
'course_id': 'CS103',
|
172 |
+
'title': 'File Handling and Exception Management',
|
173 |
+
'date': datetime.now() + timedelta(days=7),
|
174 |
+
'status': 'upcoming',
|
175 |
+
'pre_class': {
|
176 |
+
'resources': [
|
177 |
+
{'type': 'pdf', 'title': 'File Operations in Python', 'url': '/assets/file_ops.pdf'},
|
178 |
+
{'type': 'video', 'title': 'Exception Handling', 'duration': '20:00'}
|
179 |
+
],
|
180 |
+
'completion_required': True
|
181 |
+
},
|
182 |
+
'in_class': {
|
183 |
+
'topics': ['File Operations', 'Exception Handling', 'Context Managers'],
|
184 |
+
'quiz': {
|
185 |
+
'title': 'File Operations Quiz',
|
186 |
+
'questions': 8,
|
187 |
+
'duration': 20
|
188 |
+
},
|
189 |
+
'polls': [
|
190 |
+
{'question': 'How often do you handle exceptions in your code?',
|
191 |
+
'options': ['Always', 'Sometimes', 'Rarely', 'Never'],
|
192 |
+
'responses': {'Very': 10, 'Somewhat': 15, 'Not at all': 5}
|
193 |
+
}
|
194 |
+
]
|
195 |
+
},
|
196 |
+
'post_class': {
|
197 |
+
'assignments': [
|
198 |
+
{
|
199 |
+
'id': 5,
|
200 |
+
'title': 'File Processing Application',
|
201 |
+
'due_date': datetime.now() + timedelta(days=14),
|
202 |
+
'status': 'not_started'
|
203 |
+
}
|
204 |
+
]
|
205 |
+
}
|
206 |
+
}
|
207 |
+
]
|
208 |
+
|
209 |
+
# Chatbot message history
|
210 |
+
SAMPLE_CHAT_HISTORY = {
|
211 |
+
1: [
|
212 |
+
{'user': 'student1', 'message': 'What is the difference between list and tuple?', 'timestamp': datetime.now()},
|
213 |
+
{'user': 'chatbot', 'message': 'Lists are mutable (can be modified) while tuples are immutable (cannot be modified after creation).', 'timestamp': datetime.now()}
|
214 |
+
]
|
215 |
+
}
|
216 |
+
|
217 |
+
# Student progress data
|
218 |
+
SAMPLE_STUDENT_PROGRESS = {
|
219 |
+
'user1': {
|
220 |
+
1: {'pre_class': 50, 'in_class': 80, 'post_class': 90},
|
221 |
+
2: {'pre_class': 100, 'in_class': 75, 'post_class': 85},
|
222 |
+
3: {'pre_class': 50, 'in_class': 0, 'post_class': 0},
|
223 |
+
4: {'pre_class': 0, 'in_class': 0, 'post_class': 0},
|
224 |
+
5: {'pre_class': 0, 'in_class': 0, 'post_class': 0}
|
225 |
+
}
|
226 |
+
}
|