Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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