Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ backend/schedules
schedules
.env
*.tar
**/venv/

## Begin autogenerated
.gradle
Expand Down
11 changes: 10 additions & 1 deletion backend/docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,23 @@ To install the latest stable version of Vite, run ```npm install -D vite```.
A java JDK installation of version at least 21 is required to build and run this project.
You can install one with ```winget install EclipseAdoptium.Temurin.21.JDK```.

### Python
A python version of 3.10 or higher is required to run this project.


## Running

Currently, we do not have a prebuilt executable. You can run the project by executing the `run` gradle task.
There are two ways to do this:
1. Change directories to the backend folder and run `./gradlew run`
2. Open the project in IntelliJ and execute the `application/run` gradle task from
the right hand menu.
3. Navigate to `http://localhost:7070`.
3. From a terminal inside the python-backend folder, run `python -m venv venv` to create the virtual environment.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way to incorporate these steps into the build.gradle instead of having to manually do each?

4. Activate the Venv by running `venv\Scripts\activate` if using Windows, or `source venv/bin/activate` for Mac/Linux.
5. Run `pip install -r requirements.txt` to download the python backend dependencies.
6. Run `uvicorn main:app --reload` to start the application.
7. Navigate to `http://localhost:7070`.


## OCI Containers
If you have Docker or Podman installed you can create an OCI container image .tar by running `./gradlew containerBuild` in the backend folder
102 changes: 102 additions & 0 deletions frontend/src/SearchPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Toaster, toast } from "react-hot-toast";
import { useEffect, useState, useRef } from 'react';



interface CourseTime {
day: string;
start_time: string;
Expand Down Expand Up @@ -39,6 +40,10 @@ export default function SearchPage() {
const [availableTimes, setAvailableTimes] = useState<string[]>([]);
const didMount = useRef(false);
const isClearing = useRef(false);
const [text, setText] = useState(""); //for the chatbot query
const [result, setResult] = useState("Ask a question about your major's required classes!"); // the api call result
const [isOpen, setIsOpen] = useState(false); // for the modal
const [chatbotDept, setCbDept] = useState("Comp");

const getCourseId = (course: Course) =>
`${course.subject}${course.number}${course.section}${course.semester}`; //
Expand Down Expand Up @@ -313,9 +318,46 @@ export default function SearchPage() {
(_, i) => windowStart + i
);

//Chatbot query box
async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();

if(text != ""){
const response = await fetch("http://127.0.0.1:8000/query", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
text,
chatbotDept
}),
});

const data = await response.json();
setResult(data.result);
}
}

//modal styling
const modalStyle: React.CSSProperties = {
position: "fixed",
top: "0",
right: "0",
height: "100vh",
width: "350px",
background: "white",
padding: "20px",
boxShadow: "-2px 0 10px rgba(0,0,0,0.2)",
zIndex: 1000,
overflowY: "auto",
};


return (
<div className="layout">
<div><Toaster/></div>

{/* LEFT SIDEBAR */}
<div className="sidebar">
<h3>Filters</h3>
Expand Down Expand Up @@ -391,6 +433,7 @@ export default function SearchPage() {
value={query}
onChange={e => setQuery(e.target.value)}
onKeyDown={e => e.key === 'Enter' && search()}
maxLength={1200}
/>
<button onClick={search}>Search</button>
</div>
Expand Down Expand Up @@ -528,6 +571,65 @@ export default function SearchPage() {
)}
</div>
</div>
{/*Floating Chatbot Button*/}
<button
onClick={() => setIsOpen(true)}
style={{
position: "fixed",
bottom: "20px",
right: "20px",
padding: "12px 16px",
borderRadius: "50%",
fontSize: "18px",
cursor: "pointer",
}}
>
chat!
</button>
{isOpen && (
<div style={modalStyle}>
<select value={chatbotDept} onChange={(e) => setCbDept(e.target.value)}>
<option value="Comp">Computer Science B.S.</option>
<option value="Engl">English B.A.</option>
</select>

{/* Close button */}
<button
onClick={() => setIsOpen(false)}
style={{ float: "right" }}
>
X
</button>

{/* Content layout */}
<div style={{ display: "flex", flexDirection: "column", gap: "10px" }}>



{/* Left: textbox */}
<form onSubmit={handleSubmit}>
<input
value={text}
onChange={(e) => setText(e.target.value)}
placeholder="Type..."
maxLength={1200}
/>
<button type="submit">Send</button>
</form>


{/* scrollable content */}
<div style={{ flex: 1, overflowY: "auto", marginTop: "20px" }}>
<p style={{ overflowWrap: "break-word" }}>{result}</p>
</div>




</div>
</div>

)}
</div>
);
}
68 changes: 68 additions & 0 deletions python-backend/Comp.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
Computer Science B.S. suggested four year plan:
Freshman Year:
Fall:
{"department": "COMP", "number": "141", "name": "Computer Programming I", "credits": 3},
{"department": "COMP", "number": "155", "name": "Introduction to Computer Science", "credits": 3},
{"department": "MATH", "number": "161", "name": "Calculus I", "credits": 4},
one science elective (each are four credits, either PHYS 101; PHYS 102; CHEM 105 or both CHEM 111 and 113; CHEM
112 and 114; BIOL 101; or BIOL 102),
{"department": "HUMA", "number": "102", "name": "Civ and the Biblical Revelation", "credits": 3},

