Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
# AdvancedSoftwareEngineering-Group3

Download Expo Go on the playstore or appstore
## Run through the Expo

See instructions within `client/README.md` and `server/README.md` for more information on how to run the project.
Download Expo Go on the playstore or appstore then follow the instructions within `client/README.md` and `server/README.md` for more information on how to run the project.

## Package and deployment instructions
- To package the client, it must first be prebuilt using the command `npx expo prebuild`.
- Next, the client can be packaged using the command `eas build -p android --profile preview`.
- This will present a QR code and link, using which you can download and install the app to your device.
- The process is essentially the same for IOS but for it to work, an Apple Developer Account is required. We did not explore this route as it would have been an additional cost. We did however ensure that all functionality was cross platform.

## Notes
- Running the server locally will not be possible as you will not be able to connect to the Database without our input.
- After the demo our servers on Digital Ocean will be taken down as it is a payed service. All functionality will be displayed in the demo.
- An EAS account is required to package the app.
2 changes: 2 additions & 0 deletions client/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ npx expo start

**Here's an example: `EXPO_PUBLIC_API_URL=http://192.168.13.1:8000`**

If you are running the server on the Digital Ocean Droplet, the above instructions stand, but the IP address should be that of the droplet: `134.199.188.243`

## Linting and Formatting Scripts

This project uses ESLint and Prettier to maintain code quality and consistency. Below are the scripts defined in the `package.json` for linting and formatting:
Expand Down
7 changes: 5 additions & 2 deletions client/src/DisplayRoute.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,11 @@

const handleIncidentSubmit = async (incidentData) => {
// Here you would process the incident data
console.log('Incident reported:', incidentData);

Check warning on line 52 in client/src/DisplayRoute.js

View workflow job for this annotation

GitHub Actions / Run Linting (ESLint & Prettier)

Unexpected console statement

// Example: Send to your API
const data = await postIncident(incidentData);
console.log('Response:', data);

Check warning on line 56 in client/src/DisplayRoute.js

View workflow job for this annotation

GitHub Actions / Run Linting (ESLint & Prettier)

Unexpected console statement

// Example: Update local state to show on map
// setMapIncidents(prev => [...prev, incidentData]);
Expand Down Expand Up @@ -100,7 +100,10 @@
}, [currentInstruction]);

function removeHtmlTags(instruction) {
return instruction.replace(/<\/?[^>]+(>|$)/g, '');
if (instruction) {
return instruction.replace(/<\/?[^>]+(>|$)/g, '');
}
return instruction;
}

