# Project export: SlugShack

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: SlugShack connects graduating/departing students in desperately needed houses with students seeking somewhere to call home. Chatbot coached on SC Housing Crisis Studies explains complex housing system
- Devpost: https://devpost.com/software/slugshack
- GitHub: https://github.com/monkutten/JEVSCRUZHACKS2024
- Team: 0 GitHub contributor(s) — 

## Devpost submission (written by the team)

### Inspiration

While learning about the housing process in Santa Cruz as a sophomore, I found it interesting that the best way to secure cheaper and reliable housing was through word-of-mouth, where maybe your friend's friend would mention that someone they know would be moving out soon. I soon realized that despite this being a primary mechanism of passing down housing, there wasn't any specific platform dedicated to making this process more efficient. In UC Santa Cruz, one of the biggest issues with being a student here is the prospect of housing, but maybe through a system like SlugShack, the notion of "for student, by student" housing can become much more prominent, securing housing for countless students.

### What it does

Localhost website contains the user interface for the database and also the housing chatbot. This configuration allows a user to search through nearby available housing, and sort results by various metrics like distance to campus, # of vacancies, estimated monthly rent, and more. To the right of the intractable database is the chatbot, which can guide users to resources regarding affordable housing, explain some of the main causes of the SC housing crisis, and more.

### How we built it

The database of available student houses is populated by a Google Forms Questionnaire, filled out by graduating/departing students who wish to indicate that there will be a future vacancy in their home. The form populates the Google Sheets database with information like the housing address, estimated rent, and other categories. The database processes this information with the Google Places API, automatically generating metrics like distance and driving duration from an address to the UCSC campus, and writes to the spreadsheet using the Gspread API. Additionally, we also developed a Santa Cruz housing chatbot AI with the Vectara Semantic Search platform and their APIs. We provided several housing studies, guides, and informational pages all pertaining to Santa Cruz housing to train the AI. We created a local host website with Streamlit to house the SlugShack code and the housing chatbot alongside each other. Used the Streamlit API to the database as an intractable display. To incorporate the chatbot into the Streamlit website, we used Flowise AI, which piped the Vectara-powered chatbot through a OpenAi wrapper, which finally enabled us to embed the chatbot in the html of the Streamlit website.

### Challenges we ran into

For us 4, Cruzhacks is our first ever hackathon. All of us have mostly coded within our schoolwork assignments, meaning we hadn't been exposed to many of the methods and tools available in the every day tech world. For the project we were undertaking, it was extremely API heavy, and none of us had ever handled an API in depth. For us, this was our first proper project, first interaction with API's, first many things, and we struggled to find our footing. For one, we intended to use the Zillow API to query housing infomation automatically, only to find that it had been closed off recently. Because all of us are almost entirely Python programmers, we had no idea about web development, so we stayed away from it and settled on trying to make a user interface in a terminal. One of our biggest issues was with calling the Google Sheets API too often, leading to an automatic error that would halt all progress.

### Accomplishments we're proud of

On that note, it forced us to learn how to maximize the efficiency of our program, such that it now has no issues with the Sheets Api. We were all super nervous about the project we had lined up for us, it seemed like an incredibly daunting task for a group of passionate but not very experienced coders. But in the end, when we settled for a terminal python interface, we managed to create a whole website!

### What we learned

We learned an absurd amount about API's and their various forms. We learned about some of the systems that machine learning platforms use. We learned about databases through the various Google APIs, and we also learned how to set up a collaboratory Git setup.

### What's next

specific market niche, similar level of connection as Indeed/famous hackathon projects Thinking about the idea of SlugShack, I realized that SlugShack has the potential to occupy a fairly niche part of the massive real estate market: student housing. There will always be students looking for housing, and students leaving their college homes. Just by changing a few variables and some other tweaks, SlugShack can be expanded to any other college town in America, which is an exciting possibility. It opens up a new real estate world: housing for students, by students.

## README (from the GitHub repository)

# JEVSCRUZHACKS2024

CRUZHACKS 2024 Project: SlugShack  
Created in collaboration by: James Manlangit, Ethan Phan, Vishnu Naroth, Sofia Dang

Presentation:
https://docs.google.com/presentation/d/1kROJYi_mQe_RrOY-w5lhHKKvklgCcrkuRApSUdg92yY/edit?usp=sharing

