# Project export: UCSC Enrollment Helper

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: Allows student easy access to proffessor rating, reviews, and education background
- Devpost: https://devpost.com/software/ucsc-enrollment-helper
- GitHub: https://github.com/UCSC-Enrollment-Helper/EnrollmentHelper
- Team: 5 GitHub contributor(s) — Raghav Dewangan (9 commits), PranavMulakala (4 commits), mukhulm4 (4 commits), suvanamruth (4 commits), suvanamruth (1 commits)

## Devpost submission (written by the team)

### Inspiration

As current UCSC students who always struggle when finding classes on enrollment day with struggling with finding the professors ratings and their background. We wanted to make an extension that allows students to easily get a quick summary of the professor and his teaching methods.

### What it does

The extension allows students to easily look at UCSC instructors name, department, school name, average rating, difficulty rating, total ratings, percentage of people who would take a class with the teacher again. The students would have to be in the school's catalog website.

### How we built it

In the development of the "Enrollment Helper" Chrome extension, we integrated a front-end and back-end to enhance the academic experience of UCSC students. By leveraging the RateMyProfessor API, we facilitated real-time access to professor evaluations, streamlining the course selection process with an intuitive UI/UX. The extension dynamically displays aggregated data through web scraping techniques, interfacing with a local server to fetch and parse relevant academic metrics through the UCSC Catalog. We ran into many challenges. Mainly 2 that took us hours to fix. The first one was connecting all of our test scripts and UI/UX to our local server. Developing the "Enrollment Helper" Chrome extension posed a unique challenge in bridging the gap between the front-end presentation and back-end data handling, particularly when interfacing with our local server. Initial hurdles in establishing a seamless connection were overcome by implementing cross-origin resource sharing (CORS) policies, which allowed our front-end scripts, running within the Chrome extension's secure context, to request and receive data from our locally hosted server. By making asynchronous JavaScript calls and the fetch API, we were able to web scrape and relay professor ratings from RateMyProfessor API to the extension's UI. Chrome didn't support running backend python script, so that is why we had to host our own local server in order for the extension to work. We had trouble transferring output from our backend scripts to the popup.html file. The scripts were printing in the console, but we had trouble figuring out how to pass this data to our javascript files in our program. After hours of documentation we found a way for the data to appear on our chrome extension. ##

### Accomplishments we're proud of

Coming into this hackathon, we had basic knowledge on application development. Making a chrome extension with flask and hosting a local server was something we learned on the fly. We worked around the problems we faced and were able to complete the project. ##

### What we learned

We honed our skills in different areas. We navigated API integration, mastering web scraping and various JavaScript functions for effective data handling. Diving into Chrome's APIs, we learned to manage background processes and storage, while also tackling CORS. Our front-end development was refined as we crafted a user-friendly interface. Debugging with Chrome Developer Tools and using web standards were some skills we picked up on. This project not only advanced our technical expertise but also sharpened our problem-solving abilities and collaborative skills. ##

### What's next

We have a lot of plans for whats next in UCSC Enrollment Helper. We plan to have this extension available on MyUCSC enrollment page itself. We also want to get in touch with other colleges to implement this extension function as well for their own colleges. Maybe even expanding our own extension to the general schools.

## README (from the GitHub repository)

# EnrollmentHelper
UCSC Enrollment Helper chrome extension. Uses RateMyProfessor API. 

python API link:
https://pypi.org/project/RateMyProfessorAPI/


## Detected evidence (automated analysis)