Spring:
{"department": "COMP", "number": "220", "name": "Computer Programming II", "credits": 3},
{"department": "MATH", "number": "162", "name": "Calculus II", "credits": 4},
one science elective (from previous list)
{"department": "WRIT", "number": "101", "name": "Foundations of Academic Discourse", "credits": 3},
{"department": "PHYE", "number": "100", "name": "Healthful Living", "credits": 1},


Sophomore year:
Fall:
{"department": "COMP", "number": "222", "name": "Introduction to Data Structures & Algorithms", "credits": 3},
{"department": "COMP", "number": "244", "name": "Database Management", "credits": 3},
{"department": "MATH", "number": "213", "name": "Discrete Mathematics for Comp. Science", "credits": 4},
{"department": "HUMA", "number": "200", "name": "Western Civilization", "credits": 3},
Foundations of social science course (3 credits, either ECON 120 Foundations of Economics, PSYC 101 Foundations of Psychology, HIST 120 Foundations of History, PSYC 200 Cross-Cultural Psychology, HIST 141 World Geography, SOCI 101 Foundations of Sociology, HIST 204 Hist/Phil Foundations of Education, SOCI 103 Found. of Cultural Anthr., POLS 101 Foundations of Political Science, or SOCW 101 Found. of Social Work)
one credit of a general elective (any non-CS class)

Spring:
{"department": "COMP", "number": "205", "name": "Ethics, Faith, and the Conscious Mind", "credits": 3},
{"department": "COMP", "number": "233", "name": "Parallel Computing", "credits": 3},
{"department": "COMP", "number": "342", "name": "Data Communication & Networking", "credits": 3},
{"department": "MATH", "number": "214", "name": "Applied Probability & Linear Algebra", "credits": 4},
{"department": "HUMA", "number": "202", "name": "Civilization and Literature", "credits": 3},



Junior Year:
Fall:
{"department": "COMP", "number": "325", "name": "Computer Architecture & Organization", "credits": 3},
{"department": "COMP", "number": "422", "name": "Introduction to Algorithms", "credits": 3},
two three-credit computer science electives (from COMP 390 Topics in Computer Science, COMP 401 Principles of iOS Programming, COMP 402
3 Principles of Android Programming, COMP 435 Intro to Machine Learning, COMP 441 2D Game
Design/Development, COMP 442 Web Programming Technologies, COMP 445 Intro to Artificial Intelligence, COMP 446 3D Game Design/Development, COMP 447 Networked Game Design and Development, COMP 475 Advanced Security, DSCI 431 Intro to Big Data , DSCI 450 Applied
Modeling and Visualization, or ROBO 302 Mobile Robotics.)
{"department": "HUMA", "number": "301", "name": "Civilization and the Arts", "credits": 3},
two general elective credits

Spring:
{"department": "COMP", "number": "314", "name": "Automata Theory", "credits": 3},
{"department": "COMP", "number": "340", "name": "Operating Systems", "credits": 3},
{"department": "COMP", "number": "350", "name": "Software Engineering", "credits": 3},
one three-credit computer science elective (from the list above)
four credits of general electives


Senior Year:
Fall:
{"department": "COMP", "number": "448", "name": "Computer Security", "credits": 3},
{"department": "COMP", "number": "451", "name": "Senior Project I", "credits": 2},
three credits of a computer science elective
{"department": "HUMA", "number": "303", "name": "Christianity and Civilization", "credits": 3}
four credits of general electives

Spring:
{"department": "COMP", "number": "443", "name": "Programming Languages", "credits": 3},
{"department": "COMP", "number": "452", "name": "Senior Project II", "credits": 3},
one three-credit computer science elective
six credits of general electives
75 changes: 75 additions & 0 deletions python-backend/Engl.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
English B.A. Suggested Four Year Plan

Freshman Year:
Fall:
{"department": "ENGL", "number": "201", "name": "English Literature I", "credits": 3},
ENGL Elective (3 credits, from list below),
{"department": "HUMA", "number": "102", "name": "Civ and the Biblical Revelation", "credits": 3},
Foundations of Social Science course (3 credits, either ECON 120 Foundations of Economics, PSYC 101 Foundations of Psychology, HIST 120 Foundations of History, PSYC 200 Cross-Cultural Psychology, HIST 141 World Geography, SOCI 101 Foundations of Sociology, HIST 204 Hist/Phil Foundations of Education, SOCI 103 Found. of Cultural Anthr., POLS 101 Foundations of Political Science, or SOCW 101 Found. of Social Work),
Foreign Language or General Elective (3 credits)

