# Project export: UCSC Major Reqs Planner

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: CruzHacks 2024
- Tagline: Plan out your courses required for your major
- Devpost: https://devpost.com/software/ucsc-major-reqs-planner
- GitHub: https://github.com/PurrGod/college-planner-webapp
- Demo: https://yashie.me/UCSC4yearplanner/
- Team: 4 GitHub contributor(s) — Amrita Pasupuleti (33 commits), Reva Mahto (10 commits), PurrGod (9 commits), Yash (6 commits)

## Devpost submission (written by the team)

### Inspiration

Our main goal was to be absolute academic weapons. To achieve this, we needed to have a clear vision of what we were going to be doing in our 4 college years. Now the idea has been famously developed by multiple UCSC students because the struggle is real and common, but why do we still feel so lost when we plan our courses? Find out why we stand out below.

### What it does

Our website takes into consideration the prerequisites needed to advance in your course pathway. In the original UCSC course catalog, students struggle to organize their classes because of how separated information is. We made sure to have classes, respective prerequisites, and the calendar all in one page for simple, organized, and efficient planning. Additionally, these academic plans are often requested by academic advisors. So we added an ‘Export’ button that allows for various accessibility and sharing needs.

### How we built it

We webscraped the UCSC Course Catalog to obtain data on all of the course information and prerequisites for each major. We learned and used ReactJS to build the full-stack website.We add interactive buttons, images, loading signs, drag and drop, and section-wise scrolling for seamless user experience.

### Challenges we ran into

All of us had trouble setting up the environment for ReactJS (npm install, Node). We didn’t have experience with the level of debugging skills required, especially on niche systems like Ubuntu, different Mac versions, and Windows (we had all). Additionally, it was some of our first times using GitHub to collaborate on code, so we went through some pains of rewriting code we lost :0

### Accomplishments we're proud of

Despite those hurdles, we learned so much in 2 days. ReactJS is so popular and widely used, but only 1 of our 4 teammates had introductory experience with it. Regardless, we took it upon ourselves to self-learn and communicate when we needed help.

### What we learned

In team projects, work is often split up and done individually. However, our team had a great flow just by picking up tasks that needed to be done and grinding it out. The dynamics and skill distribution made the process much easier. This hackathon will go in the books as one of the best just because of our united eagerness to learn and create :)

### What's next

Learn and use AI to auto generate a 4 year plan for the user. Utilize LLM to generate customized course recommendations . Have import functionality from csv data to populate the website planner. We also plan to add features that would show the description of each class.

## README (from the GitHub repository)

# UCSC Major Classes Planner (College Planner Webapp)

A React-based planner that helps UCSC students build a quarter-by-quarter **4-year plan**. Search a major, drag & drop required courses into a calendar grid, view prerequisites, and export the plan to CSV. 

---

## Features

- **Major search** with dropdown and remote fetch of requirements/prereqs.
- **Drag & drop schedule builder** across Fall/Winter/Spring/Summer for 4 years.
- **Course detail popups** showing prerequisites.
- **Export to CSV** with a single click.
- **GE palette** for dropping GE requirements.
- Global app state via **React Context**.

---

## Tech Stack

- **Frontend:** React 18, CRA toolchain, react-dnd, react-select, reactjs-popup.
- **Utilities:** papaparse (CSV), testing-library (dev).
- **Styling:** Plain CSS modules per component.
- **Data source:** REST endpoint `.../api/majors/<Major Name>` returning `requirements` and `prerequisites`.

---

## Project Structure

```
purrgod-college-planner-webapp/
├── README.md
├── package.json
├── public/
│   ├── index.html
│   └── manifest.json
├── src/
│   ├── App.js                 # App shell + providers
│   ├── Bar.js / Bar.css       # Top bar + Export button
│   ├── Calendar.js / .css     # Main layout (search + grid + side panels)
│   ├── ClassesList.js / .css  # Major requirements list + prerequisite popup
│   ├── GEList.js / .css       # GE “tiles” palette with popup
│   ├── GridComponent.js / .css# 4x4 quarter grid, drag & drop, CSV export
│   ├── MajorContext.js        # Context for prereqs + indexing
│   ├── Search.js / .css       # Major dropdown + fetch logic
│   ├── index.js / index.css
│   └── App.css
├── web.py, mon.py             # Catalog scraper prototypes (BeautifulSoup)
└── tempCodeRunnerFile.py
```

---

## Getting Started

### Prerequisites
- Node.js 16+ and npm.

### Install & Run

```bash
# install
npm install

# start dev server
npm start

# production build
npm run build
```

The app will open at `http://localhost:3000/`.

