534 lines
18 KiB
Python
534 lines
18 KiB
Python
|
|
from flask import Flask, request, jsonify
|
|
from flask_cors import CORS
|
|
import mysql.connector
|
|
from mysql.connector import pooling
|
|
import os
|
|
|
|
# --- Database Configuration ---
|
|
DB_HOST = "mysql.theaken.com"
|
|
DB_PORT = 33306
|
|
DB_NAME = "db_A027"
|
|
DB_USER = "A027"
|
|
DB_PASSWORD = "E1CelfxqlKoj"
|
|
|
|
# --- Create a connection pool ---
|
|
try:
|
|
db_pool = pooling.MySQLConnectionPool(
|
|
pool_name="api_pool",
|
|
pool_size=5,
|
|
host=DB_HOST,
|
|
port=DB_PORT,
|
|
database=DB_NAME,
|
|
user=DB_USER,
|
|
password=DB_PASSWORD
|
|
)
|
|
print("Database connection pool created successfully.")
|
|
except mysql.connector.Error as err:
|
|
print(f"Error creating connection pool: {err}")
|
|
exit()
|
|
|
|
# --- Flask App Initialization ---
|
|
app = Flask(__name__)
|
|
CORS(app) # Enable CORS for all routes
|
|
|
|
# --- Helper Functions ---
|
|
def get_db_connection():
|
|
"""Get a connection from the pool."""
|
|
try:
|
|
return db_pool.get_connection()
|
|
except mysql.connector.Error as err:
|
|
print(f"Error getting connection from pool: {err}")
|
|
return None
|
|
|
|
def make_response(status, code, message, data=None):
|
|
"""Standardized response format."""
|
|
response = {
|
|
"status": status,
|
|
"code": code,
|
|
"message": message
|
|
}
|
|
if data is not None:
|
|
response["data"] = data
|
|
return jsonify(response), code
|
|
|
|
# --- Error Handlers ---
|
|
@app.errorhandler(404)
|
|
def not_found(error):
|
|
return make_response("error", 404, "The requested resource was not found.")
|
|
|
|
@app.errorhandler(500)
|
|
def internal_server_error(error):
|
|
return make_response("error", 500, "An internal server error occurred.")
|
|
|
|
# --- Employee API Endpoints ---
|
|
|
|
@app.route('/v1/employee', methods=['GET'])
|
|
def get_employees():
|
|
conn = get_db_connection()
|
|
if not conn:
|
|
return make_response("error", 503, "Database service unavailable.")
|
|
cursor = conn.cursor(dictionary=True)
|
|
|
|
query = "SELECT * FROM employee WHERE 1=1"
|
|
params = []
|
|
|
|
if 'id' in request.args:
|
|
query += " AND id = %s"
|
|
params.append(request.args['id'])
|
|
if 'name' in request.args:
|
|
query += " AND name LIKE %s"
|
|
params.append(f"%{request.args['name']}%")
|
|
|
|
try:
|
|
cursor.execute(query, tuple(params))
|
|
employees = cursor.fetchall()
|
|
return make_response("success", 200, "Employees retrieved successfully.", {"employees": employees, "meta": {"total": len(employees)}})
|
|
except mysql.connector.Error as err:
|
|
return make_response("error", 500, str(err))
|
|
finally:
|
|
cursor.close()
|
|
conn.close()
|
|
|
|
@app.route('/v1/employee/<string:emp_id>', methods=['GET'])
|
|
def get_employee(emp_id):
|
|
conn = get_db_connection()
|
|
if not conn:
|
|
return make_response("error", 503, "Database service unavailable.")
|
|
cursor = conn.cursor(dictionary=True)
|
|
|
|
try:
|
|
cursor.execute("SELECT * FROM employee WHERE id = %s", (emp_id,))
|
|
employee = cursor.fetchone()
|
|
if employee:
|
|
return make_response("success", 200, "Employee found.", employee)
|
|
else:
|
|
return make_response("error", 404, "Employee not found.")
|
|
except mysql.connector.Error as err:
|
|
return make_response("error", 500, str(err))
|
|
finally:
|
|
cursor.close()
|
|
conn.close()
|
|
|
|
@app.route('/v1/employee', methods=['POST'])
|
|
def create_employee():
|
|
data = request.get_json()
|
|
if not data or 'id' not in data or 'name' not in data:
|
|
return make_response("error", 400, "Missing required fields: id, name.")
|
|
|
|
conn = get_db_connection()
|
|
if not conn:
|
|
return make_response("error", 503, "Database service unavailable.")
|
|
cursor = conn.cursor(dictionary=True)
|
|
|
|
try:
|
|
cursor.execute("INSERT INTO employee (id, name) VALUES (%s, %s)", (data['id'], data['name']))
|
|
conn.commit()
|
|
cursor.execute("SELECT * FROM employee WHERE id = %s", (data['id'],))
|
|
new_employee = cursor.fetchone()
|
|
return make_response("success", 201, "Employee created successfully.", new_employee)
|
|
except mysql.connector.Error as err:
|
|
conn.rollback()
|
|
return make_response("error", 500, str(err))
|
|
finally:
|
|
cursor.close()
|
|
conn.close()
|
|
|
|
@app.route('/v1/employee/<string:emp_id>', methods=['PATCH'])
|
|
def update_employee(emp_id):
|
|
data = request.get_json()
|
|
if not data:
|
|
return make_response("error", 400, "No update data provided.")
|
|
|
|
conn = get_db_connection()
|
|
if not conn:
|
|
return make_response("error", 503, "Database service unavailable.")
|
|
cursor = conn.cursor(dictionary=True)
|
|
|
|
updates = []
|
|
params = []
|
|
if 'name' in data:
|
|
updates.append("name = %s")
|
|
params.append(data['name'])
|
|
|
|
if not updates:
|
|
return make_response("error", 400, "No valid fields to update.")
|
|
|
|
params.append(emp_id)
|
|
query = f"UPDATE employee SET {', '.join(updates)} WHERE id = %s"
|
|
|
|
try:
|
|
cursor.execute(query, tuple(params))
|
|
conn.commit()
|
|
if cursor.rowcount == 0:
|
|
return make_response("error", 404, "Employee not found or data is the same.")
|
|
|
|
cursor.execute("SELECT * FROM employee WHERE id = %s", (emp_id,))
|
|
updated_employee = cursor.fetchone()
|
|
return make_response("success", 200, "Employee updated successfully.", updated_employee)
|
|
except mysql.connector.Error as err:
|
|
conn.rollback()
|
|
return make_response("error", 500, str(err))
|
|
finally:
|
|
cursor.close()
|
|
conn.close()
|
|
|
|
@app.route('/v1/employee/<string:emp_id>', methods=['DELETE'])
|
|
def delete_employee(emp_id):
|
|
conn = get_db_connection()
|
|
if not conn:
|
|
return make_response("error", 503, "Database service unavailable.")
|
|
cursor = conn.cursor()
|
|
|
|
try:
|
|
# Note: Deleting an employee might fail if they have existing votes due to foreign key constraints.
|
|
# This should be handled by the application logic (e.g., delete votes first or prevent deletion).
|
|
cursor.execute("DELETE FROM employee WHERE id = %s", (emp_id,))
|
|
conn.commit()
|
|
if cursor.rowcount == 0:
|
|
return make_response("error", 404, "Employee not found.")
|
|
return make_response("success", 204, "Employee deleted successfully.")
|
|
except mysql.connector.Error as err:
|
|
conn.rollback()
|
|
# Check for foreign key constraint violation
|
|
if err.errno == 1451:
|
|
return make_response("error", 409, "Cannot delete employee with existing votes. Please remove their votes first.")
|
|
return make_response("error", 500, str(err))
|
|
finally:
|
|
cursor.close()
|
|
conn.close()
|
|
|
|
|
|
# --- Menu Items API Endpoints ---
|
|
|
|
@app.route('/v1/menu_items', methods=['GET'])
|
|
def get_menu_items():
|
|
conn = get_db_connection()
|
|
if not conn:
|
|
return make_response("error", 503, "Database service unavailable.")
|
|
cursor = conn.cursor(dictionary=True)
|
|
|
|
query = "SELECT * FROM menu_items WHERE 1=1"
|
|
params = []
|
|
|
|
if 'id' in request.args:
|
|
query += " AND id = %s"
|
|
params.append(request.args['id'])
|
|
if 'main_course' in request.args:
|
|
query += " AND main_course LIKE %s"
|
|
params.append(f"%{request.args['main_course']}%")
|
|
if 'side_dish' in request.args:
|
|
query += " AND side_dish LIKE %s"
|
|
params.append(f"%{request.args['side_dish']}%")
|
|
if 'addon' in request.args:
|
|
query += " AND addon LIKE %s"
|
|
params.append(f"%{request.args['addon']}%")
|
|
if 'menu_date' in request.args:
|
|
query += " AND menu_date = %s"
|
|
params.append(request.args['menu_date'])
|
|
|
|
try:
|
|
cursor.execute(query, tuple(params))
|
|
items = cursor.fetchall()
|
|
return make_response("success", 200, "Menu items retrieved successfully.", {"menu_items": items, "meta": {"total": len(items)}})
|
|
except mysql.connector.Error as err:
|
|
return make_response("error", 500, str(err))
|
|
finally:
|
|
cursor.close()
|
|
conn.close()
|
|
|
|
@app.route('/v1/menu_items/<int:item_id>', methods=['GET'])
|
|
def get_menu_item(item_id):
|
|
conn = get_db_connection()
|
|
if not conn:
|
|
return make_response("error", 503, "Database service unavailable.")
|
|
cursor = conn.cursor(dictionary=True)
|
|
|
|
try:
|
|
cursor.execute("SELECT * FROM menu_items WHERE id = %s", (item_id,))
|
|
item = cursor.fetchone()
|
|
if item:
|
|
return make_response("success", 200, "Menu item found.", item)
|
|
else:
|
|
return make_response("error", 404, "Menu item not found.")
|
|
except mysql.connector.Error as err:
|
|
return make_response("error", 500, str(err))
|
|
finally:
|
|
cursor.close()
|
|
conn.close()
|
|
|
|
@app.route('/v1/menu_items', methods=['POST'])
|
|
def create_menu_item():
|
|
data = request.get_json()
|
|
if not data or not all(k in data for k in ['main_course', 'side_dish', 'addon', 'menu_date']):
|
|
return make_response("error", 400, "Missing required fields: main_course, side_dish, addon, menu_date.")
|
|
|
|
conn = get_db_connection()
|
|
if not conn:
|
|
return make_response("error", 503, "Database service unavailable.")
|
|
cursor = conn.cursor(dictionary=True)
|
|
|
|
try:
|
|
sql = "INSERT INTO menu_items (main_course, side_dish, addon, menu_date) VALUES (%s, %s, %s, %s)"
|
|
params = (data['main_course'], data['side_dish'], data['addon'], data['menu_date'])
|
|
cursor.execute(sql, params)
|
|
new_item_id = cursor.lastrowid
|
|
conn.commit()
|
|
|
|
cursor.execute("SELECT * FROM menu_items WHERE id = %s", (new_item_id,))
|
|
new_item = cursor.fetchone()
|
|
return make_response("success", 201, "Menu item created successfully.", new_item)
|
|
except mysql.connector.Error as err:
|
|
conn.rollback()
|
|
return make_response("error", 500, str(err))
|
|
finally:
|
|
cursor.close()
|
|
conn.close()
|
|
|
|
@app.route('/v1/menu_items/<int:item_id>', methods=['PATCH'])
|
|
def update_menu_item(item_id):
|
|
data = request.get_json()
|
|
if not data:
|
|
return make_response("error", 400, "No update data provided.")
|
|
|
|
conn = get_db_connection()
|
|
if not conn:
|
|
return make_response("error", 503, "Database service unavailable.")
|
|
cursor = conn.cursor(dictionary=True)
|
|
|
|
updates = []
|
|
params = []
|
|
for key in ['main_course', 'side_dish', 'addon', 'menu_date']:
|
|
if key in data:
|
|
updates.append(f"{key} = %s")
|
|
params.append(data[key])
|
|
|
|
if not updates:
|
|
return make_response("error", 400, "No valid fields to update.")
|
|
|
|
params.append(item_id)
|
|
query = f"UPDATE menu_items SET {', '.join(updates)} WHERE id = %s"
|
|
|
|
try:
|
|
cursor.execute(query, tuple(params))
|
|
conn.commit()
|
|
if cursor.rowcount == 0:
|
|
return make_response("error", 404, "Menu item not found or data is the same.")
|
|
|
|
cursor.execute("SELECT * FROM menu_items WHERE id = %s", (item_id,))
|
|
updated_item = cursor.fetchone()
|
|
return make_response("success", 200, "Menu item updated successfully.", updated_item)
|
|
except mysql.connector.Error as err:
|
|
conn.rollback()
|
|
return make_response("error", 500, str(err))
|
|
finally:
|
|
cursor.close()
|
|
conn.close()
|
|
|
|
@app.route('/v1/menu_items/<int:item_id>', methods=['DELETE'])
|
|
def delete_menu_item(item_id):
|
|
conn = get_db_connection()
|
|
if not conn:
|
|
return make_response("error", 503, "Database service unavailable.")
|
|
cursor = conn.cursor()
|
|
|
|
try:
|
|
cursor.execute("DELETE FROM menu_items WHERE id = %s", (item_id,))
|
|
conn.commit()
|
|
if cursor.rowcount == 0:
|
|
return make_response("error", 404, "Menu item not found.")
|
|
return make_response("success", 204, "Menu item deleted successfully.")
|
|
except mysql.connector.Error as err:
|
|
conn.rollback()
|
|
if err.errno == 1451:
|
|
return make_response("error", 409, "Cannot delete menu item that has been voted on.")
|
|
return make_response("error", 500, str(err))
|
|
finally:
|
|
cursor.close()
|
|
conn.close()
|
|
|
|
|
|
# --- Employee Votes API Endpoints ---
|
|
|
|
@app.route('/v1/employee_votes', methods=['GET'])
|
|
def get_employee_votes():
|
|
conn = get_db_connection()
|
|
if not conn:
|
|
return make_response("error", 503, "Database service unavailable.")
|
|
cursor = conn.cursor(dictionary=True)
|
|
|
|
query = """
|
|
SELECT v.*, e.name
|
|
FROM employee_votes v
|
|
JOIN employee e ON v.emp_id = e.id
|
|
WHERE 1=1
|
|
"""
|
|
params = []
|
|
|
|
if 'id' in request.args:
|
|
query += " AND v.id = %s"
|
|
params.append(request.args['id'])
|
|
if 'emp_id' in request.args:
|
|
query += " AND v.emp_id = %s"
|
|
params.append(request.args['emp_id'])
|
|
if 'name' in request.args: # Search by employee name
|
|
query += " AND e.name LIKE %s"
|
|
params.append(f"%{request.args['name']}%")
|
|
if 'order_date' in request.args:
|
|
query += " AND v.order_date = %s"
|
|
params.append(request.args['order_date'])
|
|
if 'menu_item_id' in request.args:
|
|
query += " AND v.menu_item_id = %s"
|
|
params.append(request.args['menu_item_id'])
|
|
if 'order_qty' in request.args:
|
|
query += " AND v.order_qty = %s"
|
|
params.append(request.args['order_qty'])
|
|
|
|
try:
|
|
cursor.execute(query, tuple(params))
|
|
votes = cursor.fetchall()
|
|
return make_response("success", 200, "Employee votes retrieved successfully.", {"employee_votes": votes, "meta": {"total": len(votes)}})
|
|
except mysql.connector.Error as err:
|
|
return make_response("error", 500, str(err))
|
|
finally:
|
|
cursor.close()
|
|
conn.close()
|
|
|
|
@app.route('/v1/employee_votes/<int:vote_id>', methods=['GET'])
|
|
def get_employee_vote(vote_id):
|
|
conn = get_db_connection()
|
|
if not conn:
|
|
return make_response("error", 503, "Database service unavailable.")
|
|
cursor = conn.cursor(dictionary=True)
|
|
|
|
try:
|
|
cursor.execute("""
|
|
SELECT v.*, e.name
|
|
FROM employee_votes v
|
|
JOIN employee e ON v.emp_id = e.id
|
|
WHERE v.id = %s
|
|
""", (vote_id,))
|
|
vote = cursor.fetchone()
|
|
if vote:
|
|
return make_response("success", 200, "Employee vote found.", vote)
|
|
else:
|
|
return make_response("error", 404, "Employee vote not found.")
|
|
except mysql.connector.Error as err:
|
|
return make_response("error", 500, str(err))
|
|
finally:
|
|
cursor.close()
|
|
conn.close()
|
|
|
|
@app.route('/v1/employee_votes', methods=['POST'])
|
|
def create_employee_vote():
|
|
data = request.get_json()
|
|
required_fields = ['emp_id', 'order_date', 'menu_item_id', 'order_qty']
|
|
if not data or not all(k in data for k in required_fields):
|
|
return make_response("error", 400, f"Missing required fields: {', '.join(required_fields)}.")
|
|
|
|
conn = get_db_connection()
|
|
if not conn:
|
|
return make_response("error", 503, "Database service unavailable.")
|
|
cursor = conn.cursor(dictionary=True)
|
|
|
|
try:
|
|
sql = "INSERT INTO employee_votes (emp_id, order_date, menu_item_id, order_qty) VALUES (%s, %s, %s, %s)"
|
|
params = (data['emp_id'], data['order_date'], data['menu_item_id'], data['order_qty'])
|
|
cursor.execute(sql, params)
|
|
new_vote_id = cursor.lastrowid
|
|
conn.commit()
|
|
|
|
cursor.execute("""
|
|
SELECT v.*, e.name
|
|
FROM employee_votes v
|
|
JOIN employee e ON v.emp_id = e.id
|
|
WHERE v.id = %s
|
|
""", (new_vote_id,))
|
|
new_vote = cursor.fetchone()
|
|
return make_response("success", 201, "Employee vote created successfully.", new_vote)
|
|
except mysql.connector.Error as err:
|
|
conn.rollback()
|
|
if err.errno == 1452: # Foreign key constraint fails
|
|
return make_response("error", 400, "Invalid emp_id or menu_item_id. The specified employee or menu item does not exist.")
|
|
return make_response("error", 500, str(err))
|
|
finally:
|
|
cursor.close()
|
|
conn.close()
|
|
|
|
@app.route('/v1/employee_votes/<int:vote_id>', methods=['PATCH'])
|
|
def update_employee_vote(vote_id):
|
|
data = request.get_json()
|
|
if not data:
|
|
return make_response("error", 400, "No update data provided.")
|
|
|
|
conn = get_db_connection()
|
|
if not conn:
|
|
return make_response("error", 503, "Database service unavailable.")
|
|
cursor = conn.cursor(dictionary=True)
|
|
|
|
updates = []
|
|
params = []
|
|
for key in ['emp_id', 'order_date', 'menu_item_id', 'order_qty']:
|
|
if key in data:
|
|
updates.append(f"{key} = %s")
|
|
params.append(data[key])
|
|
|
|
if not updates:
|
|
return make_response("error", 400, "No valid fields to update.")
|
|
|
|
params.append(vote_id)
|
|
query = f"UPDATE employee_votes SET {', '.join(updates)} WHERE id = %s"
|
|
|
|
try:
|
|
cursor.execute(query, tuple(params))
|
|
conn.commit()
|
|
if cursor.rowcount == 0:
|
|
return make_response("error", 404, "Employee vote not found or data is the same.")
|
|
|
|
cursor.execute("""
|
|
SELECT v.*, e.name
|
|
FROM employee_votes v
|
|
JOIN employee e ON v.emp_id = e.id
|
|
WHERE v.id = %s
|
|
""", (vote_id,))
|
|
updated_vote = cursor.fetchone()
|
|
return make_response("success", 200, "Employee vote updated successfully.", updated_vote)
|
|
except mysql.connector.Error as err:
|
|
conn.rollback()
|
|
if err.errno == 1452:
|
|
return make_response("error", 400, "Invalid emp_id or menu_item_id. The specified employee or menu item does not exist.")
|
|
return make_response("error", 500, str(err))
|
|
finally:
|
|
cursor.close()
|
|
conn.close()
|
|
|
|
@app.route('/v1/employee_votes/<int:vote_id>', methods=['DELETE'])
|
|
def delete_employee_vote(vote_id):
|
|
conn = get_db_connection()
|
|
if not conn:
|
|
return make_response("error", 503, "Database service unavailable.")
|
|
cursor = conn.cursor()
|
|
|
|
try:
|
|
cursor.execute("DELETE FROM employee_votes WHERE id = %s", (vote_id,))
|
|
conn.commit()
|
|
if cursor.rowcount == 0:
|
|
return make_response("error", 404, "Employee vote not found.")
|
|
return make_response("success", 204, "Employee vote deleted successfully.")
|
|
except mysql.connector.Error as err:
|
|
conn.rollback()
|
|
return make_response("error", 500, str(err))
|
|
finally:
|
|
cursor.close()
|
|
conn.close()
|
|
|
|
# --- Main Execution ---
|
|
if __name__ == '__main__':
|
|
# To run this server:
|
|
# 1. Make sure you have Flask, Flask-Cors, and mysql-connector-python installed:
|
|
# pip install Flask Flask-Cors mysql-connector-python
|
|
# 2. Run the script:
|
|
# python api_server.py
|
|
# The server will start on http://127.0.0.1:5000
|
|
app.run(debug=True, host='0.0.0.0', port=5000)
|