diff --git a/backend/app/Dockerfile b/backend/app/Dockerfile index d3c8c450..24f47c5b 100644 --- a/backend/app/Dockerfile +++ b/backend/app/Dockerfile @@ -1,4 +1,7 @@ FROM python:3.8 +RUN apt-get update && apt-get install -y \ + default-mysql-client \ + && rm -rf /var/lib/apt/lists/* EXPOSE 5000 WORKDIR /app COPY requirements.txt /app diff --git a/backend/app/app.py b/backend/app/app.py index f0333fc9..ef3605ab 100644 --- a/backend/app/app.py +++ b/backend/app/app.py @@ -4,6 +4,7 @@ from flask_cors import CORS from apscheduler.schedulers.background import BackgroundScheduler from datetime import datetime, timedelta +import time app = Flask(__name__) CORS(app, resources={r"/api/*": {"origins": "http://localhost:3000"}}, methods=["POST", "OPTIONS", "PUT"]) @@ -23,18 +24,52 @@ 'user': 'root', 'password': 'root', 'host': 'warehouse_db', - 'port': '3308', + 'port': '3309', 'database': 'BrewandBrain_warehouse' } +try: + connection = mysql.connector.connect(**warehouse_db_config) + print("Connected to the warehouse database successfully!") + connection.close() +except mysql.connector.Error as err: + print(f"Error connecting to the warehouse database: {err}") +def wait_for_databases(): + max_retries = 30 + retry_interval = 2 # seconds + + operational_connection = None + warehouse_connection = None + + for _ in range(max_retries): + try: + operational_connection = mysql.connector.connect(**db_config) + print("Connected to the operational database successfully!") + + warehouse_connection = mysql.connector.connect(**warehouse_db_config) + print("Connected to the warehouse database successfully!") + + operational_connection.close() + warehouse_connection.close() + return + except mysql.connector.Error as err: + print(f"Error connecting to databases: {err}") + time.sleep(retry_interval) + print("Unable to connect to databases after retries.") + +wait_for_databases() def get_db_connection(config): + connection = None try: connection = mysql.connector.connect(**config) - return connection except mysql.connector.Error as err: print(f"Error connecting to the database: {err}") - return None + return connection + +def close_connection(connection): + if connection: + connection.close() # User-related functions def write_to_users(data): @@ -148,62 +183,49 @@ def get_all_reservations(): return results except mysql.connector.Error as err: print(f"Error Fetching Reservations: {err}") + def perform_warehouse_process(): + operational_connection = get_db_connection(db_config) + warehouse_connection = get_db_connection(warehouse_db_config) + try: - # Connect to operational database - operational_connection = get_db_connection(db_config) - if operational_connection: - operational_cursor = operational_connection.cursor(dictionary=True) + if not operational_connection or not warehouse_connection: + return # Return early if unable to connect to either database - # Connect to warehouse database - warehouse_connection = get_db_connection(warehouse_db_config) - if warehouse_connection: - warehouse_cursor = warehouse_connection.cursor() + with operational_connection.cursor(dictionary=True) as operational_cursor, \ + warehouse_connection.cursor() as warehouse_cursor: + operational_cursor.execute(""" + SELECT Users.UserID, Users.School, Users.Occupation, Reservations.StartTime, Reservations.EndTime + FROM Users + LEFT JOIN Reservations ON Users.UserID = Reservations.UserID + """) + users_data = operational_cursor.fetchall() + + for user in users_data: try: - # Extract data from the operational database - operational_cursor.execute(""" - SELECT UserID, School, Occupation - FROM Users - """) - users_data = operational_cursor.fetchall() - - # Transform and load data into the warehouse - for user in users_data: - # Fetch reservations for the current user - operational_cursor.execute(""" - SELECT StartTime, EndTime - FROM Reservations - WHERE UserID = %s - """, (user['UserID'],)) - reservations_data = operational_cursor.fetchall() - - # Insert user and reservation data into the warehouse - for reservation in reservations_data: - warehouse_cursor.execute( - """ - INSERT INTO UserSummary (UserID, School, Occupation, StartTime, EndTime) - VALUES (%s, %s, %s, %s, %s) - """, - (user['UserID'], user['School'], user['Occupation'], - reservation['StartTime'], reservation['EndTime']) - ) - - # Commit changes to the warehouse database - warehouse_connection.commit() - print("Warehouse process completed successfully.") - except Exception as e: - print(f"Error during ETL process: {e}") - warehouse_connection.rollback() # Rollback changes in case of an error - - warehouse_cursor.close() - warehouse_connection.close() - - operational_cursor.close() - operational_connection.close() + warehouse_cursor.execute( + """ + INSERT INTO UserSummary (UserID, School, Occupation, StartTime, EndTime) + VALUES (%s, %s, %s, %s, %s) + ON DUPLICATE KEY UPDATE School=VALUES(School), Occupation=VALUES(Occupation), + StartTime=VALUES(StartTime), EndTime=VALUES(EndTime) + """, + (user['UserID'], user['School'], user['Occupation'], + user['StartTime'], user['EndTime']) + ) + except mysql.connector.Error as e: + print(f"Error processing user {user['UserID']}: {e}") + + warehouse_connection.commit() + print("Warehouse process completed successfully.") + except mysql.connector.Error as e: + print(f"Error during ETL process: {e}") + warehouse_connection.rollback() + finally: + close_connection(operational_connection) + close_connection(warehouse_connection) - except Exception as e: - print(f"Error performing warehouse process: {e}") # Initialize the BackgroundScheduler scheduler = BackgroundScheduler() @@ -323,7 +345,20 @@ def sign_in(): return response else: - return jsonify({"message": "Invalid login credentials"}), 401 + return jsonify({"message": "Invalid login credentials"}), 401 + +@app.route('/api/get-user/', methods=['GET']) +def get_user_by_id_route(user_id): + try: + user = get_user_by_id(user_id) + if user: + return jsonify({'user': user}), 200 + else: + return jsonify({'error': 'User not found'}), 404 + except Exception as e: + print(f"Error fetching user by ID: {e}") + return jsonify({'error': str(e)}), 500 + # @app.route('/api/update-account/', methods=['PUT']) # def update_account(user_id): # try: diff --git a/backend/app/tempCodeRunnerFile.py b/backend/app/tempCodeRunnerFile.py index 39852c7d..593c85a7 100644 --- a/backend/app/tempCodeRunnerFile.py +++ b/backend/app/tempCodeRunnerFile.py @@ -1 +1,7 @@ -sign \ No newline at end of file + connection.close() + +# User-related functions +def write_to_users(data): + connection = get_db_connection(db_config) + if connection: + try: \ No newline at end of file diff --git a/backend/db/warehouse_init.sql b/backend/db/warehouse_init.sql index 9ca783ba..419d74cc 100644 --- a/backend/db/warehouse_init.sql +++ b/backend/db/warehouse_init.sql @@ -1,14 +1,30 @@ -CREATE DATABASE BrewandBrain_warehouse; +-- Create the BrewandBrain_warehouse database if not exists +CREATE DATABASE IF NOT EXISTS BrewandBrain_warehouse; +-- Use the BrewandBrain_warehouse database USE BrewandBrain_warehouse; -CREATE TABLE IF NOT EXISTS UserSummary( +-- Drop the UserSummary table if it exists +DROP TABLE IF EXISTS UserSummary; + +-- Create the UserSummary table +CREATE TABLE UserSummary ( UserID INT NOT NULL, - School VARCHAR (100), + ReservationID INT NOT NULL, + School VARCHAR(100), Occupation VARCHAR(100), - StartTime DATETIME, - EndTime DATETIME, + StartTime VARCHAR(255) NOT NULL, + EndTime VARCHAR(225) NOT NULL, + Seat VARCHAR(50), + Gender ENUM('Male', 'Female', 'Other'), PRIMARY KEY (UserID) ); -CREATE INDEX idx_UserSumamry_UserID ON UserSummary(UserID); \ No newline at end of file +-- Create an index on the UserID column +CREATE INDEX idx_UserSummary_UserID ON UserSummary(UserID); + +INSERT INTO UserSummary (UserID, ReservationID, School, Occupation, StartTime, EndTime, Seat, Gender) +VALUES + (1, 101, 'University of the Philippines', 'Student', '2023-01-01 08:00:00', '2023-01-01 10:00:00', 'A1', 'Male'), + (2, 102, 'Tech Institute', 'Engineer', '2023-01-02 09:00:00', '2023-01-02 12:00:00', 'B2', 'Female'), + (3, 103, 'Business School', 'Entrepreneur', '2023-01-03 10:00:00', '2023-01-03 11:30:00', 'C3', 'Other'); \ No newline at end of file diff --git a/backend/docker-compose.yml b/backend/docker-compose.yml index 5baef5f3..0f83a325 100644 --- a/backend/docker-compose.yml +++ b/backend/docker-compose.yml @@ -1,9 +1,8 @@ -version: "2" +version: "3.8" + services: app: build: ./app - links: - - db ports: - "5000:5000" depends_on: @@ -14,6 +13,12 @@ services: limits: cpus: "2" memory: "512M" + environment: + - SQLALCHEMY_DATABASE_URI=mysql://root:root@warehouse_db:3308/BrewandBrain_warehouse + - FLASK_RUN_HOST=0.0.0.0 + - FLASK_ENV=development + networks: + - mynetwork db: image: mysql:8.2 @@ -23,22 +28,19 @@ services: MYSQL_ROOT_PASSWORD: root volumes: - ./db/init.sql:/docker-entrypoint-initdb.d/init.sql - deploy: - resources: - limits: - cpus: "2" - memory: "512M" + networks: + - mynetwork warehouse_db: image: mysql:8.2 ports: - - "3308:3306" + - "3309:3308" environment: MYSQL_ROOT_PASSWORD: root volumes: - ./db/warehouse_init.sql:/docker-entrypoint-initdb.d/warehouse_init.sql - deploy: - resources: - limits: - cpus: "2" - memory: "512M" + networks: + - mynetwork + +networks: + mynetwork: diff --git a/frontend/app/admin_accounts/[userId]/page.tsx b/frontend/app/admin_accounts/[userId]/page.tsx index b5390103..b67f6524 100644 --- a/frontend/app/admin_accounts/[userId]/page.tsx +++ b/frontend/app/admin_accounts/[userId]/page.tsx @@ -1,5 +1,4 @@ -"use client"; - +'use client'; import React, { useState, useEffect } from "react"; import Teste from "@/app/components/account"; import CloseIcon from "@mui/icons-material/Close"; @@ -9,47 +8,60 @@ import TextInput from "@/app/components/text_input"; import Drop from "@/app/components/dropdown_button"; import Butt from "@/app/components/button"; -import { useRouter } from "next/navigation"; +import { useRouter } from "next/router"; + function Page() { const router = useRouter(); const handleBackButtonClick = () => { router.back(); }; + const userId = 1; + + const [formData, setFormData] = useState({ + userName: "", + email: "", + phoneNumber: "", + gender: "", + occupation: "", + }); useEffect(() => { document.title = "Edit Profile"; - }, []); + + const fetchUserData = async () => { + try { + const response = await fetch(`http://localhost:5000/api/get-user/${userId}`); + if (response.ok) { + const userData = await response.json(); + setFormData({ + userName: userData.Username, + email: userData.Email, + phoneNumber: userData.PhoneNumber, + gender: userData.Gender, + occupation: userData.Occupation, + }); + } else { + console.error("Error fetching user data:", await response.json()); + } + } catch (error) { + console.error("Error fetching user data:", error); + } + }; + + fetchUserData(); + }, [userId]); const options = ["Male", "Female", "Others"]; const options1 = ["Student", "Worker"]; - // Fetch user data from local storage - const storedUserData = localStorage.getItem("user"); - const initialFormData = storedUserData ? JSON.parse(storedUserData) : null; - - const [formData, setFormData] = useState<{ - userName: string; - email: string; - phoneNumber: string; - gender: string; - occupation: string; - }>({ - userName: initialFormData ? initialFormData.Username : "", - email: initialFormData ? initialFormData.Email : "", - phoneNumber: initialFormData ? initialFormData.PhoneNumber : "", - gender: initialFormData ? initialFormData.Gender : options[0], - occupation: initialFormData ? initialFormData.Occupation : options1[0], - }); - const userId = initialFormData ? initialFormData.UserID : null; // Adjust this line based on your actual property name - console.log(userId); const handleInputChange = (field: string, value: string) => { setFormData({ ...formData, [field]: value, }); }; - console.log(formData); + const handleUpdateProfile = async () => { try { const response = await fetch( @@ -65,9 +77,7 @@ function Page() { if (response.ok) { const updatedUserData = await response.json(); - localStorage.setItem("user", JSON.stringify(updatedUserData)); console.log("Profile updated successfully:", updatedUserData); - // Optionally, you can update the local state or perform other actions } else { console.error("Error updating profile:", await response.json()); } @@ -144,12 +154,6 @@ function Page() { onSelect={(value) => handleInputChange("occupation", value)} /> - {/* handleInputChange("school", value)} - /> */}
diff --git a/frontend/app/sign_in/page.tsx b/frontend/app/sign_in/page.tsx index b322bf9c..22e024af 100644 --- a/frontend/app/sign_in/page.tsx +++ b/frontend/app/sign_in/page.tsx @@ -1,142 +1,143 @@ -"use client"; -import React, { useEffect } from "react"; -import TextInput from "../components/text_input"; -import Pass from "../components/text_input_pass"; -import Butt from "../components/button"; -import { Logo, Painting } from "../components/svgs"; -import Link from "next/link"; -import { useState } from "react"; -import { motion } from "framer-motion"; -import { signIn, useSession } from "next-auth/react"; -import { useRouter } from "next/navigation"; - -function Page() { - const [username, setUsername] = useState(""); - const [password, setPassword] = useState(""); - const router = useRouter(); - - const handleUsernameChange = (value: string) => { - setUsername(value); - }; - - const handlePasswordChange = (value: string) => { - setPassword(value); - }; - - const handleLogin = async () => { - try { - const response = await fetch("http://localhost:5000/api/sign-in", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ login: username, password }), - }); - - if (response.ok) { - const data = await response.json(); - if (data.user_data.Level === "User") { - console.log("login success", data); - console.log("data", data.user_data); - localStorage.setItem("user", JSON.stringify(data.user_data)); - router.push("/reservation"); - } else { - router.push("/admin_dashboard"); - } - } else { - const errorData = await response.json(); - if (response.status === 401) { - console.error("Authentication failed:", errorData.message); - // Display an alert for incorrect username or password - alert("The User Name or Password is incorrect."); - } else { - console.error("Login failed:", errorData.message); - // Handle other types of errors here - } - } - } catch (error) { - console.error(error); - } - }; - - // const session = useSession(); - // console.log("Current session token:", session.user); - - useEffect(() => { - // Set the title directly for the browser tab - document.title = "Sign In"; - }, []); - - return ( -
-
- - - - - - - - - -
- -

- Forgot Password? -

- - - - - -
- - - - - -
- ); -} - -export default Page; + +"use client"; +import React, { useEffect } from "react"; +import TextInput from "../components/text_input"; +import Pass from "../components/text_input_pass"; +import Butt from "../components/button"; +import { Logo, Painting } from "../components/svgs"; +import Link from "next/link"; +import { useState } from "react"; +import { motion } from "framer-motion"; +import { signIn, useSession } from "next-auth/react"; +import { useRouter } from "next/navigation"; + +function Page() { + const [username, setUsername] = useState(""); + const [password, setPassword] = useState(""); + const router = useRouter(); + + const handleUsernameChange = (value: string) => { + setUsername(value); + }; + + const handlePasswordChange = (value: string) => { + setPassword(value); + }; + + const handleLogin = async () => { + try { + const response = await fetch("http://localhost:5000/api/sign-in", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ login: username, password }), + }); + + if (response.ok) { + const data = await response.json(); + if (data.user_data.Level === "User") { + console.log("login success", data); + console.log("data", data.user_data); + localStorage.setItem("user", JSON.stringify(data.user_data)); + router.push("/reservation"); + } else { + router.push("/admin_dashboard"); + } + } else { + const errorData = await response.json(); + if (response.status === 401) { + console.error("Authentication failed:", errorData.message); + // Display an alert for incorrect username or password + alert("The User Name or Password is incorrect."); + } else { + console.error("Login failed:", errorData.message); + // Handle other types of errors here + } + } + } catch (error) { + console.error(error); + } + }; + + // const session = useSession(); + // console.log("Current session token:", session.user); + + useEffect(() => { + // Set the title directly for the browser tab + document.title = "Sign In"; + }, []); + + return ( +
+
+ + + + + + + + + +
+ +

+ Forgot Password? +

+ + + + + +
+ + + + + +
+ ); +} + +export default Page;