I’m developing a Python Streamlit application for a diet recommendation system, and I’m looking to enhance it by allowing users to choose their dietary preferences. Here’s a breakdown of what I’m trying to achieve:
Description: My Streamlit application currently generates diet recommendations based on user input such as age, weight, gender, etc. I want to expand its functionality by adding a new button that enables users to specify their dietary preferences, such as vegan, vegetarian, or normal (non-vegetarian).
Proposed Enhancement: Upon landing on the diet recommendation page, users should see a new button labeled “Dietary Preference” or similar. When clicked, this button should display options for vegan, vegetarian, or normal preferences.
Functionality: After selecting their dietary preference, users should click a “Submit” or “Apply” button to update the recommendations accordingly. The application should then generate food recommendations that align with the chosen preference. based on Name column or Keyword column thats contains word Vegan or Vegetarian
import streamlit as st
import pandas as pd
from Generate_Recommendations import Generator
from random import uniform as rnd
from ImageFinder import get_images_links as find_image
from streamlit_echarts import st_echarts
from streamlit_space import space
from PIL import Image
# Initialize session state for navigation
if "current_page" not in st.session_state:
st.session_state.current_page = "welcome" # Start with the welcome page
# Function to navigate between pages
def navigate_to(page):
st.session_state.current_page = page
# Render different content based on the current page
if st.session_state.current_page == "welcome":
st.set_page_config(page_title="Diet IQ Welcome Page", page_icon="????", layout="wide")
col1, col2, col3 = st.columns(([4, 5 ,4]))
# Welcome Page
with col2:
logo = Image.open("DietIQ.png")
st.image(logo, width=500, use_column_width=True) # Center image
st.markdown("<h1 style='font-size: 35px;text-align: center; color:#232e57;'>Welcome to Diet Recommendation System</h1>", unsafe_allow_html=True)
space(lines=1)
st.markdown("<h1 style='font-size: 20px;text-align: center; color:#232e57;'>Click the button below to go to the main page.</h1>", unsafe_allow_html=True)
col4, col5, col6 = st.columns(([8, 5 ,4]))
with col5:
# Button to "navigate" to the Diet Recommendation page
st.markdown("""
<style>
.stButton>button {
background-color: #283564; /* Change color to desired color */
color: white; /* Text color */
border-radius: 30px; /* Rounded corners */
padding: 10px 20px; /* Padding */
font-size: 16px; /* Font size */
border: none; /* No border */
cursor: pointer; /* Cursor style */
}
.stButton>button:hover {
background-color: #3c3160; /* Change color on hover */
}
</style>
""", unsafe_allow_html=True)
if st.button("Click here"):
navigate_to("diet_recommendation")
elif st.session_state.current_page == "diet_recommendation":
# Diet Recommendation Page
st.set_page_config(page_title="Diet IQ Main Page", page_icon="????", layout="wide")
# Custom CSS to change the background color
bg_color = """
<style>
.stApp {
background: linear-gradient(to right, #a4b3d8, #92bbd1); /* Light blue to green gradient */
}
</style>
"""
# Insert custom CSS into Streamlit
st.markdown(bg_color, unsafe_allow_html=True)
nutritions_values=['Calories','FatContent','SaturatedFatContent','CholesterolContent','SodiumContent','CarbohydrateContent','FiberContent','SugarContent','ProteinContent']
# Streamlit states initialization
if 'person' not in st.session_state:
st.session_state.generated = False
st.session_state.recommendations=None
st.session_state.person=None
st.session_state.weight_loss_option=None
class Person:
def __init__(self,age,height,weight,gender,activity,meals_calories_perc,weight_loss):
self.age=age
self.height=height
self.weight=weight
self.gender=gender
self.activity=activity
self.meals_calories_perc=meals_calories_perc
self.weight_loss=weight_loss
def calculate_bmi(self,):
bmi=round(self.weight/((self.height/100)**2),2)
return bmi
def display_result(self,):
bmi=self.calculate_bmi()
bmi_string=f'{bmi} kg/m²'
if bmi<18.5:
category='Underweight'
color='blue'
elif 18.5<=bmi<25:
category='Normal'
color='yellow'
elif 25<=bmi<30:
category='Overweight'
color='pink'
else:
category='Obesity'
color='orange'
return bmi_string,category,color
def calculate_bmr(self):
if self.gender=='Male':
bmr=10*self.weight+6.25*self.height-5*self.age+5
else:
bmr=10*self.weight+6.25*self.height-5*self.age-161
return bmr
def calories_calculator(self):
activites=['Little/no exercise', 'Light exercise', 'Moderate exercise (3-5 days/wk)', 'Very active (6-7 days/wk)', 'Extra active (very active & physical job)']
weights=[1.2,1.375,1.55,1.725,1.9]
weight = weights[activites.index(self.activity)]
maintain_calories = self.calculate_bmr()*weight
return maintain_calories
def generate_recommendations(self,):
total_calories=self.weight_loss*self.calories_calculator()
recommendations=[]
for meal in self.meals_calories_perc:
meal_calories=self.meals_calories_perc[meal]*total_calories
if meal=='breakfast':
recommended_nutrition = [meal_calories,rnd(10,30),rnd(0,4),rnd(0,30),rnd(0,400),rnd(40,75),rnd(4,10),rnd(0,10),rnd(30,100)]
elif meal=='launch':
recommended_nutrition = [meal_calories,rnd(20,40),rnd(0,4),rnd(0,30),rnd(0,400),rnd(40,75),rnd(4,20),rnd(0,10),rnd(50,175)]
elif meal=='dinner':
recommended_nutrition = [meal_calories,rnd(20,40),rnd(0,4),rnd(0,30),rnd(0,400),rnd(40,75),rnd(4,20),rnd(0,10),rnd(50,175)]
else:
recommended_nutrition = [meal_calories,rnd(10,30),rnd(0,4),rnd(0,30),rnd(0,400),rnd(40,75),rnd(4,10),rnd(0,10),rnd(30,100)]
generator=Generator(recommended_nutrition)
recommended_recipes=generator.generate().json()['output']
recommendations.append(recommended_recipes)
for recommendation in recommendations:
for recipe in recommendation:
recipe['image_link']=find_image(recipe['Name'])
return recommendations
class Display:
def __init__(self):
self.plans=["Maintain weight","Mild weight loss","Weight loss","Extreme weight loss", "Mild weight gain" , " Weight gain", "Extreme weight gain"]
self.weights=[1,0.9,0.8,0.6,1.1,1.2,1.4]
self.losses=['-0 kg/week','-0.25 kg/week','-0.5 kg/week','-1 kg/week','+0.25 kg/week','+0.5 kg/week','+1 kg/week ']
pass
def display_bmi(self,person):
st.header('BMI CALCULATOR')
bmi_string,category,color = person.display_result()
space(lines= 0)
st.write("Body Mass Index (BMI)")
# st.markdown("<h6 style='font-size: 20px;text-align:center'>Body Mass Index (BMI)</h6>", unsafe_allow_html=True)
st.metric(label=" ", value=bmi_string)
space(lines=2)
# Create bold text with proper alignment, font size, and color
new_title = f'<p style="font-family:sans-serif; color:{color}; font-size: 35px; font-weight: bold;">{category}</p>'
# Display the styled bold text in Streamlit
st.markdown(new_title, unsafe_allow_html=True)
space(lines=4)
st.markdown(
"""
Healthy BMI range: 18.5 kg/m² - 25 kg/m².
""")
def display_calories(self,person):
st.header('CALORIES CALCULATOR')
maintain_calories=person.calories_calculator()
st.write('A guideline for how many calories to consume each day to maintain, lose, or gain weight at a chosen rate.')
st.markdown("<h1 style='font-size: 30px;'>Calories Plan </h1>", unsafe_allow_html=True)
for plan,weight,loss,col in zip(self.plans,self.weights,self.losses,st.columns(7)):
calories_per_day = round(maintain_calories * weight)
# Using newline to separate lines
metric_text = f"{plan}n: {calories_per_day} Calories/day"
# Displaying the metric with separate lines
st.metric(label="", value=metric_text, delta=loss, delta_color="inverse")
def display_recommendation(self,person,recommendations):
space(lines=4)
st.markdown("<h1 style='font-size: 30px;text-align: center;'>DIET RECOMMENDATOR </h1>", unsafe_allow_html=True)
with st.spinner('Generating recommendations...'):
meals=person.meals_calories_perc
space(lines=3)
st.markdown("<h1 style='font-size: 25px;text-align: center;'>Recommended recipes </h1>", unsafe_allow_html=True)
space(lines=3)
for meal_name,column,recommendation in zip(meals,st.columns(len(meals)),recommendations):
with column:
#st.markdown(f'<div style="text-align: center;">{meal_name.upper()}</div>', unsafe_allow_html=True)
st.markdown(f'##### {meal_name.upper()}')
for recipe in recommendation:
recipe_name=recipe['Name']
expander = st.expander(recipe_name)
recipe_link=recipe['image_link']
recipe_img=f'<div><center><img src={recipe_link} alt={recipe_name}></center></div>'
nutritions_df=pd.DataFrame({value:[recipe[value]] for value in nutritions_values})
expander.markdown(recipe_img,unsafe_allow_html=True)
expander.markdown(f'<h5 style="text-align: center;font-family:sans-serif;">Nutritional Values (g):</h5>', unsafe_allow_html=True)
expander.dataframe(nutritions_df)
expander.markdown(f'<h5 style="text-align: center;font-family:sans-serif;">Ingredients:</h5>', unsafe_allow_html=True)
for ingredient in recipe['RecipeIngredientParts']:
expander.markdown(f"""
- {ingredient}
""")
expander.markdown(f'<h5 style="text-align: center;font-family:sans-serif;">Recipe Instructions:</h5>', unsafe_allow_html=True)
for instruction in recipe['RecipeInstructions']:
expander.markdown(f"""
- {instruction}
""")
expander.markdown(f'<h5 style="text-align: center;font-family:sans-serif;">Cooking and Preparation Time:</h5>', unsafe_allow_html=True)
expander.markdown(f"""
- Cook Time : {recipe['CookTime']}min
- Preparation Time: {recipe['PrepTime']}min
- Total Time : {recipe['TotalTime']}min
""")
def display_meal_choices(self,person,recommendations):
st.subheader('Choose your meal composition:')
# Display meal compositions choices
if len(recommendations)==3:
breakfast_column,launch_column,dinner_column=st.columns(3)
with breakfast_column:
breakfast_choice=st.selectbox(f'Choose your breakfast:',[recipe['Name'] for recipe in recommendations[0]])
with launch_column:
launch_choice=st.selectbox(f'Choose your launch:',[recipe['Name'] for recipe in recommendations[1]])
with dinner_column:
dinner_choice = st.selectbox(f'Choose your dinner:', [recipe['Name'] for recipe in recommendations[2]])
choices = [breakfast_choice, launch_choice, dinner_choice]
elif len(recommendations) == 4:
breakfast_column, morning_snack, launch_column, dinner_column = st.columns(4)
with breakfast_column:
breakfast_choice = st.selectbox(f'Choose your breakfast:', [recipe['Name'] for recipe in recommendations[0]])
with morning_snack:
morning_snack = st.selectbox(f'Choose your morning_snack:', [recipe['Name'] for recipe in recommendations[1]])
with launch_column:
launch_choice = st.selectbox(f'Choose your launch:', [recipe['Name'] for recipe in recommendations[2]])
with dinner_column:
dinner_choice = st.selectbox(f'Choose your dinner:', [recipe['Name'] for recipe in recommendations[3]])
choices = [breakfast_choice, morning_snack, launch_choice, dinner_choice]
else:
breakfast_column, morning_snack, launch_column, afternoon_snack, dinner_column = st.columns(5)
with breakfast_column:
breakfast_choice = st.selectbox(f'Choose your breakfast:', [recipe['Name'] for recipe in recommendations[0]])
with morning_snack:
morning_snack = st.selectbox(f'Choose your morning_snack:', [recipe['Name'] for recipe in recommendations[1]])
with launch_column:
launch_choice = st.selectbox(f'Choose your launch:', [recipe['Name'] for recipe in recommendations[2]])
with afternoon_snack:
afternoon_snack = st.selectbox(f'Choose your afternoon:', [recipe['Name'] for recipe in recommendations[3]])
with dinner_column:
dinner_choice = st.selectbox(f'Choose your dinner:', [recipe['Name'] for recipe in recommendations[4]])
choices = [breakfast_choice, morning_snack, launch_choice, afternoon_snack, dinner_choice]
# Calculating the sum of nutritional values of the choosen recipes
total_nutrition_values = {nutrition_value: 0 for nutrition_value in nutritions_values}
for choice, meals_ in zip(choices, recommendations):
for meal in meals_:
if meal['Name'] == choice:
for nutrition_value in nutritions_values:
total_nutrition_values[nutrition_value] += meal[nutrition_value]
total_calories_chose = total_nutrition_values['Calories']
loss_calories_chose = round(person.calories_calculator() * person.weight_loss)
# Display the numerical comparison
with st.container():
st.markdown(f'<h5 style="text-align: center;font-family:sans-serif;">Total Calories in Recipes vs {st.session_state.weight_loss_option} Calories:</h5>', unsafe_allow_html=True)
st.markdown(f'<h2 style="text-align: center;font-family:sans-serif;font-size: 25px;color:black;">Total Calories in Recipes: {total_calories_chose}</h2>', unsafe_allow_html=True)
st.markdown(f'<h2 style="text-align: center;font-family:sans-serif;font-size: 25px;color:black;">{st.session_state.weight_loss_option} Calories : {loss_calories_chose}</h2>', unsafe_allow_html=True)
# Display corresponding graphs
#st.markdown(f'<h5 style="text-align: center;font-family:sans-serif;">Total Calories in Recipes vs {st.session_state.weight_loss_option} Calories:</h5>', unsafe_allow_html=True)
#total_calories_graph_options = {
#"xAxis": {
#"type": "category",
#"data": ['Total Calories you chose', f"{st.session_state.weight_loss_option} Calories"],
#},
#"yAxis": {"type": "value"},
#"series": [
#{
#"data": [
#{"value":total_calories_chose, "itemStyle": {"color":["#33FF8D","#FF3333"][total_calories_chose>loss_calories_chose]}},
#{"value": loss_calories_chose, "itemStyle": {"color": "#3339FF"}},
#],
#"type": "bar",
#}
#],
#}
#st_echarts(options=total_calories_graph_options,height="400px",)
display=Display()
# Apply padding to the title
title = """
<h1 style='text-align: center; padding-top: 5px; padding-bottom: 70;'>
Diet Recommendation Main Page
</h1>
"""
st.markdown(title, unsafe_allow_html=True)
# Other content followsv# Centered welcome message using custom HTML
welcome_message = """
<p style='text-align: center;font-size: 20px;'>
Please select following options to Generate Recommendations
</p>
"""
st.markdown(welcome_message, unsafe_allow_html=True)
with st.form("recommendation_form"):
# Create a two-column layout for age and height
col1, col2 ,col3= st.columns(([3, 2.5, 4]))
# Display inputs within each column
with col1:
space(lines=1)
st.markdown("<h1 style='font-size: 20px;'>Age</h1>", unsafe_allow_html=True)
age = st.number_input('', min_value=2, max_value=120, step=1)
space(lines=1)
st.markdown("<h1 style='font-size: 20px;'>Height(cm)</h1>", unsafe_allow_html=True)
height = st.number_input('', min_value=50, max_value=300, step=1) # Input field
space(lines=1)
st.markdown("<h1 style='font-size: 20px;'>Weight(kg)</h1>", unsafe_allow_html=True)
weight = st.number_input('', min_value=10, max_value=300, step=1)
space(lines=1)
st.markdown("<h1 style='font-size: 20px;'>Choose your weight loss/gain plan:</h1>", unsafe_allow_html=True)
option = st.selectbox('',display.plans)
st.session_state.weight_loss_option=option
weight_loss=display.weights[display.plans.index(option)]
with col2:
st.write("")
space(lines=40)
st.markdown("""
<style>
.stButton>button {
background-color: #283564; /* Change color to desired color */
color: white; /* Text color */
border-radius: 30px; /* Rounded corners */
padding: 10px 20px; /* Padding */
font-size: 16px; /* Font size */
border: none; /* No border */
cursor: pointer; /* Cursor style */
}
.stButton>button:hover {
background-color: #3c3160; /* Change color on hover */
}
</style>
""", unsafe_allow_html=True)
generated = st.form_submit_button("Click here to Generate Recommendations")
with col3:
space(lines=1)
st.markdown("<h1 style='font-size: 20px;'>Gender</h1>", unsafe_allow_html=True)
gender = st.radio('', ('Male', 'Female'))
space(lines=1)
st.markdown("<h1 style='font-size: 20px;'>Activity</h1>", unsafe_allow_html=True)
activity = st.radio('',options=['Little/no exercise', 'Light exercise', 'Moderate exercise (3-5 days/wk)', 'Very active (6-7 days/wk)',
'Extra active (very active & physical job)'])
space(lines=1)
st.markdown("<h1 style='font-size: 20px;'>Meals per day</h1>", unsafe_allow_html=True)
number_of_meals=st.slider('',min_value=3,max_value=5,step=1,value=3)
if number_of_meals==3:
meals_calories_perc={'breakfast':0.35,'lunch':0.40,'dinner':0.25}
elif number_of_meals==4:
meals_calories_perc={'breakfast':0.30,'morning snack':0.05,'lunch':0.40,'dinner':0.25}
else:
meals_calories_perc={'breakfast':0.30,'morning snack':0.05,'lunch':0.40,'afternoon snack':0.05,'dinner':0.20}
if generated:
st.session_state.generated=True
person = Person(age,height,weight,gender,activity,meals_calories_perc,weight_loss)
col1, col2,col3 = st.columns(([9, 2 ,4]))
with col1:
with st.container():
display.display_calories(person)
with col2:
st.write("")
with col3:
with st.container():
display.display_bmi(person)
space(lines=2)
with st.spinner('Generating recommendations...'):
recommendations=person.generate_recommendations()
st.session_state.recommendations=recommendations
st.session_state.person=person
if st.session_state.generated:
with st.container():
display.display_recommendation(st.session_state.person,st.session_state.recommendations)
st.success('Recommendation Generated Successfully !', icon="✅")
with st.container():
display.display_meal_choices(st.session_state.person,st.session_state.recommendations)
Specific Requests:
I need guidance on how to integrate the new button into my Streamlit application and capture the user’s preference selection.
Additionally, I’m seeking advice on how to modify the existing recommendation generation logic to consider the user’s chosen dietary preference.
Suggestions on how to structure the UI and user interaction flow for this new feature would be highly appreciated.
What I’ve Tried: So far, I’ve explored Streamlit’s documentation and experimented with adding new buttons using their components. However, I’m unsure about the best approach for integrating this specific functionality.
Omar is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.