Files changed (1) hide show
  1. app.py +326 -152
app.py CHANGED
@@ -1,181 +1,355 @@
1
- # app.py
2
-
3
- import os, random, wave, json, uuid, urllib.request, zipfile
4
  import gradio as gr
5
- from pydub import AudioSegment
6
- from vosk import Model, KaldiRecognizer
 
 
 
 
7
 
8
- # === Model Download ===
9
- MODEL_DIR = "vosk-model-small-en-us-0.15"
10
- MODEL_URL = "https://alphacephei.com/vosk/models/vosk-model-small-en-us-0.15.zip"
11
- MODEL_ZIP = "vosk-model-small-en-us-0.15.zip"
12
 
13
- if not os.path.exists(MODEL_DIR):
14
- print("🔽 Downloading Vosk model...")
15
- urllib.request.urlretrieve(MODEL_URL, MODEL_ZIP)
16
- with zipfile.ZipFile(MODEL_ZIP, "r") as zip_ref:
17
- zip_ref.extractall()
18
- print(" Model ready.")
 
 
 
 
19
 
20
- vosk_model = Model(MODEL_DIR)
 
 
 
 
 
 
21
 
22
- # === Vosk Transcription ===
23
- def transcribe_vosk(audio_path):
24
- try:
25
- if not audio_path or not os.path.exists(audio_path):
26
- return "❌ No audio file provided."
 
 
 
 
 
 
 
 
 
27
 
28
- temp_wav = f"converted_{uuid.uuid4().hex}.wav"
29
- audio = AudioSegment.from_file(audio_path)
30
- if len(audio) < 1000:
31
- return "⚠️ Please record at least 1 second."
32
 
33
- audio = audio.set_frame_rate(16000).set_channels(1).set_sample_width(2)
34
- audio.export(temp_wav, format="wav")
 
 
 
 
 
 
 
 
 
 
 
 
35
 
36
- wf = wave.open(temp_wav, "rb")
37
- rec = KaldiRecognizer(vosk_model, wf.getframerate())
38
- result = ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
 
40
- while True:
41
- data = wf.readframes(4000)
42
- if not data:
43
- break
44
- if rec.AcceptWaveform(data):
45
- result += json.loads(rec.Result()).get("text", "") + " "
 
 
 
 
 
 
 
 
 
 
46
 
47
- result += json.loads(rec.FinalResult()).get("text", "")
48
- wf.close()
49
- os.remove(temp_wav)
 
 
 
50
 
