Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
.qodo
.env
59 changes: 56 additions & 3 deletions Backend/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import os
from flask import Flask, request, jsonify
from flask_cors import CORS
from werkzeug.exceptions import BadRequest, UnsupportedMediaType
from routes.appointments import appointments_bp
from routes.tasks import tasks_bp
from routes.profile import profile_bp
Expand All @@ -10,11 +11,23 @@
from routes.weight import weight_bp
from routes.blood_pressure import bp_bp
from routes.discharge import discharge_bp
from error_handling.handlers import handle_missing_field_error, handle_not_found_error
from error_handling.error_classes import MissingFieldError, NotFoundError
from agent.agent import get_agent
import argparse


# To enable context-aware error handling
parser = argparse.ArgumentParser(description="Run the Flask backend server.")
parser.add_argument("--env", type=str, default="development", choices=["development", "production"])
args = parser.parse_args()


app = Flask(__name__)
CORS(app)

app.config['ENV'] = args.env # Set environment based on argument

app.register_blueprint(appointments_bp)
app.register_blueprint(tasks_bp)
app.register_blueprint(profile_bp)
Expand All @@ -24,6 +37,43 @@
app.register_blueprint(bp_bp)
app.register_blueprint(discharge_bp)

# Register error handlers

app.register_error_handler(MissingFieldError, handle_missing_field_error)
app.register_error_handler(NotFoundError, handle_not_found_error)

@app.errorhandler(Exception)
def handle_generic_exception(e):
"""Handle generic exceptions."""
mode = app.config.get('ENV', 'development')
if mode == 'development':
response = {"error": "Internal Server Error", "details": str(e)}
else:
response = {"error": "Something went very wrong. Try again later!"}
return jsonify(response), 500

@app.errorhandler(BadRequest)
def handle_bad_request(e):
mode = app.config.get('ENV', 'development')
if mode == 'development':
return jsonify({
"error": "Bad request",
"details": e.description
}), 400
else:
return jsonify({
"error": "Invalid request"
}), 400

@app.errorhandler(UnsupportedMediaType)
# This handles cases where Content-Type is not application/json
# This error is raised by the get_json() method of the request object
def handle_unsupported_media(e):
return jsonify({
"error": "Content-Type must be application/json"
}), 415


@app.teardown_appcontext
def teardown_db(exception):
close_db(exception)
Expand Down Expand Up @@ -183,6 +233,9 @@ def index():
return appointment_db

if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000, debug=True)


parser = argparse.ArgumentParser(description="Run the Flask backend server.")
parser.add_argument("--env", type=str, default="development", choices=["development", "production"])
args = parser.parse_args()
app.config['ENV'] = args.env
port = os.getenv("DEV_PORT", 5000) if app.config['ENV'] == 'development' else os.getenv("PROD_PORT", 8000)
app.run(host='0.0.0.0', port=port, debug=(app.config['ENV'] == 'development'))
15 changes: 15 additions & 0 deletions Backend/error_handling/error_classes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
class MissingFieldError(Exception):
"""Exception raised when a required field is missing in the input data."""
status_code = 400
def __init__(self, field_names: list):
self.field_names = field_names
self.message = "Missing required fields"
super().__init__(self.message)


class NotFoundError(Exception):
status_code = 404
def __init__(self, resource="Resource", resource_id=None):
self.resource = resource
self.resource_id = resource_id
super().__init__()
50 changes: 50 additions & 0 deletions Backend/error_handling/handlers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@

from flask import jsonify, current_app
from sqlite3 import DatabaseError, OperationalError
from functools import wraps


def create_error_response(dev_message, prod_message=None, details=None):
"""Create an error response based on the current environment."""
mode = current_app.config.get('ENV', 'development') # Default to development if not set
response = {"error": dev_message}
if mode == 'development' and details:
response["details"] = details
if mode == 'production' and prod_message:
response["error"] = prod_message
return response


def handle_db_errors(f):
"""Decorator to handle database errors and return JSON responses."""
@wraps(f)
def wrapper(*args, **kwargs):
response = {}
try:
return f(*args, **kwargs)
except OperationalError as e:
response = create_error_response("Database Operational Error", "Database Operation could not be performed. Try Again Later!", details=str(e))
return jsonify(response), 500
except DatabaseError as e:
response = create_error_response("Database Error", details=str(e))
return jsonify(response), 500
return wrapper


def handle_missing_field_error(e):
"""Handle MissingFieldError exceptions."""
mode = current_app.config.get('ENV', 'development') # Default to development if not set
if( mode == 'development'):
response = {"error": e.message, "missing_fields": e.field_names}
else:
response = {"error": e.message}
return jsonify(response), e.status_code

def handle_not_found_error(e):
"""Handle NotFoundError exceptions."""
mode = current_app.config.get('ENV', 'development') # Default to development if not set
if mode == 'development' and e.resource_id is not None:
response = {"error": f"{e.resource} with ID {e.resource_id} not found"}
else:
response = {"error": f"{e.resource} not found"}
return jsonify(response), e.status_code
136 changes: 48 additions & 88 deletions Backend/routes/appointments.py
Original file line number Diff line number Diff line change
@@ -1,119 +1,79 @@
import sqlite3
from flask import Blueprint, jsonify, request
from db.db import open_db, close_db
from db.db import open_db
from error_handling.handlers import handle_db_errors
from error_handling.error_classes import MissingFieldError, NotFoundError

appointments_bp = Blueprint('appointments', __name__)

@appointments_bp.route('/get_appointments', methods=['GET'])
@handle_db_errors
def get_appointments():
db = open_db()

try:
appointments = db.execute('SELECT * FROM appointments').fetchall()
appointments_list = [dict(appt) for appt in appointments]
appointments = db.execute('SELECT * FROM appointments').fetchall()
appointments_list = [dict(appt) for appt in appointments]

