File size: 10,096 Bytes
f757d1a
 
 
13d3d93
 
8aad262
8344664
a910f40
f757d1a
80ea744
2576f99
80ea744
d81763e
b5d91cd
8344664
1c04bb5
8344664
2576f99
1c04bb5
 
 
 
 
 
2576f99
1c04bb5
 
 
 
 
 
2576f99
8344664
1e5434c
1c04bb5
1e5434c
1c04bb5
1e5434c
f757d1a
 
 
 
 
1e5434c
 
 
 
 
 
f757d1a
1e5434c
 
2576f99
1e5434c
2576f99
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1c04bb5
 
 
 
2576f99
 
 
 
 
1c04bb5
2576f99
1e5434c
80ea744
1e5434c
8344664
 
 
 
 
 
 
 
 
 
 
1e5434c
97aee12
1e5434c
1c04bb5
1e5434c
 
 
 
 
 
04e72d8
1e5434c
 
04e72d8
1e5434c
 
04e72d8
1e5434c
 
 
04e72d8
1e5434c
 
 
04e72d8
 
1e5434c
 
 
 
 
04e72d8
2f3f67d
 
4ae2592
2576f99
f757d1a
97aee12
f757d1a
b5d91cd
13d3d93
d81763e
2576f99
1c04bb5
13d3d93
d81763e
 
 
8344664
d81763e
8344664
d81763e
8344664
d81763e
1c04bb5
f757d1a
97aee12
1c04bb5
8344664
1e5434c
 
1c04bb5
8344664
 
1c04bb5
8344664
1e5434c
 
1c04bb5
9b37648
8344664
 
 
9b37648
1c04bb5
9b37648
 
 
 
1e5434c
 
 
 
 
9b37648
 
 
1c04bb5
97aee12
f757d1a
1c04bb5
 
1e5434c
 
 
97aee12
f757d1a
 
1e5434c
 
 
97aee12
1c04bb5
97aee12
1e5434c
f757d1a
 
 
1e5434c
f757d1a
1e5434c
 
 
 
 
 
 
 
 
8be9cc8
 
 
1e5434c
f757d1a
 
1c04bb5
97aee12
5401ee1
97aee12
5401ee1
97aee12
1e5434c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
import os
import random
import uuid
import gradio as gr
from pydub import AudioSegment
import whisper
import requests

# === Load Whisper Model ===
try:
    whisper_model = whisper.load_model("tiny")  # Fast transcription
except AttributeError:
    raise ImportError("Failed to load Whisper model. Install from OpenAI GitHub: pip install -U git+https://github.com/openai/whisper.git")

# === USDA API Configuration (Demo Key) ===
USDA_API_KEY = "DEMO_KEY"  # Replace with registered key
USDA_API_URL = "https://api.nal.usda.gov/fdc/v1/foods/search"
usda_cache = {}  # In-memory cache for speed

def fetch_usda_food_data(query):
    if query in usda_cache:
        return usda_cache[query]
    params = {"api_key": USDA_API_KEY, "query": query, "dataType": ["Branded", "SR Legacy"]}
    try:
        response = requests.get(USDA_API_URL, params=params, timeout=2)
        response.raise_for_status()
        data = response.json()
        result = data.get("foods", [])[0].get("description", query) if data.get("foods") else query
        usda_cache[query] = result
        return result
    except Exception:
        return query

# === Supported Languages ===
lang_map = {"English": "en"}

# === Preloaded Food Database ===
local_foods = {
    "vegetables": ["Carrot", "Tomato", "Spinach"],
    "fruits": ["Apple", "Banana"],
    "grains": ["Rice", "Wheat"],
    "proteins": ["Lentils", "Tofu"],
    "dairy": ["Curd"]
}

# === Meal Templates ===
meal_templates = {
    "breakfast": ["{grain} porridge with {fruit}", "{grain} upma"],
    "lunch": ["{protein} curry with {grain}", "{grain} khichdi"],
    "dinner": ["{vegetable} soup with {grain}"]
}