---

## Usage Guide

1. **Search a major** in the top search bar (e.g., “Computer Science: Computer Game Design B.S.”), then click **Search**.  
   The app fetches `requirements` and `prerequisites` for the major and populates the **Major Reqs** panel. :contentReference[oaicite:2]{index=2}
2. **Drag classes** from the **Major Reqs** panel (left) or **GE’s** panel (right) into any quarter cell in the 4-year grid.
3. **View prerequisites**: click a course in **Major Reqs** to open a popup with prereq info. :contentReference[oaicite:3]{index=3}
4. **Remove a class**: double-click a class inside a grid cell to delete it from that cell. :contentReference[oaicite:4]{index=4}
5. **Export**: click **Export to CSV** in the top bar to download `4-year-plan.csv`. :contentReference

---

## Key Components & Data Flow

- **Search.js**  
  - Renders a searchable dropdown (react-select).  
  - On **Search**, calls `GET https://uscc-classes.osc-fr1.scalingo.io/api/majors/<Major Name>` and updates context with:
    - `preReq` (array/object mapping) and `majorIndexes` (course→index).  
    - Passes `requirements` to `Calendar` to render **Major Reqs**. :contentReference[oaicite:6]{index=6}

- **ClassesList.js**  
  - Displays the fetched requirement list; items are draggable.  
  - Click opens a popup showing that course’s prerequisites from context. 

- **GridComponent.js**  
  - Maintains a 4 years × 4 quarters grid (16 cells).  
  - Supports drag-in from lists, move between cells, and **double-click to delete**.  
  - Provides `ExportButton` that serializes the grid as CSV and triggers a download. 
- **GEList.js**  
  - Static set of GE codes you can drag into any quarter; includes popup scaffold.

- **MajorContext.js**  
  - Holds `preReq` and `majorIndexes` for cross-component access.

---

## Configuration Notes

- The default data API is hardcoded in `Search.js`. If you run your own backend, change:
  ```js
  const apiEndpoint = `https://uscc-classes.osc-fr1.scalingo.io/api/majors/${selectedMajor.value}`;
  ```
  to your service URL.

- **CSV Export:** The grid is flattened and exported with headers: `Year, Fall, Winter, Spring, Summer`. Quotes in course names are escaped.

---

## Optional: Scraping Scripts

- `web.py` and `mon.py` are prototype scrapers built with **requests** and **BeautifulSoup** (and `mon.py` includes a MongoDB insert).  
  They target UCSC catalog pages to collect `requirements` and `prerequisites`. These scripts are not required to run the web app and should be used responsibly (rate limits, ToS). 

---

## Roadmap

- Add auth and per-user plan saving.  
- Backend service for persistent plans and validated prerequisite graphs.  
- Course conflict detection and unit load warnings.  
- Printing and PDF export.  
- Mobile layout improvements.

---


## Detected evidence (automated analysis)

Indexed codebase: 22 recognized source files, 34 KB.
- CSS (language) — detected in the code
- HTML (language) — detected in the code
- JavaScript (language) — detected in the code
- Python (language) — detected in the code
- React (technology) — detected in the code
- MongoDB (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (25 of 25)

```
.gitignore
mon.py
package.json
public/index.html
public/manifest.json
README.md
src/App.css
src/App.js
src/Bar.css
src/Bar.js
src/Calendar.css
src/Calendar.js
src/ClassesList.css
src/ClassesList.js
src/GEList.css
src/GEList.js
src/GridComponent.css
src/GridComponent.js
src/index.css
src/index.js
src/MajorContext.js
src/Search.css
src/Search.js
tempCodeRunnerFile.py
web.py
```

### Dependencies

- package.json: @auth0/auth0-react@^2.2.4, @babel/plugin-proposal-private-property-in-object@^7.21.11, @testing-library/jest-dom@^5.17.0, @testing-library/react@^13.4.0, @testing-library/user-event@^13.5.0, papaparse@^5.4.1, react@^18.2.0, react-dnd@^16.0.1, react-dnd-html5-backend@^16.0.1, react-dom@^18.2.0, react-scripts@5.0.1, react-select@^5.8.0, reactjs-popup@^2.0.6, web-vitals@^2.1.4

### Recent commits (newest first)

- updated readme
- update
- maybe final push
- update
- update
- update
- adding
- pushing
- made the website responsive
- update
- yash's csv export!
- most best rn
- DRAG DROP WORKS
- update :)
- akilesh nd yash webscraping stuff on websitegit statusyay
- working search bar and required classes displaygit add --all
- styling pretty
- update gridcomponent
- updated grid component
- j some sytling deets

## Key source files (fetched from GitHub, selected and truncated for size)

### package.json

```
{
  "name": "planner",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@auth0/auth0-react": "^2.2.4",
    "@testing-library/jest-dom": "^5.17.0",
    "@testing-library/react": "^13.4.0",
    "@testing-library/user-event": "^13.5.0",
    "papaparse": "^5.4.1",
    "react": "^18.2.0",
    "react-dnd": "^16.0.1",
    "react-dnd-html5-backend": "^16.0.1",
    "react-dom": "^18.2.0",
    "react-scripts": "5.0.1",
    "react-select": "^5.8.0",
    "reactjs-popup": "^2.0.6",
    "web-vitals": "^2.1.4"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
  "devDependencies": {
    "@babel/plugin-proposal-private-property-in-object": "^7.21.11"
  }
}

