From 2340f541de1166eb3bfcc8c8da2e1c55164965d2 Mon Sep 17 00:00:00 2001 From: Bhavya Jain <76478989+bhavya-jain-364@users.noreply.github.com> Date: Sat, 22 Mar 2025 16:08:06 -0400 Subject: [PATCH 1/4] Enhance CourseSettingsModal with confirmation dialog for course removal and update toast notification for file uploads. Add node_modules to .gitignore. --- front_end/.gitignore | 1 + .../src/components/AssessmentSection.tsx | 4 +-- .../src/components/CourseSettingsModal.tsx | 29 +++++++++++++++++-- 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/front_end/.gitignore b/front_end/.gitignore index af2b9fe..48b225d 100644 --- a/front_end/.gitignore +++ b/front_end/.gitignore @@ -11,5 +11,6 @@ docs/_build/ .env .DS_Store privateinfobutidc.json +node_modules/ chroma_db \ No newline at end of file diff --git a/front_end/src/components/AssessmentSection.tsx b/front_end/src/components/AssessmentSection.tsx index 9aaa812..1575ce4 100644 --- a/front_end/src/components/AssessmentSection.tsx +++ b/front_end/src/components/AssessmentSection.tsx @@ -1,4 +1,3 @@ - import React, { useState } from 'react'; import { Card, CardContent } from "@/components/ui/card"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; @@ -61,8 +60,7 @@ const AssessmentSection: React.FC = () => { const handleUploadFile = (file: File) => { console.log("Assessment file uploaded:", file.name); - toast({ - title: "File Uploaded", + toast.success("File Uploaded", { description: `${file.name} has been uploaded successfully.`, duration: 3000, }); diff --git a/front_end/src/components/CourseSettingsModal.tsx b/front_end/src/components/CourseSettingsModal.tsx index 693b01b..1ba82aa 100644 --- a/front_end/src/components/CourseSettingsModal.tsx +++ b/front_end/src/components/CourseSettingsModal.tsx @@ -1,4 +1,3 @@ - import React, { useState, useEffect } from 'react'; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; @@ -36,6 +35,7 @@ const CourseSettingsModal: React.FC = ({ const [courseName, setCourseName] = useState(''); const [instructor, setInstructor] = useState(''); const [syllabus, setSyllabus] = useState(null); + const [showRemoveConfirmation, setShowRemoveConfirmation] = useState(false); // Parse current schedule and initialize form values useEffect(() => { @@ -121,6 +121,12 @@ const CourseSettingsModal: React.FC = ({ onClose(); }; + const handleConfirmRemove = () => { + onRemoveCourse(); + setShowRemoveConfirmation(false); + onClose(); + }; + return ( @@ -182,7 +188,7 @@ const CourseSettingsModal: React.FC = ({

Danger Zone

+ + {/* Confirmation Dialog */} + + + + Confirm Removal + +
+

Are you sure you want to remove "{courseName}"? This action cannot be undone.

+
+ + + + +
+
); }; From 003da68c365022ee5b796c2956b75b798a2ce40c Mon Sep 17 00:00:00 2001 From: akhkim <166858130+akhkim@users.noreply.github.com> Date: Sat, 22 Mar 2025 16:14:13 -0400 Subject: [PATCH 2/4] Updated user data query --- flask_app/app.py | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/flask_app/app.py b/flask_app/app.py index 546bb98..b3dace0 100644 --- a/flask_app/app.py +++ b/flask_app/app.py @@ -50,7 +50,49 @@ def generate_response(): response = get_response(query) return jsonify({'response': response}) + +@app.route('/api/users', methods=['POST']) +def create_user(): + # Get the JSON data from request + user_data = request.get_json() + # Validate required fields + required_fields = ['googleUid', 'email'] + for field in required_fields: + if not user_data.get(field): + return jsonify({ + "error": f"Missing required field: {field}" + }), 400 + + try: + # Format the user data for MongoDB + user_to_insert = [{ + "googleUid": user_data.get('googleUid'), + "email": user_data.get('email'), + "displayName": user_data.get('displayName'), + "photoURL": user_data.get('photoURL'), + "createdAt": datetime.datetime.utcnow(), + "lastLoginAt": datetime.datetime.utcnow() + }] + + # Insert the user into MongoDB + inserted_ids = insert_sample_users(user_to_insert, "User") + + if not inserted_ids: + return jsonify({ + "error": "Failed to create user in database" + }), 500 + + return jsonify({ + "message": "User created successfully", + "userId": str(inserted_ids[0]) + }), 201 + + except Exception as e: + return jsonify({ + "error": f"Server error: {str(e)}" + }), 500 + if __name__ == '__main__': app.run(debug=True) From 4bed0cbb21be0812ac545738f61d824e65941c2c Mon Sep 17 00:00:00 2001 From: akhkim <166858130+akhkim@users.noreply.github.com> Date: Sat, 22 Mar 2025 16:15:03 -0400 Subject: [PATCH 3/4] Updated user data query --- front_end/src/lib/auth-context.tsx | 41 +++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/front_end/src/lib/auth-context.tsx b/front_end/src/lib/auth-context.tsx index 1064ca0..aeee541 100644 --- a/front_end/src/lib/auth-context.tsx +++ b/front_end/src/lib/auth-context.tsx @@ -30,10 +30,45 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { return () => unsubscribe(); }, []); - const signInWithGoogle = async () => { +const signInWithGoogle = async () => { const provider = new GoogleAuthProvider(); try { - await signInWithPopup(auth, provider); + const result = await signInWithPopup(auth, provider); + const user = result.user; + + // Create the user data object + const userData = { + googleUid: user.uid, + email: user.email, + displayName: user.displayName, + photoURL: user.photoURL, + }; + + // Send POST request to Flask backend API + try { + const response = await axios({ + method: 'post', + url: 'http://localhost:5000/api/users', + data: userData, + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json' + } + }); + + if (response.status === 201 && response.data.userId) { + console.log('User created successfully:', response.data); + // You can store the userId in local storage or state if needed + localStorage.setItem('userId', response.data.userId); + } else { + throw new Error('Invalid response from server'); + } + + } catch (error: any) { + console.error('Error creating user:', error.response?.data?.error || error.message); + throw new Error('Failed to create user in database'); + } + } catch (error) { console.error('Error signing in with Google:', error); throw error; @@ -69,4 +104,4 @@ export function useAuth() { throw new Error('useAuth must be used within an AuthProvider'); } return context; -} \ No newline at end of file +} From 7f3eb154a235e6e4b52a36e5a87f8e5f25db93f6 Mon Sep 17 00:00:00 2001 From: John Zhang <23zhanyuc@gmail.com> Date: Sat, 22 Mar 2025 17:20:15 -0400 Subject: [PATCH 4/4] Connect to database --- flask_app/app.py | 26 +++++++++++------ flask_app/chatbot.py | 20 ++++++------- flask_app/requirements.txt | 4 ++- front_end/src/api/api.tsx | 36 ++++++++++++++++++++++++ front_end/src/components/ChatSection.tsx | 22 +++++++-------- front_end/src/lib/auth-context.tsx | 19 ++++--------- 6 files changed, 83 insertions(+), 44 deletions(-) create mode 100644 front_end/src/api/api.tsx diff --git a/flask_app/app.py b/flask_app/app.py index b3dace0..19c1451 100644 --- a/flask_app/app.py +++ b/flask_app/app.py @@ -1,7 +1,13 @@ from flask import Flask, request, jsonify +from flask_cors import CORS from flask_app.chatbot import vectorize_and_store, get_response +import datetime + +from flask_app.add_sample_users import insert_sample_users app = Flask(__name__) +CORS(app, origins="*", methods=["GET", "POST", "OPTIONS"], allow_headers=["Content-Type", "Authorization"]) + FILE_TYPES = [ "application/pdf", # PDF @@ -17,21 +23,23 @@ def root(): }) -@app.route("/upload", methods=["POST"]) +@app.route("/api/upload", methods=["POST"]) def upload_file(): """Handles user file uploads.""" file = request.files.get("file") - # course = request.json.get("course") # could be form or json dependng on how frontend sends request - # TODO: add other useful file metadata + course = request.form.get("course") if not file: return jsonify({"error": "No file provided"}), 400 + + if not course: + return jsonify({"error": "No course provided"}), 400 if file.content_type not in FILE_TYPES: return jsonify({"error": "Unsupported file type"}), 400 # Process the file and store in ChromaDB (for chatbot) - vectorize_and_store(file) + vectorize_and_store(file, course) # TODO: insert to database @@ -39,18 +47,20 @@ def upload_file(): return jsonify({"message": "File uploaded & processed successfully."}) -@app.route("/chat", methods=["POST"]) +@app.route("/api/chat", methods=["POST"]) def generate_response(): """Generate a response to user queries.""" query = request.json.get("query") + course = request.json.get("course") if not query: return jsonify({"error": "No query provided"}), 400 - response = get_response(query) + response = get_response(query, course) return jsonify({'response': response}) + @app.route('/api/users', methods=['POST']) def create_user(): # Get the JSON data from request @@ -71,8 +81,8 @@ def create_user(): "email": user_data.get('email'), "displayName": user_data.get('displayName'), "photoURL": user_data.get('photoURL'), - "createdAt": datetime.datetime.utcnow(), - "lastLoginAt": datetime.datetime.utcnow() + "createdAt": datetime.datetime.now(datetime.timezone.utc), + "lastLoginAt": datetime.datetime.now(datetime.timezone.utc) }] # Insert the user into MongoDB diff --git a/flask_app/chatbot.py b/flask_app/chatbot.py index 08a2071..2ba6999 100644 --- a/flask_app/chatbot.py +++ b/flask_app/chatbot.py @@ -16,22 +16,22 @@ HISTORY = [] # Store conversation history in-memory for the purpose of hackathon -def vectorize_and_store(file): +def vectorize_and_store(file, course): text = extract_text(file) embedding = get_embedding(text) - documents.add(ids=str(uuid.uuid4()), documents=text, embeddings=embedding) + documents.add(ids=str(uuid.uuid4()), documents=text, embeddings=embedding, metadatas={"course": course}) def get_embedding(text): - return embedding_model.encode(text) + return embedding_model.encode(text).tolist() -def query_documents(query_embedding, top_k=3): - results = documents.query(query_embeddings=query_embedding, n_results=top_k) +def query_documents(query_embedding, course, top_k=3): + results = documents.query(query_embeddings=query_embedding, n_results=top_k, where={"course": course}) return results["documents"], results["metadatas"] -def get_prompt(query, context): +def get_prompt(query, course, context): prompt = f""" Conversation History: {"\n".join([f"User: {exchange['user']}\nBot: {exchange['bot']}" for exchange in HISTORY])} @@ -42,7 +42,7 @@ def get_prompt(query, context): {context} Instructions: - You are an AI assistant designed to answer user questions based on the provided context. Focus on providing a clear and relevant answer based on the context and the conversation history if applicable. Do not mention the process of retrieving or accessing data, and keep the conversation natural and focused on the user's needs. If a piece of information is missing, simply state that you don't know it, but avoid referencing how or why you lack that information. + You are an AI assistant designed to answer user questions about their {course} course, based on the provided context. Focus on providing a clear and relevant answer based on the context and the conversation history if applicable. Do not mention the process of retrieving or accessing data, and keep the conversation natural and focused on the user's needs. If a piece of information is missing, simply state that you don't know it, but avoid referencing how or why you lack that information. Answer: """ @@ -51,13 +51,13 @@ def get_prompt(query, context): -def get_response(query): +def get_response(query, course): query_embedding = get_embedding(query) - documents, metadatas = query_documents(query_embedding) + documents, _ = query_documents(query_embedding, course) context = "\n".join([doc for doc in documents[0]]) model = genai.GenerativeModel(model_name='gemini-2.0-flash') - response = model.generate_content(get_prompt(query, context)) + response = model.generate_content(get_prompt(query, course, context)) HISTORY.append({"user": query, "bot": response}) diff --git a/flask_app/requirements.txt b/flask_app/requirements.txt index 7f8d591..16ebb6c 100644 --- a/flask_app/requirements.txt +++ b/flask_app/requirements.txt @@ -9,4 +9,6 @@ google-generativeai chromadb sentence-transformers uuid -pdfplumber \ No newline at end of file +pdfplumber +flask-cors +pymongo \ No newline at end of file diff --git a/front_end/src/api/api.tsx b/front_end/src/api/api.tsx new file mode 100644 index 0000000..06c2a45 --- /dev/null +++ b/front_end/src/api/api.tsx @@ -0,0 +1,36 @@ +export const fetchAssistantResponse = async (userMessage: string) => { + const response = await fetch('http://127.0.0.1:5000/api/chat', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ query: userMessage, course: "course2" }), + }); + + if (!response.ok) { + throw new Error('Failed to fetch AI response'); + } + + const data = await response.json(); + return data; + }; + + +export const insertUser = async (userData: object) => { + const response = await fetch('http://127.0.0.1:5000/api/users', { + method: 'post', + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json' + }, + body: JSON.stringify(userData) + }); + + if (!response.ok) { + throw new Error('Failed to insert user to database'); + } + + const data = await response.json(); + return data; + +}; \ No newline at end of file diff --git a/front_end/src/components/ChatSection.tsx b/front_end/src/components/ChatSection.tsx index dbe0fd9..a1ca194 100644 --- a/front_end/src/components/ChatSection.tsx +++ b/front_end/src/components/ChatSection.tsx @@ -4,6 +4,7 @@ import { Button } from "@/components/ui/button"; import { Textarea } from "@/components/ui/textarea"; import { ScrollArea } from "@/components/ui/scroll-area"; import { Send } from 'lucide-react'; +import { fetchAssistantResponse } from '@/api/api'; interface Message { id: string; @@ -43,7 +44,7 @@ const ChatSection: React.FC = () => { } }; - const handleSendMessage = () => { + const handleSendMessage = async () => { if (inputValue.trim() === '') return; // Add user message @@ -57,17 +58,14 @@ const ChatSection: React.FC = () => { setMessages(prev => [...prev, userMessage]); setInputValue(''); - // Simulate assistant response - setTimeout(() => { - const assistantMessage: Message = { - id: (Date.now() + 1).toString(), - text: "I've received your message. In a real implementation, this would connect to an AI assistant to provide relevant responses about your course materials.", - sender: 'assistant', - timestamp: new Date() - }; - - setMessages(prev => [...prev, assistantMessage]); - }, 1000); + const { response } = await fetchAssistantResponse(inputValue); + const assistantMessage: Message = { + id: (Date.now() + 1).toString(), + text: response, + sender: 'assistant', + timestamp: new Date() + }; + setMessages(prev => [...prev, assistantMessage]); }; const formatTime = (date: Date) => { diff --git a/front_end/src/lib/auth-context.tsx b/front_end/src/lib/auth-context.tsx index aeee541..7687c96 100644 --- a/front_end/src/lib/auth-context.tsx +++ b/front_end/src/lib/auth-context.tsx @@ -6,6 +6,7 @@ import { onAuthStateChanged, User } from 'firebase/auth'; +import { insertUser } from '@/api/api' import { auth } from './firebase'; interface AuthContextType { @@ -46,26 +47,18 @@ const signInWithGoogle = async () => { // Send POST request to Flask backend API try { - const response = await axios({ - method: 'post', - url: 'http://localhost:5000/api/users', - data: userData, - headers: { - 'Content-Type': 'application/json', - 'Accept': 'application/json' - } - }); + const data = await insertUser(userData); - if (response.status === 201 && response.data.userId) { - console.log('User created successfully:', response.data); + if (data.userId) { + console.log('User created successfully:', data); // You can store the userId in local storage or state if needed - localStorage.setItem('userId', response.data.userId); + localStorage.setItem('userId', data.userId); } else { throw new Error('Invalid response from server'); } } catch (error: any) { - console.error('Error creating user:', error.response?.data?.error || error.message); + console.error('Error creating user:', error.data?.error || error.message); throw new Error('Failed to create user in database'); }