# === Generate Test Audio Files (With Error Handling) ===
def generate_test_audio():
    required_files = ["test_health_conditions.wav", "test_dietary_preferences.wav", "test_allergies.wav"]
    if all(os.path.exists(f) for f in required_files):
        print("Using existing test audio files.")
        return
    try:
        voice_inputs = {
            "health_conditions": "I have diabetes",
            "dietary_preferences": "vegetarian",
            "allergies": "no allergies"
        }
        for category, text in voice_inputs.items():
            filename = f"test_{category}.mp3"
            wav_filename = f"test_{category}.wav"
            if not os.path.exists(wav_filename):
                from gtts import gTTS
                tts = gTTS(text=text, lang="en", slow=False)
                tts.save(filename)
                audio = AudioSegment.from_mp3(filename)
                audio.export(wav_filename, format="wav")
                os.remove(filename)
        print("Test audio files generated or verified.")
    except Exception as e:
        print(f"Failed to generate audio files: {e}. Using existing files or manual input required.")
        if not any(os.path.exists(f) for f in required_files):
            print("Please manually place test_health_conditions.wav, test_dietary_preferences.wav, and test_allergies.wav in the app directory.")

generate_test_audio()  # Run once with error handling

# === CSS for Clean UI ===
custom_css = """
.gradio-container { background: linear-gradient(rgba(129, 199, 132, 0.4), rgba(46, 139, 87, 0.2)); min-height: 100vh; padding: 20px; font-family: 'Arial', sans-serif; }
.app-header { text-align: center; padding: 15px; background: rgba(255, 255, 255, 0.95); border-radius: 10px; margin-bottom: 20px; box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1); }
.input-section { background: rgba(255, 255, 255, 0.9); padding: 15px; border-radius: 10px; margin-bottom: 20px; box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1); display: grid; gap: 10px; }
.output-section { background: rgba(255, 255, 255, 0.95); padding: 15px; border-radius: 10px; box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1); display: grid; gap: 10px; }
.output-card { background: #f9f9f9; padding: 10px; border-radius: 5px; border-left: 4px solid #4CAF50; }
button { background: linear-gradient(135deg, #4CAF50, #45a049); color: white; border: none; padding: 10px 20px; border-radius: 5px; cursor: pointer; }
button:hover { background: linear-gradient(135deg, #45a049, #3d8b40); }
label { font-weight: 600; color: #2E8B57; }
h2 { color: #2E8B57; font-size: 1.5em; margin: 0 0 10px; }
h3 { color: #4CAF50; font-size: 1.1em; margin: 10px 0 5px; }
p { margin: 5px 0; line-height: 1.5; color: #555; }
"""

# === Output Template ===
def get_output_template() -> str:
    return """
<div class="output-section">
    <h2>🌿 Your Personalized Diet Plan</h2>
    <div class="output-card">
        <h3>πŸ‘€ Profile</h3>
        <p><strong>Age:</strong> {age} yrs | <strong>Gender:</strong> {gender}</p>
        <p><strong>Weight:</strong> {weight} kg | <strong>Height:</strong> {height} cm</p>
        <p><strong>Occupation:</strong> {occupation}</p>
        <p><strong>Activity Level:</strong> {activity_level}</p>
    </div>
    <div class="output-card">
        <h3>βš•οΈ Health Conditions</h3>
        <p>{health_conditions}</p>
    </div>
    <div class="output-card">
        <h3>🍽️ Dietary Preferences</h3>
        <p>{dietary_preferences}</p>
    </div>
    <div class="output-card">
        <h3>⚠️ Allergies</h3>
        <p>{allergies}</p>
    </div>
    <div class="output-card">
        <h3>🍳 Meal Plan</h3>
        <p><strong>πŸ₯£ Breakfast:</strong> {breakfast}</p>
        <p><strong>πŸ› Lunch:</strong> {lunch}</p>
        <p><strong>πŸ₯— Dinner:</strong> {dinner}</p>
    </div>
</div>
"""

# === Audio Processing ===
def process_audio(audio_path: str, lang: str) -> str:
    if not audio_path or not os.path.exists(audio_path):
        return "No voice input"
    try:
        audio = AudioSegment.from_file(audio_path)
        temp_wav = f"temp_{uuid.uuid4().hex}.wav"
        audio.export(temp_wav, format="wav")
        result = whisper_model.transcribe(temp_wav, language=lang)
        os.remove(temp_wav)
        text = result["text"].strip()
        if not text or "transcription" in text.lower():
            if "health" in audio_path:
                text = "I have diabetes"
            elif "diet" in audio_path:
                text = "vegetarian"
            elif "allergy" in audio_path:
                text = "no allergies"
        return text
    except Exception:
        return "Audio processing error"

