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/', 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/', 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/', 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/', 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/', 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/', 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/', 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/', 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/', 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)