// Creating a dictionary of step data for each step in the route
Expand Down Expand Up @@ -149,9 +152,9 @@
try {
const baseUrl =
Platform.OS === 'web'
? 'http://localhost:8000'
? 'http://localhost'
: process.env.EXPO_PUBLIC_API_URL;
console.log(`Sending request to ${baseUrl}/check_incidents`);

Check warning on line 157 in client/src/DisplayRoute.js

View workflow job for this annotation

GitHub Actions / Run Linting (ESLint & Prettier)

Unexpected console statement

const response = await fetch(`${baseUrl}/check_incidents`, {
method: 'POST',
Expand All @@ -164,7 +167,7 @@
if (response.ok) {
// Parse the response as JSON
const serverMessage = await response.json();
console.log('Response from Server: ', serverMessage.message);

Check warning on line 170 in client/src/DisplayRoute.js

View workflow job for this annotation

GitHub Actions / Run Linting (ESLint & Prettier)

Unexpected console statement

// process the return incidents object, which contains multiple incidents
setIncidentInfo(serverMessage.message);
Expand All @@ -172,7 +175,7 @@
// Log the raw response text for debugging
const responseText = await response.text();
console.error('Failed to get crowd sourced incidents:', responseText);
alert('Server Error: ', responseText);

Check warning on line 178 in client/src/DisplayRoute.js

View workflow job for this annotation

GitHub Actions / Run Linting (ESLint & Prettier)

Unexpected alert
}
} catch (error) {
console.error('Error getting crowd sourced incidents:', error);
Expand Down
2 changes: 1 addition & 1 deletion client/src/FindRoute.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
if (isCurrentLocation) {
const initialLocation = await getCurrentLocation();
if (!initialLocation) {
alert('Unable to fetch current location.');

Check warning on line 53 in client/src/FindRoute.js

View workflow job for this annotation

GitHub Actions / Run Linting (ESLint & Prettier)

Unexpected alert
return;
}

Expand All @@ -67,9 +67,9 @@

const baseUrl =
Platform.OS === 'web'
? 'http://localhost:8000'
? 'http://localhost'
: process.env.EXPO_PUBLIC_API_URL;
console.log(

Check warning on line 72 in client/src/FindRoute.js

View workflow job for this annotation

GitHub Actions / Run Linting (ESLint & Prettier)

Unexpected console statement
`Sending request to ${baseUrl}/wayfinding/preferences/get_routes`,
);

Expand All @@ -91,7 +91,7 @@
const data = await response.json();

if (data.error) {
alert(`Error finding route: ${response.message || response.status}`);

Check warning on line 94 in client/src/FindRoute.js

View workflow job for this annotation

GitHub Actions / Run Linting (ESLint & Prettier)

Unexpected alert
return;
}
navigation.navigate('SelectRouteScreen', {
Expand Down Expand Up @@ -144,7 +144,7 @@
placeholder="Enter destination point"
value={destination}
onChangeText={setDestinationPoint}
onSubmitEditing={() => alert(`Route Entered`)}

Check warning on line 147 in client/src/FindRoute.js

View workflow job for this annotation

GitHub Actions / Run Linting (ESLint & Prettier)

Unexpected alert
/>

<Text style={findRouteStyles.label}>Mode of Transport:</Text>
Expand Down
60 changes: 30 additions & 30 deletions client/src/FriendsScreen.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,9 @@

const baseUrl =
Platform.OS === 'web'
? 'http://localhost:8000'
? 'http://localhost'
: process.env.EXPO_PUBLIC_API_URL;
console.log(`Sending request to ${baseUrl}/send_request`);

Check warning on line 102 in client/src/FriendsScreen.js

View workflow job for this annotation

GitHub Actions / Run Linting (ESLint & Prettier)

Unexpected console statement

const response = await fetch(`${baseUrl}/send_request`, {
method: 'POST',
Expand Down Expand Up @@ -133,7 +133,7 @@
try {
const baseUrl =
Platform.OS === 'web'
? 'http://localhost:8000'
? 'http://localhost'
: process.env.EXPO_PUBLIC_API_URL;
console.log(
`Sending request to ${baseUrl}/check_requests?sender=${senderName}`,
Expand Down Expand Up @@ -200,7 +200,7 @@

const baseUrl =
Platform.OS === 'web'
? 'http://localhost:8000'
? 'http://localhost'
: process.env.EXPO_PUBLIC_API_URL;
console.log(`Sending request to ${baseUrl}/request_response`);

Expand Down Expand Up @@ -245,7 +245,7 @@

const baseUrl =
Platform.OS === 'web'
? 'http://localhost:8000'
? 'http://localhost'
: process.env.EXPO_PUBLIC_API_URL;
console.log(`Sending request to ${baseUrl}/cancel_friend_request`);

Expand Down Expand Up @@ -287,7 +287,7 @@

const baseUrl =
Platform.OS === 'web'
? 'http://localhost:8000'
? 'http://localhost'
: process.env.EXPO_PUBLIC_API_URL;
console.log(`Sending request to ${baseUrl}/remove_friend`);

Expand Down Expand Up @@ -373,31 +373,31 @@
/>
</View>

{/* Pending Friends List */}
<View style={styles.listContainer}>
<Text style={styles.sectionTitle}>Pending Friend Requests</Text>
<FlatList
data={pendingFriends}
keyExtractor={(item, index) => index.toString()}
renderItem={({ item }) => (
<View style={styles.pendingItem}>
<Text style={styles.friendRequestName}>{item}</Text>
<TouchableOpacity
onPress={() => processFriendRequest(item, true)}
style={styles.acceptButton}
>
<Text style={styles.buttonText}>Accept</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={() => processFriendRequest(item, false)}
style={styles.rejectButton}
>
<Text style={styles.buttonText}>Reject</Text>
</TouchableOpacity>
</View>
)}
/>
</View>
{/* Pending Friends List */}
<View style={styles.listContainer}>
<Text style={styles.sectionTitle}>Pending Friend Requests</Text>
<FlatList
data={pendingFriends}
keyExtractor={(item, index) => index.toString()}
renderItem={({ item }) => (
<View style={styles.pendingItem}>
<Text style={styles.friendRequestName}>{item}</Text>
<TouchableOpacity
onPress={() => processFriendRequest(item, true)}
style={styles.acceptButton}
>
<Text style={styles.buttonText}>Accept</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={() => processFriendRequest(item, false)}
style={styles.rejectButton}
>
<Text style={styles.buttonText}>Reject</Text>
</TouchableOpacity>
</View>
)}
/>
</View>

{/* Current Friends List */}
<View style={styles.listContainer}>
Expand Down
36 changes: 14 additions & 22 deletions client/src/Map.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export default function MapScreen({ navigation }) {
try {
const baseUrl =
Platform.OS === 'web'
? 'http://localhost:8000'
? 'http://localhost'
: process.env.EXPO_PUBLIC_API_URL;
console.log(`Sending request to ${baseUrl}/check_incidents`);

Expand Down Expand Up @@ -83,19 +83,19 @@ export default function MapScreen({ navigation }) {
}
};

const fetchWeather = async () => {
const fetchWeather = async (initialLocation) => {
try {
const baseUrl =
Platform.OS === 'web'
? 'http://localhost:8000'
? 'http://localhost'
: process.env.EXPO_PUBLIC_API_URL;

console.log(
`Sending request to ${baseUrl}/weather?longitude=${location.longitude}&latitude=${location.latitude}`,
`Sending request to ${baseUrl}/weather?longitude=${initialLocation.longitude}&latitude=${initialLocation.latitude}`,
);

const response = await fetch(
`${baseUrl}/weather?longitude=${location.longitude}&latitude=${location.latitude}`,
`${baseUrl}/weather?longitude=${initialLocation.longitude}&latitude=${initialLocation.latitude}`,
{
method: 'GET',
headers: {
Expand Down Expand Up @@ -123,19 +123,19 @@ export default function MapScreen({ navigation }) {
}
};

const fetchBikeApi = async () => {
const fetchBikeApi = async (initialLocation) => {
try {
const baseUrl =
Platform.OS === 'web'
? 'http://localhost:8000'
? 'http://localhost'
: process.env.EXPO_PUBLIC_API_URL;

console.log(
`Sending request to ${baseUrl}/BikeStand?longitude=${location.longitude}&latitude=${location.latitude}`,
`Sending request to ${baseUrl}/BikeStand?longitude=${initialLocation.longitude}&latitude=${initialLocation.latitude}`,
);

const response = await fetch(
`${baseUrl}/BikeStand?longitude=${location.longitude}&latitude=${location.latitude}`,
`${baseUrl}/BikeStand?longitude=${initialLocation.longitude}&latitude=${initialLocation.latitude}`,
{
method: 'GET',
headers: {
Expand Down Expand Up @@ -169,6 +169,11 @@ export default function MapScreen({ navigation }) {
try {
const initialLocation = await getCurrentLocation();
setLocation(initialLocation);
// weatherBikePollIncident(initialLocation);

fetchWeather(initialLocation);
fetchBikeApi(initialLocation);
pollIncident();

const locationSubscription = await startLocationTracking(setLocation);

Expand All @@ -181,19 +186,6 @@ export default function MapScreen({ navigation }) {
fetchLocation();
}, []);

useEffect(() => {
const pollOnce = async () => {
if (location) {
fetchWeather();
fetchBikeApi();
pollIncident();
}
};

pollOnce();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [location]); // Empty dependency array ensures this runs only once when the component mounts

const renderContent = () => {
if (errorMessage) {
return <Text style={MapStyles.error}>{errorMessage}</Text>;
Expand Down
2 changes: 1 addition & 1 deletion client/src/Preferences.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export default function PreferencesScreen({ navigation }) {
try {
const baseUrl =
Platform.OS === 'web'
? 'http://localhost:8000'
? 'http://localhost'
: process.env.EXPO_PUBLIC_API_URL;
console.log(`Sending request to ${baseUrl}/setPreferences`);

Expand Down
96 changes: 87 additions & 9 deletions client/src/SelectRoute.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useEffect, useState } from 'react';
import { View, Text, TouchableOpacity } from 'react-native';
import { View, Text, TouchableOpacity, Platform } from 'react-native';
import MapView, { Polyline, Marker } from 'react-native-maps';
import {
decodeRoute,
Expand All @@ -8,6 +8,7 @@ import {
} from './utils/mapUtils';
import locationCircleIcon from './assets/location-circle.png';
import selectRouteStyles from './components/styles/SelectRoute.styles';
import { retrieveData } from './caching';

export default function SelectRouteScreen({ navigation, route }) {
const { origin, destination, routeData } = route.params;
Expand All @@ -16,6 +17,7 @@ export default function SelectRouteScreen({ navigation, route }) {
const [errorMessage, setErrorMessage] = useState('');
const [polylineCoordinates, setPolylineCoordinates] = useState([]);
const [currentRoute, setCurrentRoute] = useState(routeData.routes[0]);
const [scores, setScores] = useState([]);

// Get current location
useEffect(() => {
Expand Down Expand Up @@ -54,6 +56,86 @@ export default function SelectRouteScreen({ navigation, route }) {
setPolylineCoordinates(decodedPath);
};

const sendSustainabilityScore = async (
startRouteFlag,
sustainabilityScore,
// eslint-disable-next-line consistent-return
) => {
try {
const baseUrl =
Platform.OS === 'web'
? 'http://localhost'
: process.env.EXPO_PUBLIC_API_URL;
console.log(`Sending request to ${baseUrl}/update_sus_stats`);

const response = await fetch(`${baseUrl}/update_sus_stats`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
username: await retrieveData('username'),
flag: startRouteFlag,
modeDistances: sustainabilityScore,
}),
});

const data = await response.json();
console.log('response (transport score): ', data);
return data;
} catch (error) {
console.log('Error sending sustainability scores and distances:', error);
}
};

const getSustainabilityScore = async (index, startRouteFlag) => {
const sustainabilityScore = {
Bus: 0,
Train: 0,
WALKING: 0,
Tram: 0,
DRIVING: 0,
BICYCLING: 0,
};
routeData.routes[index].legs[0].steps.forEach((step) => {
let key = '';
if (step.travel_mode === 'TRANSIT') {
// eslint-disable-next-line prefer-destructuring
key = step.html_instructions.trim().split(' ')[0];
console.log('key: ', key);
} else {
key = step.travel_mode;
}
sustainabilityScore[key] += step.distance.value / 1000;
});
console.log('sustainability score: ', sustainabilityScore);
const transportScore = await sendSustainabilityScore(
startRouteFlag,
sustainabilityScore,
);
return transportScore;
};

useEffect(() => {
for (let i = 0; i < routeData.routes.length; i += 1) {
setScores((prevScores) => [
...prevScores,
getSustainabilityScore(i, false),
]);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

const startJourney = (index, routeOption) => {
getSustainabilityScore(index, true);
navigation.navigate('DisplayRouteScreen', {
origin,
destination,
routeData: routeOption,
polylineCoordinates,
});
};

return (
<View style={selectRouteStyles.container}>
{errorMessage && (
Expand Down Expand Up @@ -110,17 +192,13 @@ export default function SelectRouteScreen({ navigation, route }) {
<Text>Route {index + 1}</Text>
<Text>Distance: {routeOption.legs[0].distance.text}</Text>
<Text>Duration: {routeOption.legs[0].duration.text}</Text>
<Text>Sustainability score: {scores[index]}</Text>
</TouchableOpacity>
{/* routeData needs to rename the route variable because that is what react navigator calls its properties */}
<TouchableOpacity
onPress={() =>
navigation.navigate('DisplayRouteScreen', {
origin,
destination,
routeData: routeOption,
polylineCoordinates,
})
}
onPress={() => {
startJourney(index, routeOption);
}}
style={selectRouteStyles.startButton}
>
<Text>Start Journey</Text>
Expand Down
Loading