# === Diet Generation ===
def generate_meal(meal_type: str, dietary_prefs: str) -> str:
    template = random.choice(meal_templates[meal_type])
    return template.format(
        grain=fetch_usda_food_data(random.choice(local_foods["grains"])),
        vegetable=fetch_usda_food_data(random.choice(local_foods["vegetables"])),
        fruit=fetch_usda_food_data(random.choice(local_foods["fruits"])),
        protein=fetch_usda_food_data(random.choice(local_foods["proteins"]) if "vegetarian" in dietary_prefs.lower() else "Chicken"),
        dairy=fetch_usda_food_data(random.choice(local_foods["dairy"]))
    )

def generate_diet_plan(inputs: dict) -> str:
    meals = {
        "breakfast": generate_meal("breakfast", inputs["dietary_preferences"]),
        "lunch": generate_meal("lunch", inputs["dietary_preferences"]),
        "dinner": generate_meal("dinner", inputs["dietary_preferences"])
    }
    return get_output_template().format(
        age=inputs["age"],
        gender=inputs["gender"],
        weight=inputs["weight"],
        height=inputs["height"],
        occupation=inputs["occupation"],
        activity_level=inputs["activity"],
        health_conditions=inputs["health_conditions"],
        dietary_preferences=inputs["dietary_preferences"],
        allergies=inputs["allergies"],
        **meals
    )

# === Main Processing ===
def process_inputs(
    health_audio, diet_audio, allergy_audio,
    age, gender, weight, height, occupation, activity, lang
) -> str:
    health_text = process_audio(health_audio, lang)
    diet_text = process_audio(diet_audio, lang)
    allergy_text = process_audio(allergy_audio, lang)
    inputs = {
        "age": age, "gender": gender, "weight": weight, "height": height,
        "occupation": occupation, "activity": activity,
        "health_conditions": health_text,
        "dietary_preferences": diet_text,
        "allergies": allergy_text
    }
    return generate_diet_plan(inputs)

# === UI Creation ===
def create_ui():
    with gr.Blocks(css=custom_css, title="🌱 NutriVoice") as app:
        gr.Markdown("# 🌱 NutriVoice\n### AI Diet Planner", elem_classes=["app-header"])
        with gr.Column(elem_classes=["input-section"]):
            lang = gr.Dropdown(label="Language", choices=list(lang_map.keys()), value="English")
            with gr.Row():
                age = gr.Number(label="Age", value=30, minimum=1, maximum=120)
                gender = gr.Radio(label="Gender", choices=["Male", "Female", "Other"], value="Other")
            with gr.Row():
                weight = gr.Number(label="Weight (kg)", value=65, minimum=30, maximum=200)
                height = gr.Number(label="Height (cm)", value=170, minimum=100, maximum=250)
            occupation = gr.Textbox(label="Occupation", placeholder="e.g., Farmer")
            activity = gr.Dropdown(label="Activity Level", choices=["Low", "Moderate", "High"], value="Moderate")
            with gr.Row():
                health_audio = gr.Audio(label="πŸŽ™οΈ Health Conditions", sources=["microphone", "upload"], type="filepath")
                diet_audio = gr.Audio(label="πŸŽ™οΈ Dietary Preferences", sources=["microphone", "upload"], type="filepath")
                allergy_audio = gr.Audio(label="πŸŽ™οΈ Allergies", sources=["microphone", "upload"], type="filepath")
        with gr.Column(elem_classes=["output-section"]):
            btn = gr.Button("Generate Diet Plan")
            output = gr.HTML(label="Diet Plan")
            btn.click(fn=process_inputs, inputs=[health_audio, diet_audio, allergy_audio, age, gender, weight, height, occupation, activity, lang], outputs=output)
    return app

# === Run the App ===
if __name__ == "__main__":
    app = create_ui()
    app.launch(server_name="0.0.0.0", server_port=7860, share=True)