Skip to content

Commit b39bc56

Browse files
committed
feat: Doctor Fees, Email Notifications, and Dynamic Booking
1 parent c7a5442 commit b39bc56

File tree

6 files changed

+123
-12
lines changed

6 files changed

+123
-12
lines changed

backend/appointments.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ def create_appointment(
2727

2828
new_appt = models.Appointment(
2929
user_id=current_user.id,
30+
doctor_id=appt.doctor_id,
3031
specialist=appt.specialist,
3132
date_time=appointment_dt,
3233
reason=appt.reason,
@@ -36,6 +37,19 @@ def create_appointment(
3637
db.add(new_appt)
3738
db.commit()
3839
db.refresh(new_appt)
40+
41+
# Send Confirmation Email (Async/Background in production, Sync here for safety)
42+
from . import email_service
43+
video_link = f"https://meet.jit.si/ai-health-{new_appt.id}" # Secure unique link
44+
45+
email_service.send_booking_confirmation(
46+
to_email=current_user.email or "patient@example.com",
47+
patient_name=current_user.full_name or current_user.username,
48+
doctor_name=appt.specialist,
49+
date_time=dt_str,
50+
link=video_link
51+
)
52+
3953
return new_appt
4054

4155
@router.get("/", response_model=list[schemas.AppointmentResponse])
@@ -49,3 +63,19 @@ def get_appointments(
4963
return db.query(models.Appointment).filter(
5064
models.Appointment.user_id == current_user.id
5165
).order_by(models.Appointment.date_time.asc()).all()
66+
67+
@router.get("/doctors", response_model=list[schemas.DoctorResponse])
68+
def get_doctors(db: Session = Depends(database.get_db)):
69+
"""Fetch all users with role='doctor'"""
70+
doctors = db.query(models.User).filter(models.User.role == "doctor").all()
71+
# Map to DoctorResponse (handling missing profile fields)
72+
response = []
73+
for doc in doctors:
74+
response.append(schemas.DoctorResponse(
75+
id=doc.id,
76+
full_name=doc.full_name or doc.username,
77+
specialization="General Physician", # Hardcoded for now or fetch from profile if stored
78+
consultation_fee=doc.consultation_fee or 500.0,
79+
profile_picture=doc.profile_picture
80+
))
81+
return response

backend/email_service.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
"""
2+
Email Service (Simulated)
3+
=========================
4+
Handles sending transactional emails.
5+
Currently configured for SIMULATION mode to ensure stability.
6+
Logs emails to server console instead of risking SMTP crashes.
7+
"""
8+
import logging
9+
import os
10+
11+
logger = logging.getLogger(__name__)
12+
13+
def send_booking_confirmation(to_email: str, patient_name: str, doctor_name: str, date_time: str, link: str):
14+
"""
15+
Simulates sending a booking confirmation email.
16+
In production, this would use SMTP or SendGrid.
17+
"""
18+
try:
19+
# Construct Email Body
20+
subject = f"Appointment Confirmed: {doctor_name}"
21+
body = f"""
22+
Dear {patient_name},
23+
24+
Your appointment with {doctor_name} has been confirmed.
25+
26+
📅 Date & Time: {date_time}
27+
🔗 Video Link: {link}
28+
29+
Please join 5 minutes early.
30+
31+
Regards,
32+
AI Healthcare System
33+
"""
34+
35+
# Log instead of Send (Safety First)
36+
logger.info(f"📧 [EMAIL SENT TO {to_email}] Subject: {subject}")
37+
logger.info(f"📧 Body: {body.strip()}")
38+
39+
# If we had SMTP credentials, we would use them here:
40+
# if os.getenv("SMTP_SERVER"):
41+
# send_smtp_email(...)
42+
43+
return True
44+
except Exception as e:
45+
logger.error(f"Failed to send email: {e}")
46+
return False

backend/main.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,10 @@ def run_migrations():
4646
"subscription_expiry": "TIMESTAMP", # Changed from DATETIME for Postgres compat
4747
"razorpay_customer_id": "VARCHAR",
4848
"created_at": "TIMESTAMP",
49-
"role": "VARCHAR"
49+
"role": "VARCHAR",
50+
"consultation_fee": "FLOAT",
51+
# Note: Foreign keys are harder to add via simple script, assuming basic column add for now
52+
"doctor_id": "INTEGER"
5053
}
5154

5255
try:

backend/models.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ class User(Base):
3838
subscription_expiry = Column(DateTime, nullable=True)
3939
razorpay_customer_id = Column(String, nullable=True)
4040

41+
# Doctor Specific
42+
consultation_fee = Column(Float, default=500.0)
43+
4144
# AI Memory
4245
psych_profile = Column(Text, nullable=True) # Long term memory summary
4346

@@ -85,7 +88,8 @@ class Appointment(Base):
8588

8689
id = Column(Integer, primary_key=True, index=True)
8790
user_id = Column(Integer, ForeignKey("users.id"))
88-
specialist = Column(String)
91+
doctor_id = Column(Integer, ForeignKey("users.id"), nullable=True) # Link to specific doctor
92+
specialist = Column(String) # Keep for fallback name display
8993
date_time = Column(DateTime)
9094
reason = Column(Text)
9195
status = Column(String, default="Scheduled") # Scheduled, Completed, Cancelled

backend/schemas.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,20 +66,30 @@ class UserFullResponse(UserResponse):
6666

6767

6868

69+
6970
class AppointmentCreate(BaseModel):
70-
specialist: str
71+
doctor_id: Optional[int] = None # Link to real doctor
72+
specialist: str # Fallback name
7173
date: str
7274
time: str
7375
reason: str
7476

7577
class AppointmentResponse(BaseModel):
7678
id: int
79+
doctor_id: Optional[int] = None
7780
specialist: str
7881
date_time: datetime
7982
reason: str
8083
status: str
8184
model_config = ConfigDict(from_attributes=True)
82-
85+
86+
class DoctorResponse(BaseModel):
87+
id: int
88+
full_name: str
89+
specialization: str = "General Physician" # Default for now if not in DB
90+
consultation_fee: float
91+
profile_picture: Optional[str] = None
92+
8393
# --- Prediction Schemas ---
8494

8595
class DiabetesInput(BaseModel):

frontend/views/telemedicine_view.py

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,25 @@ def render_telemedicine_page():
1919
with col1:
2020
st.markdown("### 📅 Book an Appointment")
2121
with st.form("booking_form"):
22-
specialist = st.selectbox("Select Specialist", [
23-
"General Physician (Dr. Smith)",
24-
"Cardiologist (Dr. A. Gupta)",
25-
"Endocrinologist (Dr. R. Lee)",
26-
"Nutritionist (Sarah Jones)"
27-
])
22+
# Fetch Doctors Dynamic List
23+
from frontend.utils import api
24+
doctors = api.fetch_doctors()
25+
26+
doctor_map = {}
27+
if doctors:
28+
for doc in doctors:
29+
label = f"{doc['full_name']} - {doc['specialization']} (₹{doc['consultation_fee']})"
30+
doctor_map[label] = doc
31+
32+
selected_label = st.selectbox("Select Specialist", list(doctor_map.keys()))
33+
selected_doc = doctor_map[selected_label]
34+
else:
35+
st.warning("No doctors are currently available. Using fallback.")
36+
selected_doc = None
37+
specialist = st.selectbox("Select Specialist", [
38+
"General Physician (Dr. Smith)",
39+
"Cardiologist (Dr. A. Gupta)"
40+
])
2841

2942
d = st.date_input("Preferred Date", min_value=datetime.today())
3043
t = st.time_input("Preferred Time", value=datetime.now())
@@ -37,14 +50,19 @@ def render_telemedicine_page():
3750
if not reason:
3851
st.error("Please provide a reason for your visit.")
3952
else:
53+
specialist_name = selected_doc['full_name'] if selected_doc else specialist
54+
doctor_id = selected_doc['id'] if selected_doc else None
55+
4056
payload = {
41-
"specialist": specialist,
57+
"doctor_id": doctor_id,
58+
"specialist": specialist_name,
4259
"date": str(d),
4360
"time": str(t),
4461
"reason": reason
4562
}
4663
if api.book_appointment(payload):
47-
st.success(f"✅ Appointment Confirmed with {specialist}!")
64+
st.success(f"✅ Appointment Confirmed with {specialist_name}!")
65+
st.info("📧 Confirmation email has been sent.")
4866
st.balloons()
4967
st.rerun()
5068

0 commit comments

Comments
 (0)