diff --git a/.env b/.env new file mode 100644 index 0000000000000000000000000000000000000000..7382c152961f175a2eda068dcd828f281fdc3af5 --- /dev/null +++ b/.env @@ -0,0 +1,5 @@ +DB_HOST=database-1.cz0ucmk42cu5.us-east-1.rds.amazonaws.com +DB_PORT=3306 +DB_USER=admin +DB_PASSWORD=Test#321 +DB_DATABASE=cycle_connect \ No newline at end of file diff --git a/admins_service/main.py b/admins_service/main.py index a9295967746aca42cd9230234a979f24e7d48b68..5c8ad8ec7c470458d1257c2964d41f79d121343b 100644 --- a/admins_service/main.py +++ b/admins_service/main.py @@ -5,6 +5,8 @@ import sqlite3 import mysql.connector from pathlib import Path from typing import Optional, Tuple, List +from dotenv import load_dotenv +import os app = FastAPI() @@ -45,16 +47,20 @@ app.add_middleware( # database='your_database' # ) + + +load_dotenv() # Take environment variables from .env. + db_connection = mysql.connector.connect( - 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='admin', - password='Test#321', - database='cycle_connect' + host=os.getenv('DB_HOST'), + port=os.getenv('DB_PORT'), + user=os.getenv('DB_USER'), + password=os.getenv('DB_PASSWORD'), + database=os.getenv('DB_DATABASE') ) - cursor = db_connection.cursor() + # # Create the Admins table if it doesn't exist # cursor.execute(''' # CREATE TABLE IF NOT EXISTS Admins ( diff --git a/batchjob.py b/batchjob.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/bikes_service/main.py b/bikes_service/main.py index 8887c9d3a6f4ef56f52eb3ffaea8a425dcda6743..f9b6ec31cd843be0da07d9689819fb65c046e46c 100644 --- a/bikes_service/main.py +++ b/bikes_service/main.py @@ -6,7 +6,9 @@ from typing import List import sqlite3 from pathlib import Path from typing import Optional -import mysql.connector +import mysql.connector +from dotenv import load_dotenv +import os app = FastAPI() @@ -34,16 +36,19 @@ app.add_middleware( # database='your_database' # ) +load_dotenv() # Take environment variables from .env. + db_connection = mysql.connector.connect( - 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='admin', - password='Test#321', - database='cycle_connect' + host=os.getenv('DB_HOST'), + port=os.getenv('DB_PORT'), + user=os.getenv('DB_USER'), + password=os.getenv('DB_PASSWORD'), + database=os.getenv('DB_DATABASE') ) cursor = db_connection.cursor() + # Bike model class Bike(BaseModel): model: str @@ -140,11 +145,14 @@ async def read_available_bikes(): @app.get("/bikes/{bike_id}", response_model=Bike) async def read_bike(bike_id: int): - cursor.execute('SELECT * FROM Bikes WHERE bike_id = ?', (bike_id,)) + cursor.execute('SELECT * FROM Bikes WHERE bike_id = %s', (bike_id,)) bike = cursor.fetchone() if bike is None: raise HTTPException(status_code=404, detail="Bike not found") + # Format the last_maintenance_date from datetime.date to string + last_maintenance_date_formatted = bike[6].strftime('%Y-%m-%d') if bike[6] else None + # Create a Bike object from the fetched data bike_obj = Bike( model=bike[1], @@ -152,7 +160,7 @@ async def read_bike(bike_id: int): current_location=bike[3], bike_condition=bike[4], price_per_hour=bike[5], - last_maintenance_date=bike[6], + last_maintenance_date=last_maintenance_date_formatted, maintenance_history=bike[7] ) diff --git a/booking_service/main.py b/booking_service/main.py index 1a1f0b1c9d86471d9c9d948f54dc6aedb6a38554..75700e5f06d6d39483caaf6cf0b1360198cf615e 100644 --- a/booking_service/main.py +++ b/booking_service/main.py @@ -8,6 +8,10 @@ from fastapi.middleware.cors import CORSMiddleware # type: ignore import sqlite3 from pathlib import Path from typing import List +import mysql.connector +from dotenv import load_dotenv +import os + app = FastAPI() @@ -20,10 +24,18 @@ app.add_middleware( 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() + +load_dotenv() # Take environment variables from .env. + +db_connection = mysql.connector.connect( + host=os.getenv('DB_HOST'), + port=os.getenv('DB_PORT'), + user=os.getenv('DB_USER'), + password=os.getenv('DB_PASSWORD'), + database=os.getenv('DB_DATABASE') +) +cursor = db_connection.cursor() + class Booking(BaseModel): user_id: int @@ -73,7 +85,7 @@ async def get_all_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,)) + 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 = %s", (booking_id,)) booking = cursor.fetchone() # If no booking is found, raise a 404 error @@ -98,7 +110,7 @@ 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' + WHERE (user_id = %s OR bike_id = %s) AND payment_state = 'Booked' """, (booking.user_id, booking.bike_id)) existing_booking = cursor.fetchone() @@ -106,20 +118,20 @@ async def book_bike(booking: 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,)) + cursor.execute("SELECT wallet_balance FROM Users WHERE user_id = %s", (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,)) + cursor.execute("SELECT current_location, price_per_hour FROM Bikes WHERE bike_id = %s", (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: + if booking.current_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 @@ -137,16 +149,16 @@ async def book_bike(booking: Booking): 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)) + cursor.execute("UPDATE Bikes SET status = 'booked' WHERE bike_id = %s", (booking.bike_id,)) + cursor.execute("UPDATE Users SET wallet_balance = %s WHERE user_id = %s", (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', ?, ?) + VALUES (%s, %s, %s, %s, 'Booked', %s, %s) """, (booking.user_id, booking.bike_id, booking.location, blocked_amount, start_time, lock_code)) - conn.commit() + db_connection.commit() return { "user_id": booking.user_id, @@ -162,6 +174,16 @@ async def book_bike(booking: Booking): @app.put("/bookings/") async def update_booking(update: UpdateBooking): + # Establish a database connection + db_connection = mysql.connector.connect( + host='database-1.cz0ucmk42cu5.us-east-1.rds.amazonaws.com', + port='3306', + user='admin', + password='Test#321', + database='cycle_connect' + ) + cursor = db_connection.cursor() + # Check if the booking with provided booking_id, user_id, and bike_id exists cursor.execute(""" SELECT @@ -171,7 +193,7 @@ async def update_booking(update: UpdateBooking): 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 = ? + B.booking_id = %s AND B.user_id = %s AND B.bike_id = %s """, (update.booking_id, update.user_id, update.bike_id)) booking = cursor.fetchone() @@ -181,9 +203,13 @@ async def update_booking(update: UpdateBooking): # Unpack fetched details start_time, blocked_amount, price_per_hour, current_wallet_balance = booking + # Handle start_time type appropriately + if not isinstance(start_time, datetime): + start_time = datetime.fromisoformat(start_time) + # Calculate total runtime in hours end_time = datetime.now() - total_runtime = (end_time - datetime.fromisoformat(start_time)).total_seconds() / 3600 + total_runtime = (end_time - start_time).total_seconds() / 3600 # Determine final price based on total runtime if total_runtime <= 5: @@ -195,10 +221,11 @@ async def update_booking(update: UpdateBooking): 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)) + cursor.execute("UPDATE Bookings SET end_time = %s, payment_state = 'Ride Completed' WHERE booking_id = %s", (end_time.strftime('%Y-%m-%d %H:%M:%S'), update.booking_id)) + cursor.execute("UPDATE Users SET wallet_balance = %s WHERE user_id = %s", (new_wallet_balance, update.user_id)) - conn.commit() + db_connection.commit() + db_connection.close() return { "message": "Booking updated and charges applied successfully", diff --git a/rentals_service/main.py b/rentals_service/main.py index f4224ced693614770e1239396de0792304e71ce6..eca1a8a3e25b4e7951df46c9b5064700ceff3dcf 100644 --- a/rentals_service/main.py +++ b/rentals_service/main.py @@ -7,6 +7,9 @@ from pathlib import Path from typing import Optional from fastapi import HTTPException import mysql.connector +from decimal import Decimal +from dotenv import load_dotenv +import os app = FastAPI() @@ -19,58 +22,23 @@ app.add_middleware( allow_headers=["*"], ) -# # SQLite connection -# parent_directory = Path(__file__).resolve().parent.parent -# db_file_path = parent_directory / "my_ride.db" -# # print(db_file_path) -# 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' -# ) + +load_dotenv() # Take environment variables from .env. db_connection = mysql.connector.connect( - 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='admin', - password='Test#321', - database='cycle_connect' + host=os.getenv('DB_HOST'), + port=os.getenv('DB_PORT'), + user=os.getenv('DB_USER'), + password=os.getenv('DB_PASSWORD'), + database=os.getenv('DB_DATABASE') ) - cursor = db_connection.cursor() -# # Create the Rentals table if it doesn't exist -# cursor.execute(''' -# CREATE TABLE IF NOT EXISTS Rentals ( -# rental_id INTEGER PRIMARY KEY, -# id INT, -# user_id INT, -# bike_id INT, -# year INT, -# hour INT, -# season INT, -# holiday BOOLEAN, -# workingday BOOLEAN, -# weather INT, -# temp DECIMAL(5, 2), -# atemp DECIMAL(5, 2), -# humidity DECIMAL(5, 2), -# windspeed DECIMAL(5, 2), -# count INT, -# FOREIGN KEY (user_id) REFERENCES Users(user_id), -# FOREIGN KEY (bike_id) REFERENCES Bikes(bike_id) -# ) -# ''') -# conn.commit() # Rental model class Rental(BaseModel): + booking_id: int user_id: int bike_id: int year: int @@ -86,73 +54,15 @@ class Rental(BaseModel): count: int -# # Routes -# @app.post("/rentals/", response_model=Rental) -# async def create_rental(rental: Rental): -# cursor.execute(''' -# INSERT INTO Rentals -# (id, user_id, bike_id, year, hour, season, holiday, workingday, weather, temp, atemp, humidity, windspeed, count) -# VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) -# ''', ( -# rental.id, rental.user_id, rental.bike_id, rental.year, rental.hour, -# rental.season, rental.holiday, rental.workingday, rental.weather, -# rental.temp, rental.atemp, rental.humidity, rental.windspeed, rental.count -# )) -# conn.commit() -# return rental - - -# @app.post("/rentals/", response_model=Rental) -# async def create_rental(rental: Rental): -# try: -# cursor.execute(''' -# INSERT INTO Rentals -# (id, user_id, bike_id, year, hour, season, holiday, workingday, weather, temp, atemp, humidity, windspeed, count) -# VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) -# ''', ( -# rental.id, rental.user_id, rental.bike_id, rental.year, rental.hour, -# rental.season, rental.holiday, rental.workingday, rental.weather, -# rental.temp, rental.atemp, rental.humidity, rental.windspeed, rental.count -# )) -# conn.commit() -# return rental -# except Exception as e: -# # Log the exception -# print(f"Error creating rental: {e}") -# # Raise an HTTPException with status code 422 and error message -# raise HTTPException(status_code=422, detail=str(e)) - - -# @app.post("/rentals/", response_model=Rental) -# async def create_rental(rental: Rental): -# try: -# cursor.execute(''' -# INSERT INTO Rentals -# (id, user_id, bike_id, year, hour, season, holiday, workingday, weather, temp, atemp, humidity, windspeed, count) -# VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) -# ''', ( -# rental.id, rental.user_id, rental.bike_id, rental.year, rental.hour, -# rental.season, rental.holiday, rental.workingday, rental.weather, -# rental.temp, rental.atemp, rental.humidity, rental.windspeed, rental.count -# )) -# conn.commit() -# return rental -# except Exception as e: -# # Log the exception -# print(f"Error creating rental: {e}") -# # Raise an HTTPException with status code 422 and error message -# raise HTTPException(status_code=422, detail=str(e)) - - @app.post("/rentals/", response_model=Rental) async def create_rental(rental: Rental): try: cursor.execute(''' INSERT INTO Rentals - (user_id, bike_id, year, hour, season, holiday, workingday, weather, temp, atemp, humidity, windspeed, count) - VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) + (booking_id, user_id, bike_id, year, hour, season, holiday, workingday, weather, temp, atemp, humidity, windspeed, count) + VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) ''', ( - rental.user_id, rental.bike_id, rental.year, rental.hour, + rental.booking_id, rental.user_id, rental.bike_id, rental.year, rental.hour, rental.season, rental.holiday, rental.workingday, rental.weather, rental.temp, rental.atemp, rental.humidity, rental.windspeed, rental.count )) @@ -160,7 +70,7 @@ async def create_rental(rental: Rental): # Get the auto-generated id of the new rental rental_id = cursor.lastrowid # Return the created rental with the generated id - rental.id = rental_id + rental.booking_id = rental_id return rental except Exception as e: # Log the exception @@ -174,26 +84,9 @@ async def create_rental(rental: Rental): async def read_rentals(): cursor.execute('SELECT * FROM Rentals') rentals = cursor.fetchall() - rental_objects = [] - # for rental in rentals: - # rental_objects.append(Rental( - # id=rental[0], - # user_id=rental[1], - # bike_id=rental[2], - # year=rental[3], - # hour=rental[4], - # season=rental[5], - # holiday=bool(rental[6]), - # workingday=bool(rental[7]), - # weather=rental[8], - # temp=float(rental[9]), - # atemp=float(rental[10]), - # humidity=float(rental[11]), - # windspeed=float(rental[12]), - # count=int(rental[13]) # Convert to integer - # )) - for rental in rentals: - rental_objects.append(Rental( + rental_objects = [ + Rental( + booking_id=rental[0], user_id=rental[1], bike_id=rental[2], year=rental[3], @@ -207,30 +100,49 @@ async def read_rentals(): humidity=float(rental[11]), windspeed=float(rental[12]), count=rental[13] - )) - + ) for rental in rentals + ] return rental_objects @app.get("/rentals/{rental_id}", response_model=Rental) async def read_rental(rental_id: int): - cursor.execute('SELECT * FROM Rentals WHERE rental_id = ?', (rental_id,)) + cursor.execute('SELECT * FROM Rentals WHERE rental_id = %s', (rental_id,)) rental = cursor.fetchone() if rental is None: raise HTTPException(status_code=404, detail="Rental not found") - return rental + + # Convert the tuple to a dictionary + rental_dict = { + 'booking_id': rental[0], + 'user_id': rental[1], + 'bike_id': rental[2], + 'year': rental[3], + 'hour': rental[4], + 'season': rental[5], + 'holiday': bool(rental[6]), + 'workingday': bool(rental[7]), + 'weather': rental[8], + 'temp': float(rental[9]), + 'atemp': float(rental[10]), + 'humidity': float(rental[11]), + 'windspeed': float(rental[12]), + 'count': rental[13] + } + + return Rental(**rental_dict) @app.put("/rentals/{rental_id}", response_model=Rental) async def update_rental(rental_id: int, rental: Rental): cursor.execute(''' UPDATE Rentals - SET id = ?, user_id = ?, bike_id = ?, year = ?, hour = ?, season = ?, - holiday = ?, workingday = ?, weather = ?, temp = ?, atemp = ?, - humidity = ?, windspeed = ?, count = ? - WHERE rental_id = ? + SET booking_id = %s, user_id = %s, bike_id = %s, year = %s, hour = %s, season = %s, + holiday = %s, workingday = %s, weather = %s, temp = %s, atemp = %s, + humidity = %s, windspeed = %s, count = %s + WHERE rental_id = %s ''', ( - rental.id, rental.user_id, rental.bike_id, rental.year, rental.hour, + rental.booking_id, rental.user_id, rental.bike_id, rental.year, rental.hour, rental.season, rental.holiday, rental.workingday, rental.weather, rental.temp, rental.atemp, rental.humidity, rental.windspeed, rental.count, rental_id diff --git a/requirements.txt b/requirements.txt index 6c5d0fd629eb8d43f287e646dcef2a1d9fb7a8f4..a7bfeb9154d409c0ff24328460c9815f66bd274a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -32,3 +32,6 @@ uvicorn==0.29.0 uvloop==0.19.0 watchfiles==0.21.0 websockets==12.0 +mysql-connector-python +apscheduler +python-dotenv diff --git a/reviews_service/main.py b/reviews_service/main.py index 17522db3609be1940d968607b834572d1b5fd51f..bf5ff0b7302748a8cbf785b680f0b8aaa840fff0 100644 --- a/reviews_service/main.py +++ b/reviews_service/main.py @@ -5,7 +5,9 @@ from typing import List import sqlite3 from pathlib import Path from typing import Optional -import mysql.connector +import mysql.connector +from dotenv import load_dotenv +import os app = FastAPI() # Allow requests from all origins @@ -18,47 +20,20 @@ app.add_middleware( ) -# # SQLite connection -# parent_directory = Path(__file__).resolve().parent.parent -# db_file_path = parent_directory / "my_ride.db" -# # print(db_file_path) -# conn = sqlite3.connect(db_file_path) -# cursor = conn.cursor() - -# # Create the Reviews table if it doesn't exist -# cursor.execute(''' -# CREATE TABLE IF NOT EXISTS Reviews ( -# review_id INTEGER PRIMARY KEY AUTOINCREMENT, -# user_id INT, -# bike_id INT, -# rating INT, -# comment TEXT, -# review_date DATETIME, -# FOREIGN KEY (user_id) REFERENCES Users(user_id), -# FOREIGN KEY (bike_id) REFERENCES Bikes(bike_id) -# ) -# ''') -# 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' -# ) + +load_dotenv() # Take environment variables from .env. db_connection = mysql.connector.connect( - 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='admin', - password='Test#321', - database='cycle_connect' + host=os.getenv('DB_HOST'), + port=os.getenv('DB_PORT'), + user=os.getenv('DB_USER'), + password=os.getenv('DB_PASSWORD'), + database=os.getenv('DB_DATABASE') ) - cursor = db_connection.cursor() + # Review model class Review(BaseModel): user_id: int @@ -67,6 +42,21 @@ class Review(BaseModel): comment: str review_date: str +class GetReview(BaseModel): + review_id:int + user_id: int + bike_id: int + rating: int + comment: str + review_date: str + +class UpdateReview(BaseModel): + user_id: Optional[int] = None + bike_id: Optional[int] = None + rating: Optional[int] = None + comment: Optional[str] = None + review_date: Optional[str] = None + # Routes @app.post("/reviews/", response_model=Review) @@ -79,7 +69,8 @@ async def create_review(review: Review): review.user_id, review.bike_id, review.rating, review.comment, review.review_date )) db_connection.commit() - return review + review_id = cursor.lastrowid # Get the last inserted id + return {**review.dict(), "review_id": review_id} @app.get("/reviews/", response_model=List[Review]) @@ -99,27 +90,60 @@ async def read_reviews(): return review_objects -@app.get("/reviews/{review_id}", response_model=Review) +@app.get("/reviews/{review_id}", response_model=GetReview) async def read_review(review_id: int): - cursor.execute('SELECT * FROM Reviews WHERE review_id = %s', (review_id,)) + # Execute the query to fetch the review + cursor.execute('SELECT review_id, user_id, bike_id, rating, comment, review_date FROM Reviews WHERE review_id = %s', (review_id,)) review = cursor.fetchone() if review is None: raise HTTPException(status_code=404, detail="Review not found") - return review + + # Check if review_date is not None and format it as string + review_date_formatted = review[5].strftime('%Y-%m-%d %H:%M:%S') if review[5] else None + + # Return the formatted review data using the GetReview model + return GetReview( + review_id=review[0], + user_id=review[1], + bike_id=review[2], + rating=review[3], + comment=review[4], + review_date=review_date_formatted + ) @app.put("/reviews/{review_id}", response_model=Review) -async def update_review(review_id: int, review: Review): - cursor.execute(''' - UPDATE Reviews - SET user_id = %s, bike_id = %s, rating = %s, comment = %s, review_date = %s - WHERE review_id = %s - ''', ( - review.user_id, review.bike_id, review.rating, review.comment, - review.review_date, review_id - )) +async def update_review(review_id: int, review_update: UpdateReview): + updates = [] + values = [] + for field, value in review_update.dict(exclude_none=True).items(): + updates.append(f"{field} = %s") + values.append(value) + + if not updates: + raise HTTPException(status_code=400, detail="No fields provided for update") + + update_stmt = f"UPDATE Reviews SET {', '.join(updates)} WHERE review_id = %s" + values.append(review_id) + cursor.execute(update_stmt, tuple(values)) db_connection.commit() - return review + + # Fetch the updated review data + cursor.execute('SELECT review_id, user_id, bike_id, rating, comment, review_date FROM Reviews WHERE review_id = %s', (review_id,)) + updated_review = cursor.fetchone() + if updated_review: + review_dict = { + 'review_id': updated_review[0], + 'user_id': updated_review[1], + 'bike_id': updated_review[2], + 'rating': updated_review[3], + 'comment': updated_review[4], + 'review_date': updated_review[5].strftime('%Y-%m-%d %H:%M:%S') if updated_review[5] else None + } + return Review(**review_dict) + else: + raise HTTPException(status_code=404, detail="Error updating review.") + @app.delete("/reviews/{review_id}") diff --git a/users_service/main.py b/users_service/main.py index 7824bf9546283f8b89e65fe09ebc398d6bbff817..956c480b5262380b3ee1ec93112373a9d6cf1880 100644 --- a/users_service/main.py +++ b/users_service/main.py @@ -6,6 +6,10 @@ import sqlite3 import mysql.connector from pathlib import Path from typing import Optional +from datetime import datetime +from dotenv import load_dotenv +import os + app = FastAPI() @@ -18,45 +22,18 @@ app.add_middleware( allow_headers=["*"], ) -# # SQLite connection -# parent_directory = Path(__file__).resolve().parent.parent -# db_file_path = parent_directory / "my_ride.db" -# # print(db_file_path) -# conn = sqlite3.connect(db_file_path) - -# cursor = conn.cursor() - -# # Create the Users table if it doesn't exist -# cursor.execute(''' -# CREATE TABLE IF NOT EXISTS Users ( -# user_id INTEGER PRIMARY KEY, -# username TEXT, -# password TEXT, -# email TEXT, -# phone_number TEXT, -# credit_card_info TEXT, -# registration_date DATETIME, -# last_login DATETIME -# ) -# ''') -# 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' -# ) + + + +load_dotenv() # Take environment variables from .env. db_connection = mysql.connector.connect( - 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='admin', - password='Test#321', - database='cycle_connect' + host=os.getenv('DB_HOST'), + port=os.getenv('DB_PORT'), + user=os.getenv('DB_USER'), + password=os.getenv('DB_PASSWORD'), + database=os.getenv('DB_DATABASE') ) - cursor = db_connection.cursor() class User(BaseModel): @@ -67,6 +44,16 @@ class User(BaseModel): credit_card_info: Optional[str] = None registration_date: str last_login: Optional[str] = None + wallet_balance:Optional[float] = None +class UpdateUserModel(BaseModel): + username: Optional[str] = None + password: Optional[str] = None + email: Optional[str] = None + phone_number: Optional[str] = None + credit_card_info: Optional[str] = None + registration_date: Optional[str] = None + last_login: Optional[str] = None + wallet_balance:Optional[float] = None class UserResponse(BaseModel): @@ -75,12 +62,14 @@ class UserResponse(BaseModel): email: str phone_number: str registration_date: str + last_login: Optional[str] = None + wallet_balance:float # Modify the read_users() function to return a list of UserResponse objects @app.get("/users/", response_model=List[UserResponse]) async def read_users(): - cursor.execute('SELECT user_id, username, email, phone_number, registration_date FROM Users') + cursor.execute('SELECT user_id, username, email, phone_number, registration_date, wallet_balance FROM Users') print("got record") users = cursor.fetchall() user_objects = [] @@ -90,12 +79,16 @@ async def read_users(): username=user[1], email=user[2], phone_number=user[3], - registration_date=user[4].strftime('%Y-%m-%d %H:%M:%S') + registration_date=user[4].strftime('%Y-%m-%d %H:%M:%S') , + wallet_balance=user[5] ) user_objects.append(user_obj) return user_objects + + + # Routes @app.post("/users/", response_model=User) async def create_user(user: User): @@ -114,7 +107,7 @@ async def create_user(user: User): cursor.execute(''' INSERT INTO Users (username, password, email, phone_number, registration_date, last_login) - VALUES (%s, %s, %s, %s, %s) + VALUES (%s, %s, %s, %s, %s, %s) ''', ( user.username, user.password, user.email, user.phone_number, user.registration_date, user.last_login @@ -123,7 +116,7 @@ async def create_user(user: User): cursor.execute(''' INSERT INTO Users (username, password, email, phone_number, credit_card_info, registration_date) - VALUES (%s, %s, %s, %s, %s) + VALUES (%s, %s, %s, %s, %s, %s) ''', ( user.username, user.password, user.email, user.phone_number, user.credit_card_info, user.registration_date @@ -132,7 +125,7 @@ async def create_user(user: User): cursor.execute(''' INSERT INTO Users (username, password, email, phone_number, credit_card_info, registration_date, last_login) - VALUES (%s, %s, %s, %s, %s) + VALUES (%s, %s, %s, %s, %s, %s, %s) ''', ( user.username, user.password, user.email, user.phone_number, user.credit_card_info, user.registration_date, user.last_login @@ -142,28 +135,71 @@ async def create_user(user: User): -@app.get("/users/{user_id}", response_model=User) -async def read_user(user_id: int): - cursor.execute('SELECT * FROM Users WHERE user_id = ?', (user_id,)) + + +@app.get("/users/{username}", response_model=UserResponse) +async def read_user_by_username(username: str): + cursor.execute('SELECT user_id, username, email, phone_number, registration_date, last_login, wallet_balance FROM Users WHERE username = %s', (username,)) user = cursor.fetchone() if user is None: raise HTTPException(status_code=404, detail="User not found") - return user - + + # Format datetime fields as strings + registration_date = user[4].strftime('%Y-%m-%d %H:%M:%S') if user[4] else None + last_login = user[5].strftime('%Y-%m-%d %H:%M:%S') if user[5] else None + + return UserResponse( + user_id=user[0], + username=user[1], + email=user[2], + phone_number=user[3], + registration_date=registration_date, + last_login=last_login, + wallet_balance=user[6] + ) + + +@app.put("/users/{user_id}", response_model=UserResponse) +async def update_user(user_id: int, update_data: UpdateUserModel): + # Fetch the existing user data first to compare what needs to be updated + cursor.execute('SELECT * FROM Users WHERE user_id = %s', (user_id,)) + existing_user = cursor.fetchone() + if not existing_user: + raise HTTPException(status_code=404, detail="User not found") -@app.put("/users/{user_id}", response_model=User) -async def update_user(user_id: int, user: User): - cursor.execute(''' - UPDATE Users - SET username = %s, password = %s, email = %s, phone_number = %s, - credit_card_info = %s, registration_date = %s, last_login = %s - WHERE user_id = %s - ''', ( - user.username, user.password, user.email, user.phone_number, - user.credit_card_info, user.registration_date, user.last_login, user_id - )) + # Prepare the SQL query to update only provided fields + updates = [] + params = [] + for field, value in update_data.dict(exclude_none=True).items(): + if value is not None: + updates.append(f"{field} = %s") + params.append(value) + + if not updates: + raise HTTPException(status_code=400, detail="No valid fields provided for update") + + # Execute the update query + params.append(user_id) + update_stmt = f"UPDATE Users SET {', '.join(updates)} WHERE user_id = %s" + cursor.execute(update_stmt, params) db_connection.commit() - return user + + # Fetch and return the updated user data + cursor.execute('SELECT user_id, username, email, phone_number, registration_date, last_login, wallet_balance FROM Users WHERE user_id = %s', (user_id,)) + updated_user = cursor.fetchone() + if updated_user: + return UserResponse( + user_id=updated_user[0], + username=updated_user[1], + email=updated_user[2], + phone_number=updated_user[3], + registration_date=updated_user[4].strftime('%Y-%m-%d %H:%M:%S') if updated_user[4] else None, + last_login=updated_user[5].strftime('%Y-%m-%d %H:%M:%S') if updated_user[5] else None, + wallet_balance=updated_user[6] + ) + else: + raise HTTPException(status_code=404, detail="Error updating user.") + @app.delete("/users/{user_id}")