Indexed codebase: 9 recognized source files, 13 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
- Flask (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (13 of 13)

```
.DS_Store
background.js
content.js
Images/.DS_Store
manifest.json
popup.html
popup.js
professor_stats.py
README.md
requirements.txt
server.py
style.css
test.py
```

### Dependencies

No dependency index available.

### Recent commits (newest first)

- adding requirements.txt
- finished beta design
- made divs for each professor, cleaned up UI
- finally got it connected
- Commit
- Merge pull request #3 from UCSC-Enrollment-Helper/suvan
- adding
- Commit
- Merge pull request #2 from UCSC-Enrollment-Helper/web-scraper
- css changes
- professor_stats returns full string for index amount of teachers
- looking for specific tab
- lol
- Merge pull request #1 from UCSC-Enrollment-Helper/web-scraper
- adding style.css
- Commited
- Commited
- prints all professors in the file
- adding extension files
- scraper works, need to clean it up a bit though

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

### requirements.txt

```
��b e a u t i f u l s o u p 4 = = 4 . 1 2 . 3  
 b l i n k e r = = 1 . 7 . 0  
 c e r t i f i = = 2 0 2 3 . 1 1 . 1 7  
 c h a r s e t - n o r m a l i z e r = = 3 . 3 . 2  
 c l i c k = = 8 . 1 . 7  
 c o l o r a m a = = 0 . 4 . 6  
 F l a s k = = 3 . 0 . 1  
 F l a s k - C o r s = = 4 . 0 . 0  
 i d n a = = 3 . 6  
 i t s d a n g e r o u s = = 2 . 1 . 2  
 J i n j a 2 = = 3 . 1 . 3  
 l x m l = = 5 . 1 . 0  
 M a r k u p S a f e = = 2 . 1 . 4  
 R a t e M y P r o f e s s o r A P I = = 1 . 3 . 6  
 r e q u e s t s = = 2 . 3 1 . 0  
 s o u p s i e v e = = 2 . 5  
 u r l l i b 3 = = 2 . 1 . 0  
 W e r k z e u g = = 3 . 0 . 1  
 
```

### server.py

```python
from flask import Flask, jsonify, request
from flask_cors import CORS, cross_origin
import subprocess
from professor_stats import run

app = Flask(__name__)
CORS(app)

@app.route('/run_python_script')
@cross_origin()
def run_python_script():
    try:
        value = request.args.get('param_name')
        result = run(value)
        response_data = {'success': True, 'result': result}
    except subprocess.CalledProcessError as e:
        response_data = {'success': False, 'error': str(e)}

    return jsonify(response_data)

if __name__ == '__main__':
    app.run(debug=True)
```

### popup.html

```html
<!DOCTYPE html>
<html>
<head>
    <title>Enrollment Helper</title>
    <link rel="preconnect" href="https://fonts.googleapis.com%22%3E/">
    <link rel="preconnect" href="https://fonts.gstatic.com/" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=ABeeZee&display=swap" rel="stylesheet">
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <h1>Enrollment Helper</h1> <!-- Corrected closing tag -->

    <div id = "maindiv"class="professors">
</div>

    <script src="popup.js"></script>
</body>
</html>
```

### background.js

```javascript
/*
// Listen for messages from the content script
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  if (message.type === "result") {
    // Assuming message.data contains the HTML or text you want to display
    const res = message.data
    console.log("dsadsadsadsa",res)
    let resultContainer = document.getElementById('reviews');
    resultContainer.innerHTML = res
    // Display the result in the reviews section
    reviewsSection.innerHTML = message.data;
  }
  return true;
});
//chrome.runtime.sendMessage({type: "result", data: resu})
*/
chrome.storage.onChanged.addListener(function(changes) {
  if (changes.popupState) {
    // Do something with the updated popup state
    const updatedPopupState = changes.popupState.newValue;
    // ...
  }
});
```

### content.js

```javascript


console.log("Content script running");
// Check if the current URL contains "catalog.ucsc.edu"
if (window.location.href.indexOf("catalog.ucsc.edu") !== -1) {
  console.log("You are on the right page. Sending message to background script...");
  const params = new URLSearchParams(
    {param_name: window.location.href}
    )

  fetch('http://127.0.0.1:5000/run_python_script?' + params)
  .then(response => {
    console.log(response)
    return response.json()
  })
  .then(result => {
    console.log(result);
    // You can handle the result as needed
    console.log(result.result)
    //chrome.runtime.sendMessage({type: "result", data: result.result})
    chrome.storage.local.set({ "key": result.result }, function() {
      console.log("Data saved to storage");
  });
  })
    // Wait and ensure popup has loaded 
    /*
    console.log("finished waiting")
    const resultContainer = doc.getElementById('reviews');
    console.log(resultContainer)
    resultContainer.innerHTML = `
      <h2>Ratings:</h2>
      <p>${result.result}</p>
    `;
    */
  
  //.catch(error => console.error('Error executing Python script:', error));

} else {
  console.log("You are not on the right page");
}

```

### test.py

```python
import ratemyprofessor
import requests
from bs4 import BeautifulSoup

# URL of the website
url = 'https://catalog.ucsc.edu/en/current/general-catalog/courses/bioe-biology-ecology-and-evolutionary/'

# Make a GET request to the website
response = requests.get(url)

# Check if the request was successful (status code 200)
if response.status_code == 200:
    # Parse the HTML content of the page
    soup = BeautifulSoup(response.text, 'html.parser')

    # Specify the CSS selector of the specific div you want to scrape
    div_selector = '.instructor'  # Replace with the actual class or id of your div

    # Find the div using BeautifulSoup
    target_div = soup.select_one(div_selector)
    print("dsadsa",target_div)

    # Check if the div is found
    if target_div:
        # Extract the information you need from the div
        extracted_info = target_div.text.strip()

        # Print or store the extracted information
        print("Extracted Information:", extracted_info)
    else:
        print("Div not found on the page.")
else:
    print(f"Failed to retrieve the webpage. Status Code: {response.status_code}")

profName = extracted_info.replace("Instructor", "").strip()

professor = ratemyprofessor.get_professor_by_school_and_name(ratemyprofessor.get_school_by_name("UCSC"), profName)
#print(extracted_info)
#print(professor.name)

if professor is not None:
    print("%s works in the %s Department of %s." % (professor.name, professor.department, professor.school.name))
    print("Rating: %s / 5.0" % professor.rating)
    print("Difficulty: %s / 5.0" % professor.difficulty)
    print("Total Ratings: %s" % professor.num_ratings)
    if professor.would_take_again is not None:
        print(("Would Take Again: %s" % round(professor.would_take_again, 1)) + '%')
    else:
        print("Would Take Again: N/A")





```

### professor_stats.py

```python
import ratemyprofessor
import requests
from bs4 import BeautifulSoup


url = 'https://catalog.ucsc.edu/en/current/general-catalog/courses/biol-biology-molecular-cell-and-developmental/1-99/biol-20a/'


def findStats(name, ind):
    statStr = ""
    professor = ratemyprofessor.get_professor_by_school_and_name(ratemyprofessor.get_school_by_name("UCSC"), name)

    if professor is not None:
        statStr += f"{professor.name} works in the {professor.department} Department of {professor.school.name}.\n"
        statStr += f"Rating: {professor.rating} / 5.0\n"
        statStr += f"Difficulty: {professor.difficulty} / 5.0\n"
        statStr += f"Total Ratings: {professor.num_ratings}\n"
        if professor.would_take_again is not None:
            statStr += f"Would Take Again: {round(professor.would_take_again, 1)}%\n"
        else:
            statStr += "Would Take Again: N/A\n"
    return statStr



def run(url):
    response = requests.get(url)

    # Check if the request was successful (status code 200)
    if response.status_code == 200:
        # Parse the HTML content of the page
        soup = BeautifulSoup(response.text, 'html.parser')

        # Specify the CSS selector of the specific div you want to scrape
        instructor_divs = soup.find_all("div", {"class": "instructor"})

        index = 1
        prof_stats = ""

        for instructor_div in instructor_divs:
            instructor_infos = instructor_div.text.strip()
            prof_names = instructor_infos.replace("Instructor", "").strip()
            nameList = prof_names.split(',')
            print(nameList)
            if(len(nameList) == 1):
                prof_stats = prof_stats + findStats(nameList[0], index) + "!\n"
                index += 1
            else:
                for name in nameList:
                    prof_stats = prof_stats + findStats(name, index) + "!\n"
                    index += 1   
            if(index == 10):
                break
        
        return prof_stats
    else:
        print(f"Failed to retrieve the webpage. Status Code: {response.status_code}")
        return None

```

### style.css

```css
/* style.css */

body {
    font-family: 'ABeeZee', sans-serif;
    background-color: #322968; /* Dark purple background */
    color: #ffffff; /* Light text for readability */
    height: 40rem; /* Height of the popup */
    width: 30rem; /* Width of the popup */
}

h1, h2 {
    color: #f2f2f2; /* A shade of green for headings */
    margin: 0.5em 0; /* Spacing above and below headings */
}

h1 {
    margin-top: 0; /* No space above the first heading */
    text-align: center; /* Center the main title */
}

/* Hover effect for headings
h1:hover, h2:hover {
    color: #4FFFB0; // Lighter shade of green for hover
    cursor: pointer; // Changes cursor to indicate interactivity
    text-decoration: underline; // Underlines text on hover
    transition: color 0.3s ease; // Smooth transition for the color change
}
*/
.professors {
    font-size: large;
    display: flex;
    flex-direction: column;
    align-items: flex-start; /* Center alignment for all content */
    padding: 1.25rem; /* Padding inside the popup (20px converted to rem) */
    box-sizing: border-box; /* Prevents padding from expanding beyond the set width and height */
    border-radius: 1rem; /* Rounded corners for the popup (10px converted to rem) */
    overflow: hidden; /* Ensures nothing spills out the rounded corners */
    border-top: 0.1875rem solid #1d1d1d; /* Thin black line above each section (3px converted to rem) */
}

/* Common styles for the sections */
.section {
    
    width: calc(100% - 2.5rem); /* Full width accounting for padding (40px converted to rem) */
    background: rgba(255, 255, 255, 0.1); /* Slight white transparency for contrast */
    padding: 1.25rem; /* Padding inside sections (15px converted to rem) */
    margin-bottom: 0.625rem; /* Spacing between sections (10px converted to rem) */
    border-radius: 0.3125rem; /* Rounded corners for sections (5px converted to rem) */
    box-shadow: 0 0.3125rem 0.625rem rgba(0, 0, 0, 0.5); /* Subtle shadow for depth (2px and 5px converted to rem) */
}

/* Styles for the "rating" and "reviews" sections */
.rating, .reviews {
    text-align: left; /* Left align text within these sections */
}

/* Additional styling for individual review paragraphs */
.reviews h3 {
    background-color: #3f3a60; /* Darker background for each review */
    padding: 0.625rem; /* Padding for review text (10px converted to rem) */
    margin: 0.3125rem 0; /* Spacing between reviews (5px converted to rem) */
    border-radius: 0.1875rem; /* Rounded corners for review blocks (3px converted to rem) */
    box-shadow: inset 0 0.0625rem 0.1875rem rgba(0, 0, 0, 0.3); /* Inset shadow for a "pressed" effect (1px, 3px converted to rem) */
    transition: background-color 0.3s ease; /* Smooth transition for background color change */
}

/* Hover effect for individual reviews */
.reviews h3:hover {
    background-color: #504b70; /* Lighter purple background on hover */
    cursor: pointer; /* Changes cursor to indicate interactivity */
}



```

### popup.js

```javascript
document.addEventListener('DOMContentLoaded', function() {
    // Retrieve the stored data from chrome.storage.local
    chrome.storage.local.get("key", function(result) {
      if (result.key) {
        // Split the data by the '!' delimiter to separate each instructor's info
        mainDiv = document.getElementById("maindiv")
        const instructors = result.key.split('!');
        list = []
        for(let i = 0; i < instructors.length - 1;i++)
        {
            list.push(instructors[i].split("\n"));
        }
        for(let ls of list)
        {
            const profTab = document.createElement("div")
            profTab.classList.add('section')
            //profTab.classList.add()
            for (let attr of ls)   //looking at specific attribute to each professor
            {
                profAttr = document.createElement("div")
                profAttr.classList.add('section')

                profAttr.innerHTML = attr
                profTab.appendChild(profAttr)
            }   
            mainDiv.appendChild(profTab)
        }

        sectionList = document.getElementsByClassName("section")
        for(let di of sectionList)
        {
            if(di.innerHTML.length === 0)
            {
                di.remove()
            }
        }

        
        /*
        // Iterate over each instructor's info
        instructors.forEach((instructor, index) => {
          // Ensure there's a corresponding container for this instructor
          //const professorInfo = document.querySelector(`.professor-info[data-index="${index + 1}"]`);
  
          // If the container exists, populate it with data
          if (professorInfo) {
            // Split the instructor's info by the '/' delimiter
            const details = instructor.split('/');
  
            // Populate the professor's name
            const professorNameElement = professorInfo.querySelector('.professor-name');
            //professorNameElement.textContent += details[1].trim(); // Append the name to "Professor:"
  
            // Populate the rating
            const ratingElement = professorInfo.querySelector('.rating-stars');
            const ratingValue = details[2].trim(); // Assuming the rating is always the third element
            ratingElement.textContent = ratingValue; // Replace "stars" with the actual rating
  
            // For a visual representation of stars, you would create and append star images according to the rating
            // This is a placeholder for that logic
  
            // Populate the reviews
            const reviewElement = professorInfo.querySelector('.professor-reviews');
            reviewElement.textContent = details.slice(3).join(' / '); // Join the remaining details for reviews
          }
        
        });
        */
        // Optional: Clear the storage after retrieving the data
        // chrome.storage.local.remove("key", function() {
        //   console.log("Data removed from storage");
        // });
      }
    });
  });

  function savePopupState() {
    const popupState = {}; // Add any necessary state data
  
    chrome.storage.local.set({ "popupState": popupState }, function() {
      console.log("Popup state saved");
    });
  }
  
  // Save the popup state when the popup is closed
  window.addEventListener('beforeunload', savePopupState);
```