Spring:
{"department": "ENGL", "number": "202", "name": "English Literature II", "credits": 3},
{"department": "WRIT", "number": "101", "name": "Foundations of Academic Discourse", "credits": 3},
Quantitative/Logical Reasoning course (3 credits),
{"department": "PHYE", "number": "100", "name": "Healthful Living", "credits": 1},
Foreign Language or General Elective (3 credits),
General Elective (3 credits)

Sophomore Year:
Fall:
{"department": "ENGL", "number": "203", "name": "American Literature I", "credits": 3},
ENGL 205, ENGL 250, or ENGL Elective (3 credits),
{"department": "HUMA", "number": "200", "name": "Western Civilization", "credits": 3},
Natural Science with Lab (4 credits),
Foreign Language or General Elective (3 credits)

Spring:
{"department": "ENGL", "number": "204", "name": "American Literature II", "credits": 3},
ENGL Elective or {"department": "ENGL", "number": "206", "name": "Course option", "credits": 3},
SSFT Course (2 credits),
Foreign Language or General Elective (3 credits),
General Elective (5 credits)

Junior Year:
Fall:
{"department": "ENGL", "number": "351", "name": "Shakespeare", "credits": 3} or ENGL Elective,
{"department": "ENGL", "number": "302", "name": "Course option", "credits": 3} or ENGL Elective,
ENGL Elective (3 credits),
{"department": "HUMA", "number": "301", "name": "Civilization and the Arts", "credits": 3},
Natural Science with Lab (4 credits)

Spring:
ENGL Elective or {"department": "ENGL", "number": "352", "name": "Course option", "credits": 3},
ENGL Elective or {"department": "ENGL", "number": "302", "name": "Course option", "credits": 3},
{"department": "HUMA", "number": "303", "name": "Christianity and Civilization", "credits": 3},
General Electives (8 credits)

Senior Year:
Fall:
Quantitative/Logical Reasoning course (3 credits),
General Electives (13 credits)

Spring:
{"department": "ENGL", "number": "485", "name": "Senior Capstone", "credits": 3},
General Electives (13 credits)



ENGL Electives List:

ENGL 221 Science Fiction
ENGL 222 Fantasy Literature
ENGL 224 Arthurian Literature
ENGL 226 Gothic Literature
ENGL 242 19th Century English Novel
ENGL 243 20th Century English Novel
ENGL 245 19th Century American Novel
ENGL 246 20th Century American Novel
ENGL 252 Modern Drama
ENGL 254 Page to Stage (1 credit)
ENGL 261 Poetry
ENGL 262 Modern Poetry
ENGL 324 European Literature
ENGL 327 Christian Writers
ENGL 356 Literary Theory
Binary file added python-backend/__pycache__/main.cpython-313.pyc
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file should not be in the repository, please add the __pycache__ to the .gitignore

Binary file not shown.
70 changes: 70 additions & 0 deletions python-backend/main.py
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason why we shouldn't put this under backend/main/python?

Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
from fastapi import FastAPI
from pydantic import BaseModel
from fastapi.middleware.cors import CORSMiddleware
import os
from openai import OpenAI
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I ran, it couldn't find the OpenAI module. I ran the steps listed in the readme.

import json
from dotenv import load_dotenv

load_dotenv()



app = FastAPI()
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))

class QueryRequest(BaseModel):
text: str
chatbotDept: str



@app.post("/query")
def query(req: QueryRequest):

if not req.text.strip():
return {"result": "Please enter a question to begin."}

prompt = """
You are a helpful assistant that provides information or gives advice based on the required courses and suggested four-year plan for a certain major.
When given a question, you should provide an accurate and concise answer based on the information I provide, and be friendly and helpful. If you don't know the answer, say you don't know. Do not make up an answer.
Don't make anything bold in the answer you return.
If you're giving a response that includes a list of courses, please format the courses in a clear and organized way, such as using bullet points or numbering. Include newlines. This will make it easier for the user to read and understand the information.
Here is the course information and four-year plan taken from the major requirements:\n

"""

with open(req.chatbotDept + ".txt") as f:
context_data = f.read()

context_str = json.dumps(context_data, indent=2)
prompt += context_str

user_prompt = req.text


response = client.chat.completions.create(
model="gpt-5.4-nano",
messages=[
{"role": "system", "content": prompt},
{"role": "user", "content": user_prompt}
]
)

result = response.choices[0].message.content


return {"result": result}


# browser permission rule that lets the React frontend talk to the Python
# backend during development.

app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:7070"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)

Binary file added python-backend/requirements.txt
Binary file not shown.
Loading