Spaces:
Sleeping
Sleeping
| import streamlit as st | |
| import os | |
| import json | |
| from datetime import datetime | |
| import PyPDF2 | |
| import google.generativeai as genai | |
| # --- Configuration and Setup --- | |
| CLASSROOMS_DIR = "classrooms" | |
| if not os.path.exists(CLASSROOMS_DIR): | |
| os.makedirs(CLASSROOMS_DIR) | |
| # --- Helper Functions --- | |
| def extract_text_from_pdf(pdf_files): | |
| """Extracts text from a list of uploaded PDF files.""" | |
| full_text = "" | |
| for pdf_file in pdf_files: | |
| try: | |
| pdf_reader = PyPDF2.PdfReader(pdf_file) | |
| for page in pdf_reader.pages: | |
| full_text += page.extract_text() + "\n" | |
| except Exception as e: | |
| st.error(f"Error reading {pdf_file.name}: {e}") | |
| return full_text | |
| def save_classroom_data(class_name, api_key, context): | |
| """Saves the classroom data (context, API key, and chat log).""" | |
| class_dir = os.path.join(CLASSROOMS_DIR, class_name) | |
| if not os.path.exists(class_dir): | |
| os.makedirs(class_dir) | |
| # Save context | |
| with open(os.path.join(class_dir, "context.txt"), "w", encoding="utf-8") as f: | |
| f.write(context) | |
| # Save API key | |
| with open(os.path.join(class_dir, "api_key.txt"), "w") as f: | |
| f.write(api_key) | |
| # Initialize chat log | |
| with open(os.path.join(class_dir, "chat_log.json"), "w") as f: | |
| json.dump([], f) | |
| return True | |
| def get_gemini_response(api_key, context, chat_history, question): | |
| """Generates a response from the Gemini API based on the context.""" | |
| try: | |
| genai.configure(api_key=api_key) | |
| model = genai.GenerativeModel('gemini-pro') | |
| # Construct a more detailed prompt for the model | |
| prompt = ( | |
| "You are a helpful classroom assistant. Your role is to answer questions based ONLY on the provided context. " | |
| "If the answer is not found in the context, you must state that you cannot answer the question based on the provided material. " | |
| "Do not use any external knowledge.\n\n" | |
| f"**Context:**\n{context}\n\n" | |
| "**Chat History:**\n" | |
| ) | |
| for message in chat_history: | |
| prompt += f"{message['role']}: {message['content']}\n" | |
| prompt += f"**New Question:**\n{question}\n\n**Answer:**" | |
| response = model.generate_content(prompt) | |
| return response.text | |
| except Exception as e: | |
| return f"An error occurred with the Gemini API: {e}" | |
| def log_interaction(class_name, user_query, bot_response): | |
| """Logs the student's interaction in the classroom's JSON file.""" | |
| log_file = os.path.join(CLASSROOMS_DIR, class_name, "chat_log.json") | |
| try: | |
| with open(log_file, "r+") as f: | |
| logs = json.load(f) | |
| logs.append({ | |
| "timestamp": datetime.now().isoformat(), | |
| "user_query": user_query, | |
| "bot_response": bot_response | |
| }) | |
| f.seek(0) | |
| json.dump(logs, f, indent=4) | |
| except (FileNotFoundError, json.JSONDecodeError): | |
| # If file is empty or corrupt, start a new log | |
| with open(log_file, "w") as f: | |
| json.dump([{ | |
| "timestamp": datetime.now().isoformat(), | |
| "user_query": user_query, | |
| "bot_response": bot_response | |
| }], f, indent=4) | |
| # --- Streamlit UI --- | |
| st.set_page_config(page_title="Classroom Chatbot", layout="wide") | |
| # Initialize session state variables | |
| if 'role' not in st.session_state: | |
| st.session_state.role = None | |
| if 'class_name' not in st.session_state: | |
| st.session_state.class_name = None | |
| if 'chat_history' not in st.session_state: | |
| st.session_state.chat_history = [] | |
| # --- Main App Logic --- | |
| # Role Selection | |
| if st.session_state.role is None: | |
| st.title("Welcome to the Classroom Chatbot! π") | |
| st.write("Please select your role to begin.") | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| if st.button("I am a Faculty Member", use_container_width=True): | |
| st.session_state.role = "Faculty" | |
| st.rerun() | |
| with col2: | |
| if st.button("I am a Student", use_container_width=True): | |
| st.session_state.role = "Student" | |
| st.rerun() | |
| # --- Faculty View --- | |
| elif st.session_state.role == "Faculty": | |
| st.title("π Faculty Dashboard") | |
| st.header("Create a New Classroom") | |
| with st.form("create_classroom_form"): | |
| class_name = st.text_input("Classroom Name / Code", help="A unique name for your class, e.g., 'CS101-Fall24'") | |
| api_key = st.text_input("Google Gemini API Key", type="password", help="Your API key will be stored securely for this classroom's use.") | |
| uploaded_files = st.file_uploader( | |
| "Upload Course Materials (PDFs only)", | |
| type="pdf", | |
| accept_multiple_files=True | |
| ) | |
| submitted = st.form_submit_button("Create Classroom") | |
| if submitted: | |
| if not class_name or not api_key or not uploaded_files: | |
| st.warning("Please fill out all fields and upload at least one document.") | |
| elif os.path.exists(os.path.join(CLASSROOMS_DIR, class_name)): | |
| st.error(f"A classroom with the name '{class_name}' already exists. Please choose a different name.") | |
| else: | |
| with st.spinner("Processing documents and setting up classroom..."): | |
| context = extract_text_from_pdf(uploaded_files) | |
| if context: | |
| save_classroom_data(class_name, api_key, context) | |
| st.success(f"Classroom '{class_name}' created successfully!") | |
| st.info(f"Students can now join using the code: **{class_name}**") | |
| else: | |
| st.error("Could not extract any text from the uploaded PDFs. Please check the files and try again.") | |
| if st.button("Go Back to Role Selection"): | |
| for key in st.session_state.keys(): | |
| del st.session_state[key] | |
| st.rerun() | |
| # --- Student View --- | |
| elif st.session_state.role == "Student": | |
| st.title("π§βπ Student Portal") | |
| # Student joins a class | |
| if st.session_state.class_name is None: | |
| st.header("Join a Classroom") | |
| class_name_input = st.text_input("Enter the Classroom Code provided by your faculty:") | |
| if st.button("Join"): | |
| class_dir = os.path.join(CLASSROOMS_DIR, class_name_input) | |
| if os.path.isdir(class_dir): | |
| st.session_state.class_name = class_name_input | |
| st.success(f"Successfully joined classroom: {class_name_input}") | |
| st.rerun() | |
| else: | |
| st.error("Invalid classroom code. Please check with your faculty.") | |
| if st.button("Go Back to Role Selection"): | |
| for key in st.session_state.keys(): | |
| del st.session_state[key] | |
| st.rerun() | |
| # Chat interface for joined students | |
| else: | |
| st.header(f"Chatbot for: {st.session_state.class_name}") | |
| st.markdown("Ask questions about the course materials provided by your faculty.") | |
| # Load classroom data | |
| class_dir = os.path.join(CLASSROOMS_DIR, st.session_state.class_name) | |
| try: | |
| with open(os.path.join(class_dir, "context.txt"), "r", encoding="utf-8") as f: | |
| context = f.read() | |
| with open(os.path.join(class_dir, "api_key.txt"), "r") as f: | |
| api_key = f.read().strip() | |
| except FileNotFoundError: | |
| st.error("Classroom data is missing. Please contact your faculty.") | |
| st.stop() | |
| # Display chat history | |
| for message in st.session_state.chat_history: | |
| with st.chat_message(message["role"]): | |
| st.markdown(message["content"]) | |
| # Chat input | |
| if prompt := st.chat_input("What is your question?"): | |
| # Add user message to chat history | |
| st.session_state.chat_history.append({"role": "user", "content": prompt}) | |
| with st.chat_message("user"): | |
| st.markdown(prompt) | |
| # Get bot response | |
| with st.chat_message("assistant"): | |
| with st.spinner("Thinking..."): | |
| response = get_gemini_response(api_key, context, st.session_state.chat_history, prompt) | |
| st.markdown(response) | |
| # Add bot response to chat history and log it | |
| st.session_state.chat_history.append({"role": "assistant", "content": response}) | |
| log_interaction(st.session_state.class_name, prompt, response) | |
| if st.button("Leave Classroom"): | |
| st.session_state.class_name = None | |
| st.session_state.chat_history = [] | |
| st.rerun() | |