Description: 
SlugShack connects graduating/departing students in desperately needed houses with students seeking somewhere to call home. Chatbot coached on SC Housing Crisis studies explains complex housing system. A proof-of-concept of a tool that could alleviate the UCSC student housing crisis. 

How it works:
Website contains the user interface for the database and also the housing chatbot. This configuration allows a user to search through nearby available housing, and sort results by various metrics like distance to campus, # of vacancies, estimated monthly rent, and more. To the right of the intractable database is the chatbot, which can guide users to resources regarding affordable housing, explain some of the main causes of the SC housing crisis, and more. Note: All user information in the Google Sheets database is fictional.

Tools/APIs used:
flowise
github
google-maps
google-places
gspread
open-ai
streamlit
vectara

Credentials Needed:  
Vectara API Key  
Google Project API Key  
Google: credentials.json  
Vectara: token.json  

Usage:  
npx flowise start <- initializes localhost for Chatbot  
streamlit run vectarainterface.py <- initializes localhost for Streamlit Website



## Detected evidence (automated analysis)

Indexed codebase: 5 recognized source files, 13 KB.
- Python (language) — detected in the code
- OpenAI (technology) — claimed on Devpost, not found in the code
- Streamlit (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (5 of 5)

```
googlemapstest.py
graph.py
README.md
sheetsaccess.py
websiteUI.py
```

### Dependencies

No dependency index available.

### Recent commits (newest first)

- Update README.md
- Update README.md
- Update README.md
- Update README.md
- Update README.md
- Update README.md
- Update README.md
- Adding final rendition of files.
- Initial commit

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

### graph.py

```python
# Used to create user interface in terminal when running script
def CreateGraph(userdata):
    print(f"{'Address' : <60}{'Vacancies' : ^9}{'Estimated Rent' : ^20}{'Time Until Vacancy' : ^22}{'Distance (km)' : ^22}{'Driving Time (mins)' : ^18}{'Bus Time (mins)' : ^18}{'Walking Time (mins)' : >18}\n")
    for index in range(len(userdata)):
        print(f"{index+1}. {userdata[index][4] : <60}{userdata[index][5] : ^6}{userdata[index][6] : ^17}{userdata[index][7] : ^22}{userdata[index][8] : ^22}{userdata[index][9] : ^18}{userdata[index][10] : ^18}{userdata[index][11] : >18}")

# Enable a user to sort by size in a given category
def SortData(userdata, category):
    categories = {
        'vacancies': 5,
        'estimated rent': 6,
        'time until vacancy': 7,
        'distance': 8,
        'driving time': 9,
        'bus time': 10,
        'walking time': 11
    }
    userdata = sorted(userdata, key=lambda x: x[categories[category]])
    return userdata
```

### sheetsaccess.py

```python
import gspread
import os.path
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError

# If modifying these scopes, delete the file token.json.
SCOPES = ["https://www.googleapis.com/auth/spreadsheets.readonly"]

# The ID and range of a sample spreadsheet.
SAMPLE_SPREADSHEET_ID = "15J1Nm-Q3c9vG0Fi-kl-SmiW2cB_UYfn5vKu5EkzUwDQ"
SAMPLE_RANGE_NAME = "A2:H"
#SAMPLE_RANGE_NAME = "Form Responses 3!B2:L"

gc = gspread.service_account()
wks = gc.open_by_key("15J1Nm-Q3c9vG0Fi-kl-SmiW2cB_UYfn5vKu5EkzUwDQ").sheet1

def RetrieveData():
  Data = []
  creds = None
  # The file token.json stores the user's access and refresh tokens, and is
  # created automatically when the authorization flow completes for the first
  # time.
  if os.path.exists("token.json"):
    creds = Credentials.from_authorized_user_file("token.json", SCOPES)
  # If there are no (valid) credentials available, let the user log in.
  if not creds or not creds.valid:
    if creds and creds.expired and creds.refresh_token:
      creds.refresh(Request())
    else:
      flow = InstalledAppFlow.from_client_secrets_file(
          "credentials.json", SCOPES
      )
      creds = flow.run_local_server(port=0)
    # Save the credentials for the next run
    with open("token.json", "w") as token:
      token.write(creds.to_json())

  try:
    service = build("sheets", "v4", credentials=creds)

    # Call the Sheets API
    sheet = service.spreadsheets()
    result = (
        sheet.values()
        .get(spreadsheetId=SAMPLE_SPREADSHEET_ID, range=SAMPLE_RANGE_NAME)
        .execute()
    )
    values = result.get("values", [])

    if not values:
      print("No data found.")
      return

    # Iterate through rows
    for row in values:
      # Append Data in each row to list.
      rowData = [f"{row[1]}", f"{row[2]}", f"{row[3]}", f"{row[4]}", f"{row[5]}", f"{row[6]}", f"{row[7]}"]
      Data.append(rowData)
    return Data
       
  except HttpError as err:
    print(err)

# Check if cell is empty
def CheckCell(cell):
  if wks.get_values(cell) == []:
    return True
  else:
    return False

# Add data to Google Sheets  
def AddData(cell, data):
    wks.update(cell, data)

# Returns content in a row
def GetRowVals(row):
  return wks.row_values(row)


```

### websiteUI.py

```python
import streamlit as st
import streamlit.components.v1 as components
import streamlit as st
import pandas as pd
# Importing user information variables to avoid extra API Calls
from googlemapstest import userData, addressList, vacancyList, rentList, vacancyTimesList, distanceList, drivetimeList, bustimeList, walktimeList

# Description text
st.header("SlugShack: Your Friend in Housing")
st.text("Press the blue button to talk to a housing expert!")
st.text("Note: The information in the table is randomized as a proof of concept for SlugShack")
# Create two columns on Streamlit
col1, col2= st.columns([5,5])

# Implements Sheets API in Column 1
with col1:
    st.subheader("Available housing positions")
    df = pd.DataFrame({
    'Address': addressList,
    'Vacancies': vacancyList,
    'Estimated Rent' : rentList,
    'Time Until Vacancy' : vacancyTimesList,
    'Distance (km)' : distanceList,
    'Driving Time (mins)' : drivetimeList,
    'Bus Time (mins)' : bustimeList,
    'Walk Time (mins)' : walktimeList,
    })

    # User input to view housing contact information
    row = st.text_input("Please enter the row number of the address you're interested in")
    if st.button('Enter'):
        valid_row = int(row) in range(len(userData))
        f"Looks like you're interested in this property! The name of the vacating resident is {userData[int(row)][2]}. You can contact them by emailing them at {userData[int(row)][1]}, or calling them at {userData[int(row)][3]}!" if valid_row else "Enter a valid row number!"


# Implements Vectara Chatbot using Flowise HTML Embed in Column 2
with col2:
    st.subheader("Santa Cruz Housing Expert")
    components.html(
        """
        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
        <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
        <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
        <script type="module">
            import Chatbot from "https://cdn.jsdelivr.net/npm/flowise-embed/dist/web.js"
            Chatbot.init({
                chatflowid: "6a355a02-f85b-4120-8c90-c38745b772ba",
                apiHost: "http://localhost:3000",
            })
        </script>
        """,
        height=500,
    )

    df




```

### googlemapstest.py

```python
import sheetsaccess
import googlemaps

API_KEY = 'Insert API Key here'
map_client = googlemaps.Client(API_KEY)
def CalculateDistances():
    data = sheetsaccess.RetrieveData()
    sheetsData = [] #list to house information in sheets
    # Iterate through the information of an entry
    for person in data:
        index = data.index(person)
        dest = f"{person[3]}"
        home_address = '1156 High St, Santa Cruz' #Location of UCSC to compute commute time from house to campus.
        
        distanceCell = 'I' + str(index + 2)
        if sheetsaccess.CheckCell(distanceCell) == True: #If the cell is empty, update the information of this cell, and every other cell
            distance = map_client.distance_matrix(dest, home_address, mode = 'driving')['rows'][0]['elements'][0]['distance']['value']/1000
            sheetsaccess.AddData(distanceCell, distance) 
        
            timeDrivingCell = 'J' + str(index + 2)
            timeDriving = round((map_client.distance_matrix(dest, home_address, mode = "driving")['rows'][0]['elements'][0]['duration']['value'])/60)
            sheetsaccess.AddData(timeDrivingCell, timeDriving)

            timeTransitCell = 'K' + str(index+2)
            timeTransit = round((map_client.distance_matrix(dest, home_address, mode = "transit")['rows'][0]['elements'][0]['duration']['value'])/60)
            sheetsaccess.AddData(timeTransitCell, timeTransit)
        
            timeWalkCell = 'L' + str(index+2)
            timeWalk = round((map_client.distance_matrix(dest, home_address, mode = "walking")['rows'][0]['elements'][0]['duration']['value'])/60)
            sheetsaccess.AddData(timeWalkCell, timeWalk)
        sheetsData.append(sheetsaccess.GetRowVals(index+2))

    # Add the last 4 column data to the sheetsData list
    for residence in sheetsData:
        residence[8] = float(residence[8])
        residence[9] = int(residence[9])
        residence[10] = int(residence[10])
        residence[11] = int(residence[11])
    return sheetsData
        
# Retrives the data in a given category
def RetrieveCategories(userdata, category):
    data = []
    categories = {
        'address' : 4,
        'vacancies': 5,
        'estimated rent': 6,
        'time until vacancy': 7,
        'distance': 8,
        'driving time': 9,
        'bus time': 10,
        'walking time': 11
    }
    for residence in userdata:
        data.append(residence[categories[category]])
    return data

# One-time call to populate all the columns with their relevant information
userData = CalculateDistances()
addressList = RetrieveCategories(userData, 'address')
vacancyList = RetrieveCategories(userData, 'vacancies')
rentList = RetrieveCategories(userData, 'estimated rent')
vacancyTimesList = RetrieveCategories(userData, 'time until vacancy')
distanceList = RetrieveCategories(userData, 'distance')
drivetimeList = RetrieveCategories(userData, 'driving time')
bustimeList = RetrieveCategories(userData, 'bus time')
walktimeList = RetrieveCategories(userData, 'walking time')

# User interface to run in terminal. This was our original idea, until we realized we could code a website in Python with Streamlit, so we no longer have use for it.
'''

print("\nWelcome to HomelessNoMore: A program that allows you to easily search for available off-campus housing based on your needs, without the hassle and unreliability of word-of-mouth!"\
"You can also be educated on Santa Cruz housing through our chatbot!\n")
print("Please select an option from 1-3:\n1: See local available housing in SC\n2: Speak with an informed SC housing chatbot\n3:Quit")

while True:
    try:
        user_prompt = int(input())
    except ValueError:
        print("Please enter a valid number from 1-3")
        continue
    if user_prompt < 1 or user_prompt > 3:
        print("Please enter a valid number from 1-3")
        continue
    break

if user_prompt == 1:
    print("Below are available (or soon to be available) off-campus housing. Please enter a category from vacancies to walking time to sort them accordingly")
    while True:
        sheetsData = CalculateDistances()
        graph.CreateGraph(sheetsData)
        print('Please enter a sortable category to sort the residences accordingly (Vacancies/Estimated Rent/Time Until Vacancy/Distance/Driving Time/Bus Time/Walking Time)')
        print('If there is a residence you would like more info on, type in the corresponding row number')
        print('If you would like to exit the program, type q')
        try:
            user_selection = input()
            if user_selection == 'q':
                print("Have a nice day!")
                exit()
            elif user_selection.isdigit():
                user_selection = int(user_selection)
                index = user_selection-1
                print(f"Looks like you're interested in this property! The name of the vacating resident is {sheetsData[index][2]}."\
                        f" You can contact them by emailing them at {sheetsData[index][1]}, or calling them at {sheetsData[index][3]}!")
                while True:
                    user_choice = input('Would you like to keep browsing properties? (y/n)\n')
                    if user_choice == 'y' or user_choice == 'yes':
                        break
                    elif user_choice == 'n' or user_choice == 'no':
                        print("Have a nice day!")
                        exit()
                    else:
                        continue
                continue
            else:
                user_selection = user_selection.lower()
                sheetsData = graph.SortData(sheetsData, user_selection)
                continue
        
        except KeyError:
                print('Please enter a sortable category to sort the residences accordingly (Vacancies/Estimated Rent/Time Until Vacancy/Distance/Driving Time/Bus Time/Walking Time) (Case Sensitive)')
                print('If there is a residence you would like more info on, type 
[truncated — 153 more characters]
```