```

### src/index.js

```javascript
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

```

### src/App.js

```javascript
import "./App.css";
import Bar from "./Bar.js"; // Assuming bar.js is in the same directory
import Calendar from "./Calendar.js"; // Assuming calendar.js is in the same directory
import { MajorProvider } from "./MajorContext";

function App() {
  return (
    <MajorProvider>
      <div className="App">
        <div className="top-bar">
          <Bar />
        </div>
        <div>
          <Calendar />
        </div>
      </div>
    </MajorProvider>
  );
}

export default App;

```

### tempCodeRunnerFile.py

```python
print(major)

```

### mon.py

```python
import requests
from bs4 import BeautifulSoup
from pymongo import MongoClient

# MongoDB connection setup
connection_string  = "mongodb+srv://ymalegao:hackathon@major-class.bcgywbx.mongodb.net/"
client = MongoClient(connection_string)  # Update with your connection string
db = client['Majors']  # Replace with your database name
majors_col = db['classes']  # Replace with your collection name

# Function to get course prerequisites
tmp = []

def get_course_prerequisites(course_url):
    response = requests.get(course_url)
    if response.status_code == 200:
        soup = BeautifulSoup(response.content, 'html.parser')
        requirements_section = soup.find('h4')
        tmp = requirements_section.findNextSibling('p')
        prerequisites_array = (tmp.text[17:])
        prerequisites = prerequisites_array.split(" or ")
        return prerequisites
    else:
        print('Failed to retrieve the webpage.')
    return "No prerequisites found or failed to fetch."


# URL of the webpage to scrape
url = 'https://catalog.ucsc.edu/en/current/general-catalog/academic-units/baskin-engineering/computational-media/games-and-playable-media-ms/'
# url = 'https://catalog.ucsc.edu/current/general-catalog/academic-units/baskin-engineering/computational-media/computer-science-computer-game-design-bs/'
response = requests.get(url)

if response.status_code == 200:
    soup = BeautifulSoup(response.content, 'html.parser')

    # Extract major name
    major_name = soup.find('h1').text if soup.find('h1') else 'Unknown Major'
    print(major_name)
    # Initialize major data
    major_data = {
        "name": major_name,
        "requirements": [],
        "prerequisites": []
    }

    # Extract course numbers and links
    courselinks = soup.find_all(class_='sc-courselink')
    for link in courselinks:
        link_text = link.text.strip()
        print(link_text)
        if not link_text.startswith(("One", "Either", "or")):
            course_name = link_text
            course_url = 'https://catalog.ucsc.edu' + link['href']
            prerequisites = get_course_prerequisites(course_url)
            # tmp.append((course_name, course_url))
            major_data["requirements"].append(course_name)
            major_data["prerequisites"].append({course_name: prerequisites})
        else:
            continue
    print(major_data)


    # Insert data into MongoDB
    majors_col.insert_one(major_data)
    print(f"Data for {major_name} inserted into MongoDB.")
else:
    print('Failed to retrieve the webpage.')

```

### web.py

```python
import requests
from bs4 import BeautifulSoup



major = []

#key will be major and the values will be classes
core_class = {}
preq = {}

major = [ {
    "name": "Computer Science: Computer Game Design B.S.",
    "requirements": [],
    "prerequisites": {}
    },
]








classes = {}
preq = {}

coursename= []


def get_course_prerequisites(course_url, preq):
    response = requests.get(course_url)
    if response.status_code == 200:
        soup = BeautifulSoup(response.content, 'html.parser')
        requirements_section = soup.find('h4')
        tmp = requirements_section.findNextSibling('p')
        
        prerequisites_array = (tmp.text[17:])
        prerequisites = prerequisites_array.split(" or ")
        return prerequisites
    else:
        print('Failed to retrieve the webpage.')
    return "No prerequisites found or failed to fetch."


