Swift-Query / utils /blueprints /assistant.py
Darshan Kumar R
Deploy to Hugging Face Spaces (Clean)
ed0b977
Raw History Blame Contribute Delete
11.2 kB
from flask import Blueprint, render_template, request, redirect, url_for, flash, session, jsonify
from utils.logging.logger_setup import logger
from utils.db.db import mongo, get_next_id
import bcrypt
from functools import wraps
from bson import json_util
import json
assistant_bp = Blueprint('assistant', __name__, template_folder='../../templates', url_prefix='/assistant')
# --- AUTH DECORATORS ---
def assistant_login_required(f):
"""Ensures the user is logged in as an assistant."""
@wraps(f)
def decorated_function(*args, **kwargs):
if session.get('user_type') != 'assistant':
flash('You must be logged in as an assistant to view this page.', 'warning')
return redirect(url_for('auth.assistant_login'))
return f(*args, **kwargs)
return decorated_function
def assistant_role_required(role_name):
"""Ensures the assistant has the required role."""
def decorator(f):
@wraps(f)
def decorated_function(*args, **kwargs):
if session.get('role') != role_name:
flash(f'You require the {role_name} role for this page.', 'warning')
return redirect(url_for('assistant.dashboard'))
return f(*args, **kwargs)
return decorated_function
return decorator
# --- HELPER FUNCTIONS ---
def find_patient(patient_id):
"""Finds a patient by their integer ID or legacy sql_id."""
try:
pid = int(patient_id)
return mongo.db.patient.find_one({"$or": [{"id": pid}, {"sql_id": pid}]})
except (ValueError, TypeError):
return None
# --- CORE ROUTES ---
@assistant_bp.route('/dashboard')
@assistant_login_required
def dashboard():
"""Redirects assistant to their specific dashboard based on role."""
try:
role = session.get('role')
if role == 'account_creation':
return redirect(url_for('assistant.account_creation_dashboard'))
elif role == 'data_entry':
return redirect(url_for('assistant.data_entry_dashboard'))
else:
flash('Your assistant role is not defined. Please contact an administrator.', 'error')
return redirect(url_for('admin.logout'))
except Exception as e:
logger.error(f"Error in assistant dashboard redirect: {e}")
return redirect(url_for('admin.logout'))
@assistant_bp.route('/dashboard/account_creation', methods=['GET', 'POST'])
@assistant_login_required
@assistant_role_required('account_creation')
def account_creation_dashboard():
"""
Handles new patient account creation form.
Verifies no duplicate email exists before creating a new patient record.
"""
if request.method == 'POST':
try:
name = request.form['name']
email = request.form['email']
password = request.form['password']
phone = request.form.get('phone')
if mongo.db.patient.find_one({"email": email}):
flash(f'A patient with the email {email} already exists.', 'error')
else:
hashed_password = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt()).decode('utf-8')
new_id = get_next_id('patient')
new_patient = {
"id": new_id, "sql_id": new_id, "name": name, "email": email,
"password": hashed_password, "phone": phone
}
mongo.db.patient.insert_one(new_patient)
flash(f'Patient "{name}" (ID: {new_id}) created successfully!', 'success')
logger.info(f"Patient account created by {session.get('user_name')}: {email}")
return redirect(url_for('assistant.account_creation_dashboard'))
except Exception as e:
logger.error(f"Error creating patient account: {e}")
flash('An error occurred while creating the account.', 'error')
return render_template('assistant/assistant_account_creation.html')
@assistant_bp.route('/dashboard/data_entry', methods=['GET'])
@assistant_login_required
@assistant_role_required('data_entry')
def data_entry_dashboard():
"""
The main data entry interface.
Allows searching for patients and loading their related medical data.
"""
try:
query = request.args.get('q', '').strip()
selected_patient_id = request.args.get('patient_id')
patients = []
# Search logic
if query:
criteria = [{"name": {"$regex": query, "$options": "i"}},
{"email": {"$regex": query, "$options": "i"}},
{"phone": {"$regex": query, "$options": "i"}}]
if query.isdigit():
criteria.extend([{"id": int(query)}, {"sql_id": int(query)}])
patients = list(mongo.db.patient.find({"$or": criteria}))
else:
# Initial load
patients = list(mongo.db.patient.find().sort("name", 1).limit(50))
# Fetch details
selected_patient = None
related_data = {"medical_history": [], "consultations": [], "test_reports": [],
"treatments": [], "billings": [], "insurance": None}
if selected_patient_id:
selected_patient = find_patient(selected_patient_id)
elif len(patients) == 1:
# Auto-select single result
selected_patient = patients[0]
selected_patient_id = selected_patient.get('id')
if selected_patient:
resolved_id = selected_patient.get('id')
# Update Session for RAG/Chat Context
session['active_patient_id'] = resolved_id
# Fetch related data
related_data["medical_history"] = list(mongo.db.medicalhistory.find({"patient_id": resolved_id}))
related_data["consultations"] = list(mongo.db.consultation.find({"patient_id": resolved_id}))
related_data["test_reports"] = list(mongo.db.testreport.find({"patient_id": resolved_id}))
related_data["treatments"] = list(mongo.db.treatment.find({"patient_id": resolved_id}))
related_data["billings"] = list(mongo.db.billing.find({"patient_id": resolved_id}))
related_data["insurance"] = mongo.db.insurance.find_one({"patient_id": resolved_id})
return render_template(
'assistant/assistant_data_entry.html',
patients=patients,
search_query=query,
selected_patient=selected_patient,
**related_data
)
except Exception as e:
logger.error(f"Error loading data entry dashboard: {e}")
flash("An error occurred loading the dashboard.", "error")
return render_template('assistant/assistant_data_entry.html', patients=[], search_query=query, selected_patient=None)
@assistant_bp.route('/save_complete_record/<patient_id>', methods=['POST'])
@assistant_login_required
@assistant_role_required('data_entry')
def save_complete_record(patient_id):
"""
Handles the batch 'Save Complete Record' POST request.
Updates profile, insurance, and inserts new medical records in a single transaction-like flow.
"""
try:
patient = find_patient(patient_id)
if not patient:
return jsonify({"success": False, "message": "Patient not found"}), 404
data = request.get_json()
if not data:
return jsonify({"success": False, "message": "No data provided"}), 400
resolved_id = patient.get('id')
actions_taken = []
# --- Update Single-Object Fields ---
if 'profile' in data and data['profile']:
mongo.db.patient.update_one({"id": resolved_id}, {"$set": data['profile']})
actions_taken.append("Profile Updated")
if 'insurance' in data and data['insurance']:
mongo.db.insurance.update_one(
{"patient_id": resolved_id},
{"$set": data['insurance']},
upsert=True
)
actions_taken.append("Insurance Updated")
# --- Insert Items from Lists ---
list_map = {
'summary': (mongo.db.medicalhistory, 'medicalhistory'),
'consultation': (mongo.db.consultation, 'consultation'),
'test': (mongo.db.testreport, 'testreport'),
'treatment': (mongo.db.treatment, 'treatment'),
'billing': (mongo.db.billing, 'billing')
}
for key, (collection, id_name) in list_map.items():
if key in data and data[key]:
items_to_insert = []
for item in data[key]:
new_id = get_next_id(id_name)
item.update({'id': new_id, 'sql_id': new_id, 'patient_id': resolved_id})
items_to_insert.append(item)
if items_to_insert:
collection.insert_many(items_to_insert)
actions_taken.append(f"{len(items_to_insert)} {key.capitalize()} record(s) added")
if not actions_taken:
return jsonify({"success": False, "message": "No new data was staged to save."}), 400
msg = f"Success! {', '.join(actions_taken)}."
flash(msg, 'success')
logger.info(f"Complete record saved for patient {resolved_id} by {session.get('user_name')}")
return jsonify({
"success": True,
"message": msg,
"redirect": url_for('assistant.data_entry_dashboard', patient_id=resolved_id)
})
except Exception as e:
logger.error(f"Error in save_complete_record for patient {patient_id}: {e}")
return jsonify({"success": False, "message": f"Server error: {str(e)}"}), 500
@assistant_bp.route('/get_patient_data/<patient_id>', methods=['GET'])
@assistant_login_required
def get_patient_data(patient_id):
"""Fetches all data for a specific patient as JSON."""
try:
selected_patient = find_patient(patient_id)
if not selected_patient:
return jsonify({"error": "Patient not found"}), 404
resolved_id = selected_patient.get('id')
# Update Session for RAG/Chat Context
session['active_patient_id'] = resolved_id
related_data = {
"user": selected_patient, # Profile data
"medical_history": list(mongo.db.medicalhistory.find({"patient_id": resolved_id})),
"consultations": list(mongo.db.consultation.find({"patient_id": resolved_id})),
"test_reports": list(mongo.db.testreport.find({"patient_id": resolved_id})),
"treatments": list(mongo.db.treatment.find({"patient_id": resolved_id})),
"billings": list(mongo.db.billing.find({"patient_id": resolved_id})),
"insurance": mongo.db.insurance.find_one({"patient_id": resolved_id})
}
# Use bson.json_util to handle ObjectId and datetime serialization
return json.loads(json_util.dumps(related_data))
except Exception as e:
logger.error(f"Error in get_patient_data: {e}")
return jsonify({"error": "Failed to fetch patient data"}), 500