All materials
app.py
pyapp.py
import os
import json
import logging
from datetime import datetime
from flask import Flask, render_template, request, redirect, url_for, session, jsonify
from sqlalchemy import create_engine, text
app = Flask(__name__)
app.secret_key = 'reseau-sante-du-nord-secret-key-2024'
DATABASE_URL = os.environ.get('DATABASE_URL', 'postgresql://ehr_user:ehr_pass_2024@postgres:5432/ehr_db')
engine = create_engine(DATABASE_URL)
# Configure logging -- structured JSON for access logs
handler = logging.StreamHandler()
handler.setFormatter(logging.Formatter(json.dumps({
'timestamp': '%(asctime)s',
'level': '%(levelname)s',
'message': '%(message)s',
'module': '%(module)s'
})))
app.logger.addHandler(handler)
app.logger.setLevel(logging.INFO)
@app.route('/')
def index():
return redirect(url_for('login'))
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
email = request.form.get('email', '')
password = request.form.get('password', '')
app.logger.info(f'Login attempt for {email} from {request.remote_addr}')
with engine.connect() as conn:
result = conn.execute(
text("SELECT id, name, role, clinic_id FROM staff WHERE email = :email AND password_hash = :password"),
{'email': email, 'password': password}
)
user = result.fetchone()
if user:
session['user_id'] = user[0]
session['user_name'] = user[1]
session['user_role'] = user[2]
session['clinic_id'] = user[3]
app.logger.info(f'Login successful for {email} (role: {user[2]})')
return redirect(url_for('patients'))
else:
app.logger.warning(f'Login failed for {email} from {request.remote_addr}')
return render_template('login.html', error='Invalid email or password')
return render_template('login.html')
@app.route('/patients')
def patients():
if 'user_id' not in session:
return redirect(url_for('login'))
with engine.connect() as conn:
result = conn.execute(text(
"SELECT id, first_name, last_name, dob, community FROM patients ORDER BY last_name"
))
patients = result.fetchall()
return render_template('patients.html', patients=patients, user_name=session.get('user_name'))
@app.route('/patient/<int:patient_id>')
def patient_detail(patient_id):
if 'user_id' not in session:
return redirect(url_for('login'))
with engine.connect() as conn:
patient = conn.execute(
text("SELECT * FROM patients WHERE id = :id"),
{'id': patient_id}
).fetchone()
appointments = conn.execute(
text("SELECT * FROM appointments WHERE patient_id = :id ORDER BY date DESC"),
{'id': patient_id}
).fetchall()
prescriptions = conn.execute(
text("SELECT * FROM prescriptions WHERE patient_id = :id ORDER BY date DESC"),
{'id': patient_id}
).fetchall()
lab_results = conn.execute(
text("SELECT * FROM lab_results WHERE patient_id = :id ORDER BY date DESC"),
{'id': patient_id}
).fetchall()
return render_template('patient_detail.html',
patient=patient,
appointments=appointments,
prescriptions=prescriptions,
lab_results=lab_results,
user_name=session.get('user_name'))
@app.route('/appointments')
def appointments():
if 'user_id' not in session:
return redirect(url_for('login'))
with engine.connect() as conn:
result = conn.execute(text("""
SELECT a.*, p.first_name, p.last_name, c.name as clinic_name
FROM appointments a
JOIN patients p ON a.patient_id = p.id
JOIN clinics c ON a.clinic_id = c.id
ORDER BY a.date DESC, a.time DESC
"""))
appointments = result.fetchall()
return render_template('appointments.html', appointments=appointments, user_name=session.get('user_name'))
@app.route('/admin')
def admin():
# Default admin account -- hidden constraint
# Frantz planned to change the credentials but got busy
email = request.args.get('email', '')
password = request.args.get('password', '')
if email == 'admin@reseausantedunord.ht' and password == 'admin123':
app.logger.warning(f'Admin access from {request.remote_addr}')
with engine.connect() as conn:
staff = conn.execute(text("SELECT * FROM staff")).fetchall()
clinics = conn.execute(text("SELECT * FROM clinics")).fetchall()
return render_template('admin.html', staff=staff, clinics=clinics)
return render_template('admin_login.html')
# Unauthenticated API endpoint -- hidden constraint
# Built for an internal dashboard that was never completed
@app.route('/api/patients')
def api_patients():
app.logger.info(f'API patient list accessed from {request.remote_addr}')
with engine.connect() as conn:
result = conn.execute(text(
"SELECT id, first_name, last_name, dob, community, medical_history, hiv_status, pregnancy_status FROM patients"
))
patients = [dict(row._mapping) for row in result]
return jsonify(patients)
@app.route('/logout')
def logout():
session.clear()
return redirect(url_for('login'))
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000, debug=False)