# course_details_url = 'https://catalog.ucsc.edu/en/current/general-catalog/courses/math-mathematics/lower-division/math-21/'
# prerequisites = get_course_prerequisites(course_details_url)
# print(prerequisites)
# URL of the webpage you want to scrape
url = 'https://catalog.ucsc.edu/current/general-catalog/academic-units/baskin-engineering/computational-media/computer-science-computer-game-design-bs/'
future =  []
# Send a GET request to the webpage
response = requests.get(url)

# Check if the request was successful
if response.status_code == 200:
    # Parse the content using BeautifulSoup
    soup = BeautifulSoup(response.content, 'html.parser')
    
    # Extract the h1 element
    h1_element = soup.find('h1')
    if h1_element:
        print('H1 Element:', h1_element.text)
        major[0]["name"] = h1_element.text
    else:
        print('No H1 element found.')

    # Extract elements with class 'sc-coursenumber'
    coursenumbers = soup.find_all(class_='sc-coursenumber')
    for number in coursenumbers:
        print('Course Number:', number.text)

    # print(coursenumbers)
    
    # Extract elements with class 'sc-courselink'
    courselinks = soup.find_all(class_='sc-courselink')
    for link in courselinks:
        if (link.text != " One of these courses" and  link.text != " Either these courses" and link.text != " or these courses" and not link.text.startswith(" or") and not link.text.startswith(" One") and not link.text.startswith(" Either")):
            major[0]["requirements"].append(link.text)
        print('Course Link:', link['href'])
        link['href'] = 'https://catalog.ucsc.edu' + link['href']
        future.append((link.text, link['href']))

else:
    print('Failed to retrieve the webpage.')
# print(future)

i = future[0]
# print(i[0])
# print(i[1])
preq[i[0]] = get_course_prerequisites(i[1], preq)

for i in future:
    print(i[0])
    if i[0] not in preq and i[0] != " One of these courses" and  i[0] != " Either these courses" and i[0] != " or these courses" and not i[0].startswith(" or") and not i[0].startswith(" One") and not i[0].startswith(" Either"):
        preq[i[0]] = get_course_prerequisites(i[1], preq)
        major[0]["prerequisites"].append(preq[i[0]])
    # print(i[0])
    # print(i[1])
    # print(preq[i[0]])

print(major[0]["prerequisites"])

# Replace 'YOUR_WEBPAGE_URL' with the actual URL of the webpage you want to scrape.


```

### src/Bar.js

```javascript
import React from "react";
import "./Bar.css";
import { ExportButton } from "./GridComponent";

const Bar = () => {
  return (
    <div className="bar">
      <p className="top-text">UCSC Major Classes Planner</p>
      <div className="btn-container">
        <ExportButton />
      </div>
    </div>
  );
};

export default Bar;

```

### src/GEList.css

```css
.ge-list-section {
  max-height: 72vh; /* Set a maximum height for the container */
  overflow-y: auto; /* Enable vertical scrollbar if content overflows */
  margin-top: 0px;
}

.ge-class {
  margin: 1vh;
  background-color: white;
  border-radius: 8px;
  padding: 1vh;
}
.ge-class:hover {
  box-shadow: 0 0 13px #32d6ff;
  background-color: #bbdff7;
}
/* seems like wehen changing class in 
classeslist, electiveslist also changes */

```

### src/MajorContext.js

```javascript
import { createContext, useContext, useState } from 'react';

const MajorContext = createContext({});

export const useMajor = () => useContext(MajorContext);

export const MajorProvider = ({ children }) => {
  const [preReq, setPreReq] = useState([]);
  const [majorIndexes, setMajorIndexes] = useState({});

  const value = {
    preReq,
    setPreReq,
    majorIndexes,
    setMajorIndexes,
  };

  return (
    <MajorContext.Provider value={value}>
      {children}
    </MajorContext.Provider>
  );
};
```

### src/Bar.css

```css
.bar {
  background-color: rgb(27, 56, 110);
  border-bottom: 4px solid rgb(21, 44, 88);
  border-bottom-right-radius: 4px;
  border-bottom-left-radius: 4px;
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 70px;
  z-index: 1000;
  padding-left: 4vh;

  display: flex;
  justify-content: space-between;
  align-items: center;
}

.top-text {
  color: rgb(224, 224, 224);
  font-size: 35px;
  font-family: "Josefin Sans";
  padding-top: 1vh;
  margin-bottom: 25px;
  padding-left: 2px;
  margin-right: auto;
}

.btn-container {
  margin-right: 5vh;
  width: 24vh;
}

```

[12 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]