51
- return result.strip() or "⚠️ Speech not recognized clearly."
52
- except Exception as e:
53
- return f" Error: {str(e)}"
54
-
55
- # === Diet Logic ===
56
- local_foods = {
57
- "vegetables": ["carrot", "tomato", "cucumber", "spinach", "beetroot"],
58
- "grains": ["rice", "wheat", "millet", "barley"],
59
- "proteins": ["lentils", "eggs", "chicken", "soybeans", "peas"]
60
- }
61
-
62
- def generate_diet(age, gender, weight, height, occupation, activity_level,
63
- health_conditions, dietary_preferences, allergies, budget):
64
- return f"""
65
- 🌿 Personalized Healthy Diet Plan 🌿
66
- ────────────────────────���─────────────
67
- 👨‍🌾 Occupation: {occupation}
68
- ⚡ Activity Level: {activity_level}
69
- 💰 Daily Budget: ₹{budget}
70
-
71
- 🍽️ Meals for Today:
72
- 🥣 Breakfast: {random.choice(local_foods['grains'])} with {random.choice(local_foods['vegetables'])}
73
- 🍛 Lunch: {random.choice(local_foods['proteins'])} with {random.choice(local_foods['grains'])}
74
- 🥗 Dinner: {random.choice(local_foods['vegetables'])}, {random.choice(local_foods['vegetables'])} with {random.choice(local_foods['proteins'])}
75
-
76
- 🩺 Health Conditions: {health_conditions or 'None'}
77
- 🌱 Preferences: {dietary_preferences or 'None'}
78
- ⚠️ Allergies: {allergies or 'None'}
79
- """
80
-
81
- # === Translations ===
82
- translations = {
83
- "English": {
84
- "title": "🥗 Personalized Diet Generator (Voice Input)",
85
- "record_tip": "🎤 Record → Stop → Transcribe",
86
- "age": "Age", "gender": "Gender", "weight": "Weight (kg)", "height": "Height (cm)",
87
- "occupation": "Occupation", "activity": "Activity Level",
88
- "health_audio": "🩺 Speak Health Conditions", "diet_audio": "🥗 Speak Dietary Preferences",
89
- "allergy_audio": "⚠️ Speak Allergies", "health_text": "Recognized Health Conditions",
90
- "diet_text": "Recognized Dietary Preferences", "allergy_text": "Recognized Allergies",
91
- "transcribe_health": "Transcribe Health", "transcribe_diet": "Transcribe Preferences",
92
- "transcribe_allergy": "Transcribe Allergies", "budget": "Daily Budget (₹)",
93
- "generate": "Generate Diet Plan", "output": "Generated Diet Plan"
94
- },
95
- "Spanish": {
96
- "title": "🥗 Generador de Dieta Personalizada (Entrada por Voz)",
97
- "record_tip": "🎤 Grabar → Detener → Transcribir", "age": "Edad", "gender": "Género",
98
- "weight": "Peso (kg)", "height": "Altura (cm)", "occupation": "Ocupación",
99
- "activity": "Nivel de actividad", "health_audio": "🩺 Decir condiciones de salud",
100
- "diet_audio": "🥗 Decir preferencias dietéticas", "allergy_audio": "⚠️ Decir alergias",
101
- "health_text": "Condiciones reconocidas", "diet_text": "Preferencias reconocidas",
102
- "allergy_text": "Alergias reconocidas", "transcribe_health": "Transcribir salud",
103
- "transcribe_diet": "Transcribir preferencias", "transcribe_allergy": "Transcribir alergias",
104
- "budget": "Presupuesto diario (₹)", "generate": "Generar plan", "output": "Plan generado"
105
- },
106
- "Hindi": {
107
- "title": "🥗 व्यक्तिगत डाइट जनरेटर (वॉइस इनपुट)",
108
- "record_tip": "🎤 रिकॉर्ड करें → रुकें → ट्रांसक्राइब करें", "age": "आयु", "gender": "लिंग",
109
- "weight": "वज़न (किग्रा)", "height": "ऊंचाई (सेमी)", "occupation": "पेशा",
110
- "activity": "गतिविधि स्तर", "health_audio": "🩺 स्वास्थ्य स्थिति बोलें",
111
- "diet_audio": "🥗 आहार वरीयताएँ बोलें", "allergy_audio": "⚠️ एलर्जी बोलें",
112
- "health_text": "पहचानी गई स्वास्थ्य स्थितियाँ", "diet_text": "पहचानी गई आहार वरीयताएँ",
113
- "allergy_text": "पहचानी गई एलर्जी", "transcribe_health": "स्वास्थ्य ट्रांसक्राइब करें",
114
- "transcribe_diet": "आहार ट्रांसक्राइब करें", "transcribe_allergy": "एलर्जी ट्रांसक्राइब करें",
115
- "budget": "दैनिक बजट (₹)", "generate": "डाइट प्लान बनाएं", "output": "बनाया गया डाइट प्लान"
116
- },
117
- "Telugu": {
118
- "title": "🥗 వ్యక్తిగత ఆహార ప్రణాళిక (వాయిస్ ఇన్పుట్)",
119
- "record_tip": "🎤 రికార్డ్ చేయండి → ఆపు → ట్రాన్స్‌క్రైబ్", "age": "వయస్సు", "gender": "లింగం",
120
- "weight": "బరువు (kg)", "height": "ఎత్తు (cm)", "occupation": "వృత్తి", "activity": "చర్య స్థాయి",
121
- "health_audio": "🩺 ఆరోగ్య పరిస్థితులు చెప్పండి", "diet_audio": "🥗 ఆహార అభిరుచులు చెప్పండి",
122
- "allergy_audio": "⚠️ అలర్జీలు చెప్పండి", "health_text": "ఆరోగ్య పరిస్థితులు గుర్తించబడ్డాయి",
123
- "diet_text": "ఆహార అభిరుచులు గుర్తించబడ్డాయి", "allergy_text": "అలర్జీలు గుర్తించబడ్డాయి",
124
- "transcribe_health": "హెల్త్ ���్రాన్స్‌క్రైబ్", "transcribe_diet": "డైట్ ట్రాన్స్‌క్రైబ్",
125
- "transcribe_allergy": "అలర్జీ ట్రాన్స్‌క్రైబ్", "budget": "రోజువారీ బడ్జెట్ (₹)",
126
- "generate": "ప్లాన్ రూపొందించండి", "output": "ఆహార ప్రణాళిక"
127
  }
128
- }
 
 
 
 
 
