akhil-vaidya commited on
Commit
669d1ae
Β·
verified Β·
1 Parent(s): 87718b1

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +223 -38
src/streamlit_app.py CHANGED
@@ -1,40 +1,225 @@
1
- import altair as alt
2
- import numpy as np
3
- import pandas as pd
4
  import streamlit as st
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
 
6
- """
7
- # Welcome to Streamlit!
8
-
9
- Edit `/streamlit_app.py` to customize this app to your heart's desire :heart:.
10
- If you have any questions, checkout our [documentation](https://docs.streamlit.io) and [community
11
- forums](https://discuss.streamlit.io).
12
-
13
- In the meantime, below is an example of what you can do with just a few lines of code:
14
- """
15
-
16
- num_points = st.slider("Number of points in spiral", 1, 10000, 1100)
17
- num_turns = st.slider("Number of turns in spiral", 1, 300, 31)
18
-
19
- indices = np.linspace(0, 1, num_points)
20
- theta = 2 * np.pi * num_turns * indices
21
- radius = indices
22
-
23
- x = radius * np.cos(theta)
24
- y = radius * np.sin(theta)
25
-
26
- df = pd.DataFrame({
27
- "x": x,
28
- "y": y,
29
- "idx": indices,
30
- "rand": np.random.randn(num_points),
31
- })
32
-
33
- st.altair_chart(alt.Chart(df, height=700, width=700)
34
- .mark_point(filled=True)
35
- .encode(
36
- x=alt.X("x", axis=None),
37
- y=alt.Y("y", axis=None),
38
- color=alt.Color("idx", legend=None, scale=alt.Scale()),
39
- size=alt.Size("rand", legend=None, scale=alt.Scale(range=[1, 150])),
40
- ))
 
 
 
 
1
  import streamlit as st
