Spaces:
Sleeping
Sleeping
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 |
+
}
|