From 471d6425d2a28914c0e13e14cd9325d1f91a7e38 Mon Sep 17 00:00:00 2001 From: Yazan Armoush Date: Sat, 18 Nov 2023 21:52:53 -0500 Subject: [PATCH 01/16] Fixed Linting Errors --- backend/routes/carbon_auth.py | 3 +++ backend/routes/transportation.py | 16 ++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/backend/routes/carbon_auth.py b/backend/routes/carbon_auth.py index 98d282d..8a074c6 100644 --- a/backend/routes/carbon_auth.py +++ b/backend/routes/carbon_auth.py @@ -6,6 +6,9 @@ @auth.verify_token def verify_token(token: str) -> bool: + dev = True + if dev: + return dev d = FirebaseAPI.verify_google_token(token) print(f"USER AUTHENTICATED: {bool(d)}") return bool(d) diff --git a/backend/routes/transportation.py b/backend/routes/transportation.py index cb1ef66..dd58f82 100644 --- a/backend/routes/transportation.py +++ b/backend/routes/transportation.py @@ -20,6 +20,22 @@ def get_transportation(oid: str) -> Response: return jsonify({'transportation': item}) +@transportation_service.route("/get_transportations_entries_for_user_using_data_range/", methods=['POST']) +@carbon_auth.auth.login_required +def get_transportations_entries_for_user_using_date_range(user_id: str) -> Response[list[TransportationEntry]]: + data = request.get_json() + start = data.get('start') + end = data.get('end') + # Validate that both start and end dates are provided + if not start or not end: + return jsonify({'error': 'Both start and end dates are required'}) + + query = {"user_id": ObjectId(user_id), "date": {"$gte": start, "$lte": end}} + items = list(CarbonTrackDB.transportation_coll.find(query)) + items = [TransportationEntry.from_json(item).to_json() for item in items] + return jsonify({'transportations': items}) + + @transportation_service.route("/get_transportation_metric_for_today/", methods=['GET']) @carbon_auth.auth.login_required def get_transportation_metric_for_today(user_id: str) -> Response: From 03970aa242fb34dd42f89ea3790a1077de5d67ce Mon Sep 17 00:00:00 2001 From: Yazan Armoush Date: Sat, 18 Nov 2023 21:53:50 -0500 Subject: [PATCH 02/16] Added Transportation API in frontend --- frontend/src/APIs/TransportationAPI.ts | 14 ++++++++++++++ frontend/src/components/types.tsx | 9 +++++---- frontend/src/screens/dashboard.tsx | 23 ++++++++++------------- frontend/src/widgets/profileWidget.tsx | 10 ++++++---- 4 files changed, 35 insertions(+), 21 deletions(-) diff --git a/frontend/src/APIs/TransportationAPI.ts b/frontend/src/APIs/TransportationAPI.ts index be1108a..fe8eb47 100644 --- a/frontend/src/APIs/TransportationAPI.ts +++ b/frontend/src/APIs/TransportationAPI.ts @@ -15,6 +15,20 @@ export const getTransportation = async (transportationID: ObjectID): Promise => { + try { + const res = await FLASK_HTTPS.post(routeName + '/get_transportations_entries_for_user_using_data_range/' + userID.str, { + start, + end + }); + return res.data.transportation as Transportation[]; + } catch (error) { + console.error('Error fetching transportation from Flask BE: ', error); + console.error('Temp tip: have you started the backend?: '); + return undefined; + } +}; + export const getTransportationMetricForToday = async (userID: ObjectID): Promise => { try { const res = await FLASK_HTTPS.get(routeName + '/get_transportation_metric_for_today/' + userID.str); diff --git a/frontend/src/components/types.tsx b/frontend/src/components/types.tsx index 31a053d..de83e96 100644 --- a/frontend/src/components/types.tsx +++ b/frontend/src/components/types.tsx @@ -1,3 +1,5 @@ +import { type User } from "../models/User"; + export interface RootStackParamList { Home: undefined; LogIn: undefined; @@ -23,8 +25,7 @@ export interface WidgetBoxProps { } export interface profileWidgetBoxProps { - pplavatar: string; - name: string; - rank: string; - level: number; + photoURL: string + user: User + rank: number } diff --git a/frontend/src/screens/dashboard.tsx b/frontend/src/screens/dashboard.tsx index 2cd7635..88b6c12 100644 --- a/frontend/src/screens/dashboard.tsx +++ b/frontend/src/screens/dashboard.tsx @@ -1,22 +1,20 @@ -import { View, Text, StyleSheet, TouchableOpacity } from 'react-native'; import React, { useEffect, useState } from 'react'; +import { View, Text, StyleSheet, TouchableOpacity } from 'react-native'; import { useFonts } from 'expo-font'; import Colors from '../../assets/colorConstants'; -import { getUserLevel, type User } from '../models/User'; import { GetLoggedInUser } from '../APIs/UsersAPI'; import ProfileWidgetBox from '../widgets/profileWidget'; import WidgetBox from '../widgets/widgetBox'; import type { RootStackParamList } from '../components/types'; import type { StackNavigationProp } from '@react-navigation/stack'; import { useNavigation } from '@react-navigation/native'; - +import { type User } from '../models/User'; export type StackNavigation = StackNavigationProp; export default function DashBoardScreen(): JSX.Element { const [user, setUser] = useState(undefined); - const [photoURL] = useState( - 'https://upload.wikimedia.org/wikipedia/commons/thumb/2/2c/Default_pfp.svg/2048px-Default_pfp.svg.png' - ); + const [photoURL] = useState("https://upload.wikimedia.org/wikipedia/commons/thumb/2/2c/Default_pfp.svg/2048px-Default_pfp.svg.png"); + const navigation = useNavigation(); const [loaded] = useFonts({ @@ -46,16 +44,15 @@ export default function DashBoardScreen(): JSX.Element { - - + + + { diff --git a/frontend/src/widgets/profileWidget.tsx b/frontend/src/widgets/profileWidget.tsx index 262c330..bc93dd6 100644 --- a/frontend/src/widgets/profileWidget.tsx +++ b/frontend/src/widgets/profileWidget.tsx @@ -3,8 +3,9 @@ import { View, Text, Image, StyleSheet } from 'react-native'; import Colors from '../../assets/colorConstants'; import { type profileWidgetBoxProps } from '../components/types'; import { useFonts } from 'expo-font'; +import { getUserLevel } from '../models/User'; -const ProfileWidgetBox: React.FC = ({ pplavatar, name, level, rank }) => { +const ProfileWidgetBox: React.FC = ({ photoURL, user, rank }) => { const [loaded] = useFonts({ Montserrat: require('../../assets/fonts/MontserratThinRegular.ttf'), Josefin: require('../../assets/fonts/JosefinSansThinRegular.ttf'), @@ -16,10 +17,11 @@ const ProfileWidgetBox: React.FC = ({ pplavatar, name, le return ( - + - {name} - Level: {level} + {user.full_name} + Email: {user.email} + Level: {getUserLevel(user)} {rank} From ddac02fc6e6ad2e5f55e41fd363f41347f579cd7 Mon Sep 17 00:00:00 2001 From: Yazan Armoush Date: Wed, 22 Nov 2023 14:20:17 -0500 Subject: [PATCH 03/16] Added User Methods --- frontend/src/APIs/FLASK_API.tsx | 2 +- frontend/src/APIs/UsersAPI.ts | 109 ++++++++++++++++---------------- 2 files changed, 57 insertions(+), 54 deletions(-) diff --git a/frontend/src/APIs/FLASK_API.tsx b/frontend/src/APIs/FLASK_API.tsx index 982fa15..af99b25 100644 --- a/frontend/src/APIs/FLASK_API.tsx +++ b/frontend/src/APIs/FLASK_API.tsx @@ -1,6 +1,6 @@ import axios from "axios"; import firebaseService from "../utilities/firebase"; -const FLASK_LOCAL_ADDRESS: string = "http://10.0.15.206:6050"; +const FLASK_LOCAL_ADDRESS: string = "http://172.20.10.3:6050"; // Function to get the Firebase authentication token const getFirebaseAuthToken = async (): Promise => { diff --git a/frontend/src/APIs/UsersAPI.ts b/frontend/src/APIs/UsersAPI.ts index 7921cfb..4b159db 100644 --- a/frontend/src/APIs/UsersAPI.ts +++ b/frontend/src/APIs/UsersAPI.ts @@ -5,59 +5,62 @@ import firebaseService from '../utilities/firebase'; const routeName = '/users'; -export const GetLoggedInUser = async (): Promise => { - const firebaseUser = await firebaseService.getFirebaseUser(); - if (firebaseUser != null) { - if (firebaseUser.email != null) { - return await getUserByEmail(firebaseUser.email); - } - } -}; - -export const getUser = async (userID: ObjectID): Promise => { - try { - const res = await FLASK_HTTPS.get(routeName + '/user/' + userID.str); - return res.data.user as User; - } catch (error) { - console.error('Error fetching user from Flask BE: ', error); - console.error('Temp tip: have you started the backend?: '); - return undefined; - } -}; - -export const getUserByEmail = async (email: string): Promise => { - try { - const res = await FLASK_HTTPS.get(routeName + '/user_email/' + email); - return res.data.user as User; - } catch (error) { - console.error('Error fetching user from Flask BE: ', error); - console.error('Temp tip: have you started the backend?: '); - return undefined; - } -}; +export const UsersAPI = { -export const createUser = async (user: User): Promise => { - try { - const res = await FLASK_HTTPS.put(routeName + '/user', { - user, - }); - return res.data.user as User; - } catch (error) { - console.error('Error creating user in Flask BE: ', error); - console.error('Temp tip: have you started the backend?: '); - return undefined; - } -}; + GetLoggedInUser: async () => { + const firebaseUser = await firebaseService.getFirebaseUser(); + if (firebaseUser != null) { + if (firebaseUser.email != null) { + return await UsersAPI.getUserByEmail(firebaseUser.email); + } + } + }, -export const updateUser = async (user: User): Promise => { - try { - const res = await FLASK_HTTPS.patch(routeName + '/user/' + user._id.str, { - user, - }); - return res.data.user as User; - } catch (error) { - console.error('Error fetching user from Flask BE: ', error); - console.error('Temp tip: have you started the backend?: '); - return undefined; + getUser: async (userID: ObjectID)=> { + try { + const res = await FLASK_HTTPS.get(routeName + '/user/' + userID.str); + return res.data.user as User; + } catch (error) { + console.error('Error fetching user from Flask BE: ', error); + console.error('Temp tip: have you started the backend?: '); + return undefined; + } + }, + + getUserByEmail: async (email: string)=> { + try { + const res = await FLASK_HTTPS.get(routeName + '/user_email/' + email); + return res.data.user as User; + } catch (error) { + console.error('Error fetching user from Flask BE: ', error); + console.error('Temp tip: have you started the backend?: '); + return undefined; + } + }, + + createUser: async (user: User)=> { + try { + const res = await FLASK_HTTPS.put(routeName + '/user', { + user, + }); + return res.data.user as User; + } catch (error) { + console.error('Error creating user in Flask BE: ', error); + console.error('Temp tip: have you started the backend?: '); + return undefined; + } + }, + + updateUser: async (user: User)=> { + try { + const res = await FLASK_HTTPS.patch(routeName + '/user/' + user._id.str, { + user, + }); + return res.data.user as User; + } catch (error) { + console.error('Error fetching user from Flask BE: ', error); + console.error('Temp tip: have you started the backend?: '); + return undefined; + } } -}; +} From 0b32ab7f9501aff3138e2abaf32d4d4a3460442c Mon Sep 17 00:00:00 2001 From: Yazan Armoush Date: Wed, 22 Nov 2023 14:20:46 -0500 Subject: [PATCH 04/16] Updated Dashboard and Signup --- frontend/src/screens/dashboard.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/screens/dashboard.tsx b/frontend/src/screens/dashboard.tsx index 88b6c12..c5e2745 100644 --- a/frontend/src/screens/dashboard.tsx +++ b/frontend/src/screens/dashboard.tsx @@ -2,13 +2,13 @@ import React, { useEffect, useState } from 'react'; import { View, Text, StyleSheet, TouchableOpacity } from 'react-native'; import { useFonts } from 'expo-font'; import Colors from '../../assets/colorConstants'; -import { GetLoggedInUser } from '../APIs/UsersAPI'; import ProfileWidgetBox from '../widgets/profileWidget'; import WidgetBox from '../widgets/widgetBox'; import type { RootStackParamList } from '../components/types'; import type { StackNavigationProp } from '@react-navigation/stack'; import { useNavigation } from '@react-navigation/native'; import { type User } from '../models/User'; +import { UsersAPI } from '../APIs/UsersAPI'; export type StackNavigation = StackNavigationProp; export default function DashBoardScreen(): JSX.Element { @@ -23,7 +23,7 @@ export default function DashBoardScreen(): JSX.Element { }); useEffect(() => { - void GetLoggedInUser().then((res) => { + void UsersAPI.GetLoggedInUser().then((res) => { if (res != null) { setUser(res); } From 554fe6dd82e7376d85adb3d619fa5d8d136e8322 Mon Sep 17 00:00:00 2001 From: Yazan Armoush Date: Wed, 22 Nov 2023 14:21:01 -0500 Subject: [PATCH 05/16] Updated Authentication --- backend/routes/carbon_auth.py | 3 --- backend/utils/FirebaseAPI.py | 15 +++++++++++++-- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/backend/routes/carbon_auth.py b/backend/routes/carbon_auth.py index 8a074c6..98d282d 100644 --- a/backend/routes/carbon_auth.py +++ b/backend/routes/carbon_auth.py @@ -6,9 +6,6 @@ @auth.verify_token def verify_token(token: str) -> bool: - dev = True - if dev: - return dev d = FirebaseAPI.verify_google_token(token) print(f"USER AUTHENTICATED: {bool(d)}") return bool(d) diff --git a/backend/utils/FirebaseAPI.py b/backend/utils/FirebaseAPI.py index 4b12c32..0f9ff85 100644 --- a/backend/utils/FirebaseAPI.py +++ b/backend/utils/FirebaseAPI.py @@ -1,8 +1,10 @@ from typing import Optional from pprint import pprint +import flask import firebase_admin from firebase_admin import credentials, auth - +from models.user import User +from mongodb_api.carbon_track_db import CarbonTrackDB cred = credentials.Certificate("firebase.json") APP = firebase_admin.initialize_app(cred) @@ -15,9 +17,18 @@ def verify_google_token(id_token: str) -> Optional[dict]: id_token=id_token, check_revoked=True ) + @staticmethod + def get_user(id_token: str) -> Optional[User]: + user = auth.verify_id_token(id_token=id_token, check_revoked=True) + if user: + query = {"email": user.get('email')} + item = CarbonTrackDB.users_coll.find_one(query) + item = User.from_json(item) + return item + if __name__ == "__main__": d = FirebaseAPI.verify_google_token( - "eyJhbGciOiJSUzI1NiIsImtpZCI6ImE2YzYzNTNmMmEzZWMxMjg2NTA1MzBkMTVmNmM0Y2Y0NTcxYTQ1NTciLCJ0eXAiOiJKV1QifQ.eyJuYW1lIjoiWWF6YW4gQXJtb3VzaCIsInBpY3R1cmUiOiJodHRwczovL2xoMy5nb29nbGV1c2VyY29udGVudC5jb20vYS9BQ2c4b2NKMThLbUhSTDY3d2NHNDZLNDhaVF9RN0RVbExyN25rOHhJQ2s1S0VNZExVRW9xPXM5Ni1jIiwiaXNzIjoiaHR0cHM6Ly9zZWN1cmV0b2tlbi5nb29nbGUuY29tL2NhcmJvbi10cmFjay1zeXN0ZW0iLCJhdWQiOiJjYXJib24tdHJhY2stc3lzdGVtIiwiYXV0aF90aW1lIjoxNzAwMjI5MTE0LCJ1c2VyX2lkIjoiUkZSbmdJVm1Bd1BHY0pxVFVPalZOeFB4SzVzMSIsInN1YiI6IlJGUm5nSVZtQXdQR2NKcVRVT2pWTnhQeEs1czEiLCJpYXQiOjE3MDAyMjkxMTQsImV4cCI6MTcwMDIzMjcxNCwiZW1haWwiOiJ5YXphbi5hcm1vdXNoMUBnbWFpbC5jb20iLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiZmlyZWJhc2UiOnsiaWRlbnRpdGllcyI6eyJnb29nbGUuY29tIjpbIjExNzEyOTg2MDYwMzg2MDUxODQwOCJdLCJlbWFpbCI6WyJ5YXphbi5hcm1vdXNoMUBnbWFpbC5jb20iXX0sInNpZ25faW5fcHJvdmlkZXIiOiJnb29nbGUuY29tIn19.qx9G7SENxVNJG6aVncn4SjmOyyD-loFScBNKl1ndJyAX_n-851D1LC8ctw1ljnhey2TYh9Zp-yFOg0AvL4vkN0XjbzP2qJw7OLB-WPLLKjkjWK3XEGGiXirbCK29dGveQTGXz6UJ8hTMmU26ojnWKfg6ezFqI5SnQYweHP7vtZBbciKgKUSE-pHwZkhBkYCOVtS0aSmGsQpOmxliWRWxuyqkpCqUk_J2pC2p-LHXqthKGIvGvK_akBSjtZvvl-5Db96HMeMr3GF00I4NevOcVs1vkLkCvib9OshlYNPrBtjdsc9loCQe9iRAflQP6Wpm-B_NtW6x0fsTLQs_JyEmNQ" + "eyJhbGciOiJSUzI1NiIsImtpZCI6ImE2YzYzNTNmMmEzZWMxMjg2NTA1MzBkMTVmNmM0Y2Y0NTcxYTQ1NTciLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vY2FyYm9uLXRyYWNrLXN5c3RlbSIsImF1ZCI6ImNhcmJvbi10cmFjay1zeXN0ZW0iLCJhdXRoX3RpbWUiOjE3MDA2NzU5NzksInVzZXJfaWQiOiJ3UFVEaEFiM0Q5ZHpoeDJXa1RBZlBLbnhGSG0xIiwic3ViIjoid1BVRGhBYjNEOWR6aHgyV2tUQWZQS254RkhtMSIsImlhdCI6MTcwMDY3NTk3OSwiZXhwIjoxNzAwNjc5NTc5LCJlbWFpbCI6InlhemFuQGdtYWlsLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwiZmlyZWJhc2UiOnsiaWRlbnRpdGllcyI6eyJlbWFpbCI6WyJ5YXphbkBnbWFpbC5jb20iXX0sInNpZ25faW5fcHJvdmlkZXIiOiJwYXNzd29yZCJ9fQ.MRTtHwIK-sShrOUSOTq0yJvjj2svkH3X5T2Zb2hP-KzUYxXLWTRDVUtbBxCJMghqCccTm0jYVNEEd-9i4KteCbJmSLDNbqXR0qO2797fsyo0LU9YpjRyk6-NV6CpHSSMIXeuOcdgXCo65yh_aVlS-tZJg5QU5Av0RtCxHG2szIAz-gqQMuwMxi5iHSCRns67NrxrTdO-BT_lazZo5L3huiyDnE-AGGgprQQcL1I4DMRYRwkEwDLrXoM6GNIylIur0rlVWQxUlOQvUEDhOW8YenpwPm0xuVe78yWxPMFj4aMwgoe6DotMW-HfGv5jSz6baZSHg0yfsEwbtH-iUL88uQ" ) pprint(d) From 73fbc7aae540cb62a1d7f31ca9a7cbc43ae5c137 Mon Sep 17 00:00:00 2001 From: Yazan Armoush Date: Wed, 22 Nov 2023 14:22:14 -0500 Subject: [PATCH 06/16] Updated Authentication --- frontend/src/APIs/FLASK_API.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/APIs/FLASK_API.tsx b/frontend/src/APIs/FLASK_API.tsx index af99b25..320309f 100644 --- a/frontend/src/APIs/FLASK_API.tsx +++ b/frontend/src/APIs/FLASK_API.tsx @@ -1,6 +1,6 @@ import axios from "axios"; import firebaseService from "../utilities/firebase"; -const FLASK_LOCAL_ADDRESS: string = "http://172.20.10.3:6050"; +const FLASK_LOCAL_ADDRESS: string = "http://10.0.15.205:6050"; // Function to get the Firebase authentication token const getFirebaseAuthToken = async (): Promise => { From 14325d4517ae784692a23902fe05cd667a441ff0 Mon Sep 17 00:00:00 2001 From: Yazan Armoush Date: Wed, 22 Nov 2023 14:22:27 -0500 Subject: [PATCH 07/16] Added Metrics Hisotry --- backend/models/transportation.py | 50 +++++++++- backend/routes/transportation.py | 30 +++--- backend/utils/metric_resets.py | 7 -- frontend/src/APIs/TransportationAPI.ts | 98 ++++++++++--------- frontend/src/models/Transportation.ts | 11 +++ .../src/screens/transportationHistory.tsx | 28 ++++-- 6 files changed, 148 insertions(+), 76 deletions(-) diff --git a/backend/models/transportation.py b/backend/models/transportation.py index a8097e1..2d72b24 100644 --- a/backend/models/transportation.py +++ b/backend/models/transportation.py @@ -3,8 +3,9 @@ """ from __future__ import annotations +from typing import Union import json -from datetime import datetime +from datetime import datetime, timezone, timedelta from models.abstract_db_model import DB_MODEL from bson import ObjectId from bson import json_util @@ -22,7 +23,7 @@ class TransportationEntry(DB_MODEL): date: datetime def __init__(self, oid: ObjectId, user_id: ObjectId, bus: int, train: int, motorbike: int, - electric_car: int, gasoline_car: int, carbon_emissions: float, date: datetime) -> None: + electric_car: int, gasoline_car: int, carbon_emissions: float, date: Union[str, datetime]) -> None: super().__init__(oid) self.user_id = ObjectId(user_id) self.bus = bus @@ -31,7 +32,10 @@ def __init__(self, oid: ObjectId, user_id: ObjectId, bus: int, train: int, motor self.electric_car = electric_car self.gasoline_car = gasoline_car self.carbon_emissions = carbon_emissions - self.date = date + if isinstance(date, datetime): + self.date = date + else: + self.date = datetime.fromisoformat(date) def to_json(self, for_mongodb: bool = False) -> json: res = { @@ -72,5 +76,45 @@ def calculate_carbon_emissions(self) -> float: return sum([bus_carbon_emissions, train_carbon_emissions, motorbike_carbon_emissions, electric_car_carbon_emissions, gasoline_car_carbon_emissions]) + @staticmethod + def get_monthly_view(start: datetime, end: datetime, + transportationEntries: list[TransportationEntry]) -> list[dict[str, Union[list[float], str]]]: + monthly_data = [] + + # Make start date offset-aware (assuming UTC for simplicity) + start = start.replace(tzinfo=timezone.utc) + + current_month = start + while current_month <= end: + # Add the current month to the list + monthly_data.append({ + 'month': current_month.strftime('%B'), + 'year': current_month.strftime('%Y'), + 'data': [0, 0, 0, 0] + }) + + # Move to the next month + if current_month.month == 12: + current_month = datetime(current_month.year + 1, 1, 1, tzinfo=timezone.utc) + else: + current_month = datetime(current_month.year, current_month.month + 1, 1, tzinfo=timezone.utc) + + for transportation_entry in transportationEntries: + for monthly_entry in monthly_data: + if transportation_entry.date.strftime('%B') == monthly_entry['month'] \ + and transportation_entry.date.strftime('%Y') == monthly_entry['year']: + if transportation_entry.date.day < 7: + monthly_entry['data'][0] = transportation_entry.calculate_carbon_emissions() + elif transportation_entry.date.day < 14: + monthly_entry['data'][1] = transportation_entry.calculate_carbon_emissions() + elif transportation_entry.date.day < 21: + monthly_entry['data'][2] = transportation_entry.calculate_carbon_emissions() + elif transportation_entry.date.day < 28: + monthly_entry['data'][3] += transportation_entry.calculate_carbon_emissions() + else: # If a Month has 5 sunday, we add them to the fourth week + monthly_entry['data'][3] += transportation_entry.calculate_carbon_emissions() + + return monthly_data + def __repr__(self) -> str: return f'Transportation ID: {self.oid.__str__()}' diff --git a/backend/routes/transportation.py b/backend/routes/transportation.py index dd58f82..0824e0d 100644 --- a/backend/routes/transportation.py +++ b/backend/routes/transportation.py @@ -2,11 +2,13 @@ from datetime import datetime from bson import ObjectId from flask import Blueprint, Response, jsonify, request +import flask from models.transportation import TransportationEntry from mongodb_api.carbon_track_db import CarbonTrackDB from routes import carbon_auth -from utils.metric_resets import weekly_metric_reset, get_1_day_range +from utils.metric_resets import weekly_metric_reset +from utils.FirebaseAPI import FirebaseAPI transportation_service = Blueprint('/transportation', __name__) @@ -20,27 +22,31 @@ def get_transportation(oid: str) -> Response: return jsonify({'transportation': item}) -@transportation_service.route("/get_transportations_entries_for_user_using_data_range/", methods=['POST']) +@transportation_service.route("/get_transportations_entries_for_user_using_data_range", methods=['POST']) @carbon_auth.auth.login_required -def get_transportations_entries_for_user_using_date_range(user_id: str) -> Response[list[TransportationEntry]]: +def get_transportations_entries_for_user_using_date_range() -> Response: + user = FirebaseAPI.get_user(flask.request.headers.get('Authorization').split()[1]) data = request.get_json() - start = data.get('start') - end = data.get('end') + start = datetime.fromisoformat(data.get('start')) + end = datetime.fromisoformat(data.get('end')) # Validate that both start and end dates are provided if not start or not end: return jsonify({'error': 'Both start and end dates are required'}) - query = {"user_id": ObjectId(user_id), "date": {"$gte": start, "$lte": end}} + query = {"user_id": ObjectId(user.oid), "date": {"$gte": start, "$lte": end}} items = list(CarbonTrackDB.transportation_coll.find(query)) - items = [TransportationEntry.from_json(item).to_json() for item in items] - return jsonify({'transportations': items}) + transportation_items: list[TransportationEntry] = [TransportationEntry.from_json(item) for item in items] + json_items = [item.to_json() for item in transportation_items] + return jsonify({ + 'transportationEntries': json_items, + 'monthlyData': TransportationEntry.get_monthly_view(start, end, transportation_items) + }) @transportation_service.route("/get_transportation_metric_for_today/", methods=['GET']) @carbon_auth.auth.login_required def get_transportation_metric_for_today(user_id: str) -> Response: - start_of_day, end_of_day = get_1_day_range(datetime.now()) - query = {"user_id": ObjectId(user_id), "date": {"$gte": start_of_day, "$lte": end_of_day}} + query = {"user_id": ObjectId(user_id), "date": weekly_metric_reset(datetime.now())} item = CarbonTrackDB.transportation_coll.find_one(query) if item is None: create_transportation(ObjectId(user_id)) @@ -64,7 +70,9 @@ def create_transportation(user_id: ObjectId) -> Response: @carbon_auth.auth.login_required def update_transportation(oid: str) -> Response: query = {"_id": ObjectId(oid)} - transportation = TransportationEntry.from_json(request.get_json()['transportation']).to_json(for_mongodb=True) + transportation: dict = TransportationEntry.from_json(request.get_json()['transportation']).to_json(for_mongodb=True) + del transportation['date'] + del transportation['user_id'] CarbonTrackDB.transportation_coll.update_one(query, {'$set': transportation}) item = CarbonTrackDB.transportation_coll.find_one(query) item = TransportationEntry.from_json(item).to_json() diff --git a/backend/utils/metric_resets.py b/backend/utils/metric_resets.py index cb9bbfa..6598208 100644 --- a/backend/utils/metric_resets.py +++ b/backend/utils/metric_resets.py @@ -11,10 +11,3 @@ def weekly_metric_reset(d: datetime) -> datetime: previous_sunday = d - timedelta(days=days_until_sunday) date = datetime(year=previous_sunday.year, month=previous_sunday.month, day=previous_sunday.day) return date - - -def get_1_day_range(d: datetime) -> tuple[datetime, datetime]: - d = weekly_metric_reset(d) - end = datetime(year=d.year, month=d.month, day=d.day) + timedelta(days=1) - start = datetime(year=d.year, month=d.month, day=d.day) - timedelta(days=1) - return start, end diff --git a/frontend/src/APIs/TransportationAPI.ts b/frontend/src/APIs/TransportationAPI.ts index fe8eb47..5d49d59 100644 --- a/frontend/src/APIs/TransportationAPI.ts +++ b/frontend/src/APIs/TransportationAPI.ts @@ -1,54 +1,58 @@ import FLASK_HTTPS from './FLASK_API'; import type ObjectID from 'bson-objectid'; -import type { Transportation } from '../models/Transportation'; +import type { Transportation, TransportationRes } from '../models/Transportation'; const routeName = '/transportation'; -export const getTransportation = async (transportationID: ObjectID): Promise => { - try { - const res = await FLASK_HTTPS.get(routeName + '/transportation/' + transportationID.str); - return res.data.transportation as Transportation; - } catch (error) { - console.error('Error fetching transportation from Flask BE: ', error); - console.error('Temp tip: have you started the backend?: '); - return undefined; - } -}; - -export const getTransportationsEntriesForUserUsingDataRange = async (userID: ObjectID, start: Date, end: Date): Promise => { - try { - const res = await FLASK_HTTPS.post(routeName + '/get_transportations_entries_for_user_using_data_range/' + userID.str, { - start, - end - }); - return res.data.transportation as Transportation[]; - } catch (error) { - console.error('Error fetching transportation from Flask BE: ', error); - console.error('Temp tip: have you started the backend?: '); - return undefined; - } -}; - -export const getTransportationMetricForToday = async (userID: ObjectID): Promise => { - try { - const res = await FLASK_HTTPS.get(routeName + '/get_transportation_metric_for_today/' + userID.str); - return res.data.transportation as Transportation; - } catch (error) { - console.error('Error fetching transportation from Flask BE: ', error); - console.error('Temp tip: have you started the backend?: '); - return undefined; - } -}; +export const TransportationAPI = { -export const updateTransportation = async (transportation: Transportation): Promise => { - try { - const res = await FLASK_HTTPS.patch(routeName + '/transportation/' + transportation._id.str, { - transportation, - }); - return res.data.transportation as Transportation; - } catch (error) { - console.error('Error fetching transportation from Flask BE: ', error); - console.error('Temp tip: have you started the backend?: '); - return undefined; + getTransportation: async (transportationID: ObjectID) => { + try { + const res = await FLASK_HTTPS.get(routeName + '/transportation/' + transportationID.str); + return res.data.transportation as Transportation; + } catch (error) { + console.error('Error fetching transportation from Flask BE: ', error); + console.error('Temp tip: have you started the backend?: '); + return undefined; + } + }, + + getTransportationsEntriesForUserUsingDataRange: async (start: Date, end: Date) => { + try { + const res = await FLASK_HTTPS.post(routeName + '/get_transportations_entries_for_user_using_data_range', { + start, + end + }); + console.log("Yazan") + return res.data.transportation as TransportationRes; + } catch (error) { + console.error('Error fetching transportation from Flask BE: ', error); + console.error('Temp tip: have you started the backend?: '); + return undefined; + } + }, + + getTransportationMetricForToday: async (userID: ObjectID): Promise => { + try { + const res = await FLASK_HTTPS.get(routeName + '/get_transportation_metric_for_today/' + userID.str); + return res.data.transportation as Transportation; + } catch (error) { + console.error('Error fetching transportation from Flask BE: ', error); + console.error('Temp tip: have you started the backend?: '); + return undefined; + } + }, + + updateTransportation: async (transportation: Transportation): Promise => { + try { + const res = await FLASK_HTTPS.patch(routeName + '/transportation/' + transportation._id.str, { + transportation, + }); + return res.data.transportation as Transportation; + } catch (error) { + console.error('Error fetching transportation from Flask BE: ', error); + console.error('Temp tip: have you started the backend?: '); + return undefined; + } } -}; \ No newline at end of file +} \ No newline at end of file diff --git a/frontend/src/models/Transportation.ts b/frontend/src/models/Transportation.ts index 0459d94..0dcaf40 100644 --- a/frontend/src/models/Transportation.ts +++ b/frontend/src/models/Transportation.ts @@ -11,4 +11,15 @@ export interface Transportation { gasoline_car: number carbon_emmisions: number date: Date +} + +export interface MonthlyEntry { + 'data': number[] + 'month': string + 'year': string +} + +export interface TransportationRes { + transportationEntries: [] + monthlyData: [] } \ No newline at end of file diff --git a/frontend/src/screens/transportationHistory.tsx b/frontend/src/screens/transportationHistory.tsx index 6136a8f..625f67b 100644 --- a/frontend/src/screens/transportationHistory.tsx +++ b/frontend/src/screens/transportationHistory.tsx @@ -1,17 +1,16 @@ -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { View, Text, TouchableOpacity, StyleSheet, SafeAreaView, ScrollView } from 'react-native'; import Colors from '../../assets/colorConstants'; import { useFonts } from 'expo-font'; import { BarChart } from 'react-native-chart-kit'; +import { TransportationAPI } from '../APIs/TransportationAPI'; +import { type MonthlyEntry } from '../models/Transportation'; + export default function TransportationHistory(): JSX.Element { - const monthlyData = [ - { month: 'September', data: [20, 25, 30, 22] }, - { month: 'November', data: [18, 24, 16, 4] }, - // Add data for other months... - ]; - const [expandedStates, setExpandedStates] = useState(Array(monthlyData.length).fill(false)); + const [expandedStates, setExpandedStates] = useState(Array(100).fill(false)); + const [monthlyData, setMonthlyData] = useState(); const toggleExpanded = (index: number): void => { const updatedStates = [...expandedStates]; @@ -27,7 +26,20 @@ export default function TransportationHistory(): JSX.Element { Montserrat: require('../../assets/fonts/MontserratThinRegular.ttf'), Josefin: require('../../assets/fonts/JosefinSansThinRegular.ttf'), }); - if (!loaded) { + + useEffect(() => { + void TransportationAPI.getTransportationsEntriesForUserUsingDataRange(new Date(2023, 1, 1), new Date(2023, 12, 1)).then((res) => { + if (res != null) { + if (res.monthlyData != null) { + setMonthlyData(res.monthlyData) + console.log(res) + } + } + console.log(res) + }); + }, [loaded]) + + if (!loaded || monthlyData === undefined) { return <>; } From 9db9268816096b8b1cec6f8fcc5a319b4ef2d972 Mon Sep 17 00:00:00 2001 From: Yazan Armoush Date: Fri, 24 Nov 2023 13:29:59 -0500 Subject: [PATCH 08/16] Removed Firebase Warning --- frontend/src/utilities/firebase.ts | 7 ++++++- frontend/tsconfig.json | 3 +++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/frontend/src/utilities/firebase.ts b/frontend/src/utilities/firebase.ts index a286c7e..4697dde 100644 --- a/frontend/src/utilities/firebase.ts +++ b/frontend/src/utilities/firebase.ts @@ -4,7 +4,10 @@ import { createUserWithEmailAndPassword, signInWithEmailAndPassword, signOut, + initializeAuth, + getReactNativePersistence } from 'firebase/auth'; +import ReactNativeAsyncStorage from '@react-native-async-storage/async-storage'; const firebaseConfig = { apiKey: 'AIzaSyA8d5XfMBK2X4Udf-pD9vWHS1SYeex8Qo4', @@ -15,7 +18,9 @@ const firebaseConfig = { appId: '1:617921267809:web:47601de3a68a0ea29c4220', }; const app = initializeApp(firebaseConfig); -const auth = getAuth(app); +const auth = initializeAuth(app, { + persistence: getReactNativePersistence(ReactNativeAsyncStorage), +}); const firebaseService = { getFirebaseUser: async () => { diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index b9567f6..20a4b8a 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -1,6 +1,9 @@ { "extends": "expo/tsconfig.base", "compilerOptions": { + "paths": { + "@firebase/auth": ["./node_modules/@firebase/auth/dist/index.rn.d.ts"] + }, "strict": true } } From e6f62bd2a3d6db0f84eb461a76b10dda36571747 Mon Sep 17 00:00:00 2001 From: Yazan Armoush Date: Fri, 24 Nov 2023 13:32:37 -0500 Subject: [PATCH 09/16] Fixed Models Error --- backend/models/transportation.py | 20 ++++++++++---------- backend/models/user.py | 2 +- frontend/src/models/Transportation.ts | 10 +++++----- frontend/src/models/User.ts | 2 +- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/backend/models/transportation.py b/backend/models/transportation.py index 2d72b24..6c0e1d9 100644 --- a/backend/models/transportation.py +++ b/backend/models/transportation.py @@ -19,11 +19,11 @@ class TransportationEntry(DB_MODEL): motorbike: int electric_car: int gasoline_car: int - carbon_emissions: float + carbon_emissions: int date: datetime def __init__(self, oid: ObjectId, user_id: ObjectId, bus: int, train: int, motorbike: int, - electric_car: int, gasoline_car: int, carbon_emissions: float, date: Union[str, datetime]) -> None: + electric_car: int, gasoline_car: int, carbon_emissions: int, date: Union[str, datetime]) -> None: super().__init__(oid) self.user_id = ObjectId(user_id) self.bus = bus @@ -39,15 +39,15 @@ def __init__(self, oid: ObjectId, user_id: ObjectId, bus: int, train: int, motor def to_json(self, for_mongodb: bool = False) -> json: res = { - '_id': self.oid, - 'user_id': self.user_id, + '_id': self.oid.__str__(), + 'user_id': self.user_id.__str__(), 'bus': self.bus, 'train': self.train, 'motorbike': self.motorbike, 'electric_car': self.electric_car, 'gasoline_car': self.gasoline_car, 'carbon_emissions': self.calculate_carbon_emissions(), - 'date': self.date + 'date': self.date.__str__() } if for_mongodb: return res @@ -57,7 +57,7 @@ def to_json(self, for_mongodb: bool = False) -> json: def from_json(doc: json) -> TransportationEntry: return TransportationEntry( oid=ObjectId(doc["_id"]), - user_id=doc['user_id'], + user_id=ObjectId(doc['user_id']), bus=doc["bus"], train=doc["train"], motorbike=doc["motorbike"], @@ -67,14 +67,14 @@ def from_json(doc: json) -> TransportationEntry: date=doc["date"] ) - def calculate_carbon_emissions(self) -> float: + def calculate_carbon_emissions(self) -> int: bus_carbon_emissions = self.bus * 0.103 train_carbon_emissions = self.train * 0.037 motorbike_carbon_emissions = self.motorbike * 0.113 electric_car_carbon_emissions = self.electric_car * 0.4 gasoline_car_carbon_emissions = self.gasoline_car * 2.3 - return sum([bus_carbon_emissions, train_carbon_emissions, motorbike_carbon_emissions, - electric_car_carbon_emissions, gasoline_car_carbon_emissions]) + return int(sum([bus_carbon_emissions, train_carbon_emissions, motorbike_carbon_emissions, + electric_car_carbon_emissions, gasoline_car_carbon_emissions])) @staticmethod def get_monthly_view(start: datetime, end: datetime, @@ -112,7 +112,7 @@ def get_monthly_view(start: datetime, end: datetime, elif transportation_entry.date.day < 28: monthly_entry['data'][3] += transportation_entry.calculate_carbon_emissions() else: # If a Month has 5 sunday, we add them to the fourth week - monthly_entry['data'][3] += transportation_entry.calculate_carbon_emissions() + monthly_entry['data'][3] += transportation_entry.calculate_carbon_emissions() / 4 return monthly_data diff --git a/backend/models/user.py b/backend/models/user.py index 0053594..9be98dc 100644 --- a/backend/models/user.py +++ b/backend/models/user.py @@ -27,7 +27,7 @@ def __init__(self, oid: ObjectId, full_name: str, email: str, badges: list[str], def to_json(self, for_mongodb: bool = False) -> json: res = { - '_id': self.oid, + '_id': self.oid.__str__(), 'full_name': self.full_name, 'email': self.email, 'badges': self.badges, diff --git a/frontend/src/models/Transportation.ts b/frontend/src/models/Transportation.ts index 0dcaf40..a4bc84c 100644 --- a/frontend/src/models/Transportation.ts +++ b/frontend/src/models/Transportation.ts @@ -1,15 +1,15 @@ import type ObjectID from "bson-objectid"; -export interface Transportation { - _id: ObjectID +export interface TransportationEntry { + _id: string user_id: ObjectID bus: number train: number motorbike: number electric_car: number gasoline_car: number - carbon_emmisions: number + carbon_emissions: number date: Date } @@ -20,6 +20,6 @@ export interface MonthlyEntry { } export interface TransportationRes { - transportationEntries: [] - monthlyData: [] + transportationEntries: TransportationEntry[] + monthlyData: MonthlyEntry[] } \ No newline at end of file diff --git a/frontend/src/models/User.ts b/frontend/src/models/User.ts index a0f6e93..3d75fad 100644 --- a/frontend/src/models/User.ts +++ b/frontend/src/models/User.ts @@ -2,7 +2,7 @@ import type ObjectID from "bson-objectid"; export interface User { - _id: ObjectID + _id: string full_name: string email: string badges: string[] From 28581a46276060c896a0eecfc055702cb89e95ef Mon Sep 17 00:00:00 2001 From: Yazan Armoush Date: Fri, 24 Nov 2023 13:32:57 -0500 Subject: [PATCH 10/16] Fixed Routing Errors --- backend/routes/transportation.py | 15 +++++++++------ backend/routes/users.py | 1 + frontend/src/APIs/FLASK_API.tsx | 2 +- frontend/src/APIs/TransportationAPI.ts | 25 ++++++++++++------------- frontend/src/APIs/UsersAPI.ts | 7 +++---- 5 files changed, 26 insertions(+), 24 deletions(-) diff --git a/backend/routes/transportation.py b/backend/routes/transportation.py index 0824e0d..38f4dab 100644 --- a/backend/routes/transportation.py +++ b/backend/routes/transportation.py @@ -43,14 +43,15 @@ def get_transportations_entries_for_user_using_date_range() -> Response: }) -@transportation_service.route("/get_transportation_metric_for_today/", methods=['GET']) +@transportation_service.route("/get_transportation_metric_for_today", methods=['GET']) @carbon_auth.auth.login_required -def get_transportation_metric_for_today(user_id: str) -> Response: - query = {"user_id": ObjectId(user_id), "date": weekly_metric_reset(datetime.now())} +def get_transportation_metric_for_today() -> Response: + user = FirebaseAPI.get_user(flask.request.headers.get('Authorization').split()[1]) + query = {"user_id": ObjectId(user.oid), "date": weekly_metric_reset(datetime.now())} item = CarbonTrackDB.transportation_coll.find_one(query) if item is None: - create_transportation(ObjectId(user_id)) - return get_transportation_metric_for_today(user_id=user_id) + create_transportation(ObjectId(user.oid)) + return get_transportation_metric_for_today() else: item = TransportationEntry.from_json(item).to_json() return jsonify({'transportation': item}) @@ -59,7 +60,7 @@ def get_transportation_metric_for_today(user_id: str) -> Response: @carbon_auth.auth.login_required def create_transportation(user_id: ObjectId) -> Response: transportation = TransportationEntry(oid=ObjectId(), user_id=user_id, bus=0, train=0, motorbike=0, electric_car=0, - gasoline_car=0, carbon_emissions=0.0, date=weekly_metric_reset(datetime.today())) + gasoline_car=0, carbon_emissions=0, date=weekly_metric_reset(datetime.today())) transportation = transportation.to_json(for_mongodb=True) inserted_id = CarbonTrackDB.transportation_coll.insert_one(transportation).inserted_id transportation = TransportationEntry.from_json(CarbonTrackDB.transportation_coll.find_one({"_id": inserted_id})).to_json() @@ -70,7 +71,9 @@ def create_transportation(user_id: ObjectId) -> Response: @carbon_auth.auth.login_required def update_transportation(oid: str) -> Response: query = {"_id": ObjectId(oid)} + x = request.get_json() transportation: dict = TransportationEntry.from_json(request.get_json()['transportation']).to_json(for_mongodb=True) + del transportation['_id'] del transportation['date'] del transportation['user_id'] CarbonTrackDB.transportation_coll.update_one(query, {'$set': transportation}) diff --git a/backend/routes/users.py b/backend/routes/users.py index 5d691a7..5654f5d 100644 --- a/backend/routes/users.py +++ b/backend/routes/users.py @@ -52,6 +52,7 @@ def delete_user(user_id: str) -> Response: def update_user(user_id: str) -> Response: query = {"_id": ObjectId(user_id)} user = User.from_json(request.get_json()['user']).to_json(for_mongodb=True) + del user['_id'] CarbonTrackDB.users_coll.update_one(query, {'$set': user}) item = CarbonTrackDB.users_coll.find_one(query) item = User.from_json(item).to_json() diff --git a/frontend/src/APIs/FLASK_API.tsx b/frontend/src/APIs/FLASK_API.tsx index 320309f..6342285 100644 --- a/frontend/src/APIs/FLASK_API.tsx +++ b/frontend/src/APIs/FLASK_API.tsx @@ -1,6 +1,6 @@ import axios from "axios"; import firebaseService from "../utilities/firebase"; -const FLASK_LOCAL_ADDRESS: string = "http://10.0.15.205:6050"; +const FLASK_LOCAL_ADDRESS: string = "http://10.0.0.72:6050"; // Function to get the Firebase authentication token const getFirebaseAuthToken = async (): Promise => { diff --git a/frontend/src/APIs/TransportationAPI.ts b/frontend/src/APIs/TransportationAPI.ts index 5d49d59..87bfc6a 100644 --- a/frontend/src/APIs/TransportationAPI.ts +++ b/frontend/src/APIs/TransportationAPI.ts @@ -1,15 +1,14 @@ import FLASK_HTTPS from './FLASK_API'; -import type ObjectID from 'bson-objectid'; -import type { Transportation, TransportationRes } from '../models/Transportation'; +import type { TransportationEntry, TransportationRes } from '../models/Transportation'; const routeName = '/transportation'; export const TransportationAPI = { - getTransportation: async (transportationID: ObjectID) => { + getTransportation: async (transportationID: string) => { try { - const res = await FLASK_HTTPS.get(routeName + '/transportation/' + transportationID.str); - return res.data.transportation as Transportation; + const res = await FLASK_HTTPS.get(routeName + '/transportation/' + transportationID); + return res.data.transportation as TransportationEntry; } catch (error) { console.error('Error fetching transportation from Flask BE: ', error); console.error('Temp tip: have you started the backend?: '); @@ -23,8 +22,7 @@ export const TransportationAPI = { start, end }); - console.log("Yazan") - return res.data.transportation as TransportationRes; + return res.data as TransportationRes; } catch (error) { console.error('Error fetching transportation from Flask BE: ', error); console.error('Temp tip: have you started the backend?: '); @@ -32,10 +30,10 @@ export const TransportationAPI = { } }, - getTransportationMetricForToday: async (userID: ObjectID): Promise => { + getTransportationMetricForToday: async (): Promise => { try { - const res = await FLASK_HTTPS.get(routeName + '/get_transportation_metric_for_today/' + userID.str); - return res.data.transportation as Transportation; + const res = await FLASK_HTTPS.get(routeName + '/get_transportation_metric_for_today'); + return res.data.transportation as TransportationEntry; } catch (error) { console.error('Error fetching transportation from Flask BE: ', error); console.error('Temp tip: have you started the backend?: '); @@ -43,12 +41,13 @@ export const TransportationAPI = { } }, - updateTransportation: async (transportation: Transportation): Promise => { + updateTransportation: async (transportation: TransportationEntry): Promise => { try { - const res = await FLASK_HTTPS.patch(routeName + '/transportation/' + transportation._id.str, { + console.log(transportation._id) + const res = await FLASK_HTTPS.patch(routeName + '/transportation/' + transportation._id, { transportation, }); - return res.data.transportation as Transportation; + return res.data.transportation as TransportationEntry; } catch (error) { console.error('Error fetching transportation from Flask BE: ', error); console.error('Temp tip: have you started the backend?: '); diff --git a/frontend/src/APIs/UsersAPI.ts b/frontend/src/APIs/UsersAPI.ts index 4b159db..bb57111 100644 --- a/frontend/src/APIs/UsersAPI.ts +++ b/frontend/src/APIs/UsersAPI.ts @@ -1,5 +1,4 @@ import FLASK_HTTPS from './FLASK_API'; -import type ObjectID from 'bson-objectid'; import type { User } from '../models/User'; import firebaseService from '../utilities/firebase'; @@ -16,9 +15,9 @@ export const UsersAPI = { } }, - getUser: async (userID: ObjectID)=> { + getUser: async (userID: string)=> { try { - const res = await FLASK_HTTPS.get(routeName + '/user/' + userID.str); + const res = await FLASK_HTTPS.get(routeName + '/user/' + userID); return res.data.user as User; } catch (error) { console.error('Error fetching user from Flask BE: ', error); @@ -53,7 +52,7 @@ export const UsersAPI = { updateUser: async (user: User)=> { try { - const res = await FLASK_HTTPS.patch(routeName + '/user/' + user._id.str, { + const res = await FLASK_HTTPS.patch(routeName + '/user/' + user._id, { user, }); return res.data.user as User; From 3c78301bc54a56e6071c5787262740dced5470b7 Mon Sep 17 00:00:00 2001 From: Yazan Armoush Date: Fri, 24 Nov 2023 13:33:13 -0500 Subject: [PATCH 11/16] Added New Features --- backend/utils/FirebaseAPI.py | 1 + frontend/src/screens/dashboard.tsx | 12 +- frontend/src/screens/transportationForum.tsx | 179 ++++++------------ .../src/screens/transportationHistory.tsx | 44 ++++- 4 files changed, 108 insertions(+), 128 deletions(-) diff --git a/backend/utils/FirebaseAPI.py b/backend/utils/FirebaseAPI.py index 0f9ff85..f1a4ce5 100644 --- a/backend/utils/FirebaseAPI.py +++ b/backend/utils/FirebaseAPI.py @@ -13,6 +13,7 @@ class FirebaseAPI: @staticmethod def verify_google_token(id_token: str) -> Optional[dict]: + # print(id_token) return auth.verify_id_token( id_token=id_token, check_revoked=True ) diff --git a/frontend/src/screens/dashboard.tsx b/frontend/src/screens/dashboard.tsx index c5e2745..8eaf73a 100644 --- a/frontend/src/screens/dashboard.tsx +++ b/frontend/src/screens/dashboard.tsx @@ -9,10 +9,13 @@ import type { StackNavigationProp } from '@react-navigation/stack'; import { useNavigation } from '@react-navigation/native'; import { type User } from '../models/User'; import { UsersAPI } from '../APIs/UsersAPI'; +import { type TransportationEntry } from '../models/Transportation'; +import { TransportationAPI } from '../APIs/TransportationAPI'; export type StackNavigation = StackNavigationProp; export default function DashBoardScreen(): JSX.Element { const [user, setUser] = useState(undefined); + const [transportationEntry, setTransportationEntry] = useState(); const [photoURL] = useState("https://upload.wikimedia.org/wikipedia/commons/thumb/2/2c/Default_pfp.svg/2048px-Default_pfp.svg.png"); const navigation = useNavigation(); @@ -28,9 +31,14 @@ export default function DashBoardScreen(): JSX.Element { setUser(res); } }); + void TransportationAPI.getTransportationMetricForToday().then((res) => { + if (res != null) { + setTransportationEntry(res) + } + }); }, [loaded]); - if (!loaded || user === undefined) { + if (!loaded || user === undefined || transportationEntry === undefined) { return <>; } @@ -69,7 +77,7 @@ export default function DashBoardScreen(): JSX.Element { navigation.navigate('TransportationHistory'); }} > - + diff --git a/frontend/src/screens/transportationForum.tsx b/frontend/src/screens/transportationForum.tsx index ad6b1c1..e51b32a 100644 --- a/frontend/src/screens/transportationForum.tsx +++ b/frontend/src/screens/transportationForum.tsx @@ -4,22 +4,21 @@ import { View, TouchableOpacity, ScrollView, - TextInput, Modal, Linking, } from 'react-native'; -import { CheckBox } from 'react-native-elements'; import * as React from 'react'; import Icon from 'react-native-vector-icons/FontAwesome'; -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import { type StackNavigationProp } from '@react-navigation/stack'; import { type RootStackParamList } from '../components/types'; import { useNavigation } from '@react-navigation/native'; import { useFonts } from 'expo-font'; import Colors from '../../assets/colorConstants'; -import transportationQuestions from '../../assets/questions'; import { SafeAreaView } from 'react-native-safe-area-context'; import Slider from '@react-native-community/slider'; +import { TransportationAPI } from '../APIs/TransportationAPI'; +import { type TransportationEntry } from '../models/Transportation'; export type StackNavigation = StackNavigationProp; @@ -32,33 +31,32 @@ export default function TransportationForum(): JSX.Element { initialValue: number; } - const slidersData: SliderData[] = [ - { id: 1, label: 'Car', minValue: 0, maxValue: 800, initialValue: 0 }, - { id: 2, label: 'Bus', minValue: 0, maxValue: 800, initialValue: 0 }, - { id: 3, label: 'Train', minValue: 0, maxValue: 800, initialValue: 0 }, - { id: 4, label: 'Motobike', minValue: 0, maxValue: 800, initialValue: 0 }, - // Add more slider data as needed - ]; + const [transportationEntry, setTransportationEntry] = useState(); - const [sliderValues, setSliderValues] = useState( - slidersData.map((data) => data.initialValue) - ); + const [slidersData, setSliderData] = useState([]); + + const [electricCarTravel, setElectricCarTravel] = useState(0); + const [gasolineCarTravel, setGasolineCarTravel] = useState(0); + const [busTravel, setBusTravel] = useState(0); + const [trainTravel, setTrainTravel] = useState(0); + const [bikeTravel, setBikeTravel] = useState(0); const onSliderValueChange = (value: number, index: number): void => { - const updatedValues = [...sliderValues]; - updatedValues[index] = value; - setSliderValues(updatedValues); + slidersData[index].initialValue = value switch (index) { case 0: - setCarTravel(value); + setElectricCarTravel(value); break; case 1: - setBusTravel(value); + setGasolineCarTravel(value); break; case 2: setTrainTravel(value); break; case 3: + setBusTravel(value); + break; + case 4: setBikeTravel(value); break; default: @@ -90,40 +88,53 @@ export default function TransportationForum(): JSX.Element { const navigation = useNavigation(); - const data = transportationQuestions; - - const [responses, setResponses] = useState(new Array(data.length).fill('')); - - const [fuelType, setFuelType] = useState(''); - const [fuelEfficiency, setFuelEfficiency] = useState(0); - const [carTravel, setCarTravel] = useState(0); - const [busTravel, setBusTravel] = useState(0); - const [trainTravel, setTrainTravel] = useState(0); - const [bikeTravel, setBikeTravel] = useState(0); - const [modalVisible, setModalVisible] = useState(false); const handleSurveySubmit = (): void => { // Process survey responses, e.g., send them to a server - console.log('Survey Responses:', { - fuelType, - fuelEfficiency, - carTravel, - busTravel, - trainTravel, - bikeTravel, - }); - - navigation.navigate('FoodForum'); + if (transportationEntry != null) { + const newEntry: TransportationEntry = { + _id: transportationEntry._id, + user_id: transportationEntry.user_id, + bus: busTravel, + train: trainTravel, + motorbike: bikeTravel, + electric_car: electricCarTravel, + gasoline_car: gasolineCarTravel, + carbon_emissions: transportationEntry.carbon_emissions, + date: transportationEntry.date + } + void TransportationAPI.updateTransportation(newEntry).then() + } + navigation.navigate('TransportationHistory'); }; - const handleOptionSelect = (questionId: number, optionIndex: number): void => { - const updatedResponses = [...responses]; - updatedResponses[questionId] = data[questionId].options[optionIndex]; - setResponses(updatedResponses); - }; + useEffect(() => { + if (transportationEntry != null) { + setSliderData([ + { id: 1, label: 'Electric Car', minValue: 0, maxValue: 800, initialValue: transportationEntry.electric_car }, + { id: 2, label: 'Gasoline Car', minValue: 0, maxValue: 800, initialValue: transportationEntry.gasoline_car }, + { id: 3, label: 'Train', minValue: 0, maxValue: 800, initialValue: transportationEntry.train }, + { id: 4, label: 'Bus', minValue: 0, maxValue: 800, initialValue: transportationEntry.bus }, + { id: 5, label: 'Motobike', minValue: 0, maxValue: 800, initialValue: transportationEntry.motorbike }, + ]) + setElectricCarTravel(transportationEntry.electric_car); + setGasolineCarTravel(transportationEntry.gasoline_car); + setBusTravel(transportationEntry.bus); + setTrainTravel(transportationEntry.train); + setBikeTravel(transportationEntry.motorbike); + } + }, [transportationEntry]) - if (!loaded) { + useEffect(() => { + void TransportationAPI.getTransportationMetricForToday().then((res) => { + if (res != null) { + setTransportationEntry(res) + } + }) + }, [loaded]) + + if (!loaded || slidersData.length === 0) { return <>; } @@ -132,49 +143,6 @@ export default function TransportationForum(): JSX.Element { Calculate your emissions from transportation: - - {data[0].question} - {data[0].options.map((option, index) => ( - { - handleOptionSelect(data[0].id, index); - setFuelType(data[0].options[index]); - }} - /> - ))} - - - - - - Please enter your vehicle's fuel efficiency. If you don't have a vehicle, - enter 0. - - setModalVisible(!modalVisible)} - > - - - - { - setFuelEfficiency(Number(text)); - }} - /> - - @@ -205,7 +173,7 @@ export default function TransportationForum(): JSX.Element { {slidersData.map((slider, index) => ( - {slider.label}: {sliderValues[index]} km + {slider.label}: {slidersData[index].initialValue} km onSliderValueChange(value, index)} /> @@ -227,7 +195,7 @@ export default function TransportationForum(): JSX.Element { ))} - Next + Save @@ -282,20 +250,6 @@ const styles = StyleSheet.create({ color: Colors.DARKGREEN, marginBottom: 20, }, - answer: { - fontFamily: 'Montserrat', - fontSize: 17, - fontWeight: '700', - color: Colors.DARKGREEN, - }, - input: { - borderWidth: 1, - borderColor: Colors.GREY, - padding: 8, - margin: 10, - width: 200, - backgroundColor: Colors.WHITE, - }, buttoning: { backgroundColor: Colors.DARKGREEN, borderRadius: 10, @@ -308,18 +262,9 @@ const styles = StyleSheet.create({ fontWeight: '700', textAlign: 'center', }, - selectedOption: { - backgroundColor: Colors.FILLGREEN, - }, - unSelectedOption: { - backgroundColor: Colors.LIGHTFGREEN, - }, questionContainer: { paddingBottom: 30, }, - questionWithIcon: { - flexDirection: 'row', - }, infoText: { fontSize: 20, color: Colors.DARKGREEN, @@ -330,10 +275,6 @@ const styles = StyleSheet.create({ closeIcon: { marginLeft: 'auto', }, - questionIcon: { - marginLeft: 15, - paddingTop: 5, - }, silder: { height: 50, width: '100%', diff --git a/frontend/src/screens/transportationHistory.tsx b/frontend/src/screens/transportationHistory.tsx index 625f67b..419624a 100644 --- a/frontend/src/screens/transportationHistory.tsx +++ b/frontend/src/screens/transportationHistory.tsx @@ -4,13 +4,22 @@ import Colors from '../../assets/colorConstants'; import { useFonts } from 'expo-font'; import { BarChart } from 'react-native-chart-kit'; import { TransportationAPI } from '../APIs/TransportationAPI'; -import { type MonthlyEntry } from '../models/Transportation'; +import { type TransportationEntry, type MonthlyEntry } from '../models/Transportation'; +import { useNavigation } from '@react-navigation/native'; +import { type StackNavigationProp } from '@react-navigation/stack'; +import { type RootStackParamList } from '../components/types'; +import WidgetBox from '../widgets/widgetBox'; +export type StackNavigation = StackNavigationProp; export default function TransportationHistory(): JSX.Element { const [expandedStates, setExpandedStates] = useState(Array(100).fill(false)); const [monthlyData, setMonthlyData] = useState(); + const [startDate] = useState(new Date(2023, 8, 1)); + const [endDate] = useState(new Date(2023, 11, 1)); + const navigation = useNavigation(); + const [transportationEntry, setTransportationEntry] = useState(); const toggleExpanded = (index: number): void => { const updatedStates = [...expandedStates]; @@ -28,18 +37,22 @@ export default function TransportationHistory(): JSX.Element { }); useEffect(() => { - void TransportationAPI.getTransportationsEntriesForUserUsingDataRange(new Date(2023, 1, 1), new Date(2023, 12, 1)).then((res) => { + void TransportationAPI.getTransportationsEntriesForUserUsingDataRange( + startDate, endDate).then((res) => { if (res != null) { if (res.monthlyData != null) { setMonthlyData(res.monthlyData) - console.log(res) } } - console.log(res) }); - }, [loaded]) + void TransportationAPI.getTransportationMetricForToday().then((res) => { + if (res != null) { + setTransportationEntry(res) + } + }); + }, [endDate, loaded, startDate]) - if (!loaded || monthlyData === undefined) { + if (!loaded || monthlyData === undefined || transportationEntry === undefined) { return <>; } @@ -92,12 +105,22 @@ export default function TransportationHistory(): JSX.Element { withHorizontalLabels={false} /> - {/* Add more content here */} )} ))} + + + { + navigation.navigate('TransportationForum'); + }} + > + + + + ); } @@ -148,4 +171,11 @@ const styles = StyleSheet.create({ alignSelf: 'center', marginLeft: -50, }, + widgetContainer: { + padding: 10, + flexDirection: 'row', + }, + widgetBoarder: { + padding: 10, + } }); From 9ba48f98352f0dcb69accf667fab97f69e67f018 Mon Sep 17 00:00:00 2001 From: Yazan Armoush Date: Fri, 24 Nov 2023 13:34:08 -0500 Subject: [PATCH 12/16] Fixed Linting Errors --- backend/models/transportation.py | 2 +- backend/routes/transportation.py | 1 - backend/utils/FirebaseAPI.py | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/backend/models/transportation.py b/backend/models/transportation.py index 6c0e1d9..ef5d4da 100644 --- a/backend/models/transportation.py +++ b/backend/models/transportation.py @@ -5,7 +5,7 @@ from __future__ import annotations from typing import Union import json -from datetime import datetime, timezone, timedelta +from datetime import datetime, timezone from models.abstract_db_model import DB_MODEL from bson import ObjectId from bson import json_util diff --git a/backend/routes/transportation.py b/backend/routes/transportation.py index 38f4dab..5964563 100644 --- a/backend/routes/transportation.py +++ b/backend/routes/transportation.py @@ -71,7 +71,6 @@ def create_transportation(user_id: ObjectId) -> Response: @carbon_auth.auth.login_required def update_transportation(oid: str) -> Response: query = {"_id": ObjectId(oid)} - x = request.get_json() transportation: dict = TransportationEntry.from_json(request.get_json()['transportation']).to_json(for_mongodb=True) del transportation['_id'] del transportation['date'] diff --git a/backend/utils/FirebaseAPI.py b/backend/utils/FirebaseAPI.py index f1a4ce5..57479e1 100644 --- a/backend/utils/FirebaseAPI.py +++ b/backend/utils/FirebaseAPI.py @@ -1,6 +1,5 @@ from typing import Optional from pprint import pprint -import flask import firebase_admin from firebase_admin import credentials, auth from models.user import User From d4edc6c19458df589e5752bf1f7086854ea6d375 Mon Sep 17 00:00:00 2001 From: Yazan Armoush Date: Fri, 24 Nov 2023 13:38:39 -0500 Subject: [PATCH 13/16] Fixed Conflict --- frontend/src/components/appNavigation.tsx | 2 + .../src/screens/transportationEntryEdit.tsx | 282 +++++++++ frontend/src/screens/transportationForum.tsx | 599 ++++++++++-------- .../src/screens/transportationHistory.tsx | 2 +- 4 files changed, 614 insertions(+), 271 deletions(-) create mode 100644 frontend/src/screens/transportationEntryEdit.tsx diff --git a/frontend/src/components/appNavigation.tsx b/frontend/src/components/appNavigation.tsx index 74ac18f..bb8afc8 100644 --- a/frontend/src/components/appNavigation.tsx +++ b/frontend/src/components/appNavigation.tsx @@ -10,6 +10,7 @@ import Forum from '../screens/forum'; import FoodForum from '../screens/foodForum'; import EnergyForum from '../screens/energyForum'; import TransportationForum from '../screens/transportationForum'; +import TransportationEntryEdit from '../screens/transportationEntryEdit'; import FoodHistory from '../screens/foodHistory'; import TransportationHistory from '../screens/transportationHistory'; @@ -27,6 +28,7 @@ const AppNavigation = (): JSX.Element => { + diff --git a/frontend/src/screens/transportationEntryEdit.tsx b/frontend/src/screens/transportationEntryEdit.tsx new file mode 100644 index 0000000..795cb00 --- /dev/null +++ b/frontend/src/screens/transportationEntryEdit.tsx @@ -0,0 +1,282 @@ +import { + StyleSheet, + Text, + View, + TouchableOpacity, + ScrollView, + Modal, + Linking, +} from 'react-native'; +import * as React from 'react'; +import Icon from 'react-native-vector-icons/FontAwesome'; +import { useEffect, useState } from 'react'; +import { type StackNavigationProp } from '@react-navigation/stack'; +import { type RootStackParamList } from '../components/types'; +import { useNavigation } from '@react-navigation/native'; +import { useFonts } from 'expo-font'; +import Colors from '../../assets/colorConstants'; +import { SafeAreaView } from 'react-native-safe-area-context'; +import Slider from '@react-native-community/slider'; +import { TransportationAPI } from '../APIs/TransportationAPI'; +import { type TransportationEntry } from '../models/Transportation'; + +export type StackNavigation = StackNavigationProp; + +export default function TransportationEntryEdit(): JSX.Element { + interface SliderData { + id: number; + label: string; + minValue: number; + maxValue: number; + initialValue: number; + } + + const [transportationEntry, setTransportationEntry] = useState(); + + const [slidersData, setSliderData] = useState([]); + + const [electricCarTravel, setElectricCarTravel] = useState(0); + const [gasolineCarTravel, setGasolineCarTravel] = useState(0); + const [busTravel, setBusTravel] = useState(0); + const [trainTravel, setTrainTravel] = useState(0); + const [bikeTravel, setBikeTravel] = useState(0); + + const onSliderValueChange = (value: number, index: number): void => { + slidersData[index].initialValue = value + switch (index) { + case 0: + setElectricCarTravel(value); + break; + case 1: + setGasolineCarTravel(value); + break; + case 2: + setTrainTravel(value); + break; + case 3: + setBusTravel(value); + break; + case 4: + setBikeTravel(value); + break; + default: + break; + } + }; + + const openLink = async (url: string): Promise => { + const supported = await Linking.canOpenURL(url); + + if (supported) { + await Linking.openURL(url); + } else { + console.error('Cannot open the URL'); + } + }; + + const handleLinkPress = (): void => { + const url = 'https://fcr-ccc.nrcan-rncan.gc.ca/en'; + openLink(url).catch((error) => { + console.error('Error opening link:', error); + }); + }; + + const [loaded] = useFonts({ + Montserrat: require('../../assets/fonts/MontserratThinRegular.ttf'), + Josefin: require('../../assets/fonts/JosefinSansThinRegular.ttf'), + }); + + const navigation = useNavigation(); + + const [modalVisible, setModalVisible] = useState(false); + + const handleSurveySubmit = (): void => { + // Process survey responses, e.g., send them to a server + if (transportationEntry != null) { + const newEntry: TransportationEntry = { + _id: transportationEntry._id, + user_id: transportationEntry.user_id, + bus: busTravel, + train: trainTravel, + motorbike: bikeTravel, + electric_car: electricCarTravel, + gasoline_car: gasolineCarTravel, + carbon_emissions: transportationEntry.carbon_emissions, + date: transportationEntry.date + } + void TransportationAPI.updateTransportation(newEntry).then() + } + navigation.navigate('TransportationHistory'); + }; + + useEffect(() => { + if (transportationEntry != null) { + setSliderData([ + { id: 1, label: 'Electric Car', minValue: 0, maxValue: 800, initialValue: transportationEntry.electric_car }, + { id: 2, label: 'Gasoline Car', minValue: 0, maxValue: 800, initialValue: transportationEntry.gasoline_car }, + { id: 3, label: 'Train', minValue: 0, maxValue: 800, initialValue: transportationEntry.train }, + { id: 4, label: 'Bus', minValue: 0, maxValue: 800, initialValue: transportationEntry.bus }, + { id: 5, label: 'Motobike', minValue: 0, maxValue: 800, initialValue: transportationEntry.motorbike }, + ]) + setElectricCarTravel(transportationEntry.electric_car); + setGasolineCarTravel(transportationEntry.gasoline_car); + setBusTravel(transportationEntry.bus); + setTrainTravel(transportationEntry.train); + setBikeTravel(transportationEntry.motorbike); + } + }, [transportationEntry]) + + useEffect(() => { + void TransportationAPI.getTransportationMetricForToday().then((res) => { + if (res != null) { + setTransportationEntry(res) + } + }) + }, [loaded]) + + if (!loaded || slidersData.length === 0) { + return <>; + } + + return ( + + + Calculate your emissions from transportation: + + + + + + If you don't know your vehicle's fuel efficiency, it's available + online{' '} + + here + + . Select the "combination" value under Comsumption in L/100km. The average + fuel consumption of non-plug-in hybrid personal vehicles in Canada is 8.9 L / 100 + km. + + setModalVisible(!modalVisible)} + > + + + + + + + + On average, how much distance do you travel using the following methods per week: + + + {slidersData.map((slider, index) => ( + + + {slider.label}: {slidersData[index].initialValue} km + + onSliderValueChange(value, index)} + /> + + {slider.minValue} + {slider.maxValue} + + {/* You can include additional components related to this slider here */} + + ))} + + + Save + + + + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + justifyContent: 'center', + backgroundColor: Colors.LIGHTFGREEN, + }, + labelContainer: { + flexDirection: 'row', + justifyContent: 'space-between', + width: '100%', + paddingHorizontal: 12, + }, + modalBackground: { + backgroundColor: Colors.BLACKTRANS, + flex: 1, + }, + modalContainer: { + backgroundColor: Colors.WHITE, + marginHorizontal: 50, + marginVertical: 180, + padding: 20, + borderRadius: 10, + flex: 1, + flexDirection: 'row', + }, + scrollContainer: { + paddingTop: 30, + flex: 1, + paddingHorizontal: 30, + backgroundColor: Colors.LIGHTFGREEN, + }, + label: { + fontSize: 15, + }, + header: { + color: Colors.DARKGREEN, + fontFamily: 'Montserrat', + fontSize: 25, + fontWeight: '700', + marginBottom: 50, + }, + question: { + fontFamily: 'Montserrat', + fontSize: 20, + fontWeight: '700', + color: Colors.DARKGREEN, + marginBottom: 20, + }, + buttoning: { + backgroundColor: Colors.DARKGREEN, + borderRadius: 10, + marginBottom: 70, + padding: 18, + }, + buttoningText: { + color: Colors.WHITE, + fontSize: 16, + fontWeight: '700', + textAlign: 'center', + }, + questionContainer: { + paddingBottom: 30, + }, + infoText: { + fontSize: 20, + color: Colors.DARKGREEN, + }, + linkText: { + color: Colors.BLUE, + }, + closeIcon: { + marginLeft: 'auto', + }, + silder: { + height: 50, + width: '100%', + }, +}); diff --git a/frontend/src/screens/transportationForum.tsx b/frontend/src/screens/transportationForum.tsx index e51b32a..7201ba2 100644 --- a/frontend/src/screens/transportationForum.tsx +++ b/frontend/src/screens/transportationForum.tsx @@ -1,282 +1,341 @@ import { - StyleSheet, - Text, - View, - TouchableOpacity, - ScrollView, - Modal, - Linking, -} from 'react-native'; -import * as React from 'react'; -import Icon from 'react-native-vector-icons/FontAwesome'; -import { useEffect, useState } from 'react'; -import { type StackNavigationProp } from '@react-navigation/stack'; -import { type RootStackParamList } from '../components/types'; -import { useNavigation } from '@react-navigation/native'; -import { useFonts } from 'expo-font'; -import Colors from '../../assets/colorConstants'; -import { SafeAreaView } from 'react-native-safe-area-context'; -import Slider from '@react-native-community/slider'; -import { TransportationAPI } from '../APIs/TransportationAPI'; -import { type TransportationEntry } from '../models/Transportation'; - -export type StackNavigation = StackNavigationProp; - -export default function TransportationForum(): JSX.Element { - interface SliderData { - id: number; - label: string; - minValue: number; - maxValue: number; - initialValue: number; - } - - const [transportationEntry, setTransportationEntry] = useState(); - - const [slidersData, setSliderData] = useState([]); - - const [electricCarTravel, setElectricCarTravel] = useState(0); - const [gasolineCarTravel, setGasolineCarTravel] = useState(0); - const [busTravel, setBusTravel] = useState(0); - const [trainTravel, setTrainTravel] = useState(0); - const [bikeTravel, setBikeTravel] = useState(0); - - const onSliderValueChange = (value: number, index: number): void => { - slidersData[index].initialValue = value - switch (index) { - case 0: - setElectricCarTravel(value); - break; - case 1: - setGasolineCarTravel(value); - break; - case 2: - setTrainTravel(value); - break; - case 3: - setBusTravel(value); - break; - case 4: - setBikeTravel(value); - break; - default: - break; - } - }; - - const openLink = async (url: string): Promise => { - const supported = await Linking.canOpenURL(url); - - if (supported) { - await Linking.openURL(url); - } else { - console.error('Cannot open the URL'); + StyleSheet, + Text, + View, + TouchableOpacity, + ScrollView, + TextInput, + Modal, + Linking, + } from 'react-native'; + import { CheckBox } from 'react-native-elements'; + import * as React from 'react'; + import Icon from 'react-native-vector-icons/FontAwesome'; + import { useState } from 'react'; + import { type StackNavigationProp } from '@react-navigation/stack'; + import { type RootStackParamList } from '../components/types'; + import { useNavigation } from '@react-navigation/native'; + import { useFonts } from 'expo-font'; + import Colors from '../../assets/colorConstants'; + import transportationQuestions from '../../assets/questions'; + import { SafeAreaView } from 'react-native-safe-area-context'; + import Slider from '@react-native-community/slider'; + + export type StackNavigation = StackNavigationProp; + + export default function TransportationForum(): JSX.Element { + interface SliderData { + id: number; + label: string; + minValue: number; + maxValue: number; + initialValue: number; } - }; - - const handleLinkPress = (): void => { - const url = 'https://fcr-ccc.nrcan-rncan.gc.ca/en'; - openLink(url).catch((error) => { - console.error('Error opening link:', error); - }); - }; - - const [loaded] = useFonts({ - Montserrat: require('../../assets/fonts/MontserratThinRegular.ttf'), - Josefin: require('../../assets/fonts/JosefinSansThinRegular.ttf'), - }); - - const navigation = useNavigation(); - - const [modalVisible, setModalVisible] = useState(false); - - const handleSurveySubmit = (): void => { - // Process survey responses, e.g., send them to a server - if (transportationEntry != null) { - const newEntry: TransportationEntry = { - _id: transportationEntry._id, - user_id: transportationEntry.user_id, - bus: busTravel, - train: trainTravel, - motorbike: bikeTravel, - electric_car: electricCarTravel, - gasoline_car: gasolineCarTravel, - carbon_emissions: transportationEntry.carbon_emissions, - date: transportationEntry.date + + const slidersData: SliderData[] = [ + { id: 1, label: 'Car', minValue: 0, maxValue: 800, initialValue: 0 }, + { id: 2, label: 'Bus', minValue: 0, maxValue: 800, initialValue: 0 }, + { id: 3, label: 'Train', minValue: 0, maxValue: 800, initialValue: 0 }, + { id: 4, label: 'Motobike', minValue: 0, maxValue: 800, initialValue: 0 }, + // Add more slider data as needed + ]; + + const [sliderValues, setSliderValues] = useState( + slidersData.map((data) => data.initialValue) + ); + + const onSliderValueChange = (value: number, index: number): void => { + const updatedValues = [...sliderValues]; + updatedValues[index] = value; + setSliderValues(updatedValues); + switch (index) { + case 0: + setCarTravel(value); + break; + case 1: + setBusTravel(value); + break; + case 2: + setTrainTravel(value); + break; + case 3: + setBikeTravel(value); + break; + default: + break; } - void TransportationAPI.updateTransportation(newEntry).then() - } - navigation.navigate('TransportationHistory'); - }; - - useEffect(() => { - if (transportationEntry != null) { - setSliderData([ - { id: 1, label: 'Electric Car', minValue: 0, maxValue: 800, initialValue: transportationEntry.electric_car }, - { id: 2, label: 'Gasoline Car', minValue: 0, maxValue: 800, initialValue: transportationEntry.gasoline_car }, - { id: 3, label: 'Train', minValue: 0, maxValue: 800, initialValue: transportationEntry.train }, - { id: 4, label: 'Bus', minValue: 0, maxValue: 800, initialValue: transportationEntry.bus }, - { id: 5, label: 'Motobike', minValue: 0, maxValue: 800, initialValue: transportationEntry.motorbike }, - ]) - setElectricCarTravel(transportationEntry.electric_car); - setGasolineCarTravel(transportationEntry.gasoline_car); - setBusTravel(transportationEntry.bus); - setTrainTravel(transportationEntry.train); - setBikeTravel(transportationEntry.motorbike); - } - }, [transportationEntry]) - - useEffect(() => { - void TransportationAPI.getTransportationMetricForToday().then((res) => { - if (res != null) { - setTransportationEntry(res) + }; + + const openLink = async (url: string): Promise => { + const supported = await Linking.canOpenURL(url); + + if (supported) { + await Linking.openURL(url); + } else { + console.error('Cannot open the URL'); } - }) - }, [loaded]) - - if (!loaded || slidersData.length === 0) { - return <>; - } - - return ( - - - Calculate your emissions from transportation: - - - - - - If you don't know your vehicle's fuel efficiency, it's available - online{' '} - - here - - . Select the "combination" value under Comsumption in L/100km. The average - fuel consumption of non-plug-in hybrid personal vehicles in Canada is 8.9 L / 100 - km. + }; + + const handleLinkPress = (): void => { + const url = 'https://fcr-ccc.nrcan-rncan.gc.ca/en'; + openLink(url).catch((error) => { + console.error('Error opening link:', error); + }); + }; + + const [loaded] = useFonts({ + Montserrat: require('../../assets/fonts/MontserratThinRegular.ttf'), + Josefin: require('../../assets/fonts/JosefinSansThinRegular.ttf'), + }); + + const navigation = useNavigation(); + + const data = transportationQuestions; + + const [responses, setResponses] = useState(new Array(data.length).fill('')); + + const [fuelType, setFuelType] = useState(''); + const [fuelEfficiency, setFuelEfficiency] = useState(0); + const [carTravel, setCarTravel] = useState(0); + const [busTravel, setBusTravel] = useState(0); + const [trainTravel, setTrainTravel] = useState(0); + const [bikeTravel, setBikeTravel] = useState(0); + + const [modalVisible, setModalVisible] = useState(false); + + const handleSurveySubmit = (): void => { + // Process survey responses, e.g., send them to a server + console.log('Survey Responses:', { + fuelType, + fuelEfficiency, + carTravel, + busTravel, + trainTravel, + bikeTravel, + }); + + navigation.navigate('FoodForum'); + }; + + const handleOptionSelect = (questionId: number, optionIndex: number): void => { + const updatedResponses = [...responses]; + updatedResponses[questionId] = data[questionId].options[optionIndex]; + setResponses(updatedResponses); + }; + + if (!loaded) { + return <>; + } + + return ( + + + Calculate your emissions from transportation: + + + {data[0].question} + {data[0].options.map((option, index) => ( + { + handleOptionSelect(data[0].id, index); + setFuelType(data[0].options[index]); + }} + /> + ))} + + + + + + Please enter your vehicle's fuel efficiency. If you don't have a vehicle, + enter 0. setModalVisible(!modalVisible)} > - + - - - - - On average, how much distance do you travel using the following methods per week: - - - {slidersData.map((slider, index) => ( - - - {slider.label}: {slidersData[index].initialValue} km - - onSliderValueChange(value, index)} + { + setFuelEfficiency(Number(text)); + }} /> - - {slider.minValue} - {slider.maxValue} - - {/* You can include additional components related to this slider here */} - ))} - - - Save - - - - ); -} - -const styles = StyleSheet.create({ - container: { - flex: 1, - justifyContent: 'center', - backgroundColor: Colors.LIGHTFGREEN, - }, - labelContainer: { - flexDirection: 'row', - justifyContent: 'space-between', - width: '100%', - paddingHorizontal: 12, - }, - modalBackground: { - backgroundColor: Colors.BLACKTRANS, - flex: 1, - }, - modalContainer: { - backgroundColor: Colors.WHITE, - marginHorizontal: 50, - marginVertical: 180, - padding: 20, - borderRadius: 10, - flex: 1, - flexDirection: 'row', - }, - scrollContainer: { - paddingTop: 30, - flex: 1, - paddingHorizontal: 30, - backgroundColor: Colors.LIGHTFGREEN, - }, - label: { - fontSize: 15, - }, - header: { - color: Colors.DARKGREEN, - fontFamily: 'Montserrat', - fontSize: 25, - fontWeight: '700', - marginBottom: 50, - }, - question: { - fontFamily: 'Montserrat', - fontSize: 20, - fontWeight: '700', - color: Colors.DARKGREEN, - marginBottom: 20, - }, - buttoning: { - backgroundColor: Colors.DARKGREEN, - borderRadius: 10, - marginBottom: 70, - padding: 18, - }, - buttoningText: { - color: Colors.WHITE, - fontSize: 16, - fontWeight: '700', - textAlign: 'center', - }, - questionContainer: { - paddingBottom: 30, - }, - infoText: { - fontSize: 20, - color: Colors.DARKGREEN, - }, - linkText: { - color: Colors.BLUE, - }, - closeIcon: { - marginLeft: 'auto', - }, - silder: { - height: 50, - width: '100%', - }, -}); + + + + + + If you don't know your vehicle's fuel efficiency, it's available + online{' '} + + here + + . Select the "combination" value under Comsumption in L/100km. The average + fuel consumption of non-plug-in hybrid personal vehicles in Canada is 8.9 L / 100 + km. + + setModalVisible(!modalVisible)} + > + + + + + + + + On average, how much distance do you travel using the following methods per week: + + + {slidersData.map((slider, index) => ( + + + {slider.label}: {sliderValues[index]} km + + onSliderValueChange(value, index)} + /> + + {slider.minValue} + {slider.maxValue} + + {/* You can include additional components related to this slider here */} + + ))} + + + Next + + + + ); + } + + const styles = StyleSheet.create({ + container: { + flex: 1, + justifyContent: 'center', + backgroundColor: Colors.LIGHTFGREEN, + }, + labelContainer: { + flexDirection: 'row', + justifyContent: 'space-between', + width: '100%', + paddingHorizontal: 12, + }, + modalBackground: { + backgroundColor: Colors.BLACKTRANS, + flex: 1, + }, + modalContainer: { + backgroundColor: Colors.WHITE, + marginHorizontal: 50, + marginVertical: 180, + padding: 20, + borderRadius: 10, + flex: 1, + flexDirection: 'row', + }, + scrollContainer: { + paddingTop: 30, + flex: 1, + paddingHorizontal: 30, + backgroundColor: Colors.LIGHTFGREEN, + }, + label: { + fontSize: 15, + }, + header: { + color: Colors.DARKGREEN, + fontFamily: 'Montserrat', + fontSize: 25, + fontWeight: '700', + marginBottom: 50, + }, + question: { + fontFamily: 'Montserrat', + fontSize: 20, + fontWeight: '700', + color: Colors.DARKGREEN, + marginBottom: 20, + }, + answer: { + fontFamily: 'Montserrat', + fontSize: 17, + fontWeight: '700', + color: Colors.DARKGREEN, + }, + input: { + borderWidth: 1, + borderColor: Colors.GREY, + padding: 8, + margin: 10, + width: 200, + backgroundColor: Colors.WHITE, + }, + buttoning: { + backgroundColor: Colors.DARKGREEN, + borderRadius: 10, + marginBottom: 70, + padding: 18, + }, + buttoningText: { + color: Colors.WHITE, + fontSize: 16, + fontWeight: '700', + textAlign: 'center', + }, + selectedOption: { + backgroundColor: Colors.FILLGREEN, + }, + unSelectedOption: { + backgroundColor: Colors.LIGHTFGREEN, + }, + questionContainer: { + paddingBottom: 30, + }, + questionWithIcon: { + flexDirection: 'row', + }, + infoText: { + fontSize: 20, + color: Colors.DARKGREEN, + }, + linkText: { + color: Colors.BLUE, + }, + closeIcon: { + marginLeft: 'auto', + }, + questionIcon: { + marginLeft: 15, + paddingTop: 5, + }, + silder: { + height: 50, + width: '100%', + }, + }); \ No newline at end of file diff --git a/frontend/src/screens/transportationHistory.tsx b/frontend/src/screens/transportationHistory.tsx index 419624a..873dac3 100644 --- a/frontend/src/screens/transportationHistory.tsx +++ b/frontend/src/screens/transportationHistory.tsx @@ -114,7 +114,7 @@ export default function TransportationHistory(): JSX.Element { { - navigation.navigate('TransportationForum'); + navigation.navigate('TransportationEntryEdit'); }} > From fbc25c061c6823e9eb32423fd549731349ffaacc Mon Sep 17 00:00:00 2001 From: Yazan Armoush Date: Sat, 25 Nov 2023 03:13:55 -0500 Subject: [PATCH 14/16] Refactored Code --- .gitignore | 3 +- .../shelved.patch | 100 ++++++++++++++++++ backend/app.py | 20 +++- backend/models/abstract_db_model.py | 2 +- backend/models/transportation.py | 48 ++++----- backend/models/user.py | 8 +- backend/routes/transportation.py | 9 +- backend/routes/users.py | 17 ++- backend/utils/customJSONEncoder.py | 21 ++++ frontend/package.json | 6 +- frontend/src/APIs/TransportationAPI.ts | 8 +- frontend/src/APIs/UsersAPI.ts | 14 ++- frontend/src/components/types.tsx | 1 - frontend/src/models/Transportation.ts | 6 +- frontend/src/models/User.ts | 6 +- frontend/src/screens/dashboard.tsx | 2 +- frontend/src/screens/signup.tsx | 36 ++++--- .../src/screens/transportationEntryEdit.tsx | 5 +- .../src/screens/transportationHistory.tsx | 2 +- frontend/src/widgets/profileWidget.tsx | 14 +-- 20 files changed, 237 insertions(+), 91 deletions(-) create mode 100644 .idea/shelf/Uncommitted_changes_before_rebase_[Changes]/shelved.patch create mode 100644 backend/utils/customJSONEncoder.py diff --git a/.gitignore b/.gitignore index 193ebe5..8efe6ee 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ *.iml *.DS_Store node_modules -.venv \ No newline at end of file +.venv +backend/.migrations diff --git a/.idea/shelf/Uncommitted_changes_before_rebase_[Changes]/shelved.patch b/.idea/shelf/Uncommitted_changes_before_rebase_[Changes]/shelved.patch new file mode 100644 index 0000000..3935a05 --- /dev/null +++ b/.idea/shelf/Uncommitted_changes_before_rebase_[Changes]/shelved.patch @@ -0,0 +1,100 @@ +Index: backend/models/user.py +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP +<+>\"\"\"\nUser Model\n\"\"\"\n\nfrom __future__ import annotations\nimport json\nfrom models.abstract_db_model import DB_MODEL\nfrom bson import ObjectId\nfrom bson import json_util\n\n\nclass User(DB_MODEL):\n oid: ObjectId\n full_name: str\n email: str\n badges: list[str]\n friends: list[str]\n score: int\n\n def __init__(self, oid: ObjectId, full_name: str, email: str, badges: list[str], friends: list[str], score:int) -> None:\n super().__init__(oid)\n self.full_name = str(full_name)\n self.email = str(email)\n self.badges = badges\n self.friends = friends\n self.score = score\n\n def to_json(self, for_mongodb: bool = False) -> json:\n res = {\n '_id': self.oid.__str__(),\n 'full_name': self.full_name,\n 'email': self.email,\n 'badges': self.badges,\n 'friends': self.friends,\n 'score': self.score\n }\n if for_mongodb:\n return res\n return json.loads(json_util.dumps(res))\n\n @staticmethod\n def from_json(doc: json) -> User:\n return User(\n oid=ObjectId(doc[\"_id\"]),\n full_name=doc[\"full_name\"],\n email=doc[\"email\"],\n badges=doc[\"badges\"],\n friends=doc[\"friends\"],\n score=doc[\"score\"]\n )\n\n def __repr__(self) -> str:\n return f'User ID: {self.oid.__str__()}'\n +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +diff --git a/backend/models/user.py b/backend/models/user.py +--- a/backend/models/user.py (revision d4edc6c19458df589e5752bf1f7086854ea6d375) ++++ b/backend/models/user.py (date 1700864519074) +@@ -27,7 +27,7 @@ + + def to_json(self, for_mongodb: bool = False) -> json: + res = { +- '_id': self.oid.__str__(), ++ '_id': self.oid, + 'full_name': self.full_name, + 'email': self.email, + 'badges': self.badges, +Index: backend/models/transportation.py +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP +<+>\"\"\"\nTransportation Model\n\"\"\"\n\nfrom __future__ import annotations\nfrom typing import Union\nimport json\nfrom datetime import datetime, timezone\nfrom models.abstract_db_model import DB_MODEL\nfrom bson import ObjectId\nfrom bson import json_util\n\n\nclass TransportationEntry(DB_MODEL):\n oid: ObjectId\n user_id: ObjectId\n bus: int\n train: int\n motorbike: int\n electric_car: int\n gasoline_car: int\n carbon_emissions: int\n date: datetime\n\n def __init__(self, oid: ObjectId, user_id: ObjectId, bus: int, train: int, motorbike: int,\n electric_car: int, gasoline_car: int, carbon_emissions: int, date: Union[str, datetime]) -> None:\n super().__init__(oid)\n self.user_id = ObjectId(user_id)\n self.bus = bus\n self.train = train\n self.motorbike = motorbike\n self.electric_car = electric_car\n self.gasoline_car = gasoline_car\n self.carbon_emissions = carbon_emissions\n if isinstance(date, datetime):\n self.date = date\n else:\n self.date = datetime.fromisoformat(date)\n\n def to_json(self, for_mongodb: bool = False) -> json:\n res = {\n '_id': self.oid.__str__(),\n 'user_id': self.user_id.__str__(),\n 'bus': self.bus,\n 'train': self.train,\n 'motorbike': self.motorbike,\n 'electric_car': self.electric_car,\n 'gasoline_car': self.gasoline_car,\n 'carbon_emissions': self.calculate_carbon_emissions(),\n 'date': self.date.__str__()\n }\n if for_mongodb:\n return res\n return json.loads(json_util.dumps(res))\n\n @staticmethod\n def from_json(doc: json) -> TransportationEntry:\n return TransportationEntry(\n oid=ObjectId(doc[\"_id\"]),\n user_id=ObjectId(doc['user_id']),\n bus=doc[\"bus\"],\n train=doc[\"train\"],\n motorbike=doc[\"motorbike\"],\n electric_car=doc[\"electric_car\"],\n gasoline_car=doc[\"gasoline_car\"],\n carbon_emissions=doc[\"carbon_emissions\"],\n date=doc[\"date\"]\n )\n\n def calculate_carbon_emissions(self) -> int:\n bus_carbon_emissions = self.bus * 0.103\n train_carbon_emissions = self.train * 0.037\n motorbike_carbon_emissions = self.motorbike * 0.113\n electric_car_carbon_emissions = self.electric_car * 0.4\n gasoline_car_carbon_emissions = self.gasoline_car * 2.3\n return int(sum([bus_carbon_emissions, train_carbon_emissions, motorbike_carbon_emissions,\n electric_car_carbon_emissions, gasoline_car_carbon_emissions]))\n\n @staticmethod\n def get_monthly_view(start: datetime, end: datetime,\n transportationEntries: list[TransportationEntry]) -> list[dict[str, Union[list[float], str]]]:\n monthly_data = []\n\n # Make start date offset-aware (assuming UTC for simplicity)\n start = start.replace(tzinfo=timezone.utc)\n\n current_month = start\n while current_month <= end:\n # Add the current month to the list\n monthly_data.append({\n 'month': current_month.strftime('%B'),\n 'year': current_month.strftime('%Y'),\n 'data': [0, 0, 0, 0]\n })\n\n # Move to the next month\n if current_month.month == 12:\n current_month = datetime(current_month.year + 1, 1, 1, tzinfo=timezone.utc)\n else:\n current_month = datetime(current_month.year, current_month.month + 1, 1, tzinfo=timezone.utc)\n\n for transportation_entry in transportationEntries:\n for monthly_entry in monthly_data:\n if transportation_entry.date.strftime('%B') == monthly_entry['month'] \\\n and transportation_entry.date.strftime('%Y') == monthly_entry['year']:\n if transportation_entry.date.day < 7:\n monthly_entry['data'][0] = transportation_entry.calculate_carbon_emissions()\n elif transportation_entry.date.day < 14:\n monthly_entry['data'][1] = transportation_entry.calculate_carbon_emissions()\n elif transportation_entry.date.day < 21:\n monthly_entry['data'][2] = transportation_entry.calculate_carbon_emissions()\n elif transportation_entry.date.day < 28:\n monthly_entry['data'][3] += transportation_entry.calculate_carbon_emissions()\n else: # If a Month has 5 sunday, we add them to the fourth week\n monthly_entry['data'][3] += transportation_entry.calculate_carbon_emissions() / 4\n\n return monthly_data\n\n def __repr__(self) -> str:\n return f'Transportation ID: {self.oid.__str__()}'\n +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +diff --git a/backend/models/transportation.py b/backend/models/transportation.py +--- a/backend/models/transportation.py (revision d4edc6c19458df589e5752bf1f7086854ea6d375) ++++ b/backend/models/transportation.py (date 1700864519080) +@@ -39,15 +39,15 @@ + + def to_json(self, for_mongodb: bool = False) -> json: + res = { +- '_id': self.oid.__str__(), +- 'user_id': self.user_id.__str__(), ++ '_id': self.oid, ++ 'user_id': self.user_id, + 'bus': self.bus, + 'train': self.train, + 'motorbike': self.motorbike, + 'electric_car': self.electric_car, + 'gasoline_car': self.gasoline_car, + 'carbon_emissions': self.calculate_carbon_emissions(), +- 'date': self.date.__str__() ++ 'date': self.date + } + if for_mongodb: + return res +Index: frontend/src/screens/signup.tsx +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP +<+>/* eslint-disable react-native/no-unused-styles */\nimport * as React from 'react';\nimport { StyleSheet, Text, View, TouchableOpacity } from 'react-native';\nimport { useFonts } from 'expo-font';\nimport { type RootStackParamList } from '../components/types';\nimport { type StackNavigationProp } from '@react-navigation/stack';\nimport { useNavigation } from '@react-navigation/native';\nimport Colors from '../../assets/colorConstants';\nimport firebaseService from '../utilities/firebase';\nimport { createUser } from '../APIs/UsersAPI';\nimport ObjectID from 'bson-objectid';\nimport { Formik } from 'formik';\nimport * as Yup from 'yup';\nimport FormTextField from '../components/forms/formTextField';\n\nexport type StackNavigation = StackNavigationProp;\n\ninterface ISignUpFields {\n fullName: string;\n email: string;\n password: string;\n}\n\nconst SignUpSchema = Yup.object().shape({\n fullName: Yup.string().required('Full Name is required'),\n email: Yup.string().email('Invalid email').required('Email is required'),\n password: Yup.string().required('Password is required'),\n repeatPassword: Yup.string()\n .oneOf([Yup.ref('password')], 'Passwords must match')\n .required('Password confirmation is required'),\n});\n\nexport default function SignUp(): JSX.Element {\n const navigation = useNavigation();\n\n // TODO: fonts should be loaded at the global level, and not at the component level.\n\n const [loaded] = useFonts({\n Montserrat: require('../../assets/fonts/MontserratThinRegular.ttf'),\n Josefin: require('../../assets/fonts/JosefinSansThinRegular.ttf'),\n });\n\n if (!loaded) {\n return <>;\n }\n const handleSignUp = async (fields: ISignUpFields): Promise => {\n const { fullName, email, password } = fields;\n try {\n await firebaseService.createUser(email, password);\n await createUser({\n _id: new ObjectID(),\n full_name: fullName,\n email,\n badges: [],\n friends: [],\n score: 0,\n });\n console.log('User was created succesfully:', email);\n navigation.navigate('DashBoard');\n } catch (error) {\n console.error('Error when creating User:', error);\n }\n };\n\n return (\n <>\n await handleSignUp(values)}\n >\n {({ handleChange, handleBlur, handleSubmit, values, errors, touched }) => (\n \n \n Sign Up\n\n \n\n \n\n \n\n \n\n handleSubmit()}>\n Next\n \n \n \n )}\n \n \n );\n}\n\nconst styles = StyleSheet.create({\n altContainerText: {\n color: Colors.WHITE,\n fontFamily: 'Montserrat',\n fontSize: 18,\n marginHorizontal: 5,\n textAlign: 'center',\n fontWeight: '700',\n },\n buttoning: {\n backgroundColor: Colors.DARKGREEN,\n borderRadius: 10,\n marginBottom: 20,\n padding: 18,\n },\n header: {\n color: Colors.DARKGREEN,\n fontFamily: 'Montserrat',\n fontSize: 30,\n fontWeight: '700',\n marginBottom: 30,\n },\n headerBox: {\n paddingHorizontal: 30,\n },\n headerContainer: {\n flex: 1,\n justifyContent: 'center',\n },\n});\n +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +diff --git a/frontend/src/screens/signup.tsx b/frontend/src/screens/signup.tsx +--- a/frontend/src/screens/signup.tsx (revision d4edc6c19458df589e5752bf1f7086854ea6d375) ++++ b/frontend/src/screens/signup.tsx (date 1700869428968) +@@ -7,7 +7,7 @@ + import { useNavigation } from '@react-navigation/native'; + import Colors from '../../assets/colorConstants'; + import firebaseService from '../utilities/firebase'; +-import { createUser } from '../APIs/UsersAPI'; ++import { UsersAPI } from '../APIs/UsersAPI'; + import ObjectID from 'bson-objectid'; + import { Formik } from 'formik'; + import * as Yup from 'yup'; +@@ -46,9 +46,13 @@ + const handleSignUp = async (fields: ISignUpFields): Promise => { + const { fullName, email, password } = fields; + try { +- await firebaseService.createUser(email, password); +- await createUser({ +- _id: new ObjectID(), ++ await firebaseService.createUser(email, password).then((res) => { ++ console.log("res: " + res) ++ }).catch((err): void => { ++ console.log("err: " + err) ++ }); ++ await UsersAPI.createUser({ ++ _id: (new ObjectID()).toHexString(), + full_name: fullName, + email, + badges: [], +Index: backend/app.py +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP +<+># Python Imports\nfrom flask import Flask, render_template\nfrom flask_cors import CORS\n\n# Imports\nfrom routes.users import users\nfrom routes.transportation import transportation_service\n\n\napp = Flask(__name__)\n\n# Services\napp.register_blueprint(users, url_prefix=\"/users\")\napp.register_blueprint(transportation_service, url_prefix=\"/transportation\")\nCORS(app)\n\n\n@app.route(\"/\")\ndef home() -> str:\n return 'Carbon Track APP BACKEND API :: UNAUTHORIZED ACCESS'\n\n\n# This is just for testing\n@app.route(\"/google\")\ndef test_google() -> str:\n return render_template('index.html')\n\n\nif __name__ == '__main__':\n\n app.run(host='0.0.0.0', port=6050, debug=True)\n +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +diff --git a/backend/app.py b/backend/app.py +--- a/backend/app.py (revision d4edc6c19458df589e5752bf1f7086854ea6d375) ++++ b/backend/app.py (date 1700865005539) +@@ -28,4 +28,4 @@ + + if __name__ == '__main__': + +- app.run(host='0.0.0.0', port=6050, debug=True) ++ app.run(host='0.0.0.0', port=6050, debug=True, threaded=False) diff --git a/backend/app.py b/backend/app.py index 23c5e11..003bb83 100644 --- a/backend/app.py +++ b/backend/app.py @@ -1,13 +1,15 @@ # Python Imports -from flask import Flask, render_template +from bson import ObjectId +from flask import Flask, Response, jsonify, render_template from flask_cors import CORS # Imports from routes.users import users from routes.transportation import transportation_service - +from utils.customJSONEncoder import CustomJSONProvider app = Flask(__name__) +app.json = CustomJSONProvider(app) # Services app.register_blueprint(users, url_prefix="/users") @@ -16,8 +18,8 @@ @app.route("/") -def home() -> str: - return 'Carbon Track APP BACKEND API :: UNAUTHORIZED ACCESS' +def home() -> Response: + return jsonify('Carbon Track APP BACKEND API :: UNAUTHORIZED ACCESS') # This is just for testing @@ -26,6 +28,14 @@ def test_google() -> str: return render_template('index.html') +@app.route('/get_object_id') +def get_object_id(): + # Example: Creating an ObjectId + my_object_id = ObjectId() + + return jsonify({'_id': my_object_id}) + + if __name__ == '__main__': - app.run(host='0.0.0.0', port=6050, debug=True) + app.run(host='0.0.0.0', port=6050, debug=True, threaded=False) diff --git a/backend/models/abstract_db_model.py b/backend/models/abstract_db_model.py index a360b98..9cf7859 100644 --- a/backend/models/abstract_db_model.py +++ b/backend/models/abstract_db_model.py @@ -15,7 +15,7 @@ class DB_MODEL: def __init__(self, oid: ObjectId) -> None: self.oid = ObjectId(oid) - def to_json(self, for_mongodb: bool = False) -> json: + def to_json(self) -> json: raise NotImplementedError @staticmethod diff --git a/backend/models/transportation.py b/backend/models/transportation.py index ef5d4da..f936409 100644 --- a/backend/models/transportation.py +++ b/backend/models/transportation.py @@ -37,27 +37,25 @@ def __init__(self, oid: ObjectId, user_id: ObjectId, bus: int, train: int, motor else: self.date = datetime.fromisoformat(date) - def to_json(self, for_mongodb: bool = False) -> json: + def to_json(self) -> json: res = { - '_id': self.oid.__str__(), - 'user_id': self.user_id.__str__(), - 'bus': self.bus, - 'train': self.train, - 'motorbike': self.motorbike, - 'electric_car': self.electric_car, - 'gasoline_car': self.gasoline_car, - 'carbon_emissions': self.calculate_carbon_emissions(), - 'date': self.date.__str__() + "_id": self.oid, + "user_id": self.user_id, + "bus": self.bus, + "train": self.train, + "motorbike": self.motorbike, + "electric_car": self.electric_car, + "gasoline_car": self.gasoline_car, + "carbon_emissions": self.calculate_carbon_emissions(), + "date": self.date } - if for_mongodb: - return res - return json.loads(json_util.dumps(res)) + return res @staticmethod def from_json(doc: json) -> TransportationEntry: return TransportationEntry( oid=ObjectId(doc["_id"]), - user_id=ObjectId(doc['user_id']), + user_id=ObjectId(doc["user_id"]), bus=doc["bus"], train=doc["train"], motorbike=doc["motorbike"], @@ -88,9 +86,9 @@ def get_monthly_view(start: datetime, end: datetime, while current_month <= end: # Add the current month to the list monthly_data.append({ - 'month': current_month.strftime('%B'), - 'year': current_month.strftime('%Y'), - 'data': [0, 0, 0, 0] + "month": current_month.strftime("%B"), + "year": current_month.strftime("%Y"), + "data": [0, 0, 0, 0] }) # Move to the next month @@ -101,20 +99,20 @@ def get_monthly_view(start: datetime, end: datetime, for transportation_entry in transportationEntries: for monthly_entry in monthly_data: - if transportation_entry.date.strftime('%B') == monthly_entry['month'] \ - and transportation_entry.date.strftime('%Y') == monthly_entry['year']: + if transportation_entry.date.strftime("%B") == monthly_entry["month"] \ + and transportation_entry.date.strftime("%Y") == monthly_entry["year"]: if transportation_entry.date.day < 7: - monthly_entry['data'][0] = transportation_entry.calculate_carbon_emissions() + monthly_entry["data"][0] = transportation_entry.calculate_carbon_emissions() elif transportation_entry.date.day < 14: - monthly_entry['data'][1] = transportation_entry.calculate_carbon_emissions() + monthly_entry["data"][1] = transportation_entry.calculate_carbon_emissions() elif transportation_entry.date.day < 21: - monthly_entry['data'][2] = transportation_entry.calculate_carbon_emissions() + monthly_entry["data"][2] = transportation_entry.calculate_carbon_emissions() elif transportation_entry.date.day < 28: - monthly_entry['data'][3] += transportation_entry.calculate_carbon_emissions() + monthly_entry["data"][3] += transportation_entry.calculate_carbon_emissions() else: # If a Month has 5 sunday, we add them to the fourth week - monthly_entry['data'][3] += transportation_entry.calculate_carbon_emissions() / 4 + monthly_entry["data"][3] += transportation_entry.calculate_carbon_emissions() / 4 return monthly_data def __repr__(self) -> str: - return f'Transportation ID: {self.oid.__str__()}' + return f"Transportation ID: {self.oid.__str__()}" diff --git a/backend/models/user.py b/backend/models/user.py index 9be98dc..3c6354c 100644 --- a/backend/models/user.py +++ b/backend/models/user.py @@ -25,18 +25,16 @@ def __init__(self, oid: ObjectId, full_name: str, email: str, badges: list[str], self.friends = friends self.score = score - def to_json(self, for_mongodb: bool = False) -> json: + def to_json(self) -> json: res = { - '_id': self.oid.__str__(), + '_id': self.oid, 'full_name': self.full_name, 'email': self.email, 'badges': self.badges, 'friends': self.friends, 'score': self.score } - if for_mongodb: - return res - return json.loads(json_util.dumps(res)) + return res @staticmethod def from_json(doc: json) -> User: diff --git a/backend/routes/transportation.py b/backend/routes/transportation.py index 5964563..3fe02ee 100644 --- a/backend/routes/transportation.py +++ b/backend/routes/transportation.py @@ -1,4 +1,5 @@ # Python Imports +import json from datetime import datetime from bson import ObjectId from flask import Blueprint, Response, jsonify, request @@ -14,12 +15,12 @@ @transportation_service.route("/transportation/", methods=['GET']) -@carbon_auth.auth.login_required +# @carbon_auth.auth.login_required def get_transportation(oid: str) -> Response: query = {"_id": ObjectId(oid)} item = CarbonTrackDB.transportation_coll.find_one(query) item = TransportationEntry.from_json(item).to_json() - return jsonify({'transportation': item}) + return jsonify({"transportation": item}) @transportation_service.route("/get_transportations_entries_for_user_using_data_range", methods=['POST']) @@ -61,7 +62,7 @@ def get_transportation_metric_for_today() -> Response: def create_transportation(user_id: ObjectId) -> Response: transportation = TransportationEntry(oid=ObjectId(), user_id=user_id, bus=0, train=0, motorbike=0, electric_car=0, gasoline_car=0, carbon_emissions=0, date=weekly_metric_reset(datetime.today())) - transportation = transportation.to_json(for_mongodb=True) + transportation = transportation.to_json() inserted_id = CarbonTrackDB.transportation_coll.insert_one(transportation).inserted_id transportation = TransportationEntry.from_json(CarbonTrackDB.transportation_coll.find_one({"_id": inserted_id})).to_json() return transportation @@ -71,7 +72,7 @@ def create_transportation(user_id: ObjectId) -> Response: @carbon_auth.auth.login_required def update_transportation(oid: str) -> Response: query = {"_id": ObjectId(oid)} - transportation: dict = TransportationEntry.from_json(request.get_json()['transportation']).to_json(for_mongodb=True) + transportation: dict = TransportationEntry.from_json(request.get_json()['transportation']).to_json() del transportation['_id'] del transportation['date'] del transportation['user_id'] diff --git a/backend/routes/users.py b/backend/routes/users.py index 5654f5d..f9c0890 100644 --- a/backend/routes/users.py +++ b/backend/routes/users.py @@ -31,10 +31,17 @@ def get_user_by_email(user_email: str) -> Response: @carbon_auth.auth.login_required def create_user() -> Response: res: dict = request.get_json()['user'] - user = User.from_json(res).to_json(for_mongodb=True) - inserted_id = CarbonTrackDB.users_coll.insert_one(user).inserted_id - user = User.from_json(CarbonTrackDB.users_coll.find_one({"_id": inserted_id})).to_json() - return user + user = User.from_json(res) + + query = {"email": user.email.lower()} + item = CarbonTrackDB.users_coll.find_one(query) + if item is None: + user = user.to_json() + inserted_id = CarbonTrackDB.users_coll.insert_one(user).inserted_id + user = User.from_json(CarbonTrackDB.users_coll.find_one({"_id": inserted_id})).to_json() + return jsonify({'user': user}) + else: + return jsonify({'error': 'User Already Exits With Same Email, Please Log In'}) @users.route("/user/", methods=['DELETE']) @@ -51,7 +58,7 @@ def delete_user(user_id: str) -> Response: @carbon_auth.auth.login_required def update_user(user_id: str) -> Response: query = {"_id": ObjectId(user_id)} - user = User.from_json(request.get_json()['user']).to_json(for_mongodb=True) + user = User.from_json(request.get_json()['user']).to_json() del user['_id'] CarbonTrackDB.users_coll.update_one(query, {'$set': user}) item = CarbonTrackDB.users_coll.find_one(query) diff --git a/backend/utils/customJSONEncoder.py b/backend/utils/customJSONEncoder.py new file mode 100644 index 0000000..b2f1711 --- /dev/null +++ b/backend/utils/customJSONEncoder.py @@ -0,0 +1,21 @@ +from datetime import datetime +from bson import ObjectId +import json +from flask.json.provider import JSONProvider + + +class CustomJSONEncoder(json.JSONEncoder): + def default(self, obj): + if isinstance(obj, ObjectId): + return obj.__str__() + if isinstance(obj, datetime): + return obj.isoformat() + return super().default(obj) + + +class CustomJSONProvider(JSONProvider): + def dumps(self, obj, **kwargs): + return json.dumps(obj, **kwargs, cls=CustomJSONEncoder) + + def loads(self, s: str | bytes, **kwargs): + return json.loads(s, **kwargs) diff --git a/frontend/package.json b/frontend/package.json index bc461be..680f1da 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -22,12 +22,13 @@ "@react-navigation/stack": "^6.3.19", "axios": "^1.6.0", "bson-objectid": "^2.0.4", + "events": "^3.3.0", "expo": "~49.0.12", "expo-font": "~11.4.0", "expo-status-bar": "~1.6.0", "firebase": "^10.5.2", "formik": "^2.4.5", - "mongodb": "^6.2.0", + "mongodb": "^6.3.0", "react": "^18.2.0", "react-native": "0.72.5", "react-native-chart-kit": "^6.12.0", @@ -42,12 +43,15 @@ "react-native-svg": "13.9.0", "react-native-vector-icons": "^10.0.2", "react-navigation": "^5.0.0", + "stream": "^0.0.2", + "timers": "^0.1.1", "yup": "^1.3.2" }, "devDependencies": { "@babel/core": "^7.20.0", "@types/react": "~18.2.14", "@types/react-navigation": "^3.4.0", + "@types/xml2js": "^0.4.14", "@typescript-eslint/eslint-plugin": "^6.8.0", "@typescript-eslint/parser": "^6.8.0", "eslint": "^8.52.0", diff --git a/frontend/src/APIs/TransportationAPI.ts b/frontend/src/APIs/TransportationAPI.ts index 87bfc6a..b9a2610 100644 --- a/frontend/src/APIs/TransportationAPI.ts +++ b/frontend/src/APIs/TransportationAPI.ts @@ -1,13 +1,14 @@ import FLASK_HTTPS from './FLASK_API'; import type { TransportationEntry, TransportationRes } from '../models/Transportation'; +import { type ObjectId } from 'mongodb'; const routeName = '/transportation'; export const TransportationAPI = { - getTransportation: async (transportationID: string) => { + getTransportation: async (transportationID: ObjectId) => { try { - const res = await FLASK_HTTPS.get(routeName + '/transportation/' + transportationID); + const res = await FLASK_HTTPS.get(routeName + '/transportation/' + transportationID.toHexString()); return res.data.transportation as TransportationEntry; } catch (error) { console.error('Error fetching transportation from Flask BE: ', error); @@ -43,8 +44,7 @@ export const TransportationAPI = { updateTransportation: async (transportation: TransportationEntry): Promise => { try { - console.log(transportation._id) - const res = await FLASK_HTTPS.patch(routeName + '/transportation/' + transportation._id, { + const res = await FLASK_HTTPS.patch(routeName + '/transportation/' + transportation._id.toString(), { transportation, }); return res.data.transportation as TransportationEntry; diff --git a/frontend/src/APIs/UsersAPI.ts b/frontend/src/APIs/UsersAPI.ts index bb57111..89f52a6 100644 --- a/frontend/src/APIs/UsersAPI.ts +++ b/frontend/src/APIs/UsersAPI.ts @@ -1,6 +1,7 @@ import FLASK_HTTPS from './FLASK_API'; import type { User } from '../models/User'; import firebaseService from '../utilities/firebase'; +import { type ObjectId } from 'mongodb'; const routeName = '/users'; @@ -15,9 +16,9 @@ export const UsersAPI = { } }, - getUser: async (userID: string)=> { + getUser: async (userID: ObjectId)=> { try { - const res = await FLASK_HTTPS.get(routeName + '/user/' + userID); + const res = await FLASK_HTTPS.get(routeName + '/user/' + userID.toHexString()); return res.data.user as User; } catch (error) { console.error('Error fetching user from Flask BE: ', error); @@ -39,10 +40,15 @@ export const UsersAPI = { createUser: async (user: User)=> { try { + console.log(user) const res = await FLASK_HTTPS.put(routeName + '/user', { user, }); - return res.data.user as User; + if (res.data.user != null) { + return res.data.user as User; + } else if (res.data.error != null) { + return res.data.error as string; + } } catch (error) { console.error('Error creating user in Flask BE: ', error); console.error('Temp tip: have you started the backend?: '); @@ -52,7 +58,7 @@ export const UsersAPI = { updateUser: async (user: User)=> { try { - const res = await FLASK_HTTPS.patch(routeName + '/user/' + user._id, { + const res = await FLASK_HTTPS.patch(routeName + '/user/' + user._id.toString(), { user, }); return res.data.user as User; diff --git a/frontend/src/components/types.tsx b/frontend/src/components/types.tsx index de83e96..6b6bed1 100644 --- a/frontend/src/components/types.tsx +++ b/frontend/src/components/types.tsx @@ -27,5 +27,4 @@ export interface WidgetBoxProps { export interface profileWidgetBoxProps { photoURL: string user: User - rank: number } diff --git a/frontend/src/models/Transportation.ts b/frontend/src/models/Transportation.ts index a4bc84c..94fe1fd 100644 --- a/frontend/src/models/Transportation.ts +++ b/frontend/src/models/Transportation.ts @@ -1,9 +1,9 @@ -import type ObjectID from "bson-objectid"; +import { type ObjectId } from "mongodb"; export interface TransportationEntry { - _id: string - user_id: ObjectID + _id: ObjectId + user_id: ObjectId bus: number train: number motorbike: number diff --git a/frontend/src/models/User.ts b/frontend/src/models/User.ts index 3d75fad..0dc8ca6 100644 --- a/frontend/src/models/User.ts +++ b/frontend/src/models/User.ts @@ -1,12 +1,12 @@ -import type ObjectID from "bson-objectid"; +import { type ObjectId } from "mongodb"; export interface User { - _id: string + _id: ObjectId full_name: string email: string badges: string[] - friends: ObjectID[] + friends: ObjectId[] score: number } diff --git a/frontend/src/screens/dashboard.tsx b/frontend/src/screens/dashboard.tsx index 8eaf73a..24d7483 100644 --- a/frontend/src/screens/dashboard.tsx +++ b/frontend/src/screens/dashboard.tsx @@ -55,7 +55,7 @@ export default function DashBoardScreen(): JSX.Element { + /> diff --git a/frontend/src/screens/signup.tsx b/frontend/src/screens/signup.tsx index b893470..db261fe 100644 --- a/frontend/src/screens/signup.tsx +++ b/frontend/src/screens/signup.tsx @@ -7,11 +7,12 @@ import { type StackNavigationProp } from '@react-navigation/stack'; import { useNavigation } from '@react-navigation/native'; import Colors from '../../assets/colorConstants'; import firebaseService from '../utilities/firebase'; -import { createUser } from '../APIs/UsersAPI'; -import ObjectID from 'bson-objectid'; +import { UsersAPI } from '../APIs/UsersAPI'; import { Formik } from 'formik'; import * as Yup from 'yup'; import FormTextField from '../components/forms/formTextField'; +import { type ObjectId } from 'mongodb'; +import ObjectID from 'bson-objectid'; export type StackNavigation = StackNavigationProp; @@ -46,17 +47,28 @@ export default function SignUp(): JSX.Element { const handleSignUp = async (fields: ISignUpFields): Promise => { const { fullName, email, password } = fields; try { - await firebaseService.createUser(email, password); - await createUser({ - _id: new ObjectID(), - full_name: fullName, - email, - badges: [], - friends: [], - score: 0, + await firebaseService.createUser(email, password).then(async () => { + await UsersAPI.createUser({ + _id: (new ObjectID()).toHexString() as unknown as ObjectId, + full_name: fullName, + email, + badges: [], + friends: [], + score: 0, + }).then((res): void => { + console.log(res) + if (typeof res === 'string') { + // TODO: Can we make a warning on the screen using proper design? + console.warn('User was not created: ' + res); + } else { + console.log('User was created succesfully:', email); + navigation.navigate('DashBoard'); + } + }); + }).catch((err) => { + console.warn('User was not created: ' + err); }); - console.log('User was created succesfully:', email); - navigation.navigate('DashBoard'); + } catch (error) { console.error('Error when creating User:', error); } diff --git a/frontend/src/screens/transportationEntryEdit.tsx b/frontend/src/screens/transportationEntryEdit.tsx index 795cb00..28f3e76 100644 --- a/frontend/src/screens/transportationEntryEdit.tsx +++ b/frontend/src/screens/transportationEntryEdit.tsx @@ -104,9 +104,10 @@ export default function TransportationEntryEdit(): JSX.Element { carbon_emissions: transportationEntry.carbon_emissions, date: transportationEntry.date } - void TransportationAPI.updateTransportation(newEntry).then() + void TransportationAPI.updateTransportation(newEntry).then(() => { + navigation.navigate('TransportationHistory'); + }) } - navigation.navigate('TransportationHistory'); }; useEffect(() => { diff --git a/frontend/src/screens/transportationHistory.tsx b/frontend/src/screens/transportationHistory.tsx index 873dac3..b9a96e6 100644 --- a/frontend/src/screens/transportationHistory.tsx +++ b/frontend/src/screens/transportationHistory.tsx @@ -50,7 +50,7 @@ export default function TransportationHistory(): JSX.Element { setTransportationEntry(res) } }); - }, [endDate, loaded, startDate]) + }, [endDate, loaded, startDate, navigation]) if (!loaded || monthlyData === undefined || transportationEntry === undefined) { return <>; diff --git a/frontend/src/widgets/profileWidget.tsx b/frontend/src/widgets/profileWidget.tsx index bc93dd6..e23036f 100644 --- a/frontend/src/widgets/profileWidget.tsx +++ b/frontend/src/widgets/profileWidget.tsx @@ -5,7 +5,7 @@ import { type profileWidgetBoxProps } from '../components/types'; import { useFonts } from 'expo-font'; import { getUserLevel } from '../models/User'; -const ProfileWidgetBox: React.FC = ({ photoURL, user, rank }) => { +const ProfileWidgetBox: React.FC = ({ photoURL, user }) => { const [loaded] = useFonts({ Montserrat: require('../../assets/fonts/MontserratThinRegular.ttf'), Josefin: require('../../assets/fonts/JosefinSansThinRegular.ttf'), @@ -23,11 +23,6 @@ const ProfileWidgetBox: React.FC = ({ photoURL, user, ran Email: {user.email} Level: {getUserLevel(user)} - - {rank} - - - ); }; @@ -47,13 +42,6 @@ const styles = StyleSheet.create({ fontSize: 16, flex: 1, }, - rank:{ - backgroundColor: Colors.LIGHTFGREEN, - opacity: 0.2, - height: 20, - width: '25%', - borderRadius: 30, - }, name: { flex: 1, fontSize: 16, From 9230752dfe52403afd6bb4cd43da980fb44a17bc Mon Sep 17 00:00:00 2001 From: Yazan Armoush Date: Sat, 25 Nov 2023 03:32:13 -0500 Subject: [PATCH 15/16] Update Backend.postman_collection.json --- .../postman/Backend.postman_collection.json | 90 +++++++++++++------ 1 file changed, 63 insertions(+), 27 deletions(-) diff --git a/backend/postman/Backend.postman_collection.json b/backend/postman/Backend.postman_collection.json index 6d25fe3..a6bff7c 100644 --- a/backend/postman/Backend.postman_collection.json +++ b/backend/postman/Backend.postman_collection.json @@ -158,14 +158,14 @@ } }, "url": { - "raw": "{{LocalBaseURL}}/transportation/transportation/651cf22c1d4f7834e2303a44", + "raw": "{{LocalBaseURL}}/transportation/transportation/655d5c7d123217b52115b046", "host": [ "{{LocalBaseURL}}" ], "path": [ "transportation", "transportation", - "651cf22c1d4f7834e2303a44" + "655d5c7d123217b52115b046" ] } }, @@ -177,16 +177,6 @@ "disableBodyPruning": true }, "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjBkMGU4NmJkNjQ3NDBjYWQyNDc1NjI4ZGEyZWM0OTZkZjUyYWRiNWQiLCJ0eXAiOiJKV1QifQ.eyJuYW1lIjoiWWF6YW4gQXJtb3VzaCIsInBpY3R1cmUiOiJodHRwczovL2xoMy5nb29nbGV1c2VyY29udGVudC5jb20vYS9BQ2c4b2NKMThLbUhSTDY3d2NHNDZLNDhaVF9RN0RVbExyN25rOHhJQ2s1S0VNZExVRW9xPXM5Ni1jIiwiaXNzIjoiaHR0cHM6Ly9zZWN1cmV0b2tlbi5nb29nbGUuY29tL2NhcmJvbi10cmFjay1zeXN0ZW0iLCJhdWQiOiJjYXJib24tdHJhY2stc3lzdGVtIiwiYXV0aF90aW1lIjoxNjk4NTMwMzE3LCJ1c2VyX2lkIjoiUkZSbmdJVm1Bd1BHY0pxVFVPalZOeFB4SzVzMSIsInN1YiI6IlJGUm5nSVZtQXdQR2NKcVRVT2pWTnhQeEs1czEiLCJpYXQiOjE2OTg1MzAzMTcsImV4cCI6MTY5ODUzMzkxNywiZW1haWwiOiJ5YXphbi5hcm1vdXNoMUBnbWFpbC5jb20iLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiZmlyZWJhc2UiOnsiaWRlbnRpdGllcyI6eyJnb29nbGUuY29tIjpbIjExNzEyOTg2MDYwMzg2MDUxODQwOCJdLCJlbWFpbCI6WyJ5YXphbi5hcm1vdXNoMUBnbWFpbC5jb20iXX0sInNpZ25faW5fcHJvdmlkZXIiOiJnb29nbGUuY29tIn19.D586V3FjImF4P46sjEw1VuwgHXY5VTHPPrQAzC1thWqe5rASSYqxPRtn53xCD8_mxAzNjPu5zmGLmuiW0AL3x8hs4KiB8jhGlcx_z32mfDOBqvhlqgJw6HEsQRj7kziPvD1gnTt0I4xVuculKFSY6gYeyoii6_A6fN4frqoR59qzb4iNDXnHaKovleUB9VWmzG1cLcvzSP1nXSvj8tNIR_e52yTPq9tpAf-hkzs_1dTS3a65canzVOW_F6plnX79TZJ5N9Wvm7669cX8v2qUb-e5gBkMnU2i70jjheacaXVuHx_cb-JIK12VpmrsVfJGSD_uwCfODHf140lfdr_okg", - "type": "string" - } - ] - }, "method": "GET", "header": [], "body": { @@ -199,37 +189,53 @@ } }, "url": { - "raw": "{{LocalBaseURL}}/transportation/get_transportation_metric_for_today/655151e8875bab2e6811b8b8", + "raw": "{{LocalBaseURL}}/transportation/get_transportation_metric_for_today", "host": [ "{{LocalBaseURL}}" ], "path": [ "transportation", - "get_transportation_metric_for_today", - "655151e8875bab2e6811b8b8" + "get_transportation_metric_for_today" ] } }, "response": [] }, { - "name": "Get Current Transportation Copy", + "name": "Date Range", "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjBkMGU4NmJkNjQ3NDBjYWQyNDc1NjI4ZGEyZWM0OTZkZjUyYWRiNWQiLCJ0eXAiOiJKV1QifQ.eyJuYW1lIjoiWWF6YW4gQXJtb3VzaCIsInBpY3R1cmUiOiJodHRwczovL2xoMy5nb29nbGV1c2VyY29udGVudC5jb20vYS9BQ2c4b2NKMThLbUhSTDY3d2NHNDZLNDhaVF9RN0RVbExyN25rOHhJQ2s1S0VNZExVRW9xPXM5Ni1jIiwiaXNzIjoiaHR0cHM6Ly9zZWN1cmV0b2tlbi5nb29nbGUuY29tL2NhcmJvbi10cmFjay1zeXN0ZW0iLCJhdWQiOiJjYXJib24tdHJhY2stc3lzdGVtIiwiYXV0aF90aW1lIjoxNjk4NTMwMzE3LCJ1c2VyX2lkIjoiUkZSbmdJVm1Bd1BHY0pxVFVPalZOeFB4SzVzMSIsInN1YiI6IlJGUm5nSVZtQXdQR2NKcVRVT2pWTnhQeEs1czEiLCJpYXQiOjE2OTg1MzAzMTcsImV4cCI6MTY5ODUzMzkxNywiZW1haWwiOiJ5YXphbi5hcm1vdXNoMUBnbWFpbC5jb20iLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiZmlyZWJhc2UiOnsiaWRlbnRpdGllcyI6eyJnb29nbGUuY29tIjpbIjExNzEyOTg2MDYwMzg2MDUxODQwOCJdLCJlbWFpbCI6WyJ5YXphbi5hcm1vdXNoMUBnbWFpbC5jb20iXX0sInNpZ25faW5fcHJvdmlkZXIiOiJnb29nbGUuY29tIn19.D586V3FjImF4P46sjEw1VuwgHXY5VTHPPrQAzC1thWqe5rASSYqxPRtn53xCD8_mxAzNjPu5zmGLmuiW0AL3x8hs4KiB8jhGlcx_z32mfDOBqvhlqgJw6HEsQRj7kziPvD1gnTt0I4xVuculKFSY6gYeyoii6_A6fN4frqoR59qzb4iNDXnHaKovleUB9VWmzG1cLcvzSP1nXSvj8tNIR_e52yTPq9tpAf-hkzs_1dTS3a65canzVOW_F6plnX79TZJ5N9Wvm7669cX8v2qUb-e5gBkMnU2i70jjheacaXVuHx_cb-JIK12VpmrsVfJGSD_uwCfODHf140lfdr_okg", - "type": "string" + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"start\": \"2023-01-01T00:00:00Z\",\n \"end\": \"2023-12-01T00:00:00Z\"\n}", + "options": { + "raw": { + "language": "json" } - ] + } }, + "url": { + "raw": "{{LocalBaseURL}}/transportation/get_transportations_entries_for_user_using_data_range", + "host": [ + "{{LocalBaseURL}}" + ], + "path": [ + "transportation", + "get_transportations_entries_for_user_using_data_range" + ] + } + }, + "response": [] + }, + { + "name": "Update Transportation", + "request": { "method": "PATCH", "header": [], "body": { "mode": "raw", - "raw": "{\n \"transportation\": {\n \"_id\": \"6552c6a8d6755e16ea2a73bd\",\n \"bus\": 0,\n \"carbon_emissions\": 0.0,\n \"electric_car\": 3000,\n \"gasoline_car\": 200,\n \"date\": {\n \"$date\": \"2023-11-12T00:00:00Z\"\n },\n \"motorbike\": 50,\n \"plane\": 0,\n \"train\": 50,\n \"user_id\": \"655151e8875bab2e6811b8b8\"\n }\n}", + "raw": "{\n \"transportation\": {\n \"_id\": \"655d5c7d123217b52115b046\",\n \"bus\": 200,\n \"carbon_emissions\": 0.0,\n \"date\": \"2023-11-19T00:00:00Z\",\n \"electric_car\": 4000,\n \"gasoline_car\": 100,\n \"motorbike\": 0,\n \"train\": 0,\n \"user_id\": \"655151e8875bab2e6811b8b8\"\n }\n}", "options": { "raw": { "language": "json" @@ -237,19 +243,39 @@ } }, "url": { - "raw": "{{LocalBaseURL}}/transportation/transportation/6552c6a8d6755e16ea2a73bd", + "raw": "{{LocalBaseURL}}/transportation/transportation/655d625a1df1b3b87663ba71", "host": [ "{{LocalBaseURL}}" ], "path": [ "transportation", "transportation", - "6552c6a8d6755e16ea2a73bd" + "655d625a1df1b3b87663ba71" ] } }, "response": [] } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + } ] }, { @@ -267,6 +293,16 @@ "response": [] } ], + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjBiYmQyOTllODU2MmU3MmYyZThkN2YwMTliYTdiZjAxMWFlZjU1Y2EiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vY2FyYm9uLXRyYWNrLXN5c3RlbSIsImF1ZCI6ImNhcmJvbi10cmFjay1zeXN0ZW0iLCJhdXRoX3RpbWUiOjE3MDA4NDg5NjgsInVzZXJfaWQiOiJ3UFVEaEFiM0Q5ZHpoeDJXa1RBZlBLbnhGSG0xIiwic3ViIjoid1BVRGhBYjNEOWR6aHgyV2tUQWZQS254RkhtMSIsImlhdCI6MTcwMDg0ODk2OCwiZXhwIjoxNzAwODUyNTY4LCJlbWFpbCI6InlhemFuQGdtYWlsLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwiZmlyZWJhc2UiOnsiaWRlbnRpdGllcyI6eyJlbWFpbCI6WyJ5YXphbkBnbWFpbC5jb20iXX0sInNpZ25faW5fcHJvdmlkZXIiOiJwYXNzd29yZCJ9fQ.E1XaTAt-BQ6ub7AdOKV_BR8z533NJA-G9DhXI26rffCtZXj55SPpUSYd71t_jVZ2Q8RSZyrKdOkkbW9pwMKw0zUKRCWLpwS5bbWFUBvcX4qhmGiAO6-ZkHYrn9cJiHr6cToT3baPLCzIYuIFzuABjC4w3LZL4C_UQlWhfF2Qois_Qc7-XdNyNJ7e9n0bSlAlM8rOtbkIy0o4T1_9n1rVwM6hiH2ePGWTOjmjcGCSqichttqbYLIhB5NOTrhYXTuTtJtpV_wr2tdi2lDCFoMXGj8DaLVsq4URHJVf_U7JvWKUg50_8wlIZs9c6Px2ypSGIXEYEwUrjtOsIjRlPdPJDA", + "type": "string" + } + ] + }, "event": [ { "listen": "prerequest", From e34f3f817b8e0e2e28be5c43d3444bc513cec8bb Mon Sep 17 00:00:00 2001 From: Yazan Armoush Date: Sat, 25 Nov 2023 03:33:01 -0500 Subject: [PATCH 16/16] Fixed Linting --- backend/models/transportation.py | 1 - backend/models/user.py | 1 - backend/routes/transportation.py | 1 - 3 files changed, 3 deletions(-) diff --git a/backend/models/transportation.py b/backend/models/transportation.py index f936409..2b03cd2 100644 --- a/backend/models/transportation.py +++ b/backend/models/transportation.py @@ -8,7 +8,6 @@ from datetime import datetime, timezone from models.abstract_db_model import DB_MODEL from bson import ObjectId -from bson import json_util class TransportationEntry(DB_MODEL): diff --git a/backend/models/user.py b/backend/models/user.py index 3c6354c..4167c7e 100644 --- a/backend/models/user.py +++ b/backend/models/user.py @@ -6,7 +6,6 @@ import json from models.abstract_db_model import DB_MODEL from bson import ObjectId -from bson import json_util class User(DB_MODEL): diff --git a/backend/routes/transportation.py b/backend/routes/transportation.py index 3fe02ee..491701b 100644 --- a/backend/routes/transportation.py +++ b/backend/routes/transportation.py @@ -1,5 +1,4 @@ # Python Imports -import json from datetime import datetime from bson import ObjectId from flask import Blueprint, Response, jsonify, request