2
+ import os
3
+ import json
4
+ from datetime import datetime
5
+ import PyPDF2
6
+ import google.generativeai as genai
7
+
8
+ # --- Configuration and Setup ---
9
+ CLASSROOMS_DIR = "classrooms"
10
+ if not os.path.exists(CLASSROOMS_DIR):
11
+ os.makedirs(CLASSROOMS_DIR)
12
+
13
+ # --- Helper Functions ---
14
+
15
+ def extract_text_from_pdf(pdf_files):
16
+ """Extracts text from a list of uploaded PDF files."""
17
+ full_text = ""
18
+ for pdf_file in pdf_files:
19
+ try:
20
+ pdf_reader = PyPDF2.PdfReader(pdf_file)
21
+ for page in pdf_reader.pages:
22
+ full_text += page.extract_text() + "\n"
23
+ except Exception as e:
24
+ st.error(f"Error reading {pdf_file.name}: {e}")
25
+ return full_text
26
+
27
+ def save_classroom_data(class_name, api_key, context):
28
+ """Saves the classroom data (context, API key, and chat log)."""
29
+ class_dir = os.path.join(CLASSROOMS_DIR, class_name)
30
+ if not os.path.exists(class_dir):
31
+ os.makedirs(class_dir)
32
+
33
+ # Save context
34
+ with open(os.path.join(class_dir, "context.txt"), "w", encoding="utf-8") as f:
35
+ f.write(context)
36
+
37
+ # Save API key
38
+ with open(os.path.join(class_dir, "api_key.txt"), "w") as f:
39
+ f.write(api_key)
40
+
41
+ # Initialize chat log
42
+ with open(os.path.join(class_dir, "chat_log.json"), "w") as f:
43
+ json.dump([], f)
44
+
45
+ return True
46
+
47
+ def get_gemini_response(api_key, context, chat_history, question):
48
+ """Generates a response from the Gemini API based on the context."""
49
+ try:
50
+ genai.configure(api_key=api_key)
51
+ model = genai.GenerativeModel('gemini-pro')
52
+
53
+ # Construct a more detailed prompt for the model
54
+ prompt = (
55
+ "You are a helpful classroom assistant. Your role is to answer questions based ONLY on the provided context. "
56
+ "If the answer is not found in the context, you must state that you cannot answer the question based on the provided material. "
57
+ "Do not use any external knowledge.\n\n"
58
+ f"**Context:**\n{context}\n\n"
59
+ "**Chat History:**\n"
60
+ )
61
+
62
+ for message in chat_history:
63
+ prompt += f"{message['role']}: {message['content']}\n"
64
+
65
+ prompt += f"**New Question:**\n{question}\n\n**Answer:**"
66
+
67
+
68
+ response = model.generate_content(prompt)
69
+ return response.text
70
+ except Exception as e:
71
+ return f"An error occurred with the Gemini API: {e}"
72
+
73
+ def log_interaction(class_name, user_query, bot_response):
74
+ """Logs the student's interaction in the classroom's JSON file."""
75
+ log_file = os.path.join(CLASSROOMS_DIR, class_name, "chat_log.json")
76
+ try:
77
+ with open(log_file, "r+") as f:
78
+ logs = json.load(f)
79
+ logs.append({
80
+ "timestamp": datetime.now().isoformat(),
81
+ "user_query": user_query,
82
+ "bot_response": bot_response
83
+ })
84
+ f.seek(0)
85
+ json.dump(logs, f, indent=4)
86
+ except (FileNotFoundError, json.JSONDecodeError):
87
+ # If file is empty or corrupt, start a new log
88
+ with open(log_file, "w") as f:
89
+ json.dump([{
90
+ "timestamp": datetime.now().isoformat(),
91
+ "user_query": user_query,
92
+ "bot_response": bot_response
93
+ }], f, indent=4)
94
+
95
+
96
+ # --- Streamlit UI ---
97
+
98
+ st.set_page_config(page_title="Classroom Chatbot", layout="wide")
99
+
100
+ # Initialize session state variables
101
+ if 'role' not in st.session_state:
102
+ st.session_state.role = None
103
+ if 'class_name' not in st.session_state:
104
+ st.session_state.class_name = None
105
+ if 'chat_history' not in st.session_state:
106
+ st.session_state.chat_history = []
107
+
108
+
109
+ # --- Main App Logic ---
110
+
111
+ # Role Selection
112
+ if st.session_state.role is None:
113
+ st.title("Welcome to the Classroom Chatbot! πŸ“š")
114
+ st.write("Please select your role to begin.")
115
+ col1, col2 = st.columns(2)
116
+ with col1:
117
+ if st.button("I am a Faculty Member", use_container_width=True):
118
+ st.session_state.role = "Faculty"
119
+ st.rerun()
120
+ with col2:
121
+ if st.button("I am a Student", use_container_width=True):
122
+ st.session_state.role = "Student"
123
+ st.rerun()
124
+
125
+ # --- Faculty View ---
126
+ elif st.session_state.role == "Faculty":
127
+ st.title("πŸŽ“ Faculty Dashboard")
128
+ st.header("Create a New Classroom")
129
+
130
+ with st.form("create_classroom_form"):
131
+ class_name = st.text_input("Classroom Name / Code", help="A unique name for your class, e.g., 'CS101-Fall24'")
132
+ api_key = st.text_input("Google Gemini API Key", type="password", help="Your API key will be stored securely for this classroom's use.")
133
+ uploaded_files = st.file_uploader(
134
+ "Upload Course Materials (PDFs only)",
135
+ type="pdf",
136
+ accept_multiple_files=True
137
+ )
138
+ submitted = st.form_submit_button("Create Classroom")
139
+
140
+ if submitted:
141
+ if not class_name or not api_key or not uploaded_files:
142
+ st.warning("Please fill out all fields and upload at least one document.")
143
+ elif os.path.exists(os.path.join(CLASSROOMS_DIR, class_name)):
144
+ st.error(f"A classroom with the name '{class_name}' already exists. Please choose a different name.")
145
+ else:
146
+ with st.spinner("Processing documents and setting up classroom..."):
147
+ context = extract_text_from_pdf(uploaded_files)
148
+ if context:
149
+ save_classroom_data(class_name, api_key, context)
150
+ st.success(f"Classroom '{class_name}' created successfully!")
151
+ st.info(f"Students can now join using the code: **{class_name}**")
152
+ else:
153
+ st.error("Could not extract any text from the uploaded PDFs. Please check the files and try again.")
154
+
155
+ if st.button("Go Back to Role Selection"):
156
+ for key in st.session_state.keys():
157
+ del st.session_state[key]
158
+ st.rerun()
159
+
160
+
161
+ # --- Student View ---
162
+ elif st.session_state.role == "Student":
163
+ st.title("πŸ§‘β€πŸŽ“ Student Portal")
164
+
165
+ # Student joins a class
166
+ if st.session_state.class_name is None:
167
+ st.header("Join a Classroom")
168
+ class_name_input = st.text_input("Enter the Classroom Code provided by your faculty:")
169
+ if st.button("Join"):
170
+ class_dir = os.path.join(CLASSROOMS_DIR, class_name_input)
171
+ if os.path.isdir(class_dir):
172
+ st.session_state.class_name = class_name_input
173
+ st.success(f"Successfully joined classroom: {class_name_input}")
174
+ st.rerun()
175
+ else:
176
+ st.error("Invalid classroom code. Please check with your faculty.")
177
+ if st.button("Go Back to Role Selection"):
178
+ for key in st.session_state.keys():
179
+ del st.session_state[key]
180
+ st.rerun()
181
+
182
+ # Chat interface for joined students
183
+ else:
184
+ st.header(f"Chatbot for: {st.session_state.class_name}")
185
+ st.markdown("Ask questions about the course materials provided by your faculty.")
186
+
187
+ # Load classroom data
188
+ class_dir = os.path.join(CLASSROOMS_DIR, st.session_state.class_name)
189
+ try:
190
+ with open(os.path.join(class_dir, "context.txt"), "r", encoding="utf-8") as f:
191
+ context = f.read()
192
+ with open(os.path.join(class_dir, "api_key.txt"), "r") as f:
193
+ api_key = f.read().strip()
194
+ except FileNotFoundError:
195
+ st.error("Classroom data is missing. Please contact your faculty.")
196
+ st.stop()
197
+
198
+
199
+ # Display chat history
200
+ for message in st.session_state.chat_history:
201
+ with st.chat_message(message["role"]):
202
+ st.markdown(message["content"])
203
+
204
+ # Chat input
205
+ if prompt := st.chat_input("What is your question?"):
206
+ # Add user message to chat history
207
+ st.session_state.chat_history.append({"role": "user", "content": prompt})
208
+ with st.chat_message("user"):
209
+ st.markdown(prompt)
210
+
211
+ # Get bot response
212
+ with st.chat_message("assistant"):
213
+ with st.spinner("Thinking..."):
214
+ response = get_gemini_response(api_key, context, st.session_state.chat_history, prompt)
215
+ st.markdown(response)
216
+
217
+ # Add bot response to chat history and log it
218
+ st.session_state.chat_history.append({"role": "assistant", "content": response})
219
+ log_interaction(st.session_state.class_name, prompt, response)
220
+
221
+ if st.button("Leave Classroom"):
222
+ st.session_state.class_name = None
223
+ st.session_state.chat_history = []
224
+ st.rerun()
225