# Project export: Hercules

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: TreeHacks 2024
- Tagline: Hercules is an AI-Powered physical therapist that can understand and help alleviate your pain based on speech and pictures alone. Our aim is to close the healthspan-lifespan gap for our generation.
- Devpost: https://devpost.com/software/hercules-oaz7ty
- GitHub: https://github.com/mfkhalil/hercules-treehacks
- Demo: https://www.overleaf.com/read/gprryxwyfwmr#849a46
- Video: https://www.youtube.com/embed/3IlvWh1ny9s?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 1 GitHub contributor(s) — mfkhalil (16 commits)

## Devpost submission (written by the team)

### Inspiration

All three of are international students who converged on this idea based on a shared experience: our grandmothers, who dreamed of coming to see us graduate, could not make the long trip due to loss of mobility. We have also seen our parents, who are around 60 years old, unable to enjoy the things they used to with us when we were kids: playing basketball, backpacking, skiing, leaving those cherished moments as memories. We realised that even though children born today are expected to live maybe 100+ years, their quality of life in their later years depends on how we take care of our bodies today. We did extensive research into how earlier life experiences affect mobility and realised that this is an uphill battle that starts today. We thus developed an accessible and intuitive approach to tracking and understanding factors that go into your present and future mobility.

### What it does

Hercules can see and understand your pain using only your iPhone camera and/or a voice note describing your pain, therefore simplifying understanding pains in the body to even those who aren't health literate. He allows you to log any fleeting, acute, or chronic pain, all while seamlessly tracking your activity using wearable data. He will create a long-term log correlating the two, helping develop an exercise routine based on the user's specific needs and limitations, ensuring that they say safe but active enough to promote long-term mobility health.

### How we built it

We first did in-depth research on what affects mobility health in the future and what we can do to alleviate effects of it deteriorating early on. We mapped out user journeys and decided that our target users would be everyone, with an emphasis on those who still have the power to prevent future mobility issues Our product is a react-native app built in JavaScript/Node.js, and utilizes the OpenAI API, namely Whisper for speech-to-text of user voice input, GPT-3.5 Turbo for semantic understanding of that text, GPT-4 Vision for image recognition and reasoning, and OpenAI TTS for text-to-speech for Hercules' voice. We used Figma to mock up the app design while we built the bare-bones user flow, then added the styling. Additionally, although we did not have time to implement this into our app, we worked on developing a "Mobility Score" that uses wearable data to evaluate an individual's mobility, which was inspired by the Oura Ring's "Sleep Score" and "Activity Score" measurements and equations.

### Challenges we ran into

We ended up having to design and implement a lot of screens, which had to be done after implementing the image recognition and AI speech software. We also realised that there simply isn't enough data that measures changes in one's mobility long-term, and how acute pains could manifest into more serious conditions with old age. This actually made us more excited about building the product however, as it could allow us to accumulate such data, especially for those historically underrepresented in medical data such as women and minorities. Technically, the main challenge we ran into was that OpenAI's GPT-4 vision model was not recognizing body parts in some cases from the pictures we sent it. We eventually realized that it was due to OpenAI's API automatically cropping our image if it wasn't in the correct aspect ratio, and were able to fix it by implement cropping client-side.

### Accomplishments we're proud of

We were able to design and implement 80% of our screens that would be required for a fully functional product. We also managed to make Hercules a very helpful and personable AI agent that makes the user feel at ease. We are most proud that we have created a tool for our generation to enjoy the later stages in life more than our older family members ever did.

### What we learned

We understand mobility physiology far better, as well as designing not only for older generations but the older generations of the future. We also learned that the biggest causes of the health span - lifespan gap are lack of health literacy and access to personalised care, both of which could now be solved given new technologies such as AI agents, which is what we envision for our product.

### What's next

We would love to develop personalised fitness programs for those who have very specific pains and injuries but not the means to go to a doctor or physical therapist. We would likely offer these programs at a very low cost and keep them accessible in terms of ease of following them. We also want to introduce educational aspects of monitoring pain and habit-building to ensure users are maintaining the actions needed to preserve their mobility until later in life. Finally, a key indicator of mobility health is nutrition, so we want to incorporate a nutrition journal that's as easy to use as the pain log, using just a picture for Hercules to tell the user about their eating habits as it relates to their long-term mobility health.

## README (from the GitHub repository)

# Hercules
2024 TreeHacks Project by Moe, Defne, Aaron

## How to Run the Code

1. Navigate to the project's main directory:
   
   ```bash
   cd hercules-treehacks
  
3. Change to the React project directory:
   
   ```bash
   cd hercules-react

5. Create a `.env` file in `hercules-react` and add your OpenAI API key. Ensure your OpenAI account has spent at least $1, otherwise you won't be allowed to access the GPT vision model:
   
   ```bash
   EXPO_PUBLIC_OPENAI_API_KEY="your_openai_api_key"

7. Install dependencies and run the application:
   
   ```bash
   npm install
   npm start


