Spaces:
Build error
Build error
Harshal V
commited on
Commit
·
5097cc9
1
Parent(s):
9630b25
Build in-session features
Browse files- chatbot.py +45 -0
- db.py +55 -1
- file_upload_vectorize.py +24 -0
- live_polls.py +115 -0
- main.py +4 -1
- poll_db_operations.py +70 -0
- poll_db_setup.py +35 -0
- session_page.py +230 -91
chatbot.py
ADDED
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
import datetime
|
3 |
+
from db import courses_collection2, faculty_collection, students_collection, vectors_collection, chat_history_collection
|
4 |
+
from PIL import Image
|
5 |
+
from dotenv import load_dotenv
|
6 |
+
import os
|
7 |
+
from datetime import datetime
|
8 |
+
from bson import ObjectId
|
9 |
+
from file_upload_vectorize import model
|
10 |
+
|
11 |
+
load_dotenv()
|
12 |
+
MONGO_URI = os.getenv('MONGO_URI')
|
13 |
+
OPENAI_KEY = os.getenv('OPENAI_KEY')
|
14 |
+
GEMINI_KEY = os.getenv('GEMINI_KEY')
|
15 |
+
|
16 |
+
def insert_chat_message(user_id, session_id, role, content):
|
17 |
+
message = {
|
18 |
+
"role": role,
|
19 |
+
"content": content,
|
20 |
+
"timestamp": datetime.utcnow()
|
21 |
+
}
|
22 |
+
|
23 |
+
chat_history_collection.update_one(
|
24 |
+
{"user_id": ObjectId(user_id), "session_id": session_id},
|
25 |
+
{"$push": {"messages": message}, "$set": {"timestamp": datetime.utcnow()}},
|
26 |
+
upsert=True
|
27 |
+
)
|
28 |
+
|
29 |
+
|
30 |
+
def give_chat_response(user_id, session_id, question, context, prompt):
|
31 |
+
st.title("Chat with NOVA")
|
32 |
+
st.write("Ask any question and NOVA will provide an answer.")
|
33 |
+
|
34 |
+
with st.form("chat_form"):
|
35 |
+
message = st.text_input("Message")
|
36 |
+
submit = st.form_submit_button("Send")
|
37 |
+
|
38 |
+
if submit:
|
39 |
+
insert_chat_message(user_id, session_id, "user", message)
|
40 |
+
st.write(f"You: {message}")
|
41 |
+
|
42 |
+
# Get response from NOVA
|
43 |
+
response = get_nova_response(question, context, prompt, message)
|
44 |
+
insert_chat_message(user_id, session_id, "nova", response)
|
45 |
+
st.write(f"NOVA: {response}")
|
db.py
CHANGED
@@ -532,4 +532,58 @@ vector_schema = {
|
|
532 |
}
|
533 |
# Creating the Vector Collection
|
534 |
# db.create_collection("vectors", validator={"$jsonSchema": vector_schema})
|
535 |
-
vectors_collection = db['vectors']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
532 |
}
|
533 |
# Creating the Vector Collection
|
534 |
# db.create_collection("vectors", validator={"$jsonSchema": vector_schema})
|
535 |
+
vectors_collection = db['vectors']
|
536 |
+
|
537 |
+
|
538 |
+
# Creating a Chat-History Collection
|
539 |
+
# Creating a Chat-History Collection
|
540 |
+
chat_history_schema = {
|
541 |
+
"bsonType": "object",
|
542 |
+
"required": ["user_id", "session_id", "messages", "timestamp"],
|
543 |
+
"properties": {
|
544 |
+
"user_id": {
|
545 |
+
"bsonType": "objectId",
|
546 |
+
"description": "Unique identifier for the user"
|
547 |
+
},
|
548 |
+
"session_id": {
|
549 |
+
"bsonType": "string",
|
550 |
+
"description": "Identifier for the session"
|
551 |
+
},
|
552 |
+
"timestamp": {
|
553 |
+
"bsonType": "date",
|
554 |
+
"description": "Timestamp when the chat session started"
|
555 |
+
},
|
556 |
+
"messages": {
|
557 |
+
"bsonType": "array",
|
558 |
+
"description": "List of chat messages",
|
559 |
+
"items": {
|
560 |
+
"bsonType": "object",
|
561 |
+
"properties": {
|
562 |
+
"prompt": {
|
563 |
+
"bsonType": "string",
|
564 |
+
"description": "User's question or prompt"
|
565 |
+
},
|
566 |
+
"response": {
|
567 |
+
"bsonType": "string",
|
568 |
+
"description": "Assistant's response"
|
569 |
+
},
|
570 |
+
"timestamp": {
|
571 |
+
"bsonType": "date",
|
572 |
+
"description": "Timestamp of the message"
|
573 |
+
}
|
574 |
+
}
|
575 |
+
}
|
576 |
+
}
|
577 |
+
}
|
578 |
+
}
|
579 |
+
|
580 |
+
# Create the collection with the schema
|
581 |
+
# db.create_collection("chat_history", validator={"$jsonSchema": chat_history_schema})
|
582 |
+
chat_history_collection = db['chat_history']
|
583 |
+
|
584 |
+
# Create the collection with the schema
|
585 |
+
# db.create_collection("chat_history", validator={"$jsonSchema": chat_history_schema})
|
586 |
+
# chat_history_collection = db['chat_history']
|
587 |
+
|
588 |
+
|
589 |
+
# database_setup for live polls
|
file_upload_vectorize.py
CHANGED
@@ -61,6 +61,30 @@ def upload_resource(course_id, session_id, file_name, file_content, material_typ
|
|
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
|
|
|
61 |
resources_collection.insert_one(resource_data)
|
62 |
return resource_id
|
63 |
|
64 |
+
def assignment_submit(student_id, course_id, session_id, file_name, file_content, material_type):
|
65 |
+
# Extract text content from the file
|
66 |
+
text_content = extract_text_from_file(file_content)
|
67 |
+
|
68 |
+
# Read the file content
|
69 |
+
file_content.seek(0) # Reset the file pointer to the beginning
|
70 |
+
original_file_content = file_content.read()
|
71 |
+
|
72 |
+
assignment_data = {
|
73 |
+
"student_id": student_id,
|
74 |
+
"course_id": course_id,
|
75 |
+
"session_id": session_id,
|
76 |
+
"file_name": file_name,
|
77 |
+
"file_type": file_content.type,
|
78 |
+
"text_content": text_content,
|
79 |
+
"file_content": original_file_content, # Store the original file content
|
80 |
+
"material_type": material_type,
|
81 |
+
"uploaded_at": datetime.utcnow()
|
82 |
+
}
|
83 |
+
courses_collection2.update_one(
|
84 |
+
{"course_id": course_id, "sessions.session_id": session_id},
|
85 |
+
{"$push": {"sessions.$.post_class.assignments": assignment_data}}
|
86 |
+
)
|
87 |
+
|
88 |
def extract_text_from_file(uploaded_file):
|
89 |
text = ""
|
90 |
file_type = uploaded_file.type
|
live_polls.py
ADDED
@@ -0,0 +1,115 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# live_poll_feature.py
|
2 |
+
|
3 |
+
import streamlit as st
|
4 |
+
import pandas as pd
|
5 |
+
from datetime import datetime
|
6 |
+
from poll_db_operations import PollDatabase
|
7 |
+
|
8 |
+
class LivePollFeature:
|
9 |
+
def __init__(self):
|
10 |
+
self.db = PollDatabase()
|
11 |
+
|
12 |
+
def display_faculty_interface(self, session_id):
|
13 |
+
"""Display the faculty interface for managing polls"""
|
14 |
+
st.subheader("Live Polls Management")
|
15 |
+
|
16 |
+
# Create new poll
|
17 |
+
with st.expander("Create New Poll", expanded=False):
|
18 |
+
question = st.text_input("Poll Question")
|
19 |
+
|
20 |
+
num_options = st.number_input("Number of Options",
|
21 |
+
min_value=2,
|
22 |
+
max_value=6,
|
23 |
+
value=4)
|
24 |
+
|
25 |
+
options = []
|
26 |
+
for i in range(num_options):
|
27 |
+
option = st.text_input(f"Option {i+1}",
|
28 |
+
key=f"option_{i}")
|
29 |
+
if option:
|
30 |
+
options.append(option)
|
31 |
+
|
32 |
+
if st.button("Create Poll") and question and len(options) >= 2:
|
33 |
+
self.db.create_poll(
|
34 |
+
st.session_state.selected_course,
|
35 |
+
session_id,
|
36 |
+
question,
|
37 |
+
options,
|
38 |
+
st.session_state.user_id
|
39 |
+
)
|
40 |
+
st.success("Poll created successfully!")
|
41 |
+
st.rerun()
|
42 |
+
|
43 |
+
# Display active polls
|
44 |
+
active_polls = self.db.get_active_polls(session_id)
|
45 |
+
if active_polls:
|
46 |
+
st.subheader("Active Polls")
|
47 |
+
for poll in active_polls:
|
48 |
+
with st.expander(f"Poll: {poll['question']}", expanded=True):
|
49 |
+
# Display results
|
50 |
+
self._display_poll_results(poll)
|
51 |
+
|
52 |
+
if st.button("Close Poll",
|
53 |
+
key=f"close_{str(poll['_id'])}"):
|
54 |
+
self.db.close_poll(poll['_id'])
|
55 |
+
st.success("Poll closed successfully!")
|
56 |
+
st.rerun()
|
57 |
+
|
58 |
+
def display_student_interface(self, session_id):
|
59 |
+
"""Display the student interface for participating in polls"""
|
60 |
+
st.subheader("Live Polls")
|
61 |
+
|
62 |
+
active_polls = self.db.get_active_polls(session_id)
|
63 |
+
if not active_polls:
|
64 |
+
st.info("No active polls at the moment.")
|
65 |
+
return
|
66 |
+
|
67 |
+
for poll in active_polls:
|
68 |
+
with st.expander(f"Poll: {poll['question']}", expanded=True):
|
69 |
+
selected_option = st.radio(
|
70 |
+
"Your response:",
|
71 |
+
options=poll['options'],
|
72 |
+
key=f"poll_{str(poll['_id'])}"
|
73 |
+
)
|
74 |
+
|
75 |
+
if st.button("Submit Response",
|
76 |
+
key=f"submit_{str(poll['_id'])}"):
|
77 |
+
success, message = self.db.submit_response(
|
78 |
+
poll['_id'],
|
79 |
+
st.session_state.user_id,
|
80 |
+
selected_option
|
81 |
+
)
|
82 |
+
if success:
|
83 |
+
st.success(message)
|
84 |
+
else:
|
85 |
+
st.warning(message)
|
86 |
+
st.rerun()
|
87 |
+
|
88 |
+
self._display_poll_results(poll)
|
89 |
+
|
90 |
+
def _display_poll_results(self, poll):
|
91 |
+
"""Helper method to display poll results"""
|
92 |
+
responses_df = pd.DataFrame(
|
93 |
+
list(poll['responses'].items()),
|
94 |
+
columns=['Option', 'Votes']
|
95 |
+
)
|
96 |
+
|
97 |
+
total_votes = responses_df['Votes'].sum()
|
98 |
+
|
99 |
+
# Calculate percentages
|
100 |
+
if total_votes > 0:
|
101 |
+
responses_df['Percentage'] = (
|
102 |
+
responses_df['Votes'] / total_votes * 100
|
103 |
+
).round(1)
|
104 |
+
else:
|
105 |
+
responses_df['Percentage'] = 0
|
106 |
+
|
107 |
+
# Display metrics
|
108 |
+
st.metric("Total Responses", total_votes)
|
109 |
+
|
110 |
+
# Display charts
|
111 |
+
st.bar_chart(responses_df.set_index('Option')['Votes'])
|
112 |
+
|
113 |
+
# Display detailed statistics
|
114 |
+
if st.session_state.user_type == 'faculty':
|
115 |
+
st.dataframe(responses_df)
|
main.py
CHANGED
@@ -11,6 +11,8 @@ 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:
|
@@ -26,6 +28,7 @@ def login_user(username, password, user_type):
|
|
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
|
@@ -113,7 +116,7 @@ def main_dashboard():
|
|
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():
|
|
|
11 |
"""Initialize session state variables"""
|
12 |
if 'authenticated' not in st.session_state:
|
13 |
st.session_state.authenticated = False
|
14 |
+
if 'user_id' not in st.session_state:
|
15 |
+
st.session_state.user_id = None
|
16 |
if 'user_type' not in st.session_state:
|
17 |
st.session_state.user_type = None
|
18 |
if 'username' not in st.session_state:
|
|
|
28 |
user = faculty_collection.find_one({"full_name": username})
|
29 |
|
30 |
if user and check_password_hash(user['password'], password):
|
31 |
+
st.session_state.user_id = user['_id']
|
32 |
st.session_state.authenticated = True
|
33 |
st.session_state.user_type = user_type
|
34 |
st.session_state.username = username
|
|
|
116 |
|
117 |
# Main content
|
118 |
if 'selected_session' in st.session_state:
|
119 |
+
display_session_content(st.session_state.user_id, selected_course_id, st.session_state.selected_session, st.session_state.username, st.session_state.user_type)
|
120 |
|
121 |
|
122 |
def main():
|
poll_db_operations.py
ADDED
@@ -0,0 +1,70 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from pymongo import MongoClient
|
2 |
+
from datetime import datetime
|
3 |
+
from bson import ObjectId
|
4 |
+
from dotenv import load_dotenv
|
5 |
+
import os
|
6 |
+
|
7 |
+
load_dotenv()
|
8 |
+
MONGO_URI = os.getenv('MONGO_URI')
|
9 |
+
class PollDatabase:
|
10 |
+
def __init__(self):
|
11 |
+
self.client = MongoClient(MONGO_URI)
|
12 |
+
self.db = self.client["novascholar_db"]
|
13 |
+
|
14 |
+
def create_poll(self, course_id, session_id, question, options, faculty_id):
|
15 |
+
"""Create a new poll"""
|
16 |
+
poll = {
|
17 |
+
"course_id": course_id,
|
18 |
+
"session_id": session_id,
|
19 |
+
"faculty_id": faculty_id,
|
20 |
+
"question": question,
|
21 |
+
"options": options,
|
22 |
+
"status": "active",
|
23 |
+
"created_at": datetime.now(),
|
24 |
+
"responses": {option: 0 for option in options}
|
25 |
+
}
|
26 |
+
return self.db.polls.insert_one(poll)
|
27 |
+
|
28 |
+
def get_active_polls(self, session_id):
|
29 |
+
"""Get all active polls for a session"""
|
30 |
+
return list(self.db.polls.find({
|
31 |
+
"session_id": session_id,
|
32 |
+
"status": "active"
|
33 |
+
}))
|
34 |
+
|
35 |
+
def submit_response(self, poll_id, student_id, selected_option):
|
36 |
+
"""Submit a student's response to a poll"""
|
37 |
+
try:
|
38 |
+
# Record individual response
|
39 |
+
response = {
|
40 |
+
"poll_id": poll_id,
|
41 |
+
"student_id": student_id,
|
42 |
+
"selected_option": selected_option,
|
43 |
+
"submitted_at": datetime.now()
|
44 |
+
}
|
45 |
+
self.db.poll_responses.insert_one(response)
|
46 |
+
|
47 |
+
# Update aggregated results
|
48 |
+
self.db.polls.update_one(
|
49 |
+
{"_id": ObjectId(poll_id)},
|
50 |
+
{"$inc": {f"responses.{selected_option}": 1}}
|
51 |
+
)
|
52 |
+
return True, "Vote recorded successfully"
|
53 |
+
|
54 |
+
except Exception as e:
|
55 |
+
if "duplicate key error" in str(e):
|
56 |
+
return False, "You have already voted in this poll"
|
57 |
+
return False, f"Error recording vote: {str(e)}"
|
58 |
+
|
59 |
+
def close_poll(self, poll_id):
|
60 |
+
"""Close a poll"""
|
61 |
+
return self.db.polls.update_one(
|
62 |
+
{"_id": ObjectId(poll_id)},
|
63 |
+
{"$set": {"status": "closed"}}
|
64 |
+
)
|
65 |
+
|
66 |
+
def get_poll_analytics(self, poll_id):
|
67 |
+
"""Get detailed analytics for a poll"""
|
68 |
+
poll = self.db.polls.find_one({"_id": ObjectId(poll_id)})
|
69 |
+
responses = self.db.poll_responses.find({"poll_id": ObjectId(poll_id)})
|
70 |
+
return poll, list(responses)
|
poll_db_setup.py
ADDED
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from pymongo import MongoClient
|
2 |
+
from datetime import datetime
|
3 |
+
from dotenv import load_dotenv
|
4 |
+
import os
|
5 |
+
|
6 |
+
load_dotenv()
|
7 |
+
MONGO_URI = os.getenv('MONGO_URI')
|
8 |
+
def setup_mongodb():
|
9 |
+
"""Initialize MongoDB connection and create collections with indexes"""
|
10 |
+
client = MongoClient(MONGO_URI)
|
11 |
+
db = client["novascholar_db"]
|
12 |
+
|
13 |
+
# Create indexes for polls collection
|
14 |
+
db.polls.create_index([("session_id", 1), ("status", 1)])
|
15 |
+
db.polls.create_index([("course_id", 1)])
|
16 |
+
|
17 |
+
# Create unique index for poll_responses to prevent duplicate votes
|
18 |
+
db.poll_responses.create_index(
|
19 |
+
[("poll_id", 1), ("student_id", 1)],
|
20 |
+
unique=True
|
21 |
+
)
|
22 |
+
|
23 |
+
return "Database setup completed successfully"
|
24 |
+
|
25 |
+
def print_all_polls():
|
26 |
+
"""Print all polls in the database"""
|
27 |
+
client = MongoClient(MONGO_URI)
|
28 |
+
db = client["novascholar_db"]
|
29 |
+
|
30 |
+
polls = db.polls.find()
|
31 |
+
for poll in polls:
|
32 |
+
print(poll)
|
33 |
+
|
34 |
+
if __name__ == "__main__":
|
35 |
+
print(print_all_polls())
|
session_page.py
CHANGED
@@ -3,9 +3,18 @@ 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
9 |
"""Display pre-class materials for a session"""
|
10 |
st.subheader("Pre-class Materials")
|
11 |
|
@@ -41,13 +50,12 @@ def display_preclass_content(session, username):
|
|
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 |
-
|
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':
|
@@ -64,91 +72,218 @@ def display_preclass_content(session, username):
|
|
64 |
if st.button("Mark PDF as Read", key=f"pdf_{material['file_name']}"):
|
65 |
create_notification("PDF marked as read!", "success")
|
66 |
|
67 |
-
|
68 |
-
"
|
69 |
-
|
|
|
70 |
|
71 |
-
#
|
72 |
-
|
73 |
-
|
74 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
75 |
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
else:
|
86 |
-
st.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
87 |
|
88 |
-
# Live Polls
|
89 |
-
|
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 |
-
#
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
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 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
st.
|
130 |
-
|
131 |
-
|
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 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
152 |
|
153 |
def display_preclass_analytics(session):
|
154 |
"""Display pre-class analytics for faculty"""
|
@@ -185,10 +320,10 @@ 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 |
-
|
191 |
-
|
192 |
|
193 |
|
194 |
def upload_preclass_materials(session_id, course_id):
|
@@ -222,7 +357,7 @@ def upload_preclass_materials(session_id, course_id):
|
|
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()}")
|
@@ -250,18 +385,22 @@ def display_session_content(course_id, session, username):
|
|
250 |
# Display pre-class materials
|
251 |
if st.session_state.user_type == 'student':
|
252 |
with pre_class_tab:
|
253 |
-
display_preclass_content(session,
|
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:
|
|
|
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, model, assignment_submit
|
7 |
+
from db import courses_collection2, chat_history_collection, students_collection, faculty_collection, vectors_collection
|
8 |
+
from chatbot import insert_chat_message
|
9 |
+
from bson import ObjectId
|
10 |
+
from live_polls import LivePollFeature
|
11 |
+
|
12 |
+
def get_current_user():
|
13 |
+
if 'current_user' not in st.session_state:
|
14 |
+
return None
|
15 |
+
return students_collection.find_one({"_id": st.session_state.user_id})
|
16 |
+
|
17 |
+
def display_preclass_content(session, student_id):
|
18 |
"""Display pre-class materials for a session"""
|
19 |
st.subheader("Pre-class Materials")
|
20 |
|
|
|
50 |
# create_notification("Notes saved successfully!", "success")
|
51 |
|
52 |
# Display pre-class materials
|
53 |
+
print(f"student_id: {type(student_id)}")
|
54 |
+
|
55 |
materials = resources_collection.find({"session_id": session['session_id']})
|
56 |
+
print(f"materials: {type(materials)}")
|
57 |
for material in materials:
|
58 |
+
print(f"material: {type(material)}")
|
|
|
|
|
|
|
|
|
59 |
with st.expander(f"{material['file_name']} ({material['material_type'].upper()})"):
|
60 |
file_type = material.get('file_type', 'unknown')
|
61 |
if file_type == 'application/pdf':
|
|
|
72 |
if st.button("Mark PDF as Read", key=f"pdf_{material['file_name']}"):
|
73 |
create_notification("PDF marked as read!", "success")
|
74 |
|
75 |
+
user = get_current_user()
|
76 |
+
print(f"user: {type(user)}")
|
77 |
+
|
78 |
+
user = get_current_user()
|
79 |
|
80 |
+
# Chat input
|
81 |
+
# Add a check, if materials are available, only then show the chat input
|
82 |
+
if(st.session_state.user_type == "student"):
|
83 |
+
if materials:
|
84 |
+
if prompt := st.chat_input("Ask a question about Pre-class Materials"):
|
85 |
+
if len(st.session_state.messages) >= 20:
|
86 |
+
st.warning("Message limit (20) reached for this session.")
|
87 |
+
return
|
88 |
+
|
89 |
+
st.session_state.messages.append({"role": "user", "content": prompt})
|
90 |
+
|
91 |
+
# Display User Message
|
92 |
+
with st.chat_message("user"):
|
93 |
+
st.markdown(prompt)
|
94 |
+
|
95 |
+
# Get document context
|
96 |
+
context = ""
|
97 |
+
materials = resources_collection.find({"session_id": session['session_id']})
|
98 |
+
context = ""
|
99 |
+
vector_data = None
|
100 |
+
|
101 |
+
context = ""
|
102 |
+
for material in materials:
|
103 |
+
resource_id = material['_id']
|
104 |
+
vector_data = vectors_collection.find_one({"resource_id": resource_id})
|
105 |
+
if vector_data and 'text' in vector_data:
|
106 |
+
context += vector_data['text'] + "\n"
|
107 |
+
|
108 |
+
if not vector_data:
|
109 |
+
st.error("No Pre-class materials found for this session.")
|
110 |
+
return
|
111 |
+
|
112 |
+
try:
|
113 |
+
# Generate response using Gemini
|
114 |
+
context_prompt = f"""
|
115 |
+
Based on the following context, answer the user's question:
|
116 |
+
|
117 |
+
Context:
|
118 |
+
{context}
|
119 |
+
|
120 |
+
Question: {prompt}
|
121 |
+
|
122 |
+
Please provide a clear and concise answer based only on the information provided in the context.
|
123 |
+
"""
|
124 |
+
|
125 |
+
response = model.generate_content(context_prompt)
|
126 |
+
if not response or not response.text:
|
127 |
+
st.error("No response received from the model")
|
128 |
+
return
|
129 |
+
|
130 |
+
assistant_response = response.text
|
131 |
+
# Display Assistant Response
|
132 |
+
with st.chat_message("assistant"):
|
133 |
+
st.markdown(assistant_response)
|
134 |
+
|
135 |
+
# Build the message
|
136 |
+
new_message = {
|
137 |
+
"prompt": prompt,
|
138 |
+
"response": assistant_response,
|
139 |
+
"timestamp": datetime.utcnow()
|
140 |
+
}
|
141 |
+
st.session_state.messages.append(new_message)
|
142 |
+
|
143 |
+
# Update database
|
144 |
+
try:
|
145 |
+
chat_history_collection.update_one(
|
146 |
+
{
|
147 |
+
"user_id": student_id,
|
148 |
+
"session_id": session['session_id']
|
149 |
+
},
|
150 |
+
{
|
151 |
+
"$push": {"messages": new_message},
|
152 |
+
"$setOnInsert": {
|
153 |
+
"user_id": student_id,
|
154 |
+
"session_id": session['session_id'],
|
155 |
+
"timestamp": datetime.utcnow()
|
156 |
+
}
|
157 |
+
},
|
158 |
+
upsert=True
|
159 |
+
)
|
160 |
+
except Exception as db_error:
|
161 |
+
st.error(f"Error saving chat history: {str(db_error)}")
|
162 |
+
except Exception as e:
|
163 |
+
st.error(f"Error generating response: {str(e)}")
|
164 |
|
165 |
+
st.subheader("Your Chat History")
|
166 |
+
# Initialize chat messages from database
|
167 |
+
if 'messages' not in st.session_state:
|
168 |
+
existing_chat = chat_history_collection.find_one({
|
169 |
+
"user_id": student_id,
|
170 |
+
"session_id": session['session_id']
|
171 |
+
})
|
172 |
+
if existing_chat and 'messages' in existing_chat:
|
173 |
+
st.session_state.messages = existing_chat['messages']
|
174 |
else:
|
175 |
+
st.session_state.messages = []
|
176 |
+
|
177 |
+
# Display existing chat history
|
178 |
+
try:
|
179 |
+
for message in st.session_state.messages:
|
180 |
+
if 'prompt' in message and 'response' in message:
|
181 |
+
with st.chat_message("user"):
|
182 |
+
st.markdown(message["prompt"])
|
183 |
+
with st.chat_message("assistant"):
|
184 |
+
st.markdown(message["response"])
|
185 |
+
except Exception as e:
|
186 |
+
st.error(f"Error displaying chat history: {str(e)}")
|
187 |
+
st.session_state.messages = []
|
188 |
+
|
189 |
+
def display_in_class_content(session, user_type):
|
190 |
+
# """Display in-class activities and interactions"""
|
191 |
+
"""Display in-class activities and interactions"""
|
192 |
+
st.header("In-class Activities")
|
193 |
|
194 |
+
# Initialize Live Polls feature
|
195 |
+
live_polls = LivePollFeature()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
196 |
|
197 |
+
# Display appropriate interface based on user role
|
198 |
+
if user_type == 'faculty':
|
199 |
+
live_polls.display_faculty_interface(session['session_id'])
|
200 |
+
else:
|
201 |
+
live_polls.display_student_interface(session['session_id'])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
202 |
|
203 |
+
def display_post_class_content(session, student_id, course_id):
|
204 |
"""Display post-class assignments and submissions"""
|
205 |
st.header("Post-class Work")
|
206 |
|
207 |
+
if st.session_state.user_type == 'faculty':
|
208 |
+
st.subheader("Add Assignments")
|
209 |
+
# Add assignment form
|
210 |
+
with st.form("add_assignment_form"):
|
211 |
+
title = st.text_input("Assignment Title")
|
212 |
+
due_date = st.date_input("Due Date")
|
213 |
+
submit = st.form_submit_button("Add Assignment")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
214 |
|
215 |
+
if submit:
|
216 |
+
due_date = datetime.combine(due_date, datetime.min.time())
|
217 |
+
# Save the assignment to the database
|
218 |
+
assignment = {
|
219 |
+
"id": ObjectId(),
|
220 |
+
"title": title,
|
221 |
+
"due_date": due_date,
|
222 |
+
"status": "pending",
|
223 |
+
"submissions": []
|
224 |
+
}
|
225 |
+
courses_collection2.update_one(
|
226 |
+
{"course_id": course_id, "sessions.session_id": session['session_id']},
|
227 |
+
{"$push": {"sessions.$.post_class.assignments": assignment}}
|
228 |
+
)
|
229 |
+
st.success("Assignment added successfully!")
|
230 |
+
else:
|
231 |
+
# Display assignments
|
232 |
+
# assignments += courses_collection2.find_one(
|
233 |
+
# {"course_id": course_id, "sessions.session_id": session['session_id']},
|
234 |
+
# {"sessions.$.post_class.assignments": 1}
|
235 |
+
# )["sessions"][0]["post_class"]["assignments"]
|
236 |
+
session_data = courses_collection2.find_one(
|
237 |
+
{"course_id": course_id, "sessions.session_id": session['session_id']},
|
238 |
+
{"sessions.$": 1}
|
239 |
+
)
|
240 |
+
assignments = session_data["sessions"][0]["post_class"]["assignments"]
|
241 |
+
print(assignments)
|
242 |
+
for assignment in assignments:
|
243 |
+
with st.expander(f"Assignment: {assignment['title']}", expanded=True):
|
244 |
+
st.markdown(f"**Due Date:** {assignment['due_date']}")
|
245 |
+
st.markdown(f"**Status:** {assignment['status'].replace('_', ' ').title()}")
|
246 |
+
|
247 |
+
# Assignment details
|
248 |
+
st.markdown("### Instructions")
|
249 |
+
st.markdown("Complete the assignment according to the provided guidelines.")
|
250 |
+
|
251 |
+
# File submission
|
252 |
+
st.markdown("### Submission")
|
253 |
+
uploaded_file = st.file_uploader(
|
254 |
+
"Upload your work",
|
255 |
+
type=['pdf', 'py', 'ipynb'],
|
256 |
+
key=f"upload_{assignment['id']}"
|
257 |
+
)
|
258 |
+
|
259 |
+
if uploaded_file is not None:
|
260 |
+
st.success("File uploaded successfully!")
|
261 |
+
|
262 |
+
if st.button("Submit Assignment", key=f"submit_{assignment['id']}"):
|
263 |
+
# Save the file to a location and get the file URL
|
264 |
+
assignment_submit(student_id, course_id, session['session_id'], uploaded_file.name, uploaded_file, uploaded_file.type)
|
265 |
+
|
266 |
+
# Display submitted assignments
|
267 |
+
st.markdown(f"📑 [Click to view Submission]({uploaded_file['file_name']})")
|
268 |
+
if st.button("View PDF", key=f"view_pdf_{uploaded_file['file_name']}"):
|
269 |
+
st.text_area("PDF Content", uploaded_file['text_content'], height=300)
|
270 |
+
if st.button("Download PDF", key=f"download_pdf_{uploaded_file['file_name']}"):
|
271 |
+
st.download_button(
|
272 |
+
label="Download PDF",
|
273 |
+
data=uploaded_file['file_content'],
|
274 |
+
file_name=uploaded_file['file_name'],
|
275 |
+
mime='application/pdf'
|
276 |
+
)
|
277 |
+
|
278 |
+
# Feedback section (if assignment is completed)
|
279 |
+
if assignment['status'] == 'completed':
|
280 |
+
st.markdown("### Feedback")
|
281 |
+
st.info("Feedback will be provided here once the assignment is graded.")
|
282 |
+
|
283 |
+
|
284 |
+
|
285 |
+
|
286 |
+
|
287 |
|
288 |
def display_preclass_analytics(session):
|
289 |
"""Display pre-class analytics for faculty"""
|
|
|
320 |
"""Display post-class analytics for faculty"""
|
321 |
st.subheader("Post-class Analytics")
|
322 |
|
323 |
+
# # Display assignment completion rates
|
324 |
+
# for assignment in session['post_class']['assignments']:
|
325 |
+
# progress = SAMPLE_STUDENT_PROGRESS.get(assignment['id'], 0)
|
326 |
+
# display_progress_bar(progress, 100, assignment['title'])
|
327 |
|
328 |
|
329 |
def upload_preclass_materials(session_id, course_id):
|
|
|
357 |
|
358 |
|
359 |
|
360 |
+
def display_session_content(student_id, course_id, session, username, user_type):
|
361 |
st.title(f"Session {session['session_id']}: {session['title']}")
|
362 |
st.markdown(f"**Date:** {format_datetime(session['date'])}")
|
363 |
st.markdown(f"**Status:** {session['status'].replace('_', ' ').title()}")
|
|
|
385 |
# Display pre-class materials
|
386 |
if st.session_state.user_type == 'student':
|
387 |
with pre_class_tab:
|
388 |
+
display_preclass_content(session, student_id)
|
389 |
|
390 |
with in_class_tab:
|
391 |
+
display_in_class_content(session, st.session_state.user_type)
|
392 |
|
393 |
# Post-class Content
|
394 |
with post_class_tab:
|
395 |
+
display_post_class_content(session, student_id, course_id)
|
396 |
|
397 |
if st.session_state.user_type == 'faculty':
|
398 |
with pre_class_work:
|
399 |
upload_preclass_materials(session['session_id'], course_id)
|
400 |
+
with in_class_work:
|
401 |
+
display_in_class_content(session, st.session_state.user_type)
|
402 |
+
with post_class_work:
|
403 |
+
display_post_class_content(session, student_id, course_id)
|
404 |
with preclass_analytics:
|
405 |
display_preclass_analytics(session)
|
406 |
with inclass_analytics:
|