rajkhanke commited on
Commit
b6ac253
Β·
verified Β·
1 Parent(s): 893af13

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +395 -0
app.py ADDED
@@ -0,0 +1,395 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ import logging
4
+ import tempfile
5
+ import re
6
+ from dotenv import load_dotenv
7
+ from flask import Flask, request, jsonify, render_template, send_file
8
+ from groq import Groq
9
+ import google.generativeai as genai
10
+ import pandas as pd
11
+ from datetime import datetime, date, time
12
+ from apscheduler.schedulers.background import BackgroundScheduler
13
+ from twilio.rest import Client
14
+ import io
15
+
16
+ # Load environment variables
17
+ load_dotenv()
18
+
19
+ app = Flask(__name__)
20
+
21
+ # Configure API keys
22
+ GROQ_API_KEY = os.getenv('GROQ_API_KEY', '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')
23
+ GEMINI_API_KEY = os.getenv('GEMINI_API_KEY', 'AIzaSyCizcswP6vlKDMdB3HRAtVi2JbifOpbPvA')
24
+
25
+ # Initialize clients
26
+ groq_client = Groq(api_key=GROQ_API_KEY)
27
+ genai.configure(api_key=GEMINI_API_KEY)
28
+ MODEL_NAME = "gemini-1.5-flash"
29
+
30
+ # Initialize scheduler
31
+ scheduler = BackgroundScheduler()
32
+ scheduler.start()
33
+
34
+ # Logger setup
35
+ logging.basicConfig(level=logging.INFO)
36
+ logger = logging.getLogger(__name__)
37
+
38
+ # Add back the Twilio configuration
39
+ ACCOUNT_SID = 'AC490e071f8d01bf0df2f03d086c788d87'
40
+ AUTH_TOKEN = '224b23b950ad5a4052aba15893fdf083'
41
+ TWILIO_FROM = 'whatsapp:+14155238886'
42
+ PATIENT_PHONE = 'whatsapp:+917559355282'
43
+
44
+ # Initialize Twilio client
45
+ twilio_client = Client(ACCOUNT_SID, AUTH_TOKEN)
46
+
47
+
48
+ def send_whatsapp_message(message):
49
+ """Send WhatsApp notification"""
50
+ try:
51
+ msg = twilio_client.messages.create(
52
+ from_=TWILIO_FROM,
53
+ body=message,
54
+ to=PATIENT_PHONE
55
+ )
56
+ logger.info(f"WhatsApp notification sent: {msg.sid}")
57
+ return True
58
+ except Exception as e:
59
+ logger.error(f"Failed to send WhatsApp notification: {str(e)}")
60
+ return False
61
+
62
+
63
+
64
+ # define defaults for descriptive slots
65
+ DESCRIPTIVE_SLOTS = {
66
+ 'Morning': time(hour= 8, minute=0),
67
+ 'Afternoon': time(hour=13, minute=0),
68
+ 'Evening': time(hour=18, minute=0),
69
+ 'Night': time(hour=21, minute=0),
70
+ }
71
+
72
+ def parse_time_slot(time_str):
73
+ """
74
+ Try to parse β€œhh:mm AM/PM”, else map descriptive slots,
75
+ else return None.
76
+ """
77
+ try:
78
+ return datetime.strptime(time_str, '%I:%M %p')
79
+ except ValueError:
80
+ # fallback to descriptor map
81
+ slot = DESCRIPTIVE_SLOTS.get(time_str.title())
82
+ if slot:
83
+ # combine with today’s date just for hour/minute extraction
84
+ return datetime.combine(date.today(), slot)
85
+ else:
86
+ logger.warning(f"Unrecognized time format: '{time_str}'")
87
+ return None
88
+
89
+ def schedule_notifications(schedule_data):
90
+ """Schedule notifications for medications, meals, and activities."""
91
+ try:
92
+ scheduler.remove_all_jobs()
93
+
94
+ for section, prefix, key_map in [
95
+ ('medication_schedule', 'med', {'time': 'Scheduled Time', 'fields': ['Meal', 'Medication Name', 'Dosage']}),
96
+ ('meal_schedule', 'meal', {'time': 'Time', 'fields': ['Meal', 'Details']}),
97
+ ('activity_schedule', 'activity', {'time': 'Time', 'fields': ['Activity', 'Duration', 'Notes']}),
98
+ ]:
99
+ for item in schedule_data.get(section, []):
100
+ time_str = item[key_map['time']]
101
+ time_obj = parse_time_slot(time_str)
102
+ if not time_obj:
103
+ # skip invalid entries
104
+ continue
105
+
106
+ # build the message dynamically
107
+ if section == 'medication_schedule':
108
+ message = (
109
+ f"πŸ”” Medication Reminder\n"
110
+ f"Time: {time_str}\n"
111
+ f"Meal: {item['Meal']}\n"
112
+ f"Medication: {item['Medication Name']}\n"
113
+ f"Dosage: {item.get('Dosage', 'As prescribed')}"
114
+ )
115
+ elif section == 'meal_schedule':
116
+ message = (
117
+ f"🍽 Meal Reminder\n"
118
+ f"Time: {time_str}\n"
119
+ f"Meal: {item['Meal']}\n"
120
+ f"Details: {item.get('Details', '')}"
121
+ )
122
+ else: # activity_schedule
123
+ message = (
124
+ f"πŸƒ Activity Reminder\n"
125
+ f"Time: {time_str}\n"
126
+ f"Activity: {item['Activity']}\n"
127
+ f"Duration: {item.get('Duration', '')}\n"
128
+ f"Notes: {item.get('Notes', '')}"
129
+ )
130
+
131
+ job_id = f"{prefix}_{item.get(key_map['fields'][0], '').replace(' ', '_')}_{time_str}"
132
+ scheduler.add_job(
133
+ send_whatsapp_message,
134
+ trigger='cron',
135
+ args=[message],
136
+ hour=time_obj.hour,
137
+ minute=time_obj.minute,
138
+ id=job_id,
139
+ replace_existing=True
140
+ )
141
+
142
+ return True
143
+
144
+ except Exception as e:
145
+ logger.error(f"Failed to schedule notifications: {e}")
146
+ return False
147
+
148
+
149
+
150
+ # βœ… Function to Extract JSON from Response
151
+ def extract_json(text):
152
+ json_match = re.search(r'\{.*\}', text, re.DOTALL)
153
+ if json_match:
154
+ return json_match.group(0)
155
+ return None
156
+
157
+
158
+ # βœ… Function to Transcribe Audio Using Groq
159
+ def transcribe_audio(audio_file):
160
+ try:
161
+ temp_dir = tempfile.mkdtemp()
162
+ temp_path = os.path.join(temp_dir, "temp_audio.mp3")
163
+ audio_file.save(temp_path)
164
+
165
+ # Transcribe audio
166
+ with open(temp_path, "rb") as file:
167
+ transcription = groq_client.audio.transcriptions.create(
168
+ file=(temp_path, file.read()),
169
+ model="whisper-large-v3-turbo",
170
+ response_format="json",
171
+ language="en",
172
+ temperature=0.0
173
+ )
174
+
175
+ # Cleanup temporary files
176
+ os.remove(temp_path)
177
+ os.rmdir(temp_dir)
178
+
179
+ logger.info(f"Transcription successful: {transcription.text[:100]}...")
180
+ return transcription.text
181
+
182
+ except Exception as e:
183
+ logger.error(f"Transcription error: {str(e)}")
184
+ return None
185
+
186
+
187
+ # βœ… Function to Generate Care Plan Using Gemini
188
+ def generate_care_plan(conversation_text):
189
+ try:
190
+ model = genai.GenerativeModel(MODEL_NAME)
191
+ prompt = f"""
192
+ Based on the following conversation transcript between a doctor and a patient, generate a structured care plan in *valid JSON format*.
193
+
194
+ *JSON Format:*
195
+ {{
196
+ "medication_schedule": [{{"Medication Name": ""}}],
197
+ "meal_schedule": [{{"Time": "", "Meal": "", "Details": ""}}],
198
+ "activity_schedule": [{{"Time": "", "Activity": "", "Duration": "", "Notes": ""}}]
199
+ }}
200
+
201
+ *Fixed Meal Times:*
202
+ - Breakfast: 7:00 AM
203
+ - Snack: 10:00 AM
204
+ - Lunch: 1:00 PM
205
+ - Snack: 5:00 PM
206
+ - Dinner: 8:00 PM
207
+
208
+ Ensure medications align with meals. Adjust exercise based on health conditions.
209
+
210
+ *Conversation Transcript:*
211
+ {conversation_text}
212
+
213
+ *Output Strictly JSON. Do NOT add explanations or extra text.*
214
+ """
215
+
216
+ response = model.generate_content(prompt)
217
+ raw_response = response.text
218
+ logger.info(f"Raw Gemini Response: {raw_response[:100]}...")
219
+
220
+ json_text = extract_json(raw_response)
221
+ if json_text:
222
+ return json.loads(json_text)
223
+
224
+ logger.error("No valid JSON found in response.")
225
+ return create_default_care_plan()
226
+
227
+ except Exception as e:
228
+ logger.error(f"Care plan generation error: {str(e)}")
229
+ return create_default_care_plan()
230
+
231
+
232
+ # βœ… Function to Create a Default Care Plan (Fallback)
233
+ def create_default_care_plan():
234
+ return {
235
+ "medication_schedule": [],
236
+ "meal_schedule": [
237
+ {"Time": "7:00 AM", "Meal": "Breakfast", "Details": ""},
238
+ {"Time": "1:00 PM", "Meal": "Lunch", "Details": ""},
239
+ {"Time": "8:00 PM", "Meal": "Dinner", "Details": ""}
240
+ ],
241
+ "activity_schedule": []
242
+ }
243
+
244
+
245
+ # βœ… Route: Homepage
246
+ @app.route('/')
247
+ def index():
248
+ return render_template('index.html')
249
+
250
+
251
+ # βœ… Route: Generate Care Plan
252
+ @app.route("/generate_care_plan", methods=["POST"])
253
+ def generate_care_plan_route():
254
+ try:
255
+ if 'audio' not in request.files:
256
+ return jsonify({"error": "No audio file provided"}), 400
257
+
258
+ audio_file = request.files['audio']
259
+ if not audio_file:
260
+ return jsonify({"error": "Empty audio file"}), 400
261
+
262
+ transcript = transcribe_audio(audio_file)
263
+ if not transcript:
264
+ return jsonify({"error": "Transcription failed"}), 500
265
+
266
+ care_plan = generate_care_plan(transcript)
267
+ return jsonify(care_plan)
268
+
269
+ except Exception as e:
270
+ logger.error(f"Error in generate_care_plan_route: {str(e)}")
271
+ return jsonify({"error": str(e)}), 500
272
+
273
+
274
+ # βœ… Route: Generate Medication Schedule
275
+ @app.route('/generate_schedule', methods=['POST'])
276
+ def generate_schedule():
277
+ try:
278
+ data = request.get_json()
279
+ medicines = data.get("medicines", [])
280
+ schedule_list = []
281
+
282
+ meal_times = {
283
+ 3: [("Breakfast", "7:00 AM"), ("Lunch", "1:00 PM"), ("Dinner", "8:00 PM")],
284
+ 2: [("Breakfast", "7:00 AM"), ("Dinner", "8:00 PM")],
285
+ 1: [("Dinner", "8:00 PM")]
286
+ }
287
+
288
+ for med in medicines:
289
+ medication_name = med.get("medication_name", "")
290
+ frequency = int(med.get("frequency", 1))
291
+ dosage = med.get("dosage", "")
292
+
293
+ for meal, time in meal_times.get(frequency, []):
294
+ schedule_list.append({
295
+ "Medication Name": medication_name,
296
+ "Dosage": dosage,
297
+ "Meal": meal,
298
+ "Scheduled Time": time
299
+ })
300
+
301
+ # Schedule notifications
302
+ schedule_data = {
303
+ 'medication_schedule': schedule_list,
304
+ 'meal_schedule': data.get('meal_schedule', []),
305
+ 'activity_schedule': data.get('activity_schedule', [])
306
+ }
307
+ notifications_scheduled = schedule_notifications(schedule_data)
308
+
309
+ return jsonify({
310
+ "schedule_list": schedule_list,
311
+ "notifications_scheduled": notifications_scheduled
312
+ })
313
+
314
+ except Exception as e:
315
+ logger.error(f"Schedule generation error: {str(e)}")
316
+ return jsonify({"error": str(e)}), 500
317
+
318
+
319
+ @app.route('/download_schedule', methods=['POST'])
320
+ def download_schedule():
321
+ try:
322
+ data = request.json
323
+
324
+ # Create Excel writer object
325
+ output = io.BytesIO()
326
+ with pd.ExcelWriter(output, engine='xlsxwriter') as writer:
327
+ workbook = writer.book
328
+
329
+ # Convert medication schedule to DataFrame and write to Excel
330
+ if data.get('medication_schedule'):
331
+ med_df = pd.DataFrame(data['medication_schedule'])
332
+ med_df = med_df.sort_values('Time') if 'Time' in med_df.columns else med_df
333
+ med_df.to_excel(writer, sheet_name='Medication Schedule', index=False)
334
+
335
+ worksheet = writer.sheets['Medication Schedule']
336
+ header_format = workbook.add_format({
337
+ 'bold': True,
338
+ 'bg_color': '#3498db',
339
+ 'font_color': 'white'
340
+ })
341
+
342
+ for col_num, value in enumerate(med_df.columns.values):
343
+ worksheet.write(0, col_num, value, header_format)
344
+ worksheet.set_column(col_num, col_num, 15) # Set column width
345
+
346
+ # Write meal schedule
347
+ if data.get('meal_schedule'):
348
+ meal_df = pd.DataFrame(data['meal_schedule'])
349
+ meal_df = meal_df.sort_values('Time')
350
+ meal_df.to_excel(writer, sheet_name='Meal Schedule', index=False)
351
+
352
+ worksheet = writer.sheets['Meal Schedule']
353
+ header_format = workbook.add_format({
354
+ 'bold': True,
355
+ 'bg_color': '#2ecc71',
356
+ 'font_color': 'white'
357
+ })
358
+
359
+ for col_num, value in enumerate(meal_df.columns.values):
360
+ worksheet.write(0, col_num, value, header_format)
361
+ worksheet.set_column(col_num, col_num, 15)
362
+
363
+ # Write activity schedule
364
+ if data.get('activity_schedule'):
365
+ activity_df = pd.DataFrame(data['activity_schedule'])
366
+ activity_df = activity_df.sort_values('Time')
367
+ activity_df.to_excel(writer, sheet_name='Activity Schedule', index=False)
368
+
369
+ worksheet = writer.sheets['Activity Schedule']
370
+ header_format = workbook.add_format({
371
+ 'bold': True,
372
+ 'bg_color': '#e74c3c',
373
+ 'font_color': 'white'
374
+ })
375
+
376
+ for col_num, value in enumerate(activity_df.columns.values):
377
+ worksheet.write(0, col_num, value, header_format)
378
+ worksheet.set_column(col_num, col_num, 15)
379
+
380
+ output.seek(0)
381
+ return send_file(
382
+ output,
383
+ mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
384
+ as_attachment=True,
385
+ download_name='daily_schedule.xlsx'
386
+ )
387
+
388
+ except Exception as e:
389
+ logger.error(f"Error generating Excel file: {str(e)}")
390
+ return jsonify({"error": str(e)}), 500
391
+
392
+
393
+ # βœ… Run Flask App
394
+ if __name__ == '__main__':
395
+ app.run(debug=True, threaded=True)