129
 
130
- # === UI ===
131
- def render_ui(language="English"):
132
- t = translations[language]
133
- with gr.Blocks() as demo:
134
- gr.Markdown(f"## {t['title']}")
135
- gr.Markdown(t["record_tip"])
136
 
137
- lang_sel = gr.Dropdown(list(translations.keys()), value=language, label="🌐 Language")
 
 
 
 
 
 
 
 
 
138
 
139
- with gr.Row():
140
- age = gr.Number(label=t["age"], value=30)
141
- gender = gr.Radio(["Male", "Female", "Other"], label=t["gender"])
142
- weight = gr.Number(label=t["weight"], value=70)
143
- height = gr.Number(label=t["height"], value=170)
144
 
145
- occupation = gr.Textbox(label=t["occupation"])
146
- activity_level = gr.Radio(["Low", "Medium", "High"], label=t["activity"], value="Medium")
 
 
 
 
 
 
147
 
148
- health_audio = gr.Audio(label=t["health_audio"], type="filepath")
149
- health_text = gr.Textbox(label=t["health_text"])
 
 
 
 
 
150
 
151
- diet_audio = gr.Audio(label=t["diet_audio"], type="filepath")
152
- diet_text = gr.Textbox(label=t["diet_text"])
 
 
 
 
 
 
 
 
 
153
 
154
- allergy_audio = gr.Audio(label=t["allergy_audio"], type="filepath")
155
- allergy_text = gr.Textbox(label=t["allergy_text"])
 
 
 
 
 
 
 
 
156
 
157
- with gr.Row():
158
- gr.Button(t["transcribe_health"]).click(transcribe_vosk, inputs=health_audio, outputs=health_text)
159
- gr.Button(t["transcribe_diet"]).click(transcribe_vosk, inputs=diet_audio, outputs=diet_text)
160
- gr.Button(t["transcribe_allergy"]).click(transcribe_vosk, inputs=allergy_audio, outputs=allergy_text)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
161
 