## Detected evidence (automated analysis)

Indexed codebase: 16 recognized source files, 82 KB.
- JavaScript (language) — detected in the code
- OpenAI (technology) — detected in the code
- React (technology) — detected in the code
- Node.js (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (28 of 28)

```
.gitignore
hercules-react/.gitignore
hercules-react/App.js
hercules-react/app.json
hercules-react/assets/fonts/apercu_bold_italic_pro.otf
hercules-react/assets/fonts/apercu_bold_pro.otf
hercules-react/assets/fonts/apercu_medium_italic_pro.otf
hercules-react/assets/fonts/apercu_medium_pro.otf
hercules-react/assets/fonts/apercu_regular_italic_pro.otf
hercules-react/assets/fonts/apercu_regular_pro.otf
hercules-react/assets/fonts/Nohemi-Bold.otf
hercules-react/assets/fonts/Nohemi-Regular.otf
hercules-react/babel.config.js
hercules-react/contexts/DiscomfortContext.js
hercules-react/package.json
hercules-react/screens/DashboardScreen.js
hercules-react/screens/DescribeMotionScreen.js
hercules-react/screens/FinishLogNewDiscomfortScreen.js
hercules-react/screens/FollowUpNewDiscomfortScreen.js
hercules-react/screens/LogDiscomfortScreen.js
hercules-react/screens/LogExistingDiscomfortScreen.js
hercules-react/screens/LogNewDiscomfortScreen.js
hercules-react/screens/ProfileScreen.js
hercules-react/screens/UpdateDiscomfortScreen.js
hercules-react/screens/WhenNewDiscomfortScreen.js
hercules-react/screens/WoohooScreen.js
hercules-react/screens/WrappingUpNewDiscomfortScreen.js
README.md
```

### Dependencies

- hercules-react/package.json: @babel/core@^7.20.0, @expo/vector-icons@^14.0.0, @react-navigation/bottom-tabs@^6.5.12, @react-navigation/native@^6.1.10, @react-navigation/stack@^6.3.21, axios@^1.6.7, expo@~50.0.7, expo-app-loading@^2.1.1, expo-av@^13.10.5, expo-camera@^14.0.5, expo-font@^11.10.3, expo-image-manipulator@^11.8.0, expo-splash-screen@^0.26.4, expo-status-bar@~1.11.1, install@^0.13.0, moment@^2.30.1, openai@^4.28.0, react@18.2.0, react-native@0.73.4, react-native-dotenv@^3.4.10, react-native-fs@^2.20.0

### Recent commits (newest first)

- cleared existing discomfort for demo
- omg finally done
- Update README.md
- change name
- deleted
- WORKING
- working
- halfway done with style, committing to save current prompts
- functinoal - now need to do style stuff
- added when and record screens
- added audio understanding
- body part from image working
- tons of new screens and audio files but they all do nothing rn
- added initial screens and audio playing
- init app
- Initial commit

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

### hercules-react/package.json

```
{
  "name": "hercules-react",
  "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/vector-icons": "^14.0.0",
    "@react-navigation/bottom-tabs": "^6.5.12",
    "@react-navigation/native": "^6.1.10",
    "@react-navigation/stack": "^6.3.21",
    "axios": "^1.6.7",
    "expo": "~50.0.7",
    "expo-app-loading": "^2.1.1",
    "expo-av": "^13.10.5",
    "expo-camera": "^14.0.5",
    "expo-font": "^11.10.3",
    "expo-image-manipulator": "^11.8.0",
    "expo-splash-screen": "^0.26.4",
    "expo-status-bar": "~1.11.1",
    "install": "^0.13.0",
    "moment": "^2.30.1",
    "openai": "^4.28.0",
    "react": "18.2.0",
    "react-native": "0.73.4",
    "react-native-dotenv": "^3.4.10",
    "react-native-fs": "^2.20.0"
  },
  "devDependencies": {
    "@babel/core": "^7.20.0"
  },
  "private": true
}

```

### hercules-react/App.js

```javascript
import React, { useState, useEffect } from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import DashboardScreen from './screens/DashboardScreen';
import LogDiscomfortScreen from './screens/LogDiscomfortScreen';
import LogExistingDiscomfortScreen from './screens/LogExistingDiscomfortScreen';
import LogNewDiscomfortScreen from './screens/LogNewDiscomfortScreen';
import UpdateDiscomfortScreen from './screens/UpdateDiscomfortScreen';
import WhenNewDiscomfortScreen from './screens/WhenNewDiscomfortScreen';
import DescribeMotionScreen from './screens/DescribeMotionScreen';
import FollowUpNewDiscomfortScreen from './screens/FollowUpNewDiscomfortScreen';
import FinishLogNewDiscomfortScreen from './screens/FinishLogNewDiscomfortScreen';
import WrappingUpNewDiscomfortScreen from './screens/WrappingUpNewDiscomfortScreen';
import WoohooScreen from './screens/WoohooScreen';
import ProfileScreen from './screens/ProfileScreen';
import { DiscomfortProvider } from './contexts/DiscomfortContext';
import { Ionicons } from '@expo/vector-icons';
import * as Font from 'expo-font';
import * as SplashScreen from 'expo-splash-screen';
import { LogBox, TouchableOpacity } from 'react-native';

LogBox.ignoreAllLogs();

SplashScreen.preventAutoHideAsync();

const fetchFonts = () => {
  return Font.loadAsync({
    'ApercuBold': require('./assets/fonts/apercu_bold_italic_pro.otf'),
    'ApercuRegular': require('./assets/fonts/apercu_regular_pro.otf'),
    'NohemiRegular': require('./assets/fonts/Nohemi-Regular.otf'),
    'NohemiBold': require('./assets/fonts/Nohemi-Bold.otf'),
  });
};
const Stack = createStackNavigator();
const Tab = createBottomTabNavigator();

function BottomTabNavigator() {
  return (
    <Tab.Navigator
      screenOptions={({ route }) => ({
        tabBarIcon: ({ focused, color, size }) => {
          let iconName;
          if (route.name === 'Summary') {
            iconName = 'bar-chart';
          } else if (route.name === 'Log') {
            iconName = 'book';
          } else if (route.name === 'Profile') {
            iconName = 'person';
          }
          return <Ionicons name={iconName} size={size} color={color} />;
        },
        headerShown: false, // Hide the header
        tabBarActiveTintColor: '#FFAF53',
        tabBarInactiveTintColor: '#FFFFFF',
        tabBarStyle: {
          backgroundColor: '#292929',
          borderRadius: 100,
          position: 'absolute',
          left: 20,
          right: 20,
          bottom: 30, // This ensures the tab bar is above any device-specific screen curvature or navigation bar.
          height: 70, // Total height of the tab bar.
          paddingBottom: 10, // Adjust this value as needed to center the icons vertically.
        },
        
      })}
    >
      <Tab.Screen name="Summary" component={MainStackNavigator} />
      <Tab.Screen name="Log" component={LogStackNavigator} 
      listeners={({ navigation, route }) => ({
          tabPress: e => {
            // Prevent default action
            e.preventDefault();
            // Navigate to the initial route of the Log Stack
            navigation.navigate('LogDiscomfortScreen');
          },
        })}/>
      <Tab.Screen name="Profile" component={ProfileScreen} />
    </Tab.Navigator>
  );
}

function LogStackNavigator() {
  return (
    <Stack.Navigator
      screenOptions={{
        headerShown: false,
        cardStyle: { backgroundColor: '#FFFDEE' },
      }}>
      <Stack.Screen name="LogDiscomfortScreen" component={LogDiscomfortScreen} />
      <Stack.Screen name="LogExistingDiscomfortScreen" component={LogExistingDiscomfortScreen} />
      <Stack.Screen name="LogNewDiscomfortScreen" component={LogNewDiscomfortScreen} />
      <Stack.Screen name="UpdateDiscomfortScreen" component={UpdateDiscomfortScreen} />
      <Stack.Screen name="WhenNewDiscomfortScreen" component={WhenNewDiscomfortScreen} />
      <Stack.Screen name="DescribeMotionScreen" component={DescribeMotionScreen} />
      <Stack.Screen name="FollowUpNewDiscomfortScreen" component={FollowUpNewDiscomfortScreen} />
      <Stack.Screen name="WrappingUpNewDiscomfortScreen" component={WrappingUpNewDiscomfortScreen} />
      <Stack.Screen name="FinishLogNewDiscomfortScreen" component={FinishLogNewDiscomfortScreen} />
      <Stack.Screen name="WoohooScreen" component={WoohooScreen} />
    </Stack.Navigator>
  );
}

function MainStackNavigator() {
  return (
    <Stack.Navigator
      initialRouteName="DashboardScreen"
      screenOptions={{
        headerShown: false,
        cardStyle: { backgroundColor: '#FFFDEE' },
      }}
    >
      <Stack.Screen
            name="DashboardScreen"
            component={DashboardScreen}
          />
          <Stack.Screen
            name="LogDiscomfortScreen"
            component={LogDiscomfortScreen}
          />
          <Stack.Screen
            name="LogExistingDiscomfortScreen"
            component={LogExistingDiscomfortScreen}
          />
          <Stack.Screen
            name="LogNewDiscomfortScreen"
            component={LogNewDiscomfortScreen}
          />
          <Stack.Screen
            name="UpdateDiscomfortScreen"
            component={UpdateDiscomfortScreen}
          />
          <Stack.Screen
            name="WhenNewDiscomfortScreen"
            component={WhenNewDiscomfortScreen}
          />
          <Stack.Screen
            name="DescribeMotionScreen"
            component={DescribeMotionScreen}
          />
          <Stack.Screen
            name="FollowUpNewDiscomfortScreen"
            component={FollowUpNewDiscomfortScreen}
          />
          <Stack.Screen
            name="FinishLogNewDiscomfortScreen"
            component={FinishLogNewDiscomfortScreen}
          />
          <Stack.Screen
            name="ProfileScreen"
            component={ProfileScreen}
    
[truncated — 1284 more characters]
```

### hercules-react/babel.config.js

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

```

### hercules-react/contexts/DiscomfortContext.js

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

const DiscomfortContext = createContext();

export const useDiscomforts = () => useContext(DiscomfortContext);

export const DiscomfortProvider = ({ children }) => {
    const [discomforts, setDiscomforts] = useState([
        {
            bodyPart: 'neck',
            whenPain: 'All the time',
            motion: 'Turning head',
            followUp: 'Ice pack',
            painLevel: 5,
            date: new Date().toISOString(),
        }
    ]);

    return (
        <DiscomfortContext.Provider value={{ discomforts, setDiscomforts }}>
            {children}
        </DiscomfortContext.Provider>
    );
};
```

### hercules-react/screens/UpdateDiscomfortScreen.js

```javascript
import React, { useEffect } from 'react';
import { View, Text, StyleSheet } from 'react-native';
import { Audio } from 'expo-av';
import { useNavigation } from '@react-navigation/native';
import { Ionicons } from '@expo/vector-icons';

const UpdateDiscomfortScreen = ({ route }) => {
    const navigation = useNavigation();
    const { discomfort } = route.params;

    return (
        <View style={styles.container}>
            <View style={styles.header}>
                <Text style={styles.headerText}>This is where you would update your discomfort for <Text style={styles.boldHeaderText}>{discomfort.bodyPart}</Text></Text>
            </View>
            <View style={styles.body}>
                <Text style={styles.bodyText}>Unfortunately, Hercules's makers ran out of time...</Text>
            </View>
            <View style={styles.buttonContainer}>
                <View style={styles.button}>
                    <Text style={styles.buttonText} onPress={() => navigation.navigate('DashboardScreen')}>Return to Dashboard</Text>
                </View>
            </View>
        </View>
    );
};

const styles = StyleSheet.create({
    container: {
        flex: 1,
        alignItems: 'center',
        backgroundColor: '#FFFDEE',
        flexDirection: 'column',
        height: '100%',
        justifyContent: 'center',
    },
    top: {
        width: '100%',
        height: '25%',
        justifyContent: 'flex-end',
        alignItems: 'center',
    },
    backArrow: {
        marginLeft: 18,
        width: '10%',
    },
    header: {
        width: '100%',
        // height: '13%',
        justifyContent: 'center',
    },
    headerText: {
        fontFamily: 'NohemiRegular',
        fontSize: 35,
        marginLeft: 30,
        marginRight: 30,
        marginTop: 10
    },
    boldHeaderText: {
        fontFamily: 'NohemiBold',
        fontSize: 35,
        marginLeft: 30,
        marginRight: 30
    },
    body: {
        width: '100%',
        justifyContent: 'flex-end',
        width: '100%',
    },
    bodyText: {
        fontFamily: 'ApercuRegular',
        fontSize: 20,
        marginLeft: 30,
        marginRight: 30,
        marginTop: 20
    },
    boldBodyText: {
        fontFamily: 'ApercuBold',
        fontSize: 20,
        marginLeft: 30,
        marginRight: 30
    },
    buttonContainer: {
        width: '100%',
        alignItems: 'center',
        marginBottom: 50,
        marginTop: 20,
    },
    button: {
        width: '88%',
        marginBottom: 10,
        backgroundColor: '#292929',
        borderRadius: 15,
        alignItems: 'center',
        flexDirection: 'row',
        justifyContent: 'space-between',
        paddingRight: 30
    },
    buttonText: {
        fontFamily: 'ApercuRegular',
        fontSize: 20,
        color: 'white',
        textAlign: 'center',
        padding: 18,
        paddingLeft: 25
    },
});

export default UpdateDiscomfortScreen;

```

### hercules-react/screens/LogDiscomfortScreen.js

```javascript
import React, { useEffect } from 'react';
import { View, Text, StyleSheet, Button, TouchableOpacity } from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import { useNavigation } from '@react-navigation/native';
import { Audio } from 'expo-av';

const LogDiscomfortScreen = () => {
    const sound = new Audio.Sound();
    const navigation = useNavigation();

    useEffect(() => {
        // Function to load and play the sound
        async function loadAndPlay() {
            try {
                await sound.loadAsync(require('../assets/audio/LogDiscomfortScreen.mp3'));
                await sound.playAsync();
            } catch (error) {
                console.log('Error loading and playing sound:', error);
            }
        }

        // Call the function
        loadAndPlay();

        return () => {
            // Function to stop and unload the sound
            async function stopAndUnload() {
                await sound.stopAsync();
                await sound.unloadAsync();
            }

            // Call the function
            stopAndUnload();
        };
    }, []);

    return (
        <View style={styles.container}> 
            <View style={styles.top}>
            </View>
            <View style={styles.header}>
                <Text style={styles.headerText}>Let's log your symptoms.</Text>
            </View>
            <View style={styles.body}>
                <Text style={styles.bodyText}>Would you like to log a new entry or update an existing one?</Text>
            </View>
            <View style={styles.buttonContainer}>
                <TouchableOpacity style={styles.button} onPress={() => navigation.navigate('LogNewDiscomfortScreen')}>
                    <Text style={styles.buttonText}>Log a new entry.</Text>
                </TouchableOpacity>
                <TouchableOpacity style={styles.button} onPress={() => navigation.navigate('LogExistingDiscomfortScreen')}>
                    <Text style={styles.buttonText}>Update an existing entry.</Text>
                </TouchableOpacity>
            </View>
        </View>
    );
};

const styles = StyleSheet.create({
    container: {
        flex: 1,
        alignItems: 'center',
        backgroundColor: '#FFFDEE',
        flexDirection: 'column',
        height: '100%',
    },
    top: {
        width: '100%',
        height: '12%',
        justifyContent: 'flex-end',
    },
    header: {
        width: '100%',
        height: '13%',
        justifyContent: 'center',
    },
    headerText: {
        fontFamily: 'NohemiRegular',
        fontSize: 35,
        marginLeft: 30,
        marginRight: 30
    },
    body: {
        width: '100%',
        height: '15%',
        justifyContent: 'flex-end',
    },
    bodyText: {
        fontFamily: 'ApercuRegular',
        fontSize: 20,
        marginLeft: 30,
        marginRight: 30
    },
    buttonContainer: {
        width: '100%',
        height: '40%',
        alignItems: 'center',
        marginBottom: 20,
        marginTop: 30,
    },
    button: {
        width: '88%',
        marginBottom: 10,
        backgroundColor: '#292929',
        borderRadius: 15,
        alignItems: 'flex-start'
    },
    buttonText: {
        fontFamily: 'ApercuRegular',
        fontSize: 20,
        color: 'white',
        textAlign: 'center',
        padding: 18,
        paddingLeft: 25
    }
});

export default LogDiscomfortScreen;

```

### hercules-react/screens/WoohooScreen.js

```javascript
import React, { useEffect } from 'react';
import { View, Text, StyleSheet } from 'react-native';
import { Audio } from 'expo-av';
import { useNavigation } from '@react-navigation/native';
import { Ionicons } from '@expo/vector-icons';

const WoohooScreen = () => {
    const navigation = useNavigation();
    const sound = new Audio.Sound();

    useEffect(() => {
        async function loadAndPlay() {
            try {
                await sound.loadAsync(require('../assets/audio/Woohoo.mp3'));
                await sound.playAsync();
            } catch (error) {
                console.log('Error loading and playing sound:', error);
            }
        }

        loadAndPlay();

        return () => {
            async function stopAndUnload() {
                await sound.stopAsync();
                await sound.unloadAsync();
            }

            stopAndUnload();
        };
    }, []);

    return (
        <View style={styles.container}>
            <View style={styles.top}>
                <Ionicons name="trophy" size={180} color="black" />
            </View>
            <View style={styles.header}>
                <Text style={styles.headerText}><Text style={styles.boldHeaderText}>Woohoo!</Text> Your logs have now been updated.</Text>
            </View>
            <View style={styles.body}>
                <Text style={styles.bodyText}>Hercules is proud of you. {'<3'}</Text>
            </View>
            <View style={styles.buttonContainer}>
                <View style={styles.button}>
                    <Text style={styles.buttonText} onPress={() => navigation.navigate('DashboardScreen')}>Return to Dashboard</Text>
                </View>
            </View>
        </View>
    );
};

const styles = StyleSheet.create({
    container: {
        flex: 1,
        alignItems: 'center',
        backgroundColor: '#FFFDEE',
        flexDirection: 'column',
        height: '100%',
        justifyContent: 'center',
    },
    top: {
        width: '100%',
        height: '25%',
        justifyContent: 'flex-end',
        alignItems: 'center',
    },
    backArrow: {
        marginLeft: 18,
        width: '10%',
    },
    header: {
        width: '100%',
        // height: '13%',
        justifyContent: 'center',
    },
    headerText: {
        fontFamily: 'NohemiRegular',
        fontSize: 35,
        marginLeft: 30,
        marginRight: 30,
        marginTop: 10
    },
    boldHeaderText: {
        fontFamily: 'NohemiBold',
        fontSize: 35,
        marginLeft: 30,
        marginRight: 30
    },
    body: {
        width: '100%',
        justifyContent: 'flex-end',
        width: '100%',
    },
    bodyText: {
        fontFamily: 'ApercuRegular',
        fontSize: 20,
        marginLeft: 30,
        marginRight: 30,
        marginTop: 20
    },
    boldBodyText: {
        fontFamily: 'ApercuBold',
        fontSize: 20,
        marginLeft: 30,
        marginRight: 30
    },
    buttonContainer: {
        width: '100%',
        alignItems: 'center',
        marginBottom: 50,
        marginTop: 20,
    },
    button: {
        width: '88%',
        marginBottom: 10,
        backgroundColor: '#292929',
        borderRadius: 15,
        alignItems: 'center',
        flexDirection: 'row',
        justifyContent: 'space-between',
        paddingRight: 30
    },
    buttonText: {
        fontFamily: 'ApercuRegular',
        fontSize: 20,
        color: 'white',
        textAlign: 'center',
        padding: 18,
        paddingLeft: 25
    },
});

export default WoohooScreen;

```

### hercules-react/screens/WrappingUpNewDiscomfortScreen.js

```javascript
import React, { useState, useEffect } from 'react';
import { View, Text, StyleSheet, Button, TouchableOpacity, ActivityIndicator } from 'react-native';
import { Audio } from 'expo-av';
import { useNavigation } from '@react-navigation/native';
import OpenAI from "openai";
import { Ionicons } from '@expo/vector-icons';

const openai = new OpenAI({ apiKey: process.env.EXPO_PUBLIC_OPENAI_API_KEY });

const WrappingUpNewDiscomfortScreen = ({ route }) => {
    const { bodyPart, selectedOptions, motion, followUp } = route.params;
    const navigation = useNavigation();
    const [sound] = useState(new Audio.Sound());
    const [loading, setLoading] = useState(false);
    
    useEffect(() => {
        return sound ? () => {
            sound.unloadAsync();
        } : undefined;
    }, [sound]);

    useEffect(() => {
        async function loadAndPlay() {
            try {
                await sound.loadAsync(require('../assets/audio/WrappingUpNewDiscomfortScreen.mp3'));
                await sound.playAsync();
            } catch (error) {
                console.log('Error loading and playing sound:', error);
            }
        }

        loadAndPlay();

        return () => {
            async function stopAndUnload() {
                await sound.stopAsync();
                await sound.unloadAsync();
            }

            stopAndUnload();
        };
    }, []);

    return (
        <View style={styles.container}>
        <View style={styles.top}>
        </View>
        <View style={styles.header}>
            <Text style={styles.headerText}>Wrapping up.</Text>
        </View>
        <View style={styles.body}>
            <Text style={styles.bodyText}>On a scale from 1 to 10, how would you rate your pain or discomfort right now?</Text>
        </View>
            <View style={styles.painScaleContainer}>
                {[...Array(10).keys()].map((index) => (
                    <TouchableOpacity
                        key={index}
                        style={styles.button}
                        onPress={() => {
                            navigation.navigate('FinishLogNewDiscomfortScreen', { bodyPart, selectedOptions, motion, followUp, painLevel: index + 1 })
                        }}
                    >
                        <Text style={styles.buttonText}>{index + 1}</Text>
                    </TouchableOpacity>
                ))}
            </View>
            {loading && <ActivityIndicator size="large" />}
        </View>
    );
};

const styles = StyleSheet.create({
    container: {
        flex: 1,
        alignItems: 'center',
        backgroundColor: '#FFFDEE',
        flexDirection: 'column',
        height: '100%',
        justifyContent: 'flex-start',
    },
    top: {
        width: '100%',
        height: '12%',
        justifyContent: 'flex-end',
    },
    backArrow: {
        marginLeft: 18,
        width: '10%',
    }, header: {
        width: '100%',
        height: '13%',
        justifyContent: 'center',
    },
    headerText: {
        fontFamily: 'NohemiRegular',
        fontSize: 35,
        marginLeft: 30,
        marginRight: 30
    },
    body: {
        width: '100%',
        height: '15%',
        justifyContent: 'flex-end',
        width: '100%',
    },
    bodyText: {
        fontFamily: 'ApercuRegular',
        fontSize: 20,
        marginLeft: 30,
        marginRight: 30
    },
    painScaleContainer: {
        width: '100%',
        height: '40%',
        alignItems: 'center',
        flexDirection: 'row',
        justifyContent: 'space-around',
        padding:20
    },
    button: {
        width: 30,
        height: 30,
        marginBottom: 10,
        borderRadius: 3,
        backgroundColor: '#292929',
        alignItems: 'center',
        flexDirection: 'row',
        justifyContent: 'center',
    },
    buttonText: {
        fontFamily: 'ApercuRegular',
        fontSize: 20,
        color: 'white',
        textAlign: 'center',
    },

});

export default WrappingUpNewDiscomfortScreen;

```

### hercules-react/screens/LogExistingDiscomfortScreen.js

```javascript
import React, { useEffect } from 'react';
import { View, Text, StyleSheet, TouchableOpacity, ScrollView } from 'react-native';
import { Audio } from 'expo-av';
import { useNavigation } from '@react-navigation/native';
import { useDiscomforts } from '../contexts/DiscomfortContext';
import moment from 'moment';

const LogExistingDiscomfortScreen = () => {
    const sound = new Audio.Sound();
    const navigation = useNavigation();
    const { discomforts, setDiscomforts } = useDiscomforts();

    useEffect(() => {
        async function loadAndPlay() {
            try {
                await sound.loadAsync(require('../assets/audio/LogExistingDiscomfortScreen.mp3'));
                await sound.playAsync();
            } catch (error) {
                console.log('Error loading and playing sound:', error);
            }
        }

        loadAndPlay();

        return () => {
            async function stopAndUnload() {
                await sound.stopAsync();
                await sound.unloadAsync();
            }

            stopAndUnload();
        };
    }, []);

    return (
        <View style={styles.container}>
            <View style={styles.top}>
            </View>
            <View style={styles.header}>
                <Text style={styles.headerText}>Update an entry.</Text>
            </View>
            <View style={styles.body}>
                <Text style={styles.bodyText}>Select an entry to update.</Text>
            </View>
            <ScrollView contentContainerStyle={styles.scrollContainer}>
                {discomforts.map((discomfort, index) => (
                    <View key={index} style={styles.discomfortItem}>
                        <Text style={styles.title}>{discomfort.bodyPart}</Text>
                        <Text style={styles.date}>{moment(discomfort.date).format('MMMM Do YYYY')}</Text>
                        <TouchableOpacity
                            style={styles.button}
                            onPress={() => navigation.navigate('UpdateDiscomfortScreen', { discomfort })}>
                            <Text style={styles.buttonText}>Update</Text>
                        </TouchableOpacity>
                    </View>
                ))}
            </ScrollView>
        </View>
    );
};

const styles = StyleSheet.create({
    container: {
        flex: 1,
        alignItems: 'center',
        backgroundColor: '#FFFDEE',
        flexDirection: 'column',
        height: '100%',
        justifyContent: 'flex-start',
    },
    top: {
        width: '100%',
        height: '12%',
        justifyContent: 'flex-end',
    },
    backArrow: {
        marginLeft: 18,
        width: '10%',
    }, 
    header: {
        width: '100%',
        height: '13%',
        justifyContent: 'center',
    },
    headerText: {
        fontFamily: 'NohemiRegular',
        fontSize: 35,
        marginLeft: 30,
        marginRight: 30
    },
    body: {
        width: '100%',
        justifyContent: 'flex-end',
        width: '100%',
    },
    bodyText: {
        fontFamily: 'ApercuRegular',
        fontSize: 20,
        marginLeft: 30,
        marginRight: 30
    },
    scrollContainer: {
        flexGrow: 1,
        justifyContent: 'flex-start',
        alignItems: 'center',
        width: '100%', 
        marginTop: 20, // Add margin for spacing
    },
    discomfortItem: {
        marginVertical: 10,
        alignItems: 'center',
        backgroundColor: '#292929', // Match the button color for background
        borderRadius: 15, // Match the border radius
        paddingVertical: 18, // Add padding for spacing
        paddingHorizontal: 60, // Add padding for spacing
        marginBottom: 10, // Adjust the margin for spacing
    },
    title: {
        fontFamily: 'ApercuRegular', // Match the font family
        fontSize: 20, // Adjust the font size
        color: 'white', // Match the text color
    },
    date: {
        fontFamily: 'ApercuRegular', // Match the font family
        fontSize: 14, // Adjust the font size
        color: 'grey', // Keep the color for differentiation
        marginBottom: 10, // Add margin for spacing between text and button
    },
    button: {
        backgroundColor: '#FFFDEE', // Use a contrasting color for the button
        borderRadius: 15, // Match the border radius
        paddingHorizontal: 30, // Add padding
        paddingVertical: 10, // Add padding
    },
    buttonText: {
        fontFamily: 'ApercuRegular', // Match the font family
        fontSize: 18, // Adjust the font size
        color: '#292929', // Contrast color for button text
    },
});

export default LogExistingDiscomfortScreen;

```

### hercules-react/screens/FinishLogNewDiscomfortScreen.js

```javascript
import React, {useState, useEffect} from 'react';
import { View, Text, StyleSheet, Button, TouchableOpacity } from 'react-native';
import { useNavigation } from '@react-navigation/native';
import { useDiscomforts } from '../contexts/DiscomfortContext';
import { Audio } from 'expo-av';
import { Ionicons } from '@expo/vector-icons';

const FinishLogNewDiscomfortScreen = ({ route }) => {
    const { bodyPart, selectedOptions, motion, followUp, painLevel } = route.params;
    const navigation = useNavigation();
    const { discomforts, setDiscomforts } = useDiscomforts();
    const sound = new Audio.Sound();

    useEffect(() => {
        async function loadAndPlay() {
            try {
                await sound.loadAsync(require('../assets/audio/FinishLogNewDiscomfortScreen.mp3'));
                await sound.playAsync();
            } catch (error) {
                console.log('Error loading and playing sound:', error);
            }
        }

        loadAndPlay();

        return () => {
            async function stopAndUnload() {
                await sound.stopAsync();
                await sound.unloadAsync();
            }

            stopAndUnload();
        };
    }, []);

    const finishLogging = () => {
        const newDiscomfort = {
            bodyPart,
            whenPain: selectedOptions.join(', '),
            motion,
            followUp,
            painLevel,
            date: new Date().toISOString(), // Storing the current date
        };

        // Update state with the new discomfort log
        // If passing setDiscomforts through props, ensure it's received in this component
        setDiscomforts((prevDiscomforts) => [...prevDiscomforts, newDiscomfort]);

        navigation.navigate('WoohooScreen');
    };

    return (
        <View style={styles.container}>

            <View style={styles.top}>
            </View>
            <View style={styles.header}>
                <Text style={styles.headerText}>Summary.</Text>
            </View>
            <View style={styles.body}>
                <Text style={styles.bodyText}><Text style={styles.boldBodyText}>Body Part:</Text> {bodyPart}</Text>
                <Text style={styles.bodyText}><Text style={styles.boldBodyText}>When:</Text> {selectedOptions.join(', ')}</Text>
                {motion && (<Text style={styles.bodyText}><Text style={styles.boldBodyText}>Motion:</Text> {motion}</Text>)}
                {followUp && (<Text style={styles.bodyText}><Text style={styles.boldBodyText}>Follow-up:</Text> {followUp}</Text>)}
                <Text style={styles.bodyText}><Text style={styles.boldBodyText}>Pain Level:</Text> {painLevel}</Text>
            </View>
            <View style={styles.buttonContainer}>
                <TouchableOpacity style={styles.button} onPress={finishLogging}>
                    <Text style={styles.buttonText}>Finish Logging</Text>
                </TouchableOpacity>
                <TouchableOpacity style={styles.button} onPress={() => navigation.navigate('DashboardScreen')}>
                    <Text style={styles.buttonText}>Cancel</Text>
                </TouchableOpacity>
            </View>
        </View>
    );
};

const styles = StyleSheet.create({
    container: {
        flex: 1,
        alignItems: 'center',
        backgroundColor: '#FFFDEE',
        flexDirection: 'column',
        height: '100%',
        justifyContent: 'flex-start',
    },
    top: {
        width: '100%',
        height: '12%',
        justifyContent: 'flex-end',
    },
    backArrow: {
        marginLeft: 18,
        width: '10%',
    },
    header: {
        width: '100%',
        height: '13%',
        justifyContent: 'center',
    },
    headerText: {
        fontFamily: 'NohemiRegular',
        fontSize: 35,
        marginLeft: 30,
        marginRight: 30
    },
    boldHeaderText: {
        fontFamily: 'NohemiBold',
        fontSize: 35,
        marginLeft: 30,
        marginRight: 30
    },
    body: {
        width: '100%',
        height: '25%',
        justifyContent: 'flex-end',
        width: '100%',
    },
    bodyText: {
        fontFamily: 'ApercuRegular',
        fontSize: 20,
        marginLeft: 30,
        marginRight: 30
    },
    boldBodyText: {
        fontFamily: 'ApercuBold',
        fontSize: 20,
        marginLeft: 30,
        marginRight: 30
    },
    buttonContainer: {
        width: '100%',
        height: '40%',
        alignItems: 'center',
        marginBottom: 20,
        marginTop: 30,
    },
    button: {
        width: '88%',
        marginBottom: 10,
        backgroundColor: '#292929',
        borderRadius: 15,
        alignItems: 'center',
        flexDirection: 'row',
        justifyContent: 'space-between',
        paddingRight: 30
    },
    buttonText: {
        fontFamily: 'ApercuRegular',
        fontSize: 20,
        color: 'white',
        textAlign: 'center',
        padding: 18,
        paddingLeft: 25
    },
});

export default FinishLogNewDiscomfortScreen;

```

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