return jsonify(appointments_list), 200

except sqlite3.OperationalError:
return jsonify({"error": "Database Error"}), 500
finally:
close_db(db)
return jsonify(appointments_list), 200


@appointments_bp.route('/get_appointment/<int:appointment_id>', methods=['GET'])
@handle_db_errors
def get_appointment(appointment_id):
db = open_db()

if not appointment_id:
return jsonify({"error": "Appointment ID is required"}), 400

try:
appointment = db.execute('SELECT * FROM appointments WHERE id = ?', (appointment_id,)).fetchone()
if not appointment:
return jsonify({"error": "Appointment not found"}), 404
appointment = db.execute('SELECT * FROM appointments WHERE id = ?', (appointment_id,)).fetchone()
if not appointment:
raise NotFoundError(resource="Appointment entry", resource_id=appointment_id)

return jsonify(dict(appointment)), 200
return jsonify(dict(appointment)), 200

except sqlite3.OperationalError:
return jsonify({"error": "Database Error"}), 500
finally:
close_db(db)

@appointments_bp.route('/add_appointment', methods=['POST'])
@handle_db_errors
def add_appointment():
db = open_db()
try:
data = request.json
title = data.get('title')
content = data.get('content')
appointment_date = data.get('appointment_date')
appointment_time = data.get('appointment_time')
appointment_location = data.get('appointment_location')

if not all([title, content, appointment_date, appointment_time, appointment_location]):
return jsonify({"error": "Missing required fields"}), 400

db.execute(
'INSERT INTO appointments (title, content, appointment_date, appointment_time, appointment_location, appointment_status) VALUES (?, ?, ?, ?, ?, ?)',
(title, content, appointment_date, appointment_time, appointment_location, 'pending')
)
db.commit()

return jsonify({"status": "success", "message": "Appointment added successfully"}), 200
data = request.get_json()
required = ['title', 'content', 'appointment_date', 'appointment_time', 'appointment_location']
missing = [field for field in required if field not in data]
if missing:
raise MissingFieldError(missing)

db.execute(
'INSERT INTO appointments (title, content, appointment_date, appointment_time, appointment_location, appointment_status) VALUES (?, ?, ?, ?, ?, ?)',
(data["title"], data["content"], data["appointment_date"], data["appointment_time"], data["appointment_location"], 'pending')
)
db.commit()
return jsonify({"status": "success", "message": "Appointment added successfully"}), 201

except sqlite3.OperationalError:
return jsonify({"error": "Database Error"}), 500
finally:
close_db(db)

@appointments_bp.route('/update_appointment/<int:appointment_id>', methods=['PUT'])
@appointments_bp.route('/update_appointment/<int:appointment_id>', methods=['PATCH'])
@handle_db_errors
def update_appointment(appointment_id):
db = open_db()

existing_appointment = db.execute('SELECT * FROM appointments WHERE id = ?', (appointment_id,)).fetchone()
if not existing_appointment:
return jsonify({"error": "Appointment not found"}), 404

try:
data = request.json
title = data.get('title')
content = data.get('content')
appointment_date = data.get('appointment_date')
appointment_time = data.get('appointment_time')
appointment_location = data.get('appointment_location')
appointment_status = data.get('appointment_status', 'pending')

if not all([title, content, appointment_date, appointment_time, appointment_location]):
return jsonify({"error": "Missing required fields"}), 400

db.execute(
'UPDATE appointments SET title = ?, content = ?, appointment_date = ?, appointment_time = ?, appointment_location = ?, appointment_status = ? WHERE id = ?',
(title, content, appointment_date, appointment_time, appointment_location, appointment_status, appointment_id)
)
db.commit()

return jsonify({"status": "success", "message": "Appointment updated successfully"}), 200
raise NotFoundError(resource="Appointment entry", resource_id=appointment_id)
data = request.get_json()
title = data.get("title", existing_appointment["title"])
content = data.get("content", existing_appointment["content"])
appointment_date = data.get("appointment_date", existing_appointment["appointment_date"])
appointment_time = data.get("appointment_time", existing_appointment["appointment_time"])
appointment_location = data.get("appointment_location", existing_appointment["appointment_location"])
appointment_status = data.get("appointment_status", existing_appointment["appointment_status"])

db.execute(
'UPDATE appointments SET title = ?, content = ?, appointment_date = ?, appointment_time = ?, appointment_location = ?, appointment_status = ? WHERE id = ?',
(title, content, appointment_date, appointment_time, appointment_location, appointment_status, appointment_id)
)
db.commit()
return jsonify({"status": "success", "message": "Appointment updated successfully"}), 200

except sqlite3.OperationalError:
return jsonify({"error": "Database Error"}), 500
finally:
close_db(db)

@appointments_bp.route('/delete_appointment/<int:appointment_id>', methods=['DELETE'])
@handle_db_errors
def delete_appointment(appointment_id):
db = open_db()

existing_appointment = db.execute('SELECT * FROM appointments WHERE id = ?', (appointment_id,)).fetchone()
if not existing_appointment:
return jsonify({"error": "Appointment not found"}), 404

try:
db.execute('DELETE FROM appointments WHERE id = ?', (appointment_id,))
db.commit()

return jsonify({"status": "success", "message": "Appointment deleted successfully"}), 200

except sqlite3.OperationalError:
return jsonify({"error": "Database Error"}), 500
finally:
close_db(db)
result = db.execute('DELETE FROM appointments WHERE id = ?', (appointment_id,))
if result.rowcount == 0:
raise NotFoundError(resource="Appointment entry", resource_id=appointment_id)
db.commit()
return jsonify({"status": "success", "message": "Appointment deleted successfully"}), 200
Loading