# Project export: RecycleThis

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: Instantly classify items as recyclable, compostable, or disposable. Navigate to nearest disposal sites. Eco-friendly sorting made easy!
- Devpost: https://devpost.com/software/recycle-this
- GitHub: https://github.com/iaggarw1/recycle-this
- Result: winner ([MLH - MongoDB] Best Use of MongoDB Atlas)
- Team: 3 GitHub contributor(s) — SageRiverLee (5 commits), Yash-kumar7 (2 commits), Ishaan Aggarwal (1 commits)

## Devpost submission (written by the team)

### Inspiration

While walking around CruzHacks, we found many items misplaced in incorrect bins. We decided that if we wanted to solve environmental issues, we might as well start with those which are closest to us.

### What it does

RecycleThis is an iOS application that classifies proper disposal of items, and provides waste management tips. In addition, it further decreases the challenge of sustainability by providing locations for the nearest recycling bins in your area! With a single tap, users can effortlessly help maintain a clean community for themselves and those around them.

### How we built it

RecycleThis is a React Native application that utilizes a combined model of Google Cloud Platform and openAI APIs to assist users in their sustainability goals. With a custom AI Tensorflow model, the application can also classify the proper disposal classification for items, and display the Geo-location of relevant bins with Google Places..

### Challenges we ran into

This was the first hackathon for many of our members, and we all have little experience with our chosen platform of React Native. One particular issue we ran into was the deprecation and incompatibility of several libraries, requiring novel solutions that deviated from documentation.

### Accomplishments we're proud of

We have a working Speech Recognition System, that is able to record and log voice conversations while in range of the microphone. Also, our ability to quickly adapt and learn new frameworks exceeded our expectations. Coming to this event with little experience in creating machine learning models, we managed to research, develop, and train models within the deadline.

### What we learned

We learned a lot about Front-End Development in React Native, particularly in IOS development using Expo. We also learned the importance of the initial brainstorming phase of product development, as our first hurdle was surprisingly difficult.

### What's next

Throughout this weekend, we found some notable aspects we hope to improve upon in the future. General performance fixes in speed of the program and accuracy of the models would be helpful, as well as increased compatibility to account for non-ios users. RecycleThis also aims to further improve through a more accessible and appealing UI interface and additional features in later updates.

## README (from the GitHub repository)


# Recycle This

Recycle This is a project to improve the sustainability of UCSC, and eventually the future. We are building a mobile application that uses voice recognition to determine an objects waste type, and finds a set of recycling bin's near you if needed.




