Spaces:
Running
Running
first commit
Browse files- app.py +244 -0
- creds.json +12 -0
- requirements.txt +7 -0
app.py
ADDED
@@ -0,0 +1,244 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
import pandas as pd
|
3 |
+
import numpy as np
|
4 |
+
import gspread
|
5 |
+
import random
|
6 |
+
import requests
|
7 |
+
import altair as alt
|
8 |
+
from PIL import Image
|
9 |
+
|
10 |
+
|
11 |
+
logo_url = "https://www.gradina-slavu.ro/"
|
12 |
+
logo_path = "https://static.wixstatic.com/media/268e9b_fb1da1f5fc304d15beee1d2e581d5d0c~mv2.png/v1/fill/w_134,h_62,al_c,q_85,usm_0.66_1.00_0.01,enc_auto/nume.png"
|
13 |
+
|
14 |
+
# Display the logo with a black background and centered alignment
|
15 |
+
st.markdown(
|
16 |
+
f"""
|
17 |
+
<div style='background-color: rgba(128,128,128,0.4); text-align: center;'>
|
18 |
+
<a href='{logo_url}' target='_blank'>
|
19 |
+
<img src='{logo_path}' width='150'>
|
20 |
+
</a>
|
21 |
+
</div>
|
22 |
+
""",
|
23 |
+
unsafe_allow_html=True,
|
24 |
+
)
|
25 |
+
|
26 |
+
st.title('RETETE - Selecteaza produs din sidebar')
|
27 |
+
st.write(' ')
|
28 |
+
|
29 |
+
sa= gspread.service_account(filename='creds.json')
|
30 |
+
# Open the Google Sheets file
|
31 |
+
sh = sa.open_by_url("https://docs.google.com/spreadsheets/d/1GZjbDLYTgRhKHtvgT868ursHYzXXmHF23w86-3KNZN0/edit#gid=489241774")
|
32 |
+
|
33 |
+
worksheet=sh.get_worksheet(0)
|
34 |
+
worksheet1=sh.get_worksheet(1)
|
35 |
+
worksheet2=sh.get_worksheet(2)
|
36 |
+
|
37 |
+
rows = worksheet.get_all_values()
|
38 |
+
rows1 = worksheet1.get_all_values()
|
39 |
+
rows2 = worksheet2.get_all_values()
|
40 |
+
df=pd.DataFrame(rows)
|
41 |
+
df1=pd.DataFrame(rows1)
|
42 |
+
df2=pd.DataFrame(rows2)
|
43 |
+
df2=df2.iloc[1:]
|
44 |
+
|
45 |
+
|
46 |
+
options=['stevie','rosii cherry','rosii tocate','suc de rosii','rosii cherry uscate','pesto de busuioc','carne vegetala','pate de ciuperci','zacusca de vinete','zacusca de peste','zacusca de fasole','magiun de prune','hrean in otet','legume uscate']
|
47 |
+
valori_optime=['2000','280','25','50','70','20','1600','17000','50','5000','75','5','10','80','1.4','1.6','18','2','400','6','6','550','12','1000','15','350','1000','3500','2400','15','2','5','35','3.5','300','2']
|
48 |
+
selected_option = st.sidebar.selectbox("Selecteaza un produs", options)
|
49 |
+
|
50 |
+
|
51 |
+
def select_random_value(df, selected_option):
|
52 |
+
# Select a random item from the list
|
53 |
+
selected_item = selected_option
|
54 |
+
|
55 |
+
# Find the corresponding values in the dataframe
|
56 |
+
matching_rows = df[df[3] == selected_item]
|
57 |
+
corresponding_values = matching_rows[0].tolist()
|
58 |
+
|
59 |
+
# Select a random value from the corresponding values
|
60 |
+
if corresponding_values:
|
61 |
+
selected_value = random.choice(corresponding_values)
|
62 |
+
return selected_value
|
63 |
+
else:
|
64 |
+
return None
|
65 |
+
|
66 |
+
reteta_selectata=select_random_value(df1,selected_option)
|
67 |
+
|
68 |
+
d2 = df2.set_index(0).to_dict('index')
|
69 |
+
d2 = {k: {k2: float(v2) for k2, v2 in v.items()} for k, v in d2.items()}
|
70 |
+
|
71 |
+
discarded_keys=['Zaharuri','Grasimi saturate','Omega-6','Vit A','Vit C','Vit D','Vit E','Vit K','Pantothenic acid-B5','Choline-B4','Betaine','Sodiu','Cupru','Mangan','Seleniu','Fluor','Cholesterol','Phytosterols']
|
72 |
+
|
73 |
+
def select_optimized_keys(d, value, optimals, discarded_keys):
|
74 |
+
optimals = [float(x) for x in optimals]
|
75 |
+
|
76 |
+
# Normalize the values for all keys except discarded ones
|
77 |
+
normalized = {}
|
78 |
+
for k, v in d.items():
|
79 |
+
if k not in discarded_keys:
|
80 |
+
values = [v2 for k2, v2 in v.items() if k2 not in discarded_keys]
|
81 |
+
min_value, max_value = min(values), max(values)
|
82 |
+
normalized[k] = {k2: (v2 - min_value) / (max_value - min_value) for k2, v2 in v.items() if k2 not in discarded_keys}
|
83 |
+
|
84 |
+
matching_key = [k for k, v in d.items() if k == value][0]
|
85 |
+
other_keys = [k for k in d.keys() if k != matching_key]
|
86 |
+
|
87 |
+
# Choose a random second key
|
88 |
+
second_key = random.choice(other_keys)
|
89 |
+
|
90 |
+
# Pick the 6 parameters with bigger gaps for optimizing the third key
|
91 |
+
gaps = {}
|
92 |
+
for k, v in normalized.items():
|
93 |
+
if k != matching_key and k != second_key:
|
94 |
+
gaps[k] = max(v.values()) - min(v.values())
|
95 |
+
sorted_gaps = sorted(gaps.items(), key=lambda x: x[1], reverse=True)
|
96 |
+
third_keys = [x[0] for x in sorted_gaps][:8]
|
97 |
+
|
98 |
+
def difference(key):
|
99 |
+
return sum(np.abs(sum([normalized[k][j] for k in [second_key, key]]) * 4 - optimals[i]) for i, j in enumerate(normalized[matching_key].keys()))
|
100 |
+
|
101 |
+
# Optimize the third key with the 6 parameters with bigger gaps
|
102 |
+
optimized_key = min(third_keys, key=difference)
|
103 |
+
|
104 |
+
return [second_key, optimized_key, matching_key]
|
105 |
+
selected_keys=select_optimized_keys(d2, reteta_selectata, valori_optime,discarded_keys)
|
106 |
+
selected_keys=list(selected_keys)
|
107 |
+
selected_keys.reverse()
|
108 |
+
|
109 |
+
|
110 |
+
def search_values(lst, dct):
|
111 |
+
result = {}
|
112 |
+
for item in lst:
|
113 |
+
for key, value in dct.items():
|
114 |
+
if item == key:
|
115 |
+
result[item] = value
|
116 |
+
return result
|
117 |
+
|
118 |
+
# create a dictionary to hold the new dataframes
|
119 |
+
dfs = {}
|
120 |
+
|
121 |
+
# loop through the categories and create a new dataframe for each one
|
122 |
+
for reteta in selected_keys:
|
123 |
+
dfs[reteta] = df1[df1[0] == reteta]
|
124 |
+
|
125 |
+
dfs2={}
|
126 |
+
for reteta in selected_keys:
|
127 |
+
dfs2[reteta] = df1[df1[0] == reteta].iloc[:,6:]
|
128 |
+
|
129 |
+
|
130 |
+
def make_clickable(url):
|
131 |
+
return f'<a href="{url}" target="_blank">{url}</a>'
|
132 |
+
|
133 |
+
for i in range(len(selected_keys)):
|
134 |
+
dfs1 = dfs[selected_keys[i]]
|
135 |
+
dfs1 = pd.DataFrame(dfs1)
|
136 |
+
images_displayed = False
|
137 |
+
# update 'path' column with formatted URLs
|
138 |
+
dfs1[6] = dfs1[6].apply(make_clickable)
|
139 |
+
for index, row in dfs1.iterrows():
|
140 |
+
image_path = row[6]
|
141 |
+
if not image_path:
|
142 |
+
continue
|
143 |
+
# extract URL from formatted string
|
144 |
+
url = image_path.split('"')[1]
|
145 |
+
if not url.startswith('http'):
|
146 |
+
continue
|
147 |
+
try:
|
148 |
+
image = Image.open(requests.get(url, stream=True).raw)
|
149 |
+
image = image.resize((300,300))
|
150 |
+
with st.container():
|
151 |
+
col1, col2, col3 = st.columns([1, 2, 1])
|
152 |
+
col2.image(image, caption=selected_keys[i], width=300, use_column_width=True)
|
153 |
+
images_displayed = True
|
154 |
+
except:
|
155 |
+
continue
|
156 |
+
if images_displayed:
|
157 |
+
dfs1 = dfs1.iloc[:,3:6]
|
158 |
+
dfs1 = dfs1.reset_index(drop=True)
|
159 |
+
indexcol = ['Ingrediente', 'Gramaj', 'Preparare']
|
160 |
+
dfs1.columns = indexcol
|
161 |
+
st.subheader(selected_keys[i])
|
162 |
+
st.write(dfs1,index=False)
|
163 |
+
|
164 |
+
else:
|
165 |
+
st.warning(f"No image found for {selected_keys[i]}")
|
166 |
+
|
167 |
+
|
168 |
+
|
169 |
+
|
170 |
+
final_result=search_values(selected_keys,d2)
|
171 |
+
|
172 |
+
Finaldf=pd.DataFrame(final_result)
|
173 |
+
Finaldf=Finaldf.round(0)
|
174 |
+
new_index=['Calorii','Carbohidrati','Fibre dietetice','Zaharuri','Grasimi','Grasimi saturate','Omega-3','Omega-6','Proteina','Vit A','Vit C','Vit D','Vit E','Vit K','Thiamin-B1','Riboflavin-B2','Niacin-B3','Vitamina-B6','Folate-B9','Vitamina-B12','Pantothenic acid-B5','Choline-B4','Betaine','Calciu','Fier','Magneziu','Fosfor','Potasiu','Sodiu','Zinc','Cupru','Mangan','Seleniu','Fluor','Cholesterol','Phytosterols']
|
175 |
+
Finaldf = Finaldf.set_index(pd.Index(new_index))
|
176 |
+
|
177 |
+
def add_dict_values(d):
|
178 |
+
result = {}
|
179 |
+
keys = set().union(*(d[key].keys() for key in d))
|
180 |
+
for key in keys:
|
181 |
+
result[key] = sum(d[k].get(key, 0) for k in d)
|
182 |
+
return result
|
183 |
+
|
184 |
+
valori_meniu_d=add_dict_values(final_result)
|
185 |
+
valori_meniu=list(valori_meniu_d.values())
|
186 |
+
|
187 |
+
def subtract_lists(list1, list2):
|
188 |
+
result = [max(0,float(i) - float(j)*3.5) for i, j in zip(list1, list2)]
|
189 |
+
return result
|
190 |
+
|
191 |
+
Valori_ramase=subtract_lists(valori_optime,valori_meniu)
|
192 |
+
|
193 |
+
Finaldf['valori ramase dupa portii de 350g']=Valori_ramase
|
194 |
+
Finaldf['Valori mediu portii de 350g']=valori_meniu
|
195 |
+
Finaldf['Valori mediu portii de 350g']=Finaldf['Valori mediu portii de 350g'].apply(lambda x: float(x) * 3.5)
|
196 |
+
Finaldf['Valori Optime']=valori_optime
|
197 |
+
st.write(Finaldf)
|
198 |
+
|
199 |
+
Valori_normate = [(float(valori_optime[i]) - Valori_ramase[i]) / float(valori_optime[i]) for i in range(len(valori_optime))]
|
200 |
+
Valori_ramase_normate=[ Valori_ramase[i] / float(valori_optime[i]) for i in range(len(valori_optime))]
|
201 |
+
chartdf = pd.DataFrame({
|
202 |
+
'Procente ramase de consumat':pd.Categorical(new_index, categories=new_index),
|
203 |
+
'max_values':Valori_normate,
|
204 |
+
'Procente': Valori_ramase_normate,
|
205 |
+
})
|
206 |
+
|
207 |
+
# Create a Categorical datatype for the x-axis
|
208 |
+
x_scale = alt.Scale(domain=new_index, zero=False)
|
209 |
+
# Define the chart using Altair
|
210 |
+
chart = alt.Chart(chartdf).mark_bar().encode(
|
211 |
+
x=alt.X('Procente ramase de consumat:O', scale=x_scale),
|
212 |
+
y='Procente',
|
213 |
+
color=alt.condition(
|
214 |
+
alt.datum.results > alt.datum.max_values,
|
215 |
+
alt.value('red'), # Set the color to red if the result is greater than the max value
|
216 |
+
alt.value('blue'), # Set the color to blue if the result is less than or equal to the max value
|
217 |
+
)
|
218 |
+
).properties(
|
219 |
+
width=alt.Step(40), # Set the width of each bar
|
220 |
+
)
|
221 |
+
|
222 |
+
# Display the chart in Streamlit
|
223 |
+
st.altair_chart(chart, use_container_width=True)
|
224 |
+
|
225 |
+
#sidebar for categories
|
226 |
+
|
227 |
+
# Define columns to display and exclude
|
228 |
+
all_columns = new_index
|
229 |
+
df.iloc[0, 2:] = new_index
|
230 |
+
# set the column names to the values in the first row
|
231 |
+
df = df.set_axis(df.iloc[0], axis=1)
|
232 |
+
|
233 |
+
# remove the first row from the dataframe
|
234 |
+
df = df.iloc[1:]
|
235 |
+
# Define sidebar widgets
|
236 |
+
sort_col = st.sidebar.selectbox('Selecteaza o categorie', all_columns)
|
237 |
+
st.sidebar.write("Optiunea suplimentare, !!!va genera noi retete cu fiecare selectie!!!, foloseste-o separat de optiunea principala pentru a gasi cele mai bogate ingrediente intr-o anumita categorie! (/100g)")
|
238 |
+
|
239 |
+
# Filter and sort dataframe
|
240 |
+
df[sort_col] = df[sort_col].astype(float)
|
241 |
+
display_df = df[[df.columns[0], sort_col]].sort_values(by=sort_col, ascending=False)
|
242 |
+
|
243 |
+
# Display dataframe
|
244 |
+
st.write(display_df)
|
creds.json
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"type": "service_account",
|
3 |
+
"project_id": "informatii-nutritive",
|
4 |
+
"private_key_id": "8eccc68e0f1978bd8bd9521fd60bda28c012dd48",
|
5 |
+
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDFeVWwJ7vcrFCG\neqzmPLLejh+Voe/bF51YZuITNY33oHBR3bOPlNmqvK+lpR0fo6eslK1NonpiFnjN\nXZpS2XuFWoxTYMBEfgwkeKdwg+Ic/3vGYPd0EsHsnV+c0BJ2JKQiCmlA4F6+hnue\nr+rbb97hEI+8+cA+CRbI8cZqp/fXWFT3uSNpLsdMbjMJ04T5GSgtaUVfxKL701/f\nEpmPPmr9FIqGmr69EoVek8NQyhv9yqJ+YYPcJ5vuyiyRAbeN5l/mWkmCgg3OUbFZ\n8xAPy6qfUEbE6AKKkg0Lude8ZJ15emRyoFJ7GapDAcSlCg3gp/D0KEft48IMqfRD\ntzRtXjNRAgMBAAECggEAAqakHw3oWsg9Hn/lzwNwTUZnAySU4TjaJdG7LxHU6GtQ\n4h4tS45cNNSheibcN5W7Dm/5X1M3szquJFVYRsyXnWhR2O6xyFmaQtddPBGoBsah\nPBmY/6xn7sK7ge1Vbq8Dqw4s3flOQN6pKOSx0YB0mkpwsmWIxTWRT/NoqbJOjadr\ngObkEjl6gVuVXLj+lkNjW57/WaLy/pc2zNm0fmKhjcQfFmb/v/Gvfv/1AYnh9Tj/\n4tJ1qdPhVm+k05Re0FK1VI2goqQxiOvXQyow/mO4z4XpuKBfzNQAAGuhZhAGW2T6\niBibN7niNrYoW1F78nP5w16zi62RSjvXYTu9///K5wKBgQDxmQ6/dkbK6zqtffc4\nmKA1o2wFY8WzKnCxQjt9fMdFrgtZHObWxYWAVBBklrUpZnl8KmpM++fCA7z1mBCn\n+0VBZjcK7Pau85ilSTYsPN7zsn+5ByU8nhDmzM2GE1Nvry9QZhiVXmPAJf6ccdj0\n/dDqIblIAnJw19L4S6hYPzPXfwKBgQDRPurJt3guxFEDTlwoAzMo/bzJFH8d7Teg\nxbivbVw/cZc73Eg4VejXXZoAne5E4xE6KqEMBLE+Zrk2HhvoUTxP9BBb6mo1uwAo\nuapIkAUJSML5Cf2X3o3V9V7knIkgqEE/ggZzytp4hTMmG5XoKkxKf75GVJQD96zM\n+YowPq/dLwKBgQC7P6+IgTk5CLu/v4Oen9x79h9AxiVySZByMi09gzpHLS+qb5YS\nG6Du5ziVXaEnZz79FzqwVAU5//+yMZunNj5lA73hf+tpiW9jm6ya+MqsAkqvs3QO\n2UusNkuMgOoIA7UeESM4kaGqSUhfJHXM2/PRozWhNBRSlVP0EV92lfWuLQKBgBzl\n3pErOHrk+/dD7DBOeNNRb7Apl4CvkT4v8Fix9sqASo2m14aIPtb3g1xQc2aqIWfy\nGTnKsBvZHf0eXWZRSc4U++llz3NKNzRop97LnRZyKRMx0QcXYmlJMOjLwZh0BEbc\nEz4Mpu18H9tEdgiRRuYHDGU0Kxj9LK1GTVJqZelbAoGBALIKyKNWNcgBOiZblfnz\nOkOLh1fV6gjmBhraF71P6XbkBxb35JHzQx/KbgMm2/OBKdTJIjeZNMFH0MDP7mEr\neaq9M7cHOTnTG3YUx6MqSwwK5exKIhlgNyP1TmdOqfOQ51aKK6bNSY9+troOC3My\ncTFhPQEc1fG2fD6sen57BRl5\n-----END PRIVATE KEY-----\n",
|
6 |
+
"client_email": "accesinformatiinutritive@informatii-nutritive.iam.gserviceaccount.com",
|
7 |
+
"client_id": "101057535754295785164",
|
8 |
+
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
|
9 |
+
"token_uri": "https://oauth2.googleapis.com/token",
|
10 |
+
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
|
11 |
+
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/accesinformatiinutritive%40informatii-nutritive.iam.gserviceaccount.com"
|
12 |
+
}
|
requirements.txt
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
altair==4.2.2
|
2 |
+
gspread==5.7.2
|
3 |
+
numpy==1.24.2
|
4 |
+
pandas==1.3.5
|
5 |
+
Pillow==9.4.0
|
6 |
+
requests==2.22.0
|
7 |
+
streamlit==1.18.1
|