Cwru Free Food Finder

Overview
 

For Case Western's 2024 hackathon two friends and I built a webapp for locating free food on our campus. We used a fine tuned OpenAI model to read all events from our university's event management system and determined whether they had food, of what type, and to generate other metadata. We then displayed this data on a appealing MUI-React UI, and set up a deployment at free-cwru-food.404wolf.com! We added various additional features like a calendar integration, and have continued to improve the site.
featuredacademic

Free food finder website

Main website
Main website

Description

For Case Western's 2024 hackathon two friends (Mars and Marc) and I got together and built a webapp for locating free food on campus. We used a fine tuned OpenAI model to read all events on our universities event management service page and determine whether they had food, of what type, and to generate other metadata. We then displayed this data on a appealing MUI-React UI, which can be found at free-cwru-food.404wolf.com. The github is located here.

Inspiration

Though we were not supposed to begin working on our hackathon project before the official start time, we did decide to meet to brainstorm for the project a bit ahead of time. The two friends I teamed up with had previously worked on a separate hackathon at WashU to create an app for automatically registering for classes as spots opened up. Taking inspiration, we also wanted to work on a project that interfaced with some campus service to make it better and easier to use, but weren't sure which. We knew it'd need to be something that had an obvious and applicable use case, and something vague enough that we could make it more prompt-specific when the hackathon were to actually start.

CampusGroups events
CampusGroups events

As we brainstormed, we came up with a list of a few campus services that we could extend. Canvas is our school's portal for classes -- course announcements, grading, etc. We thought of adding a "Submit to Canvas" button Google Docs extension, or a "scheduled submit" option for Cavnas. I proposed the idea of making a syllabus scanner to find easy important details on each classes' syllabus. Eventually, we decided on creating some sort of analysis tool for our school's event management system CampusGroups. CampusGroups is a great platform for garnering all events happening on campus, but it can be a bit intimidating and confusing to filter through all the events.

We thought that, since we'd be heavily constrained on time, we would start with something more simple and constrained, which is when I came up with the specific idea of filtering for events that had food. Funny as it may be, free food is a big motivator for college students everywhere, including Case. Wouldn't it be great to have a website that analyzes the descriptions of all events on campus and screens for the ones that have free food? So that's just what we did.

Prompt

For the actual event's prompt, it was an AI based theme. The track we participated in was the "maker" track. We thought that our choice to use AI as an analysis tool would fit well with the prompt.

Prompt: Welcome to the AI-Powered Innovation Challenge, where visionaries and trailblazers converge to harness the transformative potential of Artificial Intelligence (AI). In this Hackathon's Innovation Track, your goal is to leverage AI technologies to pioneer groundbreaking solutions that address real-world challenges and propel us into a future defined by innovation. Imagine a world where AI is not just a tool but a catalyst for unprecedented innovation across all sectors. Your mission is to conceptualize and develop a project that embodies this vision, pushing the boundaries of what's possible with AI-driven innovation. Whether it's revolutionizing industries, improving societal well-being, or enhancing human capabilities, let AI be the engine driving your creative and innovative endeavor.

The Project

When we outlined the project, we decided on a few core features, and since then, we've added some more. To start, we made the main page of the website a grid of events that each had free food. Since CampusGroups does have its own proper event registration system, and some events require registration, we knew that we'd want a way for people to easily access the actual CampusGroups page, so we made the tiles clickable links. Because of this, we thought it'd be okay to truncate the descriptions and information about the event, since the event tiles would serve primarily as information about the food content of the event.

Our goal was to provide food-centric event information, so we spent some time brainstorming what food-related information we could feasibly gather from event descriptions, and that would be useful. We decided that the cuisine of the food would be most important, and then for fun also added an AI generated rating of the food. Since we noticed that there are a lot of food-drive type events, we also added a flag for whether the event was a volunteering event (so, not really free food for the taking).

How it works

The project's basic structure is as follows: we have a user facing frontend, which is a react based website, which hooks into a database that has all the food-related events. Every 5 days an automatic google run lambda is run to fetch new events. Below are some more specific details about the innerworkings.