162
- budget = gr.Number(label=t["budget"], value=200)
163
- output = gr.Textbox(label=t["output"], lines=20, show_copy_button=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
164
 
165
- gr.Button(t["generate"]).click(
166
- generate_diet,
167
- inputs=[age, gender, weight, height, occupation, activity_level,
168
- health_text, diet_text, allergy_text, budget],
169
- outputs=output
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
170
  )
171
 
172
- def reload_lang(new_lang):
173
- return render_ui(new_lang)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
174
 
175
- lang_sel.change(fn=reload_lang, inputs=lang_sel, outputs=None)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
176
 
177
  return demo
178
 
179
- # === Launch ===
180
  if __name__ == "__main__":
181
- render_ui("English").launch(server_name="0.0.0.0", server_port=7860)
 
 
 
 
 
1
  import gradio as gr
2
+ import numpy as np
3
+ from transformers import WhisperProcessor, WhisperForConditionalGeneration
4
+ import json
5
+ import hashlib
6
+ import os
7
+ import logging
8
 
9
+ # Set up logging
10
+ logging.basicConfig(level=logging.INFO)
11
+ logger = logging.getLogger(__name__)
 
12
 
13
+ # Preload Whisper model for audio transcription
14
+ try:
15
+ logger.info("Preloading Whisper model...")
16
+ processor = WhisperProcessor.from_pretrained("openai/whisper-small")
17
+ speech_model = WhisperForConditionalGeneration.from_pretrained("openai/whisper-small")
18
+ logger.info("Whisper model preloaded successfully")
19
+ except Exception as e:
20
+ logger.error(f"Error preloading Whisper model: {e}")
21
+ processor = WhisperProcessor.from_pretrained("openai/whisper-small")
22
+ speech_model = WhisperForConditionalGeneration.from_pretrained("openai/whisper-small")
23
 
24
+ # Predefined local food data (replacing USDA Local Food Directories API)
25
+ LOCAL_FOODS = [
26
+ {"name": "Rice", "price_per_unit": 60, "unit": "kg", "source": "Local Mandi"},
27
+ {"name": "Dal", "price_per_unit": 120, "unit": "kg", "source": "Farmers' Market"},
28
+ {"name": "Potatoes", "price_per_unit": 30, "unit": "kg", "source": "Local Vendor"},
29
+ {"name": "Chicken", "price_per_unit": 300, "unit": "kg", "source": "Poultry Farm"}
30
+ ]
31
 
32
+ # Custom theme with improved visibility
33
+ custom_theme = gr.themes.Base(
34
+ primary_hue=gr.themes.colors.teal,
35
+ secondary_hue=gr.themes.colors.yellow,
36
+ neutral_hue=gr.themes.colors.gray,
37
+ text_size=gr.themes.sizes.text_lg,
38
+ font=[gr.themes.GoogleFont("Noto Sans")]
39
+ )
40
+
41
+ # Convert height to feet and inches
42
+ def format_height(height_ft):
43
+ feet = int(height_ft)
44
+ inches = round((height_ft - feet) * 12)
45
+ return f"{feet} ft {inches} in"
46
 
47
+ # Encrypt user data for caching
48
+ def encrypt_data(data):
49
+ return hashlib.sha256(data.encode()).hexdigest()
 
50
 
51
+ # Transcribe audio to text
52
+ def transcribe_audio(audio):
53
+ if audio is None:
54
+ return "Farmer"
55
+ try:
56
+ audio_data = np.frombuffer(audio, dtype=np.int16)
57
+ input_features = processor(audio_data, sampling_rate=16000, return_tensors="pt").input_features
58
+ predicted_ids = speech_model.generate(input_features, max_new_tokens=30)
59
+ transcription = processor.decode(predicted_ids[0], skip_special_tokens=True)
60
+ logger.info(f"Transcribed audio to: {transcription}")
61
+ return transcription[:50]
62
+ except Exception as e:
63
+ logger.error(f"Audio transcription failed: {e}")
64
+ return "Farmer"
65
 
66
+ # Translate function for multilingual support
67
+ def translate_output(text, lang):
68
+ translations = {
69
+ "en": {
70
+ "app_title": "Personalized Diet Generator",
71
+ "dietitian_review": "Dietitian Review",
72
+ "your_feedback": "Your Feedback",
73
+ "age": "Age (years)",
74
+ "gender": "Gender",
75
+ "weight": "Weight (kg)",
76
+ "height": "Height (ft)",
77
+ "occupation": "Occupation",
78
+ "activity_level": "Activity Level",
79
+ "health_conditions": "Health Conditions (e.g., diabetes)",
80
+ "dietary_preferences": "Dietary Preferences (e.g., vegetarian)",
81
+ "allergies": "Allergies",
82
+ "weekly_budget": "Weekly Budget (₹)",
83
+ "pincode": "Pincode",
84
+ "cultural_preferences": "Cultural Preferences",
85
+ "consent": "I consent to data processing",
86
+ "status_success": "Diet plan generated successfully",
87
+ "status_cached": "Loaded cached diet plan (offline mode)",
88
+ "status_error": "Error: Please consent to data processing",
89
+ "feedback_dietitian": "Feedback submitted successfully!",
90
+ "feedback_user": "Thank you for your feedback!",
91
+ "monday": "Monday", "tuesday": "Tuesday", "wednesday": "Wednesday",
92
+ "thursday": "Thursday", "friday": "Friday", "saturday": "Saturday", "sunday": "Sunday",
93
+ "breakfast": "Breakfast", "lunch": "Lunch", "dinner": "Dinner"
94
+ },
95
+ "hi": {
96
+ "app_title": "वैयक्तिकृत आहार जनरेटर",
97
+ "dietitian_review": "डायटिशियन समीक्षा",
98
+ "your_feedback": "आपकी प्रतिक्रिया",
99
+ "age": "आयु (वर्ष)",
100
+ "gender": "लिंग",
101
+ "weight": "वजन (किग्रा)",
102
+ "height": "ऊंचाई (फीट)",
103
+ "occupation": "पेशा",
104
+ "activity_level": "गतिविधि स्तर",
105
+ "health_conditions": "स्वास्थ्य स्थितियाँ (जैसे, मधुमेह)",
106
+ "dietary_preferences": "आहार प्राथमिकताएँ (जैसे, शाकाहारी)",
107
+ "allergies": "एलर्जी",
108
+ "weekly_budget": "साप्ताहिक बजट (₹)",
109
+ "pincode": "पिनकोड",
110
+ "cultural_preferences": "सांस्कृतिक प्राथमिकताएँ",
111
+ "consent": "मैं डेटा प्रसंस्करण के लिए सहमति देता हूँ",
112
+ "status_success": "आहार योजना सफलतापूर्वक उत्पन्न की गई",
113
+ "status_cached": "संरक्षित आहार योजना लोड की गई (ऑफलाइन मोड)",
114
+ "status_error": "त्रुटि: कृपया डेटा प्रसंस्करण के लिए सहमति दें",
115
+ "feedback_dietitian": "प्रतिक्रिया सफलतापूर्वक जमा की गई!",
116
+ "feedback_user": "आपकी प्रतिक्रिया के लिए धन्यवाद!",
117
+ "monday": "सोमवार", "tuesday": "मंगलवार", "wednesday": "बुधवार",
118
+ "thursday": "गुरुवार", "friday": "शुक्रवार", "saturday": "शनिवार", "sunday": "रविवार",
119
+ "breakfast": "नाश्ता", "lunch": "दोपहर का भोजन", "dinner": "रात का भोजन"
120
+ }
121
+ }
122
+ lang_map = {"English": "en", "Hindi": "hi"}
123
+ lang_code = lang_map.get(lang, "en")
124
+ return translations.get(lang_code, translations["en"])
125
 
126
+ # Generate diet plan
127
+ def generate_diet_plan(age, gender, weight, height, occupation, activity_level, health_conditions,
128
+ dietary_preferences, allergies, budget, zip_code, cultural_preferences, consent, lang, audio=None):
129
+ logger.info("Generating diet plan with inputs: age=%s, gender=%s, weight=%s, height=%s, occupation=%s, consent=%s",
130
+ age, gender, weight, height, occupation, consent)
131
+ if not consent:
132
+ return translate_output("status_error", lang), [], ""
133
+ if audio is not None:
134
+ occupation = transcribe_audio(audio)
135
+ user_data = {
136
+ "age": age, "gender": gender, "weight": weight, "height": height, "occupation": occupation,
137
+ "activity_level": activity_level, "health_conditions": health_conditions,
138
+ "dietary_preferences": dietary_preferences, "allergies": allergies, "budget": budget,
139
+ "cultural_preferences": cultural_preferences
140
+ }
141
+ user_id = encrypt_data(json.dumps(user_data))
142
 
143
+ cached_plan = load_cached_plan(user_id)
144
+ if cached_plan:
145
+ logger.info("Loaded cached diet plan for user_id: %s", user_id)
146
+ diet_plan_output = format_diet_plan(cached_plan, lang)
147
+ local_foods_output = format_local_foods(LOCAL_FOODS, lang)
148
+ return translate_output("status_cached", lang), local_foods_output, diet_plan_output
149
 
150
+ formatted_height = format_height(height)
151
+ diet_plan = {
152
+ "Monday": {"breakfast": "Roti with dal", "lunch": "Rice and potato curry", "dinner": "Chicken curry"},
153
+ "Tuesday": {"breakfast": "Poha", "lunch": "Dal and roti", "dinner": "Vegetable stir-fry"},
154
+ "Wednesday": {"breakfast": "Upma", "lunch": "Rice and sambar", "dinner": "Chicken gravy"},
155
+ "Thursday": {"breakfast": "Idli with chutney", "lunch": "Roti and dal", "dinner": "Potato sabzi"},
156
+ "Friday": {"breakfast": "Dosa", "lunch": "Rice and chicken curry", "dinner": "Dal and roti"},
157
+ "Saturday": {"breakfast": "Puri with aloo", "lunch": "Vegetable pulao", "dinner": "Chicken fry"},
158
+ "Sunday": {"breakfast": "Paratha", "lunch": "Rice and sambar", "dinner": "Dal tadka"}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
159
  }
160
+ if "South Indian" in cultural_preferences.lower():
161
+ diet_plan.update({"Monday": {"breakfast": "Idli with sambar", "lunch": "Rice and rasam", "dinner": "Vegetable curry"},
162
+ "Friday": {"breakfast": "Dosa with chutney", "lunch": "Rice and fish curry", "dinner": "Dal"}})
163
+ elif "North Indian" in cultural_preferences.lower():
164
+ diet_plan.update({"Monday": {"breakfast": "Paratha with curd", "lunch": "Roti and paneer", "dinner": "Dal makhani"},
165
+ "Friday": {"breakfast": "Puri with aloo", "lunch": "Roti and chole", "dinner": "Vegetable pulao"}})
166
 
167
+ cache_diet_plan(user_id, diet_plan)
168
+ logger.info("Diet plan generated and cached for user_id: %s", user_id)
169
+ diet_plan_output = format_diet_plan(diet_plan, lang)
170
+ local_foods_output = format_local_foods(LOCAL_FOODS, lang)
171
+ return translate_output("status_success", lang), local_foods_output, diet_plan_output
 
172
 
173
+ # Format diet plan and local foods
174
+ def format_diet_plan(diet_plan, lang):
175
+ trans = translate_output("", lang)
176
+ output = ""
177
+ for day, meals in diet_plan.items():
178
+ output += f"### {trans[day.lower()]}\n"
179
+ output += f"- **{trans['breakfast']}**: {meals['breakfast']}\n"
180
+ output += f"- **{trans['lunch']}**: {meals['lunch']}\n"
181
+ output += f"- **{trans['dinner']}**: {meals['dinner']}\n\n"
182
+ return output
183
 
184
+ def format_local_foods(local_foods, lang):
185
+ return [f"{food['name']} - ₹{food['price_per_unit']}/{food['unit']} from {food['source']}" for food in local_foods]
 
 
 
186
 
187
+ # Cache and load functions for offline support
188
+ def cache_diet_plan(user_id, diet_plan):
189
+ logger.info(f"Caching diet plan for user: {user_id}")
190
+ try:
191
+ with open(f"cache_{user_id}.json", "w") as f:
192
+ json.dump(diet_plan, f)
193
+ except Exception as e:
194
+ logger.error(f"Error caching diet plan: {e}")
195
 
196
+ def load_cached_plan(user_id):
197
+ logger.info(f"Loading cached diet plan for user: {user_id}")
198
+ try:
199
+ with open(f"cache_{user_id}.json", "r") as f:
200
+ return json.load(f)
201
+ except:
202
+ return None
203
 
204
+ # Submit feedback
205
+ def submit_dietitian_feedback(feedback, lang):
206
+ logger.info(f"Submitting dietitian feedback: {feedback}")
207
+ if not feedback or feedback.strip() == "":
208
+ return "Error: Please provide feedback"
209
+ try:
210
+ with open("dietitian_feedback.json", "a") as f:
211
+ f.write(json.dumps({"feedback": feedback, "lang": lang}) + "\n")
212
+ except Exception as e:
213
+ logger.error(f"Error saving dietitian feedback: {e}")
214
+ return translate_output("feedback_dietitian", lang)
215
 
216
+ def submit_user_feedback(feedback, lang):
217
+ logger.info(f"Submitting user feedback: {feedback}")
218
+ if not feedback or feedback.strip() == "":
219
+ return "Error: Please provide feedback"
220
+ try:
221
+ with open("user_feedback.json", "a") as f:
222
+ f.write(json.dumps({"feedback": feedback, "lang": lang}) + "\n")
223
+ except Exception as e:
224
+ logger.error(f"Error saving user feedback: {e}")
225
+ return translate_output("feedback_user", lang)
226
 
227
+ # Main interface
228
+ def main():
229
+ with gr.Blocks(title="Personalized Diet Generator", theme=custom_theme, css="""
230
+ .container { max-width: 900px; margin: 20px auto; padding: 25px; background: #00695C; border-radius: 15px; box-shadow: 0 4px 8px rgba(0,0,0,0.1); }
231
+ .card { background: #BF360C; padding: 20px; margin-bottom: 25px; border-radius: 10px; box-shadow: 0 2px 6px rgba(0,0,0,0.1); color: #FFFFFF; }
232
+ .diet-plan { min-height: 400px; font-size: 18px; color: #FFFFFF; }
233
+ .gr-button { background: #FFCA28; color: #000000; padding: 12px 25px; border: none; border-radius: 8px; font-size: 16px; }
234
+ .gr-button:hover { background: #FFB300; }
235
+ .gr-textbox { font-size: 16px; padding: 10px; border: 2px solid #FFCA28; border-radius: 5px; background: #FFFFFF; color: #000000; }
236
+ .gr-slider { width: 100%; }
237
+ .gr-dropdown { font-size: 16px; padding: 10px; background: #FFFFFF; color: #000000; }
238
+ h3 { color: #FFCA28; font-size: 24px; }
239
+ """) as demo:
240
+ language = gr.Dropdown(
241
+ choices=["English", "Hindi"],
242
+ value="English",
243
+ label="Select Language"
244
+ )
245
 
246
+ def update_interface(lang):
247
+ trans = translate_output("", lang)
248
+ return gr.Markdown(f"### {trans['app_title']}", elem_classes="container"), \
249
+ gr.Markdown(f"### {trans['dietitian_review']}", visible=True), \
250
+ gr.Markdown(f"### {trans['your_feedback']}", visible=True), \
251
+ gr.Slider(minimum=18, maximum=80, value=35, label=trans["age"], step=1), \
252
+ gr.Dropdown(choices=["Male", "Female", "Other"], value="Male", label=trans["gender"]), \
253
+ gr.Slider(minimum=20, maximum=150, value=60, label=trans["weight"], step=0.5), \
254
+ gr.Slider(minimum=4.0, maximum=6.5, value=5.2, step=0.01, label=trans["height"]), \
255
+ gr.Textbox(label=trans["occupation"], value="Farmer"), \
256
+ gr.Microphone(label=trans["occupation"] + " (Optional)"), \
257
+ gr.Dropdown(choices=["Low", "Moderate", "High"], value="Moderate", label=trans["activity_level"]), \
258
+ gr.Textbox(label=trans["health_conditions"], value="None"), \
259
+ gr.Textbox(label=trans["dietary_preferences"], value="None"), \
260
+ gr.Textbox(label=trans["allergies"], value="None"), \
261
+ gr.Slider(minimum=830, maximum=41500, value=5000, label=trans["weekly_budget"]), \
262
+ gr.Textbox(label=trans["pincode"], value="500075"), \
263
+ gr.Textbox(label=trans["cultural_preferences"], value="South Indian"), \
264
+ gr.Checkbox(label=trans["consent"], value=False)
265
 
266
+ language.change(
267
+ fn=update_interface,
268
+ inputs=[language],
269
+ outputs=[
270
+ gr.Markdown(elem_classes="container"),
271
+ gr.Markdown(visible=True),
272
+ gr.Markdown(visible=True),
273
+ gr.Slider(),
274
+ gr.Dropdown(),
275
+ gr.Slider(),
276
+ gr.Slider(),
277
+ gr.Textbox(),
278
+ gr.Microphone(),
279
+ gr.Dropdown(),
280
+ gr.Textbox(),
281
+ gr.Textbox(),
282
+ gr.Textbox(),
283
+ gr.Slider(),
284
+ gr.Textbox(),
285
+ gr.Textbox(),
286
+ gr.Checkbox()
287
+ ]
288
  )
289
 
290
+ trans_init = translate_output("", "English")
291
+ with gr.Row(elem_classes="container"):
292
+ with gr.Column(scale=1, elem_classes="card"):
293
+ age = gr.Slider(minimum=18, maximum=80, value=35, label=trans_init["age"], step=1)
294
+ gender = gr.Dropdown(choices=["Male", "Female", "Other"], value="Male", label=trans_init["gender"])
295
+ weight = gr.Slider(minimum=20, maximum=150, value=60, label=trans_init["weight"], step=0.5)
296
+ height = gr.Slider(minimum=4.0, maximum=6.5, value=5.2, step=0.01, label=trans_init["height"])
297
+ occupation = gr.Textbox(label=trans_init["occupation"], value="Farmer")
298
+ audio_input = gr.Microphone(label=trans_init["occupation"] + " (Optional)")
299
+ with gr.Column(scale=1, elem_classes="card"):
300
+ activity_level = gr.Dropdown(choices=["Low", "Moderate", "High"], value="Moderate", label=trans_init["activity_level"])
301
+ health_conditions = gr.Textbox(label=trans_init["health_conditions"], value="None")
302
+ dietary_preferences = gr.Textbox(label=trans_init["dietary_preferences"], value="None")
303
+ allergies = gr.Textbox(label=trans_init["allergies"], value="None")
304
+ budget = gr.Slider(minimum=830, maximum=41500, value=5000, label=trans_init["weekly_budget"])
305
+
306
+ with gr.Row(elem_classes="container"):
307
+ with gr.Column(scale=1, elem_classes="card"):
308
+ zip_code = gr.Textbox(label=trans_init["pincode"], value="500075")
309
+ cultural_preferences = gr.Textbox(label=trans_init["cultural_preferences"], value="South Indian")
310
+ consent = gr.Checkbox(label=trans_init["consent"], value=False)
311
+ submit_button = gr.Button("Generate Diet Plan")
312
+
313
+ with gr.Row(elem_classes="container"):
314
+ with gr.Column(scale=1, elem_classes="card"):
315
+ status = gr.Textbox(label="Status")
316
+ local_foods = gr.Textbox(label="Local Food Sources", lines=4)
317
+ diet_plan = gr.Markdown(label="Weekly Diet Plan", elem_classes="diet-plan")
318
 
319
+ with gr.Row(elem_classes="container"):
320
+ with gr.Column(scale=1, elem_classes="card"):
321
+ gr.Markdown(f"### {trans_init['dietitian_review']}")
322
+ dietitian_feedback = gr.Textbox(label="Dietitian Feedback", value="Plan looks balanced. Suggest adding more greens.")
323
+ dietitian_submit = gr.Button("Submit Feedback")
324
+ dietitian_output = gr.Textbox(label="Feedback Status")
325
+
326
+ with gr.Row(elem_classes="container"):
327
+ with gr.Column(scale=1, elem_classes="card"):
328
+ gr.Markdown(f"### {trans_init['your_feedback']}")
329
+ user_feedback = gr.Textbox(label="Your Feedback")
330
+ user_submit = gr.Button("Submit Feedback")
331
+ user_output = gr.Textbox(label="Feedback Status")
332
+
333
+ # Event handlers
334
+ submit_button.click(
335
+ fn=generate_diet_plan,
336
+ inputs=[age, gender, weight, height, occupation, activity_level, health_conditions,
337
+ dietary_preferences, allergies, budget, zip_code, cultural_preferences, consent, language, audio_input],
338
+ outputs=[status, local_foods, diet_plan]
339
+ )
340
+ dietitian_submit.click(
341
+ fn=submit_dietitian_feedback,
342
+ inputs=[dietitian_feedback, language],
343
+ outputs=dietitian_output
344
+ )
345
+ user_submit.click(
346
+ fn=submit_user_feedback,
347
+ inputs=[user_feedback, language],
348
+ outputs=user_output
349
+ )
350
 
351
  return demo
352
 
 
353
  if __name__ == "__main__":
354
+ demo = main()
355
+ demo.launch(server_name="0.0.0.0", server_port=7860)