![Logo](https://i.imgur.com/SFMaqNV.png)


## Features

- Geolocation
- Voice Recognition
- Trained AI Waste Wanagement Classification


## Screenshots

![Front Page](https://cdn.discordapp.com/attachments/1196582859255849102/1198669651752792154/IMG_1247.png?ex=65bfbf1b&is=65ad4a1b&hm=6366e8a19389ae7430c012ba4f51e03985a322421c1837832ba9e2733de88019&)
![Home Page](https://media.discordapp.net/attachments/1196582859255849102/1198669216849592462/IMG_1244.png?ex=65bfbeb3&is=65ad49b3&hm=96eebbadaf6a02c656a37a73efb6ea09c392bd1aa9b04a011d8f5f2112592421&=&format=webp&quality=lossless&width=648&height=1340)
![Camera Widget](https://media.discordapp.net/attachments/1196582859255849102/1198669216056877177/IMG_1245.png?ex=65bfbeb3&is=65ad49b3&hm=4b90f590ee6cc06ba844b9322761f3d5dcacbe0d7b7b1e9d92fd02828b79e53b&=&format=webp&quality=lossless&width=656&height=1342)
![Image Recognition](https://media.discordapp.net/attachments/1196582859255849102/1198669217428414625/IMG_1246.png?ex=65bfbeb3&is=65ad49b3&hm=bc3a7c4239fdfd03c34b6ed76fe05eab3a7b87da4ee03d2af785b1429b1a7a3e&=&format=webp&quality=lossless&width=654&height=1342)


# Here 4 Food
Just a set of broke hungry college students.
## Authors
- [@iaggarw1](https://www.github.com/iaggarw1)
- [@sageriverlee](https://www.github.com/sageriverlee)
- [@YJSong30](https://www.github.com/YJSong30)
- [@Yash-kumar7](https://www.github.com/Yash-kumar7)



## Demo

Insert gif or link to demo



## Detected evidence (automated analysis)

Indexed codebase: 13 recognized source files, 33 KB.
- JavaScript (language) — detected in the code
- Python (language) — detected in the code
- React (technology) — detected in the code
- Java (language) — claimed on Devpost, not found in the code
- MongoDB (technology) — claimed on Devpost, not found in the code
- Node.js (technology) — claimed on Devpost, not found in the code
- TypeScript (language) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (25 of 25)

```
README.md
recycle-this/.gitignore
recycle-this/App.js
recycle-this/app.json
recycle-this/babel.config.js
recycle-this/CameraScan.js
recycle-this/components/AudioRecorder.js
recycle-this/components/importrequests.py
recycle-this/components/LocationService.js
recycle-this/components/openAIModule.js
recycle-this/components/RecyclingModel.js
recycle-this/components/text_model.json
recycle-this/item_waste_management.json
recycle-this/package.json
recycle-this/Recycling_model.ipynb
recycle-this/screens/.HomeScreen.js.swm
recycle-this/screens/.HomeScreen.js.swn
recycle-this/screens/.HomeScreen.js.swo
recycle-this/screens/.HomeScreen.js.swp
recycle-this/screens/CreateAccount.js
recycle-this/screens/HomeScreen.js
recycle-this/screens/Login.js
recycle-this/text_model.json
recycle-this/tsconfig.json
recycle-this/utils/getLocation.js
```

### Dependencies

- recycle-this/package.json: @babel/core@^7.20.0, @expo/metro-runtime@^3.1.1, @types/react@~18.2.45, axios@^1.6.5, expo@~50.0.2, expo-av@~13.10.3, expo-location@~16.5.2, expo-status-bar@~1.11.1, react@18.2.0, react-dom@18.2.0, react-native@0.73.2, react-native-maps@^1.9.1, react-native-vector-icons@^10.0.3, react-native-web@~0.19.6, typescript@^5.1.3

### Recent commits (newest first)

- Update README.md
- Added model training
- Delete recycle-this/java-backend
- Create java-backend
- Finishing touches for CruzHacks 2024! WOOHOO
- Updating changes
- Merge branch 'main' of https://github.com/iaggarw1/recycle-this
- made api worky
- Added model
- fixed speech recognition, removed node modules
- working speech recognition
- qol
- Merge branch 'main' of http://www.github.com/iaggarw1/recycle-this
- audio recording functionality
- README.mdUpdated.
- readme updated
- Merging in to main
- Merge branch 'main' of https://github.com/iaggarw1/recycle-this
- voice button active, recording unknown. maps active
- Merge pull request #1 from iaggarw1/sage

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

### recycle-this/package.json

```
{
  "name": "recycle-this",
  "version": "1.0.0",
  "main": "node_modules/expo/AppEntry.js",
  "scripts": {
    "start": "expo start",
    "android": "expo start --android",
    "ios": "expo start --ios",
    "web": "expo start --web"
  },
  "dependencies": {
    "@expo/metro-runtime": "^3.1.1",
    "axios": "^1.6.5",
    "expo": "~50.0.2",
    "expo-location": "~16.5.2",
    "expo-status-bar": "~1.11.1",
    "react": "18.2.0",
    "react-dom": "18.2.0",
    "react-native": "0.73.2",
    "react-native-maps": "^1.9.1",
    "react-native-vector-icons": "^10.0.3",
    "react-native-web": "~0.19.6",
    "expo-av": "~13.10.3"
  },
  "devDependencies": {
    "@babel/core": "^7.20.0",
    "@types/react": "~18.2.45",
    "typescript": "^5.1.3"
  },
  "private": true
}

```

### recycle-this/App.js

```javascript
import { StatusBar } from 'expo-status-bar';
import { StyleSheet, Text, View, SafeAreaView, Button, Image, TouchableOpacity, Icon } from 'react-native';
import React, { useState, useEffect, useRef } from 'react';
import { Camera } from 'expo-camera';
import HomeScreen from './screens/HomeScreen';
import { shareAsync } from 'expo-sharing';
import * as MediaLibrary from 'expo-media-library';
import { FontAwesome } from 'react-native-vector-icons';
import Login from './screens/Login';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { NavigationContainer } from '@react-navigation/native';
import CreateAccount from './screens/CreateAccount';

const Stack = createNativeStackNavigator();

const App = () => {

  return (
    <NavigationContainer>
      <Stack.Navigator initialRouteName='Welcome'>
        <Stack.Screen name="Welcome" component={Login} />
        <Stack.Screen name="Home" component={HomeScreen} />
        <Stack.Screen name="CreateAccount" component={CreateAccount} />

      </Stack.Navigator>
    </NavigationContainer>
  );
};

export default App;
```

### recycle-this/babel.config.js

```javascript
module.exports = function(api) {
  api.cache(true);
  return {
    presets: ['babel-preset-expo'],
  };
};

```

### recycle-this/CameraScan.js

```javascript
import React, { useState, useEffect, useRef } from 'react';
import { StyleSheet, View, TouchableOpacity, Text, SafeAreaView, Button, Image  } from 'react-native';
import { Camera, CameraType, requestCameraPermissionsAsync, getCameraPermissionsAsync, WhiteBalance,  } from 'expo-camera';
import { FontAwesome } from 'react-native-vector-icons';
import * as MediaLibrary from 'expo-media-library';
import { shareAsync } from 'expo-sharing';
import { StatusBar } from 'expo-status-bar';
import { Alert } from 'react-native';
import FileSystem from 'react-native-filesystem';

const CameraScan = ( { onHide }) => {
  let cameraRef = useRef();
  const [hasCameraPermission, setHasCameraPermission] = useState();
  const [hasMediaLibraryPermission, setHasMediaLibraryPermission] = useState();
  const [photo, setPhoto] = useState(null);

  useEffect(() => {
    (async () => {
      const cameraPermission = await Camera.requestCameraPermissionsAsync();
      const mediaLibraryPermission =
        await MediaLibrary.requestPermissionsAsync();
      setHasCameraPermission(cameraPermission.status === "granted");
      setHasMediaLibraryPermission(mediaLibraryPermission.status === "granted");
    })();
  }, []);

  if (hasCameraPermission === undefined) {
    return <Text>Requesting permissions...</Text>;
  } else if (!hasCameraPermission) {
    return (
      <Text>
        Permission for camera not granted. Please change this in settings.
      </Text>
    );
  }

  const recyclableMaterials = ['plastic', 'glass', 'paper', 'metal']; // List of recyclable material types

  const takePic = async () => {
    let options = {
      quality: 1,
      base64: true,
      exif: false,
    };
  
    try {
      let newPhoto = await cameraRef.current.takePictureAsync(options);
      let item_data = await query(newPhoto); // Assuming this returns an array of objects with label and score
  
      setPhoto(newPhoto); // Update state with the new photo
      console.log(newPhoto.uri); // Log the URI of the new photo
  
      // Analyze the item data to determine if it's recyclable, disposable, or not identifiable
      const recyclableMaterials = ['plastic', 'glass', 'paper', 'metal'];
      let recyclableScore = 0;
      let otherScore = 0;
      let isIdentifiable = true;
      
      // Sum the scores for recyclable materials and 'others'
      item_data.forEach(item => {
        if (recyclableMaterials.includes(item.label.toLowerCase())) {
          recyclableScore += item.score;
        } else if (item.label.toLowerCase() === 'others') {
          otherScore += item.score;
        }
      });
      
      // Determine the recyclability of the item
      if (recyclableScore > otherScore) {
        Alert.alert("Recyclable", "This item is recyclable. Please move to the closest recycling bin to recycle it.");
      } else if (otherScore > recyclableScore) {
        Alert.alert("Disposable", "This item is disposable. Please dispose of it in the nearest waste bin.");
      } else {
        // Check if the scores are close to each other, implying non-identifiability
        const threshold = 0.1; // Define a threshold for score differences
        isIdentifiable = !item_data.some(item => Math.abs(item.score - (1 / item_data.length)) > threshold);
        
        if (isIdentifiable) {
          Alert.alert("Unidentifiable", "The item is not able to be identified as recyclable or disposable.");
        } else {
          Alert.alert("Indeterminate", "This item is mixed and cannot be determined.");
        }
      }
    } catch (error) {
      console.error('Error taking picture or querying item type:', error);
    }
  };
  

try{
  if (photo) {
    let sharePic = () => {
      shareAsync(photo.uri).then(() => {
        setPhoto(undefined);
      });
    };

    let savePhoto = () => {
      MediaLibrary.saveToLibraryAsync(photo.uri).then(() => {
        setPhoto(undefined);
      });
    };

  }
} catch (error)
{

};


  return (
    <Camera style={styles.camera} ref={cameraRef}>
      <View style={styles.buttonContainer}>
        <TouchableOpacity style={styles.takePicButton} onPress={takePic}>
          <Text style={styles.takePicText}>Capture</Text>
        </TouchableOpacity>
      </View>
    </Camera>
  );
};
function getLabelOfHighestScore(data) {
  if (!Array.isArray(data)) {
    console.error('Invalid data: expected an array');
    return;
  }

  const highestScoreLabel = data.reduce((highest, current) => {
    console.log(`In function: ${(current.score > highest.score) ? current : highest}`);
    return (current.score > highest.score) ? current : highest;
  }, { score: -Infinity }); // Start with a lowest possible score

  console.log(`Exiting function: ${highestScoreLabel.label}`);
  return highestScoreLabel.label;
}



const uploadImageToModel = async (imageUri) => {
  console.log("Uploading image to model's endpoint");
  const data = new FormData();
  data.append("photo", {
    uri: imageUri,
    type: "image/jpeg",
    name: "upload.jpg",
  });


};
async function query(uri){
    try {

        const response = await fetch("https://api-inference.huggingface.co/models/pyesonekyaw/recycletree_materials", 
        {
          headers: { Authorization: "Bearer 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" },
          method: "POST",
          body: uri,
        }
        );
        
        var json;
        const result = await response.json().then((result)=>{
            console.log(result);
            json = result;
        });
        console.log(json[0].label);
        
        return json;
      } catch (error) {
        console.error(error);
      }
}
//   const [type, setType] = useState(CameraType.back);
//   const [permission, requestPermission] = Camera.useCameraPermissions();

//   function toggleCameraType() {
//     setType(current => (current === CameraType.back ? CameraType.front : CameraType.back));
//   }

//   useEffect(() => {
//     (async () => {
//       if (!permission) {
//         await request
[truncated — 981 more characters]
```

### recycle-this/components/RecyclingModel.js

```javascript
import axios from 'axios';
import * as tf from '@tensorflow/tfjs';
import { Platform } from 'react-native';
async function RecyclingModel(prompt){
    console.log("wakie talkie")
    const model = await tf.loadLayersModel('localstorage://text_model');
    const pred = await model.predict(prompt)
    console.log(pred);
    console.log("well the function works ig")
}
```

### recycle-this/utils/getLocation.js

```javascript
import * as Location from 'expo-location';

const getLocation = async () => {
  let { status } = await Location.requestForegroundPermissionsAsync();
  if (status !== 'granted') {
    console.error('Permission to access location was denied');
    return;
  }

  let location = await Location.getCurrentPositionAsync({});
  return location;
};

export default getLocation;

```

### recycle-this/components/importrequests.py

```python
import requests

API_URL = "https://api-inference.huggingface.co/models/Giecom/giecom-vit-model-clasification-waste"
headers = {"Authorization": "Bearer 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"}

def query(filename):
    with open(filename, "rb") as f:
        data = f.read()
    response = requests.post(API_URL, headers=headers, data=data)
    print(response.json())
    return response.json()

output = query("water.jpg")
```

### recycle-this/components/LocationService.js

```javascript
// LocationService.js
import { useState, useEffect } from 'react';
import getLocation from '../utils/getLocation';

export const useLocationService = () => {
  const [location, setLocation] = useState(null);

  useEffect(() => {
    const fetchLocation = async () => {
      try {
        const loc = await getLocation();
        setLocation(loc);
      } catch (error) {
        console.error('Error fetching location:', error);
        // Handle location error (e.g., permissions not granted)
      }
    };

    fetchLocation();
  }, []);

  return location;
};

```

### recycle-this/components/openAIModule.js

```javascript
import OpenAI from "openai";


async function openaiCall(message) {
  console.log("yeeteth");
  const openai = new OpenAI({apiKey:'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'});
  
  const completion = await openai.chat.completions.create({
    messages: [
      {
        role: "system",
        content: "You are a helpful assistant designed to output JSON.",
      },
      { role: "user", content: "Who won the world series in 2020?" },
    ],
    model: "gpt-3.5-turbo-1106",
    response_format: { type: "json_object" },
  });

return completion;
}

export default openaiCall;
```

### recycle-this/screens/CreateAccount.js

```javascript
import React, { useState } from 'react';
import { View, StyleSheet, Image, Alert, Text } from 'react-native';
import { useNavigation } from '@react-navigation/native';
import { Button, TextInput } from 'react-native-paper';
import axios from 'axios';


function CreateAccount() {
  const navigation = useNavigation();
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");


  const handleSubmit = async ()  => {
    navigation.navigate("Welcome");
    try {
      const response = await axios.post(
        "https://test-8wlq.onrender.com/api/user/register",
        {
          email,
          password,
        }
      );

      if (response.status === 200) {
        Alert.alert("Signup Successful");

      } else {
        Alert.alert("Signup Failed");
      }
    } catch (error) {
      console.log(error);
    }
    Alert.alert("Your account has been created!");
  };


  return (
    <View style={styles.container}>
      <Image source={require("../logo.png")} style={styles.logo} />
      <TextInput
        style={styles.textboxes}
        autoCapitalize="none"
        placeholder="Email"
        onChangeText={setEmail} 
        value={email} 
      />
      <TextInput
        style={styles.textboxes}
        autoCapitalize="none"
        secureTextEntry={true}
        placeholder="Password"
        onChangeText={setPassword}
        value={password}
      />

      <Button mode="contained" onPress={handleSubmit} style={styles.button}>
        Create Account
      </Button>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: "center",
    padding: 20,
    marginTop: 10,
  },
  textboxes: {
    marginBottom: 40,

  },
  button: {
    marginTop: 15,
    paddingVertical: 8,
  },
  logo: {
    width: 340,
    height: 390,
    resizeMode: "contain",
    marginBottom: 30,
  },
});

export default CreateAccount
```

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