diff --git a/admins_service/main.py b/admins_service/main.py index a75efda4f942d4ca881b43b100ea1d030b95a8ff..a9295967746aca42cd9230234a979f24e7d48b68 100644 --- a/admins_service/main.py +++ b/admins_service/main.py @@ -37,13 +37,22 @@ app.add_middleware( # cursor = conn.cursor() +# db_connection = mysql.connector.connect( +# host='database_mysql', # or '127.0.0.1' for IPv4 loopback +# port='3306', # Specify the port number here +# user='root', +# password='root_password', +# database='your_database' +# ) + db_connection = mysql.connector.connect( - host='database_mysql', # or '127.0.0.1' for IPv4 loopback + host='database-1.cz0ucmk42cu5.us-east-1.rds.amazonaws.com', # or '127.0.0.1' for IPv4 loopback port='3306', # Specify the port number here - user='root', - password='root_password', - database='your_database' + user='admin', + password='Test#321', + database='cycle_connect' ) + cursor = db_connection.cursor() # # Create the Admins table if it doesn't exist diff --git a/bikes_service/main.py b/bikes_service/main.py index 28103db17d55903c864b8ae8a84642f4d0d52b30..8887c9d3a6f4ef56f52eb3ffaea8a425dcda6743 100644 --- a/bikes_service/main.py +++ b/bikes_service/main.py @@ -35,11 +35,11 @@ app.add_middleware( # ) db_connection = mysql.connector.connect( - host='database_mysql', # or '127.0.0.1' for IPv4 loopback + host='database-1.cz0ucmk42cu5.us-east-1.rds.amazonaws.com', # or '127.0.0.1' for IPv4 loopback port='3306', # Specify the port number here - user='root', - password='root_password', - database='your_database' + user='admin', + password='Test#321', + database='cycle_connect' ) cursor = db_connection.cursor() @@ -118,6 +118,25 @@ async def read_bikes(): bike_objects.append(bike_obj) return bike_objects +@app.get("/bikes/available", response_model=List[BikeResponse]) +async def read_available_bikes(): + cursor.execute('SELECT * FROM Bikes WHERE bike_status = "available"') + bikes = cursor.fetchall() + bike_objects = [] + for bike in bikes: + bike_obj = BikeResponse( + bike_id=bike[0], + model=bike[1], + bike_status=bike[2], + current_location=bike[3], + bike_condition=bike[4], + price_per_hour=bike[5], + last_maintenance_date=str(bike[6]), # Convert date to string + maintenance_history=bike[7] + ) + bike_objects.append(bike_obj) + return bike_objects + @app.get("/bikes/{bike_id}", response_model=Bike) async def read_bike(bike_id: int): @@ -129,9 +148,9 @@ async def read_bike(bike_id: int): # Create a Bike object from the fetched data bike_obj = Bike( model=bike[1], - status=bike[2], - location=bike[3], - condition=bike[4], + bike_status=bike[2], + current_location=bike[3], + bike_condition=bike[4], price_per_hour=bike[5], last_maintenance_date=bike[6], maintenance_history=bike[7] @@ -143,14 +162,14 @@ async def read_bike(bike_id: int): @app.put("/bikes/{bike_id}", response_model=Bike) async def update_bike(bike_id: int, bike: Bike): cursor.execute(''' - UPDATE Bikes - SET model = ?, status = ?, location = ?, condition = ?, - price_per_hour = ?, last_maintenance_date = ?, maintenance_history = ? - WHERE bike_id = ? - ''', ( - bike.model, bike.status, bike.location, bike.condition, - bike.price_per_hour, bike.last_maintenance_date, bike.maintenance_history, bike_id - )) + UPDATE Bikes + SET model = %s, bike_status = %s, current_location = %s, bike_condition = %s, + price_per_hour = %s, last_maintenance_date = %s, maintenance_history = %s + WHERE bike_id = %s +''', ( + bike.model, bike.bike_status, bike.current_location, bike.bike_condition, + bike.price_per_hour, bike.last_maintenance_date, bike.maintenance_history, bike_id +)) db_connection.commit() return bike @@ -160,4 +179,3 @@ async def delete_bike(bike_id: int): cursor.execute('DELETE FROM Bikes WHERE bike_id = ?', (bike_id,)) db_connection.commit() return {"message": "Bike deleted successfully"} - diff --git a/booking_service/Dockerfile b/booking_service/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..4222f300a8939133a41f49cf0c6ac2d32c92c711 --- /dev/null +++ b/booking_service/Dockerfile @@ -0,0 +1,20 @@ +# Use the official Python image as the base image +FROM python:3.9-slim + +# Set the working directory in the container +WORKDIR /app + +# Copy the dependencies file to the working directory +COPY requirements.txt . + +# Install any dependencies +RUN pip install --no-cache-dir -r requirements.txt + +# Copy the rest of the application code to the working directory +COPY . . + +# Expose the port specified by the environment variable +EXPOSE 8006 + +# Command to run the application +CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8006"] diff --git a/booking_service/main.py b/booking_service/main.py new file mode 100644 index 0000000000000000000000000000000000000000..1a1f0b1c9d86471d9c9d948f54dc6aedb6a38554 --- /dev/null +++ b/booking_service/main.py @@ -0,0 +1,212 @@ +from datetime import datetime # Imports the datetime module +import random +from typing import Optional, List +import random +from fastapi import FastAPI, HTTPException # type: ignore +from pydantic import BaseModel # type: ignore +from fastapi.middleware.cors import CORSMiddleware # type: ignore +import sqlite3 +from pathlib import Path +from typing import List + +app = FastAPI() + +# CORS configuration +app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_credentials=True, + allow_methods=["GET", "POST", "PUT", "DELETE"], + allow_headers=["*"], +) + +parent_directory = Path(__file__).resolve().parent.parent +db_file_path = parent_directory / "my_ride.db" +conn = sqlite3.connect(db_file_path, check_same_thread=False) +cursor = conn.cursor() + +class Booking(BaseModel): + user_id: int + bike_id: int + duration_hours: int + location:str # Assuming you pass how many hours the bike will be booked + +class UpdateBooking(BaseModel): + booking_id: int + user_id: int + bike_id: int + +class BookingResponse(BaseModel): + booking_id: int + user_id: int + bike_id: int + location: str + blocked_amount: float + payment_state: str + start_time: datetime + end_time: Optional[datetime] + lock_code: str + + class Config: + arbitrary_types_allowed = True + + +def generate_lock_code(): + return ''.join(random.choices('0123456789', k=6)) + +@app.get("/bookings/", response_model=List[BookingResponse]) +async def get_all_bookings(): + cursor.execute("SELECT booking_id, user_id, bike_id, location, blocked_amount, payment_state, start_time, end_time, lock_code FROM Bookings") + bookings = cursor.fetchall() + return [BookingResponse( + booking_id=booking[0], + user_id=booking[1], + bike_id=booking[2], + location=booking[3], + blocked_amount=booking[4], + payment_state=booking[5], + start_time=booking[6], + end_time=booking[7], + lock_code=booking[8] + ) for booking in bookings] + +@app.get("/bookings/{booking_id}", response_model=BookingResponse) +async def get_booking(booking_id: int): + # Execute the SQL query to fetch booking details by booking_id + cursor.execute("SELECT booking_id, user_id, bike_id, location, blocked_amount, payment_state, start_time, end_time, lock_code FROM Bookings WHERE booking_id = ?", (booking_id,)) + booking = cursor.fetchone() + + # If no booking is found, raise a 404 error + if not booking: + raise HTTPException(status_code=404, detail="Booking not found") + + # Return the booking details in a structured format + return BookingResponse( + booking_id=booking[0], + user_id=booking[1], + bike_id=booking[2], + location=booking[3], + blocked_amount=booking[4], + payment_state=booking[5], + start_time=booking[6], + end_time=booking[7], + lock_code=booking[8] + ) + +@app.post("/bookings/") +async def book_bike(booking: Booking): + # Check for existing active booking for the user_id or bike_id + cursor.execute(""" + SELECT 1 FROM Bookings + WHERE (user_id = ? OR bike_id = ?) AND payment_state = 'Booked' + """, (booking.user_id, booking.bike_id)) + existing_booking = cursor.fetchone() + + if existing_booking: + raise HTTPException(status_code=400, detail="Already Booked") + + # Fetch user details + cursor.execute("SELECT wallet_balance FROM Users WHERE user_id = ?", (booking.user_id,)) + user_details = cursor.fetchone() + if not user_details: + raise HTTPException(status_code=404, detail="User not found.") + wallet_balance = user_details[0] + + # Fetch bike details and validate the location + cursor.execute("SELECT location, price_per_hour FROM Bikes WHERE bike_id = ?", (booking.bike_id,)) + bike_details = cursor.fetchone() + if not bike_details: + raise HTTPException(status_code=404, detail="Bike not found.") + db_location, price_per_hour = bike_details + + if booking.location != db_location: + raise HTTPException(status_code=400, detail="Bike not available at the specified location.") + + # Calculate the amount to be blocked on the user's wallet + blocked_amount = price_per_hour * 5 + + # Validate wallet balance + if wallet_balance < blocked_amount: + raise HTTPException(status_code=400, detail="Insufficient wallet balance.") + + # Calculate new wallet balance + new_wallet_balance = wallet_balance - blocked_amount + + # Generate lock code and current time + lock_code = generate_lock_code() + start_time = datetime.now() + + # Update bike status and user's wallet balance + cursor.execute("UPDATE Bikes SET status = 'booked' WHERE bike_id = ?", (booking.bike_id,)) + cursor.execute("UPDATE Users SET wallet_balance = ? WHERE user_id = ?", (new_wallet_balance, booking.user_id)) + + # Insert a new booking record with lock code + cursor.execute(""" + INSERT INTO Bookings (user_id, bike_id, location, blocked_amount, payment_state, start_time, lock_code) + VALUES (?, ?, ?, ?, 'Booked', ?, ?) + """, (booking.user_id, booking.bike_id, booking.location, blocked_amount, start_time, lock_code)) + + conn.commit() + + return { + "user_id": booking.user_id, + "bike_id": booking.bike_id, + "location": booking.location, + "blocked_amount": blocked_amount, + "new_wallet_balance": new_wallet_balance, + "lock_code": lock_code, + "start_time": start_time.strftime('%Y-%m-%d %H:%M:%S'), + "message": "Bike booked successfully" + } + + +@app.put("/bookings/") +async def update_booking(update: UpdateBooking): + # Check if the booking with provided booking_id, user_id, and bike_id exists + cursor.execute(""" + SELECT + B.start_time, B.blocked_amount, Bi.price_per_hour, U.wallet_balance + FROM + Bookings B + JOIN Bikes Bi ON B.bike_id = Bi.bike_id + JOIN Users U ON B.user_id = U.user_id + WHERE + B.booking_id = ? AND B.user_id = ? AND B.bike_id = ? + """, (update.booking_id, update.user_id, update.bike_id)) + booking = cursor.fetchone() + + if not booking: + raise HTTPException(status_code=404, detail="No matching booking found.") + + # Unpack fetched details + start_time, blocked_amount, price_per_hour, current_wallet_balance = booking + + # Calculate total runtime in hours + end_time = datetime.now() + total_runtime = (end_time - datetime.fromisoformat(start_time)).total_seconds() / 3600 + + # Determine final price based on total runtime + if total_runtime <= 5: + final_price = blocked_amount - (total_runtime * price_per_hour) + else: + final_price = blocked_amount + + # Update wallet balance + new_wallet_balance = current_wallet_balance + final_price + + # Update Booking and Users records + cursor.execute("UPDATE Bookings SET end_time = ?, payment_state = 'Ride Completed' WHERE booking_id = ?", (end_time, update.booking_id)) + cursor.execute("UPDATE Users SET wallet_balance = ? WHERE user_id = ?", (new_wallet_balance, update.user_id)) + + conn.commit() + + return { + "message": "Booking updated and charges applied successfully", + "booking_id": update.booking_id, + "user_id": update.user_id, + "bike_id": update.bike_id, + "end_time": end_time.strftime('%Y-%m-%d %H:%M:%S'), + "final_price": final_price, + "new_wallet_balance": new_wallet_balance, + "payment_state": "Ride Completed" + } \ No newline at end of file diff --git a/booking_service/requirements.txt b/booking_service/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..f93e2cb5c7e364519000770d8325d5d515a9e71d --- /dev/null +++ b/booking_service/requirements.txt @@ -0,0 +1,35 @@ +annotated-types==0.6.0 +anyio==4.3.0 +certifi==2024.2.2 +click==8.1.7 +dnspython==2.6.1 +email_validator==2.1.1 +exceptiongroup==1.2.0 +fastapi==0.110.0 +greenlet==3.0.3 +h11==0.14.0 +httpcore==1.0.5 +httptools==0.6.1 +httpx==0.27.0 +idna==3.6 +itsdangerous==2.1.2 +Jinja2==3.1.3 +MarkupSafe==2.1.5 +mysql-connector-python==8.4.0 +orjson==3.10.0 +pydantic==2.6.4 +pydantic-extra-types==2.6.0 +pydantic-settings==2.2.1 +pydantic_core==2.16.3 +python-dotenv==1.0.1 +python-multipart==0.0.9 +PyYAML==6.0.1 +sniffio==1.3.1 +SQLAlchemy==2.0.29 +starlette==0.36.3 +typing_extensions==4.10.0 +ujson==5.9.0 +uvicorn==0.29.0 +uvloop==0.19.0 +watchfiles==0.21.0 +websockets==12.0 diff --git a/rentals_service/main.py b/rentals_service/main.py index 0b12d38638ac01f12b6896609d411ed0c1855360..f4224ced693614770e1239396de0792304e71ce6 100644 --- a/rentals_service/main.py +++ b/rentals_service/main.py @@ -26,12 +26,20 @@ app.add_middleware( # conn = sqlite3.connect(db_file_path) # cursor = conn.cursor() +# db_connection = mysql.connector.connect( +# host='database_mysql', # or '127.0.0.1' for IPv4 loopback +# port='3306', # Specify the port number here +# user='root', +# password='root_password', +# database='your_database' +# ) + db_connection = mysql.connector.connect( - host='database_mysql', # or '127.0.0.1' for IPv4 loopback + host='database-1.cz0ucmk42cu5.us-east-1.rds.amazonaws.com', # or '127.0.0.1' for IPv4 loopback port='3306', # Specify the port number here - user='root', - password='root_password', - database='your_database' + user='admin', + password='Test#321', + database='cycle_connect' ) cursor = db_connection.cursor() diff --git a/reviews_service/main.py b/reviews_service/main.py index e27a452e02ae6863b6c82f41dadc8fdbea959a49..17522db3609be1940d968607b834572d1b5fd51f 100644 --- a/reviews_service/main.py +++ b/reviews_service/main.py @@ -40,13 +40,22 @@ app.add_middleware( # ''') # conn.commit() +# db_connection = mysql.connector.connect( +# host='database_mysql', # or '127.0.0.1' for IPv4 loopback +# port='3306', # Specify the port number here +# user='root', +# password='root_password', +# database='your_database' +# ) + db_connection = mysql.connector.connect( - host='database_mysql', # or '127.0.0.1' for IPv4 loopback + host='database-1.cz0ucmk42cu5.us-east-1.rds.amazonaws.com', # or '127.0.0.1' for IPv4 loopback port='3306', # Specify the port number here - user='root', - password='root_password', - database='your_database' + user='admin', + password='Test#321', + database='cycle_connect' ) + cursor = db_connection.cursor() diff --git a/sql_files/common_insert.sql b/sql_files/common_insert.sql index dc084b5161a8e9bf2e4337a4d34b94342f78d475..51a58666d8ac587f1524a8d333c245e535da2d2d 100644 --- a/sql_files/common_insert.sql +++ b/sql_files/common_insert.sql @@ -14,6 +14,5 @@ INSERT INTO Reviews (review_id, user_id, bike_id, rating, comment, review_date) VALUES (1, 1, 101, 4, 'Great bike for off-road trails!', '2024-03-29 12:30:00'); -INSERT INTO Admins (admin_id, username, password, email, phone_number) -VALUES -(1, 'admin1', 'adminpass', 'admin1@example.com', '+1122334455'); +INSERT INTO Admins (username, password, email, phone_number) +VALUES ('admin1', 'adminpass', 'admin1@example.com', '+1122334455'); diff --git a/users_service/main.py b/users_service/main.py index 61852a90ccf26f519653388333a4b30c0a9b2aa7..7824bf9546283f8b89e65fe09ebc398d6bbff817 100644 --- a/users_service/main.py +++ b/users_service/main.py @@ -41,13 +41,22 @@ app.add_middleware( # ''') # conn.commit() +# db_connection = mysql.connector.connect( +# host='database_mysql', # or '127.0.0.1' for IPv4 loopback +# port='3306', # Specify the port number here +# user='root', +# password='root_password', +# database='your_database' +# ) + db_connection = mysql.connector.connect( - host='database_mysql', # or '127.0.0.1' for IPv4 loopback + host='database-1.cz0ucmk42cu5.us-east-1.rds.amazonaws.com', # or '127.0.0.1' for IPv4 loopback port='3306', # Specify the port number here - user='root', - password='root_password', - database='your_database' + user='admin', + password='Test#321', + database='cycle_connect' ) + cursor = db_connection.cursor() class User(BaseModel):