Frameworks

Cloud run logs fetching events
Cloud run logs fetching events

We also decided early on on the tools that we wanted to use. Since this would be a webapp, we'd obviously be using javascript, and chose to use typescript to save us the pain of hard to debug type and reference errors.

We decided that it'd be easiest to use React for our frontend, and to integrate a backend with nextjs. nextjs is a super cool framework that is sort of like create-react-app in that it bootstraps the react project creation process, but unique in that it adds many of its own abstractions like powerful server side components and "server actions" for running code server side, and sets up directory based routing. It lets you write API routes in the same folder and even files as your react UI code, which makes it super easy to rapidly develop a webapp. And, Vercel , the company that maintains it, offers free hobbist hosting.

I was the primary author of the frontend part of our app, and to get a nice looking UI up and running quickly, I decided to use react material UI (MUI), and tailwindcss. MUI provides a bunch of premade components that work and look nice out of the box, and tailwind lets you customize styles with simple classes, much like bootstrap, but with more flexibility.

For our database to store the events, we chose to set up a free tier MongoDb database, since, besides being free, we'd still be prototyping the app as we were building it due to the fast paced nature of a hackathon. Being able to not have to worry much about migrations or constantly updating a schema would be very helpful. We used the default MongoDb client instead of bothering to learn and implement mongoose (schema enforcement for MongoDb for Typescript), and just wrote our out Typescript interfaces to prevent typing issues.

Getting the Events

One of the first challenges we had to tackle was actually going about getting a listing of all the CampusGroups events from a script without a webpage. After some exploring I found that the ical integration that CampusGroups offers to add events to your calendar had most of the descriptions in it too! By just fetching the ical from CampusGroups we were most of the way there.

However, the public URL fails to include some important information about the event; namely, the location for most events is hidden. To solve this issue, I wrote a basic script that uses a headless chromium browser to fetch an auth token, which we use to make authed requests to CampusGroups to get information like event location.

1async function getAuthHeaders(caseId: string, casePassword: string) { 2 const browser = await puppeteer.launch({ 3 headless: true, 4 args: ["--no-sandbox"], 5 ignoreDefaultArgs: ["--disable-extensions"], 6 }); 7 const page = await browser.newPage(); 8 9 await page.goto("https://login.case.edu/cas/login"); 10 await page.type("#username", caseId); 11 await page.type("#password", casePassword); 12 await page.click('input[name="submit"]'); 13 14 await page.goto("https://www.campusgroups.com/shibboleth/login?idp=cwru"); 15 await page.waitForSelector('[id="button-menu-mobile"]', { timeout: 100000 }); 16 await page.goto(URL); 17 18 const cookies = await page.cookies(); 19 const headers = { 20 Cookie: cookies.map((ck) => `${ck.name}=${ck.value}`).join("; "), 21 }; 22 await browser.close(); 23 24 return headers; 25}

Screening Events

To screen all the events and populate the database with the events that had food and the metadata about the food, we decided to use an OpenAI fine tuned model. The idea behind a fine tuned model is that you take a base model from OpenAI, like GPT3 (regular tier chat GPT at the time of writing), and then feed it a bunch of example inputs and outputs, like so.

1{ 2 description: 3 "Join the African Student Association at our annual Karaoke event, Battle of the Mics, where you can watch and/or sing your favorite African songs, from Afrobeats to Amapiano! Enjoy lighthearted competition, great music, delicious food, and an incredible time to start off the spring semester.", 4 rating: "7", 5 cuisine: "Cultural", 6 volunteer: "false", 7 onCampus: "true", 8 }, 9 { 10 description: 11 "Join us for Grind training on the FAB floor at think[box]. Proper PPE is Required [short sleeves, long pants, close-toed shoes, hair tied back, no jewelry An ability badge is required to complete this training. If you do not yet have an ability badge, you will need to complete a 15-minute safety tour prior to your training time", 12 rating: "0", 13 cuisine: "None", 14 volunteer: "false", 15 onCampus: "true", 16 }, 17 { 18 description: 19 "Brew CWRU is hosting a field trip to Cleveland's very own Phoenix Coffee Co's HQ in downtown Cleveland! During this field trip, Phoenix Coffee's director will take us through various processes of QA/QC in specialty coffee including cupping, roasting, and dialing in a coffee recipe. Spots are limited due to transportation, so make sure you can commit to the registration and do not miss out on this amazing opportunity!", 20 rating: "4", 21 cuisine: "Beverages", 22 volunteer: "false", 23 onCampus: "false", 24 },

Then you run a "training" session, which cost us only about $2, and you can use the fine tuned model by referencing its id as a model for OpenAI requests, in conjunction with a prompt, like this:

1const completion = await openai.chat.completions.create({ 2 messages: [ 3 { 4 role: "system", 5 content: `Your job is to read the description of a student-run event located at a local university, and determine whether the event provides food or not to people attending the event. You are also tasked with rating the food on a scale from 1 to 10, with 1 representing cheap canned food and 10 representing a fully prepared feast. If no part of the event mentions food, rate the food a 0. If there is food mentioned at all, give it at least a rating of 1, and at most a rating of 10. If there is food provided, to classify what cuisine the food is, which MUST be one of the following values: Pizza, Beverages, Cultural, Healthy, Sweets, Unknown, and None. Additionally, indicate whether this event is for volunteers or is offering food to the community. If it is, say true, otherwise if it is not or if taking food, say false. Additionally, indicate whether the location is on the university campus, or requires traveling off campus, also true or false (say true if it is unknown). Output each of these fields in a JSON object with the fields "rating", "cuisine", "volunteer", and "onCampus" 6 7 Make sure that if there is food mentioned in the description that the rating is not a 0. Give a rating of 0 only if the event will not have any food.`, 8 }, 9 { role: "user", content: description }, 10 ], 11 model: FINE_TUNED_MODEL, 12 }); 13

Using the OpenAI API is basically like simulating a user interaction with ChatGPT, but a bit more customization. What's super cool is that the AI gives us a json formatted response consistently and always in the right shape. I doubted the safety of this, but OpenAi's own documentation notes that this is a good method for this sort of thing.

We packaged this scanning system into a library that I then dockerized, and deployed to a Google cloud run lambda. I set it up to run every 2 days, so that events consistently get added to the database.

Finishing Touches

Mobile website
Mobile website
Google calendar integration
Google calendar integration

To finalize the website, I added a few neat additional features. I added a ical integration that lets you add all food events to your iCal supported calendar (like google calendar), and an add to Google Calendar link (if you click on the dates on any of the tiles it should take you to a pre-filled Google Calendar link). Getting Google Calendar template links to work was super cool, it's just a url template:

`https://www.google.com/calendar/render?action=TEMPLATE&text=${escapedName}&details=${escapedDescription}&location=${escapedLocation}&dates=${formattedDate}/${formattedEndDate}`;

I also added a filtering system, which initially was client side, but I moved to the backend to reduce client strain and bandwidth. I added basic Vercel analytics to the website, and have been improving the mobile UI too.

After the hackathon we've done some optimization, improved event fetching concurrency, and in general cleaned up the UI a bit. I also added skeletons to the website for the loading screen, since the website dynamically loads.

Results

In the end, we presented to a panel of 5 judges, and came in first for the event, winning some money for our group. We had a fully completed webapp with a deployment and mobile UI, and overall I was very happy with how things turned out. It was a great experience, we learned a lot about OpenAI's fine tuning API, MUI, and the other frameworks we used. It was my first time using Mongo, and the flexibility it offered really was great. I look forward to hopefully participating in other hackathons down the line!

For some additional things that I'd like to look into adding, I'm considering adding Case SSO for getting more intimate event details like location, along with adding an email attendees button. Case SSO is technically not open source, but the system that Case uses is a common Java framework and getting the auth token from a redirect isn't hard. Retuning the model to have better training data, and also adding more information to the event tiles